# 将 BgFilter 部署到 Linux 服务器 > English version: [DEPLOY.md](DEPLOY.md) 本指南涵盖在 Linux 服务器上运行 BgFilter HTTP 服务(`POST /remove-background`、 `GET /healthz`)所需的全部步骤。默认按 **CPU** 部署(推荐默认);涉及 GPU 的地方会单独标注。 服务入口是 `app.py`(`uvicorn app:app`)。API 契约和算法细节见 [README.md](README.md)。 下面第 1–12 节是**手动 CPU 部署**(systemd + nginx,推荐默认)。如果你部署在 **AutoDL GPU 盒子**上,走第 0 节的一键脚本更快;两者是各自独立的部署路线。 --- ## 0. 快速路径:AutoDL 一键部署(GPU) 在 AutoDL GPU 实例上,用仓库自带的一键脚本部署,无需手动装依赖 / 配 systemd: ```bash # 假设仓库已 checkout 到实例(脚本不会 git pull),使用 server-edition 分支 cd /root/BGfilter-server # 你的实际路径 scripts/deploy_autodl.sh --token ``` 脚本**幂等**地完成:装依赖 + 拉模型权重 + 按 CUDA 版本装对应 CuPy(GPU 前景合成) + 起一个 **supervisord** 实例(崩溃自动重启)+ 往 `~/.bashrc` 写交互登录自启, 最后启动服务并校验 `/healthz`。 参数: | 参数 | 默认 | 说明 | | --- | --- | --- | | `--token ` | 无 | 鉴权 token,设置/更新后 `/remove-background` 要求 header `X-Genarrative-Image-Token`。**省略则沿用已有 token**(重跑改别的参数不会误关鉴权);只有首次无 token 才开放。 | | `--open` | — | 显式删除 token、以开放模式运行(无鉴权)。 | | `--port ` | `6006` | 容器内监听端口(对应 AutoDL 的公网映射端口)。 | | `--config ` | `configs/gpu.yaml` | 流水线配置。 | | `--log-dir ` | `~/autodl-tmp` | 日志目录(默认 AutoDL 持久数据盘——容器重置不丢,`/tmp` 会丢)。含 `bgfilter.log`(散文事件日志)和 `bgfilter-access.jsonl`(结构化每请求日志,应用自轮转)。 | **前提**:仓库已 checkout(脚本不 `git pull`)、root 用户、torch/CUDA 由 AutoDL 镜像提供、 python 在 `/root/miniconda3`。 **两个 AutoDL 固有的坑**(脚本管不了): - **公网 URL 映射**(如 `6006 → https://...:8443`)要在 **AutoDL 网页控制台**里设,脚本不负责。 - **容器重启后**服务不会自动起来——AutoDL 没有 systemd / 开机钩子,自启写在 `~/.bashrc` 里, 需要**开一个交互式 shell(SSH / JupyterLab 终端)** 才会触发拉起。 **服务管理**(supervisorctl 与 python 同目录;socket 在 `~/bgfilter-supervisor.sock`, supervisord 配置在 `~/supervisord.conf`): ```bash S="/root/miniconda3/bin/supervisorctl -c $HOME/supervisord.conf" $S status bgfilter # 状态 + 运行时长 $S restart bgfilter # 重启(如改了 configs/*.yaml 之后) $S stop bgfilter # 停服务(supervisord 本身继续跑) $S start bgfilter # 再启动 $S shutdown # 全部停掉,包括 supervisord 自己 tail -f ~/autodl-tmp/bgfilter.log # 事件日志:启动/告警/堆栈(散文) tail -f ~/autodl-tmp/bgfilter-access.jsonl # 访问日志:每请求一条 JSON 记录 tail -f ~/supervisord.log # supervisord 自己的日志(服务起不来时看) ``` 若 supervisord 本身没在跑——容器刚重启还没开过 shell,或报 socket 残留错误——照 `~/.bashrc` 自启的方式手动拉起: ```bash rm -f ~/bgfilter-supervisor.sock ~/supervisord.pid /root/miniconda3/bin/supervisord -c ~/supervisord.conf ``` 改 `~/supervisord.conf` 本身(端口、日志路径等)的方式是重跑部署脚本——它会重新生成 该文件并做 `reread + update + restart`。 > 改了 `configs/*.yaml`(比如开 `cross_check.reuse_as_seg`)后,用上面的 `restart bgfilter` > 让服务重新加载配置——配置只在启动时读入,没有请求级开关。 --- ## 1. 前置条件 - Linux x86-64(在 Ubuntu 上验证过;其它主流发行版同理)。 - 推荐 **Python 3.11–3.13**。更新的版本(如 3.14)也可能可用,但部分 ML 包可能还没发布对应 wheel——尽量选一个 `torch` / `onnxruntime` / `opencv` / `pymatting` 都有预编译 wheel 的版本。 - `git`,以及 venv 模块(Debian/Ubuntu 上是 `python3-venv`)。 - 磁盘空间:Python 环境约 3 GB + 模型权重约 1 GB。 - **一次性**联网即可(用于装依赖和下模型);服务跑起来后可完全离线。 ```bash sudo apt update sudo apt install -y python3-venv python3-pip git ``` ## 2. 获取代码 ```bash sudo mkdir -p /opt/genarrative-image-host/bgfilter-service sudo chown "$USER" /opt/genarrative-image-host/bgfilter-service cd /opt/genarrative-image-host/bgfilter-service git clone . # 或把仓库拷到这里;使用 server-edition 分支 ``` ## 3. Python 环境 ```bash python3 -m venv .venv . .venv/bin/activate python -m pip install -U pip ``` ## 4. 安装依赖(CPU) **先从 CPU wheel 源装 PyTorch**——否则 `pip` 会拉默认的 CUDA 构建,附带约 2–3 GB 的 `nvidia-*` 包,CPU 机器完全用不上: ```bash pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu ``` `requirements.txt` 里已经固定用 **`opencv-python-headless`**(GUI 构建依赖 `libGL.so.1` 等无头服务器没有的库),所以无需改动。直接装其余依赖——此时 `torch`/`torchvision` 已满足,不会再拉 CUDA: ```bash pip install -r requirements.txt ``` 验证整套依赖可导入、且 torch 是 CPU 构建(`cuda= None`): ```bash python -c "import torch,cv2,fastapi,transformers,pymatting,onnxruntime,kornia,timm; \ print('torch', torch.__version__, 'cuda=', torch.version.cuda)" ``` > 在墙内,给第二条 `pip install` 加上 `-i https://pypi.tuna.tsinghua.edu.cn/simple` 提速。 ## 5. 准备模型权重 默认用到三个模型:`hustvl/vitmatte-base-composition-1k`(抠图)、 `ZhengPeng7/BiRefNet`(默认分割)、`skytnt/anime-seg`(可选分割)。 第四个模型 `ZhengPeng7/BiRefNet_HR-matting`(约 425 MB)只有在启用 **cross-check(交叉校验) 否决**时才需要——见本节末尾说明。 **一键方式(推荐)**:跑仓库自带脚本(无参数),经 hf-mirror 把所有模型下到 `models/` (绕过代理、禁用 Xet、已存在的文件夹自动跳过)。跑完主 app 即离线全功能可用: ```bash python scripts/fetch_weights.py ``` 下面两种手动放法用于更精细的控制(比如共享 HF 缓存、只下部分模型),**二选一**。 ### 方案 A —— HuggingFace 缓存 + 离线模式(推荐) 先下载到一个缓存目录,之后离线运行: ```bash export HF_HOME=/opt/genarrative-image-host/bgfilter-service/model-cache/hf hf download hustvl/vitmatte-base-composition-1k hf download ZhengPeng7/BiRefNet hf download skytnt/anime-seg hf download ZhengPeng7/BiRefNet_HR-matting # 仅在使用 cross-check 时(见下) ``` 运行时把 `HF_HOME` 指向同一路径,并设 `HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1`(见 §6)。 也可以在另一台机器下好,把 `hub/` 目录整个拷过来。 ### 方案 B —— 权重作为普通文件夹放进项目 把每个模型放进 `models//`,加载器会直接用它(不走 HF 缓存、不联网)。 没放对应文件夹时会回退到 repo id,所以这是可选功能。 **文件夹名必须和 repo 末段名完全一致**——加载器靠这个找到它 (`ZhengPeng7/BiRefNet` → `models/BiRefNet`)。各模型需要的文件: ```text <项目根目录>/ └── models/ ├── vitmatte-base-composition-1k/ # ViTMatte 抠图模型 │ ├── config.json │ ├── preprocessor_config.json │ └── pytorch_model.bin # 约 369 MB ├── BiRefNet/ # 默认分割模型(trust_remote_code) │ ├── config.json │ ├── BiRefNet_config.py # 自定义代码——trust_remote_code 必需 │ ├── birefnet.py # 自定义代码——trust_remote_code 必需 │ └── model.safetensors # 约 424 MB ├── anime-seg/ # 可选分割模型(seg_model=anime-seg) │ └── isnetis.onnx # 约 168 MB └── BiRefNet_HR-matting/ # 可选——仅用于 cross-check 否决 ├── config.json ├── BiRefNet_config.py # 自定义代码——trust_remote_code 必需 ├── birefnet.py # 自定义代码——trust_remote_code 必需 └── model.safetensors # 约 425 MB ``` 说明: - **ViTMatte**:需要 `config.json`、`preprocessor_config.json` 和权重 (`pytorch_model.bin`;若有 `model.safetensors` 也可)。 - **BiRefNet**:两个 `.py` 文件是必需的——`trust_remote_code` 会执行它们来构建模型。 和 `config.json` + `model.safetensors` 放在一起。(repo 里的 `handler.py` / `requirements.txt` / `README.md` 用不到。) - **anime-seg**:只读 `isnetis.onnx`,其它都不需要。 - `anime-seg` 和 `BiRefNet_HR-matting` 都是可选的——不用 `seg_model=anime-seg` / cross-check 否决时,各自的文件夹可以不放。 - **`cpu-fast` lane**(§6)只加载 **ViTMatte + BiRefNet**;8 GB 机器用这条 lane 时, `anime-seg` 和 `BiRefNet_HR-matting` 都可以完全不放。 (`scripts/fetch_weights.py` 仍会把 4 个都下齐,以保证完整功能。) 最省事的填充方式是用 `hf download`(它也会顺带拉几个用不到的文件如 `README.md`,无害): ```bash hf download hustvl/vitmatte-base-composition-1k --local-dir models/vitmatte-base-composition-1k hf download ZhengPeng7/BiRefNet --local-dir models/BiRefNet hf download skytnt/anime-seg --local-dir models/anime-seg hf download ZhengPeng7/BiRefNet_HR-matting --local-dir models/BiRefNet_HR-matting # 仅 cross-check ``` 用 `BGFILTER_WEIGHTS_DIR` 可改基目录。`models/` 已被 gitignore。 ### cross-check 否决(默认开启) 管线会再跑一个 matting 模型(`ZhengPeng7/BiRefNet_HR-matting`,约 425 MB)作为独立的 "第二意见"——它做什么、为什么,见 [README_ZH.md](README_ZH.md) → *管线*。部署侧要点: - **CLI 和 HTTP 服务都默认开启**,因此**必须备好这个模型**(上面方案 A 或 B)。 ⚠️ 离线服务器(`HF_HUB_OFFLINE=1`)必须在第一个请求前就备好它,否则每个默认请求都失败。 - 服务级关闭:`BGFILTER_CROSS_CHECK=0`;按请求覆盖:表单字段 `cross_check`。 开启时服务启动即预载。 - 配置里 `cross_check.reuse_as_seg: true`(默认**关**)让这次前向兼作分割掩码: 少载一个模型、CPU 快约 20 秒/张、GPU 省约 1 GB 显存。仅配置文件可设——改动需重启。 ### 背景模式(flat vs complex) `background_mode=complex`(非纯色/实景背景)**不需要额外权重**——复用已部署的 BiRefNet 分割器 + ViTMatte,跳过色键相关阶段。cross-check 否决在 complex 模式下 **默认关闭**(传 `cross_check=on` 启用)。字段语义与响应头见 [README_ZH.md](README_ZH.md) → *HTTP 服务*。 ### 墙内下载(国内网络) 若直连 HuggingFace 被墙,用镜像 **并绕过本地代理、禁用 Xet**——这是稳定可用的组合: ```bash export HF_ENDPOINT=https://hf-mirror.com # 国内镜像 export NO_PROXY='*' # 绕过代理;镜像是国内直连 export HF_HUB_DISABLE_XET=1 # 这些 repo 是 Xet 存储;强制走经典 HTTP ``` 这样能避开两个坑:(1) 镜像 + 走海外的代理会让镜像把 `resolve` 又重定向回 `huggingface.co`,新版 `huggingface_hub` 会因此报 `FileMetadataError`; (2) 装了 `hf-xet` 时 Xet 下载路径在此环境下会瞬间失败。 ## 6. 配置(环境变量) | 变量 | 默认 | 作用 | | --- | --- | --- | | `BGFILTER_CONFIG` | `configs/default.yaml` | 管线配置文件 | | `BGFILTER_DEVICE` | `cpu` | 同时覆盖 model 和 segmentation 的 device(GPU 用 `cuda`)| | `BGFILTER_MAX_IMAGE_PIXELS` | `4194304` | 超过则返回 `413`(约 4 MP)| | `BGFILTER_ACCESS_LOG` | — | **结构化访问日志**的路径(JSONL,每请求一条记录 + 一条 `startup` 元记录;应用自轮转,50 MB × 5)。字段:`request_id`、参数、尺寸、分阶段耗时(`t_queue/fetch/decode/process/total_ms`)、状态码、失败时截断的 `exc`;该 id 同时以 `X-Request-ID` 响应头返回。不设置 = 记录回退到 stderr(开发用)。散文**事件日志**(启动/告警/堆栈)始终走 stderr 由 supervisord/journald 捕获——每请求数据绝不落在散文流里。 | | `BGFILTER_LOG_LEVEL` | `INFO` | 根日志级别(事件流)。 | | `BGFILTER_FETCH_MAX_BYTES` | `26214400` | `image_url` 模式:下载体上限(约 25 MB)→ `413`。无 SSRF 过滤——务必开鉴权 / 调用方可信。 | | `BGFILTER_FETCH_TIMEOUT` | `15` | `image_url` 模式:fetch 读超时(秒;连接超时固定 5 秒)。出站 fetch 并发固定为 `2 × cpu_workers`(硬顶 64)。 | | `BGFILTER_PRELOAD` | `1` | 启动时预加载默认模型(首个请求不用冷加载)| | `BGFILTER_CROSS_CHECK` | 配置(`on`)| 强制开/关 cross-check 否决(`0` 关闭;需 `BiRefNet_HR-matting`)| | `BGFILTER_CPU_WORKERS` | 配置 `server.cpu_workers`(`1`)| 请求流水线的线程池大小。`1` = 严格串行(一次一个请求)。`>1` 允许这么多请求并发,重叠各自的 CPU 段,同时用一把 GPU 互斥锁串行化模型前向——让 GPU 别闲着。CPU 为瓶颈时(如关 cross-check)收益最大。可写进配置的 `server:` 段,或用此 env 变量按机器覆盖。 | | `BGFILTER_GPU_CONCURRENCY` | 配置 `server.gpu_concurrency`(`1`)| 允许几个请求同时进入 GPU 前向。`1` = 单流(安全)。`2` 让两次前向在 SM 上共同调度(batch-1 喂不满 SM),提升 GPU 瓶颈下的吞吐——但并发激活会成倍占显存,按 `权重 + N × 单次前向峰值 ≤ 显存` 来定(32GB 卡上 `2` 在 2048 cross-check 时峰值约 19GB;≤16GB 卡保持 `1`)。需 `cpu_workers>1` 才有意义。配置键 `server.gpu_concurrency`,此 env 变量覆盖它。 | | `BGFILTER_AUTH_TOKEN` | — | 若设置,`/remove-background` 要求 header `X-Genarrative-Image-Token` 等于它(常量时间比较;否则 `401`)。不设 = **开放**(鉴权交给前置代理)。`/healthz` 始终开放。见 §10。 | | `BGFILTER_WEIGHTS_DIR` | `models` | 本地权重文件夹的查找目录(方案 B)| | `HF_HOME` | — | HF 缓存位置(方案 A)| | `HF_HUB_OFFLINE` / `TRANSFORMERS_OFFLINE` | — | 权重缓存好后,生产设为 `1` | | `OMP_NUM_THREADS` / `MKL_NUM_THREADS` | — | 限制 CPU 线程数(如 `4`)| ### 配置文件:`default.yaml` 与 `cpu-fast` lane `BGFILTER_CONFIG` 决定用哪个管线配置。仓库自带两个: - **`configs/default.yaml`** —— 完整画质。cross-check 否决**默认开**(额外加载 `BiRefNet_HR-matting`@2048 作第二意见),边缘最好。较重:大图时峰值工作集轻松到 两位数 GB,需要内存宽裕的机器(或 GPU)。 - **`configs/cpu-fast.yaml`** —— 精简 CPU lane,为 **8 GB 机器**调校。fp32、只用 BiRefNet 分割、**无 cross-check、无 anime-seg**、分块注意力(`attn_query_chunk: 512`)。只加载 **两个**权重(base ViTMatte + BiRefNet)。实测峰值工作集:**2048×1152(2.4 MP)约 5.6 GB**、 2048×2048(4.2 MP)约 7.1 GB;约 14–15 秒/张(1.5 MP)。 > **≤8 GB 的服务器请设 `BGFILTER_CONFIG=configs/cpu-fast.yaml`。** 默认 lane + 大图 > 正是把 ViTMatte 注意力瞬时内存推到 OOM 的组合——worker 被 kill(SIGKILL),前面的 > nginx 返回 **502**,而应用日志显示 `DefaultCPUAllocator: can't allocate memory`。 有两个模型旋钮放在**配置文件**里(`model:` 段),不是环境变量: | 配置项 | 默认 | 作用 | | --- | --- | --- | | `model.precision` | `fp32` | `fp32` 或 `bf16`。bf16 **仅**在有原生内核的硬件上启用(CUDA,或带 AVX512-BF16/AMX 的 CPU);否则告警并回退 fp32。也可在 `segmentation:` / `cross_check:` 下按模型单独设。 | | `model.attn_query_chunk` | `2048` | ViTMatte 全局注意力的 query 行分块。调小(如 `512`)以几个百分点的额外算力换掉 O(N²) 内存瞬时尖峰;输出逐比特相同。设 `0` 关闭分块(走原始一次性注意力)。 | > **despill 默认关闭**(所有 lane,含 cpu-fast)。它沿背景色调方向压前景色度,没有位置/语义护栏,所以主体与背景**同色相**时(如蓝背景前的蓝西装)会被褪色/偏色(约 ΔE 35)。仅当"去边缘溢色"比"同色系保真"更重要时,才在配置里 `despill: {enabled: true}` 单独开启。 ## 7. 运行服务 ```bash BGFILTER_ACCESS_LOG=logs/bgfilter-access.jsonl \ python -m uvicorn app:app --host 127.0.0.1 --port 18083 --workers 1 --no-access-log ``` - **必须保留 `--workers 1`。** 每个 worker 各加载一整套模型,多 worker 会成倍占内存 (8 GB 机器会 OOM)。服务已用全局锁串行化推理。 - `--host 127.0.0.1` 只对本机开放(面向同机调用方)。跨机访问见 §10。 - `--no-access-log` 关掉 uvicorn 自己的散文版每请求日志——它是应用结构化访问记录 (§6 `BGFILTER_ACCESS_LOG`)的子集、纯重复,且每请求数据不该进散文流。 ## 8. 作为 systemd 服务运行 `/etc/systemd/system/genarrative-bgfilter.service`: ```ini [Unit] Description=Genarrative BgFilter background removal service After=network-online.target Wants=network-online.target [Service] Type=simple WorkingDirectory=/opt/genarrative-image-host/bgfilter-service Environment=HF_HOME=/opt/genarrative-image-host/bgfilter-service/model-cache/hf Environment=HF_HUB_OFFLINE=1 Environment=TRANSFORMERS_OFFLINE=1 Environment=HF_HUB_DISABLE_XET=1 Environment=BGFILTER_CONFIG=configs/default.yaml Environment=BGFILTER_DEVICE=cpu Environment=BGFILTER_MAX_IMAGE_PIXELS=4194304 Environment=BGFILTER_PRELOAD=1 Environment=BGFILTER_ACCESS_LOG=/opt/genarrative-image-host/bgfilter-service/logs/bgfilter-access.jsonl Environment=OMP_NUM_THREADS=4 Environment=MKL_NUM_THREADS=4 ExecStart=/opt/genarrative-image-host/bgfilter-service/.venv/bin/uvicorn app:app --host 127.0.0.1 --port 18083 --workers 1 --no-access-log Restart=on-failure RestartSec=5 MemoryMax=8G NoNewPrivileges=true PrivateTmp=true ProtectSystem=full ReadWritePaths=/opt/genarrative-image-host [Install] WantedBy=multi-user.target ``` ≤8 GB 机器请把配置那行改成精简 lane: `Environment=BGFILTER_CONFIG=configs/cpu-fast.yaml`(见 §6)——默认 lane 大图会 OOM。 若用方案 B(本地 `models/` 文件夹)而非 HF 缓存,去掉 `HF_HOME` 和离线那几行—— 那种权重不需要缓存也不联网。 ```bash sudo systemctl daemon-reload sudo systemctl enable --now genarrative-bgfilter sudo systemctl status genarrative-bgfilter journalctl -u genarrative-bgfilter -f # 等到 "Application startup complete" ``` ## 9. 验证 ```bash curl -s http://127.0.0.1:18083/healthz # {"ok":true,"service":"bgfilter","version":"0.1.0","defaultSegModel":"birefnet","device":"cpu"} curl -sS -F "file=@input.png" -F "screen_color=#CFEFFF" \ http://127.0.0.1:18083/remove-background -o out.png # 非纯色/实景背景(无色键;此模式 cross-check 默认关) curl -sS -F "file=@scene.jpg" -F "background_mode=complex" \ http://127.0.0.1:18083/remove-background -o out.png ``` ## 10. 对外暴露 单元里绑的是 `127.0.0.1`——面向同机调用方(例如 Genarrative Rust BFF,通过 `GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_BASE_URL` 调用)。要从其它主机访问: - **反向代理(推荐):** 前面放 nginx/caddy,终止 TLS、加限流,转发到 `127.0.0.1:18083`。 - **直接暴露:** 把 `--host` 改成 `0.0.0.0`,并用防火墙限制来源 IP。 切勿把无鉴权的服务裸暴露到公网(见下方鉴权)。 ### 鉴权 app 层 token 鉴权是**可选开启**,由 `BGFILTER_AUTH_TOKEN` 控制: - **设置它** → `/remove-background` 要求 header `X-Genarrative-Image-Token: ` (常量时间比较);缺失或错误返回 `401`。`/healthz` 保持开放。**当 app 被直接暴露** (`--host 0.0.0.0`、或云端端口映射无代理)时用这个。 - **不设置** → app **开放**(fail-open),并在启动时打一条 WARNING。仅当前置代理 已经鉴权时才可这样——例如生产机 nginx 自己校验 `X-Genarrative-Image-Token`, 其后的 app 就无需 token。 header 名和 nginx 层一致,所以调用方只换 base URL + token 值,请求结构不变: ```bash curl -H "X-Genarrative-Image-Token: " \ -F "file=@input.png" \ https:///remove-background -o out.png ``` ## 11. 容量与性能 - CPU 吞吐:**`cpu-fast` lane** 约 **14–15 秒/张(1.5 MP)**(base ViTMatte + 一个 BiRefNet@1024,分块注意力)。**默认 lane** 更慢——cross-check 会多跑一次 HR-matting@2048 前向。请求被全局锁串行,单实例按每分钟几张估算。 - **默认 lane,工作站 CPU 参考(Ryzen 9700X,原生 bf16,32 GB;cross-check 开且 `reuse_as_seg` 开,分块注意力):** | 输入 | 精度 | warm / 每图 | 峰值内存 | |---|---|---|---| | 1024×1536 | fp32 | ~45 s | — | | 1024×1536 | bf16 | ~33 s | — | | 2048×2048 | bf16 | ~53 s | 11.4 GB(批处理)、8.1 GB(单图) | 不开 `reuse_as_seg` 要加上主 BiRefNet@1024 前向(每图约 +20 s)。内存上限来自 cross-check 的 HR 前向(无论图多大都固定 `input_size` 2048)。分块注意力之前, 同一个 2048² bf16 run 峰值 25.2 GB;完全未优化的 fp32 估计要 55–60 GB。无原生 bf16 的 CPU(如 Zen 2)自动回落 fp32——这类机器上内存或延迟紧张时优先 `--no-cross-check`。 - 要提吞吐:上 GPU(`BGFILTER_DEVICE=cuda`,装 CUDA 版 torch),大约快一个数量级; 和/或多实例 + 负载均衡(每个都 `--workers 1`)。 - **GPU 部署(实测,RTX 5070 Ti 16 GB,完整管线——cross-check 开,bf16):** 约 1–2 秒/张(4 MP 也只要 2 秒),峰值 VRAM 分配约 10.9 GB(开 `reuse_as_seg` 为 10.1 GB)。真正决定「装不装得下」的是分配器的 *reserved* 占用。`bgfilter` 在 Linux/WSL2 上会**默认设置** `PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True` (在 torch 加载前;原生 Windows 不支持该选项;显式设过的环境变量优先)。 实测效果:reserved 15.3 GB → **12.0 GB**(开 reuse;默认不开为 13.3 GB),且还 **快约 10%**——完整管线在 16 GB 卡上即使旁边挂着桌面会话也有约 2–3.5 GB 余量。 没有 expandable segments(如原生 Windows)时 reserved 约 15.3–16.2 GB:在 16 GB 卡上从很悬到直接溢出。另外 `bf16` 是 16 GB 可行的前提——fp32 的 reserved 约 17 GB,直接溢出。 - 内存:分块注意力(`attn_query_chunk`)把 ViTMatte 的 O(N²) 瞬时尖峰削平,于是峰值由 稠密计算而非注意力矩阵决定。`cpu-fast` lane 实测峰值 **约 5.6 GB@2.4MP / 7.1 GB@4.2MP**—— 配合默认 4 MP 输入上限,`MemoryMax=8G` 对单 CPU worker 是稳妥上限。默认 lane(cross-check 开) 需要更多余量。 - **让 RSS 保持平稳(glibc)。** 每处理完一张图,服务会调用 glibc 的 `malloc_trim(0)` (`bgfilter/memtune.py`)把 BiRefNet@2048 前向释放的大块激活还给 OS——否则 ptmalloc 会把它们留在 arena 里,RSS 会随请求逐步抬高。想进一步降内存**并**提升 CPU 吞吐,可以 预载 **jemalloc**(PyTorch 对 CPU 推理的推荐),并让它激进归还脏页: ```ini # 放进 systemd unit 的 [Service] 段 Environment=LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 Environment=MALLOC_CONF=background_thread:true,dirty_decay_ms:0,muzzy_decay_ms:0 ``` (`apt install libjemalloc2`;tcmalloc 用 `libtcmalloc_minimal.so.4` 同理。) jemalloc/tcmalloc 自己管理归还,此时 `malloc_trim` 会自动空转。若无法预载分配器, 在 unit 环境里设 `MALLOC_ARENA_MAX=2`、`MALLOC_TRIM_THRESHOLD_=0` 也能抑制 ptmalloc 滞留。 ## 12. 故障排查 | 现象 | 原因 / 解决 | | --- | --- | | `pip` 拉了 `nvidia-*`/`cuda-*`(巨大) | §4 里漏了 CPU 源。用 `--index-url https://download.pytorch.org/whl/cpu` 装 torch。 | | `import cv2` 报 `ImportError: libGL.so.1` | 在无 GUI 机器上用了 `opencv-python`。换成 `opencv-python-headless`(§4)。 | | 建 venv 时 `ensurepip is not available` | 装 `python3-venv`(§1)。 | | 下载时 `FileMetadataError: ... not on huggingface.co` | 镜像与代理冲突。绕过代理(`NO_PROXY='*'`)并设 `HF_HUB_DISABLE_XET=1`(§5)。 | | `LocalEntryNotFoundError`、0 字节 | Xet 下载路径失败;设 `HF_HUB_DISABLE_XET=1`。 | | `503 service not ready` | 启动时模型还在加载;等 "Application startup complete"。 | | 刚设 `BGFILTER_CROSS_CHECK` 后(离线)报 `500 inference failed` | `BiRefNet_HR-matting` 没备好;下到缓存/`models/`(§5)。 | | 负载下 OOM / 被 kill | worker 多于一个,或 `MemoryMax` 太低。保持 `--workers 1`。 | | 大图时 worker 被 `kill`(SIGKILL)/ 返回 `502`,日志显示 `DefaultCPUAllocator: can't allocate memory` | 大图触发 ViTMatte OOM。改用 `BGFILTER_CONFIG=configs/cpu-fast.yaml`(§6),和/或调小 `BGFILTER_MAX_IMAGE_PIXELS`。 | | 大文件上传在代理层被 `413` 拒 | nginx `client_max_body_size`(默认 1 MB)——在 server 块里调大(如 `client_max_body_size 12m;`);这和应用的 `BGFILTER_MAX_IMAGE_PIXELS` 是两道独立限制。 | | `400 could not auto-detect background colour` | 输入没有干净的纯色边框;传 `screen_color=#RRGGBB`。 |