主服务提供 OCR、图像向量(QA-CLIP)和人脸向量(InsightFace),并会把 /clip/txt 代理转发到独立部署的 Text-CLIP CPU 服务。仓库另外补充了一个仅面向本地 Windows 开发机的并行 Image-CLIP CUDA 子项目,目录为 image-clip/,不影响主服务 OpenVINO 实现。本文档仅保留最终用户部署、运行和配置说明。
- Python 3.12
- 已准备模型目录(至少包含):
models/qa-clip/openvino(主线 FP16 IR;主服务镜像只打包openvino_image_fp16.*,Text-CLIP 镜像只打包openvino_text_fp16.*)models/qa-clip/openvino_fp32(FP32 对照组 IR,仅供本地精度观测脚本使用;默认不会进入 Docker 构建上下文或任一运行时镜像)models/qa-clip/huggingface(仅scripts/convert.py和本地 Windows CUDAimage-clip/子项目需要;默认不会进入 Docker 构建上下文)models/insightface/models/antelopev2(主服务镜像需要;至少保留scrfd_10g_bnkps.onnx与glintr100.onnx)
- 主服务入口:
app/server.py - 主服务仍由
app/server.py暴露路由;同目录app/bootstrap.py、app/image_io.py、app/text_clip_proxy.py仅用于维护性拆分,不改变启动方式和接口语义 - Text-CLIP 服务入口:
text-clip/app/server.py - Windows 本地 CUDA Image-CLIP 子项目命令行入口:
image-clip/starter.py - Windows 本地 CUDA Image-CLIP 子项目服务实现入口:
image-clip/app/server.py - QA-CLIP 离线转换脚本:
scripts/convert.py scripts/convert.py默认只清理 OpenVINO 导出产物与 OpenVINO cache,不会重复删除 Hugging Face cache 或本地 QA-CLIP snapshot;脚本会优先复用models/qa-clip/huggingface本地 snapshot,其次复用cache/huggingface,仅在本地都缺失时才回源下载TencentARC/QA-CLIP-ViT-L-14scripts/convert.py会顺序导出两套 QA-CLIP OpenVINO IR:主线models/qa-clip/openvino/openvino_image_fp16.xml与models/qa-clip/openvino/openvino_text_fp16.xml固定使用 OpenVINO 原生ov.save_model(..., compress_to_fp16=True),同时生成 FP32 对照组models/qa-clip/openvino_fp32/openvino_image_fp32.xml与models/qa-clip/openvino_fp32/openvino_text_fp32.xml- 仓库不再保留单独的
scripts/convert_fp16.py;FP16 与 FP32 对照组统一由scripts/convert.py一次导出完成 scripts/indicate_precision_impact.py默认比较models/qa-clip/openvino_fp32与主线models/qa-clip/openvino,并输出models/qa-clip/openvino/precision_impact.json- 独立 Text-CLIP 服务已自带 tokenizer 与词表资源,不再依赖主服务
app/目录 requirements.txt当前固定insightface==0.7.3,并显式包含onnx与opencv-python-headless;其中onnx用于 InsightFace 首次懒加载时在受控 runtime copy 中修正glintr100.onnx的识别输出 batch 元数据,主服务镜像构建时还会先卸载依赖链带入的opencv-python/opencv-contrib-python*,再只回装opencv-python-headless
以下仅保留当前仍支持且建议用户配置的环境变量。兼容别名、已固定为内部基线的变量,以及 docker compose 插值变量不再写入 README。
| 环境变量 | 说明 | 默认值 |
|---|---|---|
API_AUTH_KEY |
API Key;为空或 no-key 时关闭鉴权 |
mt_photos_ai_extra |
CLIP_IMAGE_BATCH |
/clip/img 标准预处理后的微批上限 |
8 |
CLIP_IMAGE_BATCH_WAIT_MS |
/clip/img 微批等待窗口,单位毫秒 |
5 |
CLIP_INFERENCE_DEVICE |
/clip/img 使用的 OpenVINO 设备;请求 AUTO/GPU 时需要可用 GPU |
跟随 INFERENCE_DEVICE |
INFERENCE_DEVICE |
主容器默认 OpenVINO 设备 | AUTO |
INFERENCE_EXEC_TIMEOUT |
非文本任务执行超时,单位秒 | max(30, INFERENCE_TASK_TIMEOUT) |
INFERENCE_QUEUE_MAX_SIZE |
/clip/img、/ocr、/represent 共享图片名额上限;运行时硬上限仍为 10 |
10 |
INFERENCE_QUEUE_TIMEOUT |
非文本任务排队超时,单位秒 | 跟随 INFERENCE_TASK_TIMEOUT |
INFERENCE_TASK_TIMEOUT |
兼容旧配置的总超时基线;未单独设置时会派生排队/执行超时 | 10 |
INSIGHTFACE_BATCH_WAIT_MS |
/represent recognition 聚合窗口,单位毫秒 |
5 |
INSIGHTFACE_OV_DEVICE |
InsightFace OpenVINO EP 推理设备;支持 AUTO / CPU / GPU |
AUTO |
INSIGHTFACE_OV_ENABLE_OPENCL_THROTTLING |
是否启用 OpenCL throttling | false |
INSIGHTFACE_OV_NUM_THREADS |
InsightFace OpenVINO EP 线程数;-1 为运行时默认 |
-1 |
LOG_LEVEL |
日志级别;同时作用于 mt_photos_ai.* 与 uvicorn.* |
WARNING |
MODEL_PATH |
模型根目录 | <repo>/models |
NON_TEXT_IDLE_RELEASE_SECONDS |
主容器非文本模型空闲释放窗口;<=0 表示关闭 |
60 |
OCR_EXEC_TIMEOUT |
OCR 执行超时,单位秒 | max(30, INFERENCE_EXEC_TIMEOUT) |
OCR_MAX_CONCURRENT_REQUESTS |
OCR 应用层最大并发请求数;不会超过共享图片名额 | 4 |
OCR_PREWARM_DELAY_SECONDS |
RapidOCR 一次性后台预热延迟,单位秒 | 1.0 |
OCR_PREWARM_ENABLED |
是否启用一次性后台 RapidOCR 预热 | false |
OV_CACHE_DIR |
OpenVINO 编译缓存目录;当前主要作用于主服务自管的 OpenVINO 路径 | <repo>/cache/openvino |
PORT |
服务端口;同时影响镜像入口与健康检查 | 8060 |
TEXT_CLIP_API_KEY |
主服务转发 /clip/txt 时使用的上游 API Key;未设置时复用 API_AUTH_KEY |
跟随 API_AUTH_KEY |
TEXT_CLIP_REQUEST_TIMEOUT |
主服务转发 /clip/txt 的上游请求超时,单位秒 |
30 |
TEXT_CLIP_SERVER_URL |
独立 Text-CLIP 服务 URL;主服务会自动拼接 /clip/txt,并把请求体原样转发给上游 |
http://127.0.0.1:8061 |
| 环境变量 | 说明 | 默认值 |
|---|---|---|
API_AUTH_KEY |
API Key;为空或 no-key 时关闭鉴权 |
mt_photos_ai_extra |
LOG_LEVEL |
日志级别;同时作用于 mt_photos_ai.text_clip 与 uvicorn.* |
WARNING |
MODEL_PATH |
文本模型根目录 | <repo>/models |
OV_CACHE_DIR |
Text-CLIP OpenVINO 编译缓存目录 | <repo>/cache/openvino |
PORT |
Text-CLIP 服务端口;同时影响镜像入口与健康检查 | 8061 |
补充说明:
- 开发机本地验证时,主服务建议把主要后端统一设为
CPU:INFERENCE_DEVICE=CPU、CLIP_INFERENCE_DEVICE=CPU、INSIGHTFACE_OV_DEVICE=CPU;如需走主服务/clip/txt代理,请同时把TEXT_CLIP_SERVER_URL设为本机 Text-CLIP 服务地址。 - 主容器的 Vision-CLIP / OCR / InsightFace 统一由
/app内的非文本子进程管理,按请求懒加载,并可按NON_TEXT_IDLE_RELEASE_SECONDS自动释放;Text-CLIP 容器启动即加载文本模型,进程存活期间常驻内存,不参与空闲释放或主容器/restart*,主服务对/clip/txt只做原始请求体 HTTP 转发。 - 主服务
/clip/img的视觉 PPP 预处理固定为resize -> center crop -> BGR->RGB -> /255 -> mean/std -> NCHW;如果需要重建 QA-CLIP IR,请重新执行py -3.12 scripts/convert.py以保持与当前“主线 FP16 + FP32 对照组”导出基线一致。 - 如需评估主线 FP16 与 FP32 对照组之间的 embedding 保真度、结构差异和体积变化,直接执行
py -3.12 scripts/indicate_precision_impact.py即可。 - 主容器在
/restart、/restart_v2、/restartV2、/restartv2或空闲释放完成后,会直接结束当前非文本子进程;匿名内存由子进程退出统一回收,下一次/clip/img、/ocr、/represent请求再按需重建新的非文本子进程与对应模型。 - 当
/represent、/clip/img、/ocr混合到达时,主服务会继续保持“单活非文本模型族”串行切换,但等待旧模型族退场的过程不会再阻塞整个事件循环;父进程现在还会按到达顺序做 FIFO 轮换,避免某一族请求在高并发下插队或长期饥饿。 - 主服务会在非文本子进程退出后输出一条进程/cgroup 内存拆分日志,至少包含
VmRSS/RssAnon/RssFile/RssShmem与cgroup_anon/cgroup_file/cgroup_shmem,用于判断释放后剩余内存主要来自匿名内存、文件页缓存还是 shared memory。 - InsightFace 现在会以低残留 ORT session 基线加载:关闭 CPU memory arena、关闭 memory pattern,并固定单 lane session
inter_op/intra_op线程数为1。这会优先减少/represent卸载后的匿名内存残留,而不是追求极限吞吐。 /restart、/restart_v2、/restartV2、/restartv2现已统一语义:同步释放当前非文本子进程,不重启主服务进程。- OCR 已切到
rapidocr==3.8.0原生最简初始化:仅把Det/Cls/Rec.engine_type设为openvino,其它全部使用上游默认配置。 - RapidOCR 会在首次加载 OCR 时按上游内置 URL 检查并下载默认模型;镜像不再内置 OCR 模型,因此首次
/ocr在空缓存环境下需要联网,并且容器运行用户需要可写的rapidocr/models目录。 PORT会同时影响容器入口和健康检查;如果修改它,请同步调整docker-compose.yml的ports:或docker run -p。- 如果手动执行
uvicorn server:app,最早期的 uvicorn bootstrap 日志仍以 CLI--log-level为准。
- 确认 Python 版本:
py -3.12 -V- (可选)创建并激活虚拟环境:
py -3.12 -m venv .venv
.\.venv\Scripts\activate- (可选)设置环境变量:
$env:API_AUTH_KEY="your_secret_key"
$env:INFERENCE_DEVICE="CPU"
$env:CLIP_INFERENCE_DEVICE="CPU"
$env:CLIP_IMAGE_BATCH="8"
$env:INSIGHTFACE_OV_DEVICE="CPU"
$env:TEXT_CLIP_SERVER_URL="http://127.0.0.1:8061"
$env:LOG_LEVEL="INFO"- 如需使用
/clip/txt,先启动独立 Text-CLIP 服务:
cd text-clip\app
py -3.12 server.py- 如需使用主服务接口,再另开终端启动主服务:
cd app
py -3.12 server.py启动后,客户端可统一访问主服务端口;主服务收到 /clip/txt 请求时会按 TEXT_CLIP_SERVER_URL 把原始请求体转发到独立 Text-CLIP 服务。本地 Windows 验证和测试命令统一建议显式使用 py -3.12,避免落到其他解释器版本。
如需在本地 Windows 开发机上单独跑 CUDA 版 Image-CLIP,可改用 image-clip/ 子项目;该子项目使用独立依赖文件 image-clip/requirement.txt,可在仓库根目录直接执行 py -3.12 image-clip\starter.py,或进入 image-clip\ 后执行 py -3.12 starter.py。更完整的环境变量与冒烟说明见 image-clip/README.md。
如需手动执行 uvicorn server:app,请显式传入 --port / --log-level,例如 uvicorn server:app --host 0.0.0.0 --port 8060 --log-level info。
推荐宿主环境:
- Debian 13(或兼容发行版)
- Docker Engine 24+ 与 Docker Compose v2
- Intel iGPU 场景下,宿主机可见
/dev/dri
如需在 Debian 13 宿主或自定义镜像中补齐 Intel Xe 依赖,可参考:
apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
libdrm2 \
libglib2.0-0 \
libgomp1 \
libze1 \
ocl-icd-libopencl1 \
mesa-opencl-icd说明:
- 参考 OpenVINO 与 Intel GPU 官方文档,容器内 OpenVINO GPU 运行时需要
intel-opencl-icd+ Level Zero 运行库(Debian 包名libze-intel-gpu1),以及libze1/ocl-icd-libopencl1。 - 当前主 Dockerfile 使用两阶段构建:builder 阶段在
/home/appuser/.venv与/home/appuser/wheels内完成编译链安装、wheel 预构建、离线依赖安装,并沿用“先卸载所有 OpenCV Python 变体,再只回装opencv-python-headless”的清洁安装链;若传递依赖带入opencv-python/opencv-contrib-python*,会在 builder 内卸载并清理对应 wheel,runtime 阶段只复制瘦身后的.venv并加入PATH,不再重新执行pip install,也不再把 wheelhouse 带进最终镜像。 - builder 还会裁剪 venv 中运行时不需要的
pip/wheel包、include/share目录、__pycache__、测试目录和静态头文件,以进一步压缩最终镜像体积;当前服务依赖会直接随用户目录下的.venv一起进入最终镜像。 - 根目录
.dockerignore会先排除 Hugging Face snapshot、FP32 IR、InsightFace 运行时派生目录和本地开发目录;在 Docker Engine 24+ / BuildKit 下,Dockerfile.dockerignore与text-clip/DockerFile-TextCLIP.dockerignore还会进一步按镜像裁掉不相关的模型与源码,避免主镜像构建时混入文本 IR,或 Text-CLIP 镜像构建时混入视觉 IR / InsightFace 模型。 - 因此当前运行时镜像不再包含
libgl1、libsm6、libxext6、libxrender1,也不再打包mesa-vulkan-drivers、intel-media-va-driver-non-free。 - Intel GPU 固件属于宿主机职责;若宿主 Debian 13 需要补齐固件,请在宿主机安装
firmware-misc-nonfree(或兼容包名firmware-misc-non-free),而不是放进应用容器。 - Dockerfile 已固化为清华 APT + 清华 PyPI 镜像;APT 基础列表、sid pin 文件和安装包名单都直接写在仓库文件中,便于回溯与审计。
- Dockerfile 使用 BuildKit cache mount 复用
apt/pip下载缓存;在 Docker Engine 24+ / Compose v2 下,重复构建通常可直接命中这两类缓存。 - 当前镜像构建阶段会临时启用 sid 源,仅安装
intel-opencl-icd与libze-intel-gpu1。 - 独立 Text-CLIP 镜像也改为两阶段构建,只复制瘦身后的
.venv、openvino_text_fp16.*和 tokenizer 资源;它固定走 OpenVINO CPU,不安装 Intel GPU runtime、不需要/dev/dri,也不会混入 OpenCV Python 包、视觉 FP16 IR、FP32 对照组或 Hugging Face snapshot。 - 主服务镜像只保留
openvino_image_fp16.*、InsightFaceantelopev2和运行时必需脚本;不会混入openvino_text_fp16.*、openvino_fp32/*、qa-clip/huggingface/*或buffalo_l。 - 容器内不安装
xserver-xorg-video-intel(该包用于 Xorg 显示栈,不是本服务的无头推理运行前提)。 - 服务上传读图链已统一改为 OpenCV 原生解码,镜像不再包含
ffmpeg/ffprobe、VAAPI/oneVPL/QSV 媒体栈,也不预装clinfo这类诊断工具。 - 若服务日志出现
available_devices=['CPU'],即使/dev/dri可见,也通常意味着容器里缺少可用的 Intel GPU OpenVINO/OpenCL runtime,或/dev/dri并非真实的 Intel DRM render node。 - 参考文档:
- OpenVINO GPU 设备配置与依赖:https://docs.openvino.ai/2025/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device.html
- Intel Linux GPU Driver(OpenCL/Level Zero 运行时包):https://dgpu-docs.intel.com/driver/installation.html
- 先准备镜像。
docker-compose.example.yml只引用image:,不会在docker compose up时构建:
docker build -t mt-photos-ai-openvino .
docker build -f text-clip/DockerFile-TextCLIP -t mt-photos-ai-text-clip .如果镜像已经在本地存在,或你改为从镜像仓库拉取,可以跳过这一步。
- 准备配置文件:
cp docker-compose.example.yml docker-compose.yml-
按宿主机实际情况修改
group_add中的video/renderGID(Debian 默认通常是44/109)。 -
按需调整
docker-compose.yml:
- 生产环境建议覆盖
API_AUTH_KEY - 主容器需要映射
/dev/dri并设置正确的video/render组;mt-photos-ai-text-clip固定走 CPU,不需要/dev/dri mt-photos-ai-text-clip启动后会常驻加载文本模型;主服务请通过TEXT_CLIP_SERVER_URL=http://mt-photos-ai-text-clip:8061代理转发/clip/txt- 有 Intel iGPU 且已映射
/dev/dri时,主容器建议使用INFERENCE_DEVICE=AUTO、CLIP_INFERENCE_DEVICE=AUTO、INSIGHTFACE_OV_DEVICE=AUTO - 如需修改服务监听端口,请同时调整
PORT和ports:映射 - 如需挂载自定义模型或自定义 OpenVINO cache 目录,再显式覆盖
MODEL_PATH/OV_CACHE_DIR - 首次
/ocr若容器内还没有 RapidOCR 默认模型,会在线下载;离线环境请先完成一次带网络的 OCR 预热 - 若
/clip/img仍未跑满 GPU,可结合业务流量逐步调大CLIP_IMAGE_BATCH,并保持CLIP_IMAGE_BATCH_WAIT_MS在个位数毫秒级,避免明显放大单请求尾延迟 - 如需限制 OCR 首次冷加载带来的单次长尾,可显式设置
OCR_EXEC_TIMEOUT=30 /represent当前固定为单 lane OpenVINO EP 推理 + 4 请求聚合预算;如需权衡吞吐与尾延迟,可只小幅调整INSIGHTFACE_BATCH_WAIT_MS
- 启动服务:
docker compose up -d- 查看状态:
docker compose ps
docker compose logs -f mt-photos-ai-openvino mt-photos-ai-text-clip先创建一个供两个容器互访的用户网络:
docker network create mt-photos-ai-netdocker run -d \
--name mt-photos-ai-text-clip \
--network mt-photos-ai-net \
--init \
-p 8061:8061 \
-e API_AUTH_KEY=mt_photos_ai_extra \
-e LOG_LEVEL=WARNING \
-e PORT=8061 \
mt-photos-ai-text-clip
docker run -d \
--name mt-photos-ai-openvino \
--network mt-photos-ai-net \
--init \
-p 8060:8060 \
--device /dev/dri:/dev/dri \
--group-add $(getent group video | cut -d: -f3) \
--group-add $(getent group render | cut -d: -f3) \
-e API_AUTH_KEY=mt_photos_ai_extra \
-e CLIP_IMAGE_BATCH=8 \
-e CLIP_INFERENCE_DEVICE=AUTO \
-e INFERENCE_DEVICE=AUTO \
-e INSIGHTFACE_OV_DEVICE=AUTO \
-e LOG_LEVEL=WARNING \
-e NON_TEXT_IDLE_RELEASE_SECONDS=60 \
-e OCR_EXEC_TIMEOUT=30 \
-e PORT=8060 \
-e TEXT_CLIP_SERVER_URL=http://mt-photos-ai-text-clip:8061 \
mt-photos-ai-openvino如需自定义模型目录或 OpenVINO cache 目录,可继续追加 -e MODEL_PATH=...、-e OV_CACHE_DIR=...。如需修改任一服务的 PORT,请同步调整对应的 -p <host_port>:<container_port>。如果 Text-CLIP 容器使用了不同的鉴权密钥,请额外给主容器设置 -e TEXT_CLIP_API_KEY=...。
首次部署建议执行以下检查:
docker exec -it mt-photos-ai-openvino ls -l /dev/dri
docker exec -it mt-photos-ai-openvino python -c "import openvino as ov; print(ov.Core().available_devices)"若请求了 GPU 推理但容器内 GPU 设备不可用,服务会直接报错并终止启动。
主服务不再随镜像内置 OCR 模型。/ocr 首次懒加载时会由 rapidocr==3.8.0 按上游内置 default_models.yaml 自动检查并下载默认模型,运行时只额外把 Det、Cls、Rec 的 engine_type 改为 openvino。
- 首次加载 OCR 时需要可访问上游内置模型 URL
- Docker 镜像已预创建并授权
rapidocr/models目录,容器内无需再手动挂载models/rapidocr - 如需验证上游内置样例图片,可直接使用官方文档里的
img_url=https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/master/resources/test_files/ch_en_num.jpg
推荐先在 Docker 容器内执行 InsightFace 专项冒烟:
docker build -f text-clip/DockerFile-TextCLIP -t mt-photos-ai-text-clip .
docker build -t mt-photos-ai-openvino .
docker run --rm -it \
-e INFERENCE_DEVICE=CPU \
-e CLIP_INFERENCE_DEVICE=CPU \
-e INSIGHTFACE_OV_DEVICE=CPU \
mt-photos-ai-openvino \
python scripts/smoke_insightface.py --device CPU如宿主机已映射 Intel iGPU 的 /dev/dri,可进一步执行 GPU 冒烟:
docker run --rm -it \
--device /dev/dri:/dev/dri \
--group-add $(getent group video | cut -d: -f3) \
--group-add $(getent group render | cut -d: -f3) \
-e INFERENCE_DEVICE=CPU \
-e CLIP_INFERENCE_DEVICE=CPU \
-e INSIGHTFACE_OV_DEVICE=GPU \
mt-photos-ai-openvino \
python scripts/smoke_insightface.py --device GPU脚本会校验:
OpenVINOExecutionProvider仍是 detection/recognition 的首位 provider,且device_type与请求一致- InsightFace 运行事实已收敛到单 lane detector/recognition + 固定 CPU 预处理 + 最多 4 路预处理 worker + 默认 4 请求 admission/聚合上限
- 运行时模型目录已经收敛到
_runtime_models/models/antelopev2 - 本地
/representpipeline 与原生FaceAnalysis.get的检测框、分数和 embedding 语义保持一致 - 原生 detector.detect 与 batched recognition 路径均保持稳定
- 并发
/represent聚合路径与顺序/represent路径一致,不改变检测框、分数和 embedding 语义 release_models_for_restart()后 face runtime 引用已释放,且能够重新加载
如需专项验证混合 /represent、/clip/img、/ocr 请求的乱序到达与轻量压力场景,可直接运行:
python scripts/smoke_non_text_process.py --mixed-burst 24 --concurrency 6该脚本会依次做顺序切换校验、/represent -> /clip/img 乱序回归、六种有序两两轮换校验,以及一个使用假子进程 worker 的轻量微压测,并输出各操作的平均延迟、P95 和 worker 重启次数。
服务启动后,也可以再做基础端点检查:
curl -s http://127.0.0.1:8060/
curl -s -X POST http://127.0.0.1:8060/check -H "api-key: mt_photos_ai_extra"
curl -s -X POST http://127.0.0.1:8060/clip/txt -H "api-key: mt_photos_ai_extra" -H "Content-Type: application/json" -d '{"text":"smoke"}'
curl -s -X POST http://127.0.0.1:8061/check -H "api-key: mt_photos_ai_extra"
curl -s -X POST http://127.0.0.1:8061/clip/txt -H "api-key: mt_photos_ai_extra" -H "Content-Type: application/json" -d '{"text":"smoke"}'除 GET / 外,业务端点在启用鉴权时都需要携带 api-key 请求头。
MIT License