TEI 降级 bge-base:N97 上 CPU 从 94% 压回 80%

1441 字
7 分钟
TEI 降级 bge-base:N97 上 CPU 从 94% 压回 80%

NAS 上用 TEI 做中文嵌入,批量任务一跑 CPU 就是 85–94%,别的容器被饿死、任务直接失败。

把模型从 bge-large 降到 bge-base,再留一个核给系统,TEI 峰值回到 70–80%。

代价写在最前面:维度从 1024 降到 768,已有向量库必须全量重建,新旧不能混用。

开始之前#

要不要降这一档#

只有一种情况该降:批量任务把 CPU 打到 90% 以上,同一台机器上别的容器开始超时或失败。降档换来的是稳定,不是速度。

CPU 还有余量就别动。bge-large 在近义去重、术语歧义这类细粒度区分上确实更准,降到 base 会丢掉这部分。

重灌向量的成本也要先算进去:文本越多越贵,而且中途新旧维度混在一起会直接报写入失败。

硬件与版本#

实测机器是极空间 Z4 Pro,Intel N97,四核四线程、没有 AVX-512。四线程这个数字决定了后面每一个参数。

镜像用 ghcr.io/huggingface/text-embeddings-inference:cpu-1.9,模型 bge-base-zh-v1.5,输出 768 维。

先定好这几个值#

下面命令和配置里出现的占位符,先想好各自换成什么:

占位符换成什么
/path/to/tei-project放 docker-compose.yml 的目录
tei.example.com给这个服务用的域名;不走反代就把 traefik 那几行标签删掉
traefik-net反代所在的 docker 网络名
向量库集合名实际的 collection / 表名,第四步要清空它

一、下载 bge-base 模型#

在宿主机上进项目目录,两种方式选一种:

Terminal window
cd /path/to/tei-project
# 方式 A:git(需要 git-lfs)
git lfs install
git clone https://huggingface.co/BAAI/bge-base-zh-v1.5 ./models/bge-base-zh-v1.5
# 方式 B:huggingface-cli(推荐,断点续传更稳)
pip install -U "huggingface_hub[cli]"
huggingface-cli download BAAI/bge-base-zh-v1.5 \
--local-dir ./models/bge-base-zh-v1.5 \
--local-dir-use-symlinks False

国内拉得慢就先设镜像再走方式 B:

Terminal window
export HF_ENDPOINT=https://hf-mirror.com

下载完确认这几个文件在:

Terminal window
ls -lh ./models/bge-base-zh-v1.5
# 至少要有 config.json、model.safetensors(或 pytorch_model.bin)、tokenizer.json

二、改 docker-compose.yml#

只有模型路径、名称和性能参数变了,网络和标签结构不动:

services:
tei-embedding:
image: ghcr.io/huggingface/text-embeddings-inference:cpu-1.9
container_name: tei-embedding
restart: unless-stopped
volumes:
- ./models/bge-base-zh-v1.5:/data/model:ro
command:
- --model-id
- /data/model
- --served-model-name
- bge-base-zh-v1.5
- --port
- "80"
- --max-concurrent-requests
- "48"
- --max-client-batch-size
- "16"
- --max-batch-tokens
- "4096"
- --tokenization-workers
- "2"
environment:
- RUST_LOG=warn
- OMP_NUM_THREADS=3
- RAYON_NUM_THREADS=3
deploy:
resources:
limits: { cpus: '3.0', memory: 4G }
reservations: { cpus: '1.0', memory: 1G }
networks:
- traefik-net
labels:
- "com.centurylinklabs.watchtower.enable=false"
# 原来有 traefik 路由标签的话按需加回:
# - "traefik.enable=true"
# - "traefik.http.routers.tei.rule=Host(`tei.example.com`)"
# - "traefik.http.services.tei.loadbalancer.server.port=80"
networks:
traefik-net:
external: true

参数说明:N97 四核上怎么分#

参数值为什么
OMP_NUM_THREADS / RAYON_NUM_THREADS3限制计算线程,永远留一个核给系统和反代——这条是止住任务失败的关键
cpus3.0硬上限,和上面的线程数呼应,两处都要写
max-concurrent-requests48base 更轻,能比 large 承受更高并发;超出直接返回 429,而不是排队到超时
max-batch-tokens4096bge 的上下文只有 512,批再大也用不上,而批越大单批延迟越高
memory4Gbase 比 large 省内存,从 6G 降到 4G 够用

三、重启服务#

Terminal window
docker compose pull # 可选,确保镜像最新
docker compose up -d
docker logs -f tei-embedding # 看到 Ready / Starting HTTP server 就是起来了

四、全量重建向量库#

维度从 1024 变成 768,旧向量和新模型不兼容,这一步不能跳。

  1. 清空原向量库里这个模型产生的集合或表(Milvus / Qdrant / pgvector 按各自的方式操作)。
  2. 用新服务对全部历史文本重新生成向量并写回。
  3. 把库里的维度定义改成 768。

schema 里写死 1024 的话要先改 schema 再重灌,否则写入会报维度不匹配。

五、验证三件事#

健康检查:

Terminal window
curl http://tei-embedding:80/health
# 走反代: curl https://tei.example.com/health

生成一条向量,确认维度是 768:

Terminal window
curl -s http://tei-embedding:80/embed \
-H 'Content-Type: application/json' \
-d '{"inputs":"痛苦往往源于见多识广后的欲望膨胀,能平静管理欲望的人才真正快乐。"}' \
| python3 -c "import sys,json;v=json.load(sys.stdin);print('dim =',len(v[0]))"
# 期望: dim = 768

盯一眼 CPU:

Terminal window
docker stats tei-embedding

批量任务跑起来时应该稳定在 3 核以内,整机峰值 70–80%,不再打到 94%,别的容器也不会被饿死。三条都对上,到这里已经能用了。

可选:回滚到 large#

volumes 和 --served-model-name 改回 bge-large-zh-v1.5,cpus 仍然保持 3.0,max-batch-tokens 降到 2048。

⚠️ 回滚同样要重灌向量库,维度回到 1024。换模型这件事没有单向的便宜路。

几个选择的理由#

为什么是 base,不是 small 也不是 large#

本场景是观点、金句、研报片段的主题级语义检索,不需要区分近义词。三档的取舍:

模型维度什么时候用代价
bge-small-zh-v1.5512文本普遍不到 200 字,而且 base 还嫌慢主题级检索也开始丢细节
bge-base-zh-v1.5768主题级语义检索,也就是本场景近义词、术语歧义分不开
bge-large-zh-v1.51024需要近义去重、区分术语歧义N97 这种小主机上 CPU 常态打满

选中间那档是因为丢掉的正好是用不上的能力:本场景从来不需要判断两句话是不是近义。

为什么一定要留一个核#

TEI 默认按可用核数开线程,四核机器上等于全占,系统和反代就没有 CPU 可用。表现不是「慢一点」,而是请求超时、容器被判不健康。

真正把任务失败止住的是留核这一步,换模型只是让同样的负载更轻。

高精度排序该用 reranker#

以后要做金句去重这类需要细粒度区分的事,用独立的 reranker 做二次精排。不必为此让嵌入服务常态高负载——精排只作用在召回回来的少量候选上。

同一台 NAS 上服务多了以后还有一堆约定要守,那些写在在 NAS 上用 Coolify 部署自建服务:十条约定里。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

TEI 降级 bge-base:N97 上 CPU 从 94% 压回 80%
https://blog.always1ov.com/posts/tei-embedding-bge-base-n97/
作者
薯饼
发布于
2026-09-08
许可协议
CC BY-NC-SA 4.0