README.md
Translation Module
translation/ 是当前项目翻译能力的主目录。
如果要开发、部署、联调、压测翻译服务,优先看这份文档。
对应服务:
- translator service:
http://127.0.0.1:6006 - 业务侧统一通过 <code>translation/client.py</code> 调用
- 服务内统一通过 <code>translation/service.py</code> 路由到具体翻译实现
相关脚本与报告:
- 启动脚本:<code>scripts/start_translator.sh</code>
- 虚拟环境:<code>scripts/setup_translator_venv.sh</code>
- 模型下载:<code>scripts/download_translation_models.py</code>
- 本地模型压测:<code>benchmarks/translation/benchmark_translation_local_models.py</code>
- 聚焦压测脚本:<code>benchmarks/translation/benchmark_translation_local_models_focus.py</code>
- 基线性能报告:<code>perf_reports/20260318/translation_local_models/README.md</code>
- CT2 扩展报告:<code>perf_reports/20260318/translation_local_models_ct2/README.md</code>
- CT2 聚焦调优报告:<code>perf_reports/20260318/translation_local_models_ct2_focus/README.md</code>
- NLLB T4 商品标题专项报告:<code>perf_reports/20260318/nllb_t4_product_names_ct2/README.md</code>
1. 设计目标
翻译模块采用:
- 一个 translator service
- 多个 capability backend
- 一个统一外部接口:
model + scene
这套设计的目标是:
- 翻译能力可以独立扩展、独立启停
- scene、语言码、prompt 模板、模型方向约束等翻译域知识集中在
translation/ - 配置尽量集中在 <code>config/config.yaml</code> 的
services.translation - 配置错误应尽早报错,不做静默兼容和隐式回退
2. 目录结构
核心文件:
- <code>translation/client.py</code> 业务侧 HTTP client,供 query/indexer 等模块调用
- <code>translation/service.py</code> translator service 内部的统一编排层
- <code>translation/settings.py</code> 翻译配置的规范化与校验辅助函数
- <code>translation/scenes.py</code> scene 规范和值校验
- <code>translation/languages.py</code> 语言码映射、Marian 方向映射等静态知识
- <code>translation/prompts.py</code> LLM 翻译 prompt 模板
- <code>translation/protocols.py</code> 输入输出协议类型
后端实现:
- <code>translation/backends/qwen_mt.py</code> Qwen-MT 云端翻译
- <code>translation/backends/llm.py</code> 通用 LLM 翻译
- <code>translation/backends/deepl.py</code> DeepL 翻译
- <code>translation/backends/local_ctranslate2.py</code> 本地 CTranslate2 翻译模型,包括 NLLB 和 Marian/OPUS MT
3. 配置约定
翻译的部署配置统一放在:
- <code>config/config.yaml</code> ->
services.translation
示例:
services:
translation:
service_url: "http://127.0.0.1:6006"
default_model: "llm"
default_scene: "general"
timeout_sec: 10.0
cache:
ttl_seconds: 62208000
sliding_expiration: true
capabilities:
qwen-mt:
enabled: true
backend: "qwen_mt"
model: "qwen-mt-flash"
base_url: "https://dashscope-us.aliyuncs.com/compatible-mode/v1"
timeout_sec: 10.0
use_cache: true
llm:
enabled: true
backend: "llm"
model: "qwen-flash"
base_url: "https://dashscope-us.aliyuncs.com/compatible-mode/v1"
timeout_sec: 30.0
use_cache: true
deepl:
enabled: false
backend: "deepl"
api_url: "https://api.deepl.com/v2/translate"
timeout_sec: 10.0
use_cache: true
nllb-200-distilled-600m:
enabled: true
backend: "local_nllb"
model_id: "facebook/nllb-200-distilled-600M"
model_dir: "./models/translation/facebook/nllb-200-distilled-600M"
ct2_model_dir: "./models/translation/facebook/nllb-200-distilled-600M/ctranslate2-float16"
ct2_compute_type: "float16"
ct2_conversion_quantization: "float16"
ct2_auto_convert: true
ct2_inter_threads: 4
ct2_max_queued_batches: 32
ct2_batch_type: "examples"
ct2_decoding_length_mode: "source"
ct2_decoding_length_extra: 8
ct2_decoding_length_min: 32
device: "cuda"
torch_dtype: "float16"
batch_size: 16
max_input_length: 256
max_new_tokens: 64
num_beams: 1
use_cache: true
opus-mt-zh-en:
enabled: true
backend: "local_marian"
model_id: "Helsinki-NLP/opus-mt-zh-en"
model_dir: "./models/translation/Helsinki-NLP/opus-mt-zh-en"
ct2_model_dir: "./models/translation/Helsinki-NLP/opus-mt-zh-en/ctranslate2-float16"
ct2_compute_type: "float16"
ct2_conversion_quantization: "float16"
ct2_auto_convert: true
device: "cuda"
torch_dtype: "float16"
batch_size: 16
max_input_length: 256
max_new_tokens: 256
num_beams: 1
use_cache: true
opus-mt-en-zh:
enabled: true
backend: "local_marian"
model_id: "Helsinki-NLP/opus-mt-en-zh"
model_dir: "./models/translation/Helsinki-NLP/opus-mt-en-zh"
ct2_model_dir: "./models/translation/Helsinki-NLP/opus-mt-en-zh/ctranslate2-float16"
ct2_compute_type: "float16"
ct2_conversion_quantization: "float16"
ct2_auto_convert: true
device: "cuda"
torch_dtype: "float16"
batch_size: 16
max_input_length: 256
max_new_tokens: 256
num_beams: 1
use_cache: true
配置边界:
config.yaml只放部署和运行参数 例如service_url、default_model、default_scene、enabled、base_url、api_url、model_dir、device- translation 目录内部放翻译静态知识 例如 scene 规则、语言码映射、prompt 模板、Marian 方向约束
说明:
service_url、default_model、default_scene只从 YAML 读取- 不再通过环境变量静默覆盖翻译行为配置
- 密钥仍通过环境变量提供
local_nllb/local_marian当前由 CTranslate2 运行;首次启动时若ct2_model_dir不存在,会从model_dir自动转换
4. 环境变量
当前翻译模块主要依赖:
# Qwen / LLM
DASHSCOPE_API_KEY=sk-xxx
# DeepL
DEEPL_AUTH_KEY=xxx
服务启动端口仍可以由启动脚本环境控制:
TRANSLATION_HOST=0.0.0.0
TRANSLATION_PORT=6006
5. Scene 规则
当前只支持 3 个标准 scene:
generalsku_nameecommerce_search_query
定义位置:
约定:
scene是公共接口字段- 不再接受旧的
context - 不再对外暴露
prompt - LLM prompt 在服务内根据
scene自动生成
6. 对外 HTTP 接口
服务入口在:
默认地址:
http://localhost:6006
提供接口:
POST /translateGET /health
6.1 POST /translate
请求体:
{
"text": "商品名称",
"target_lang": "en",
"source_lang": "zh",
"model": "opus-mt-zh-en",
"scene": "sku_name"
}
字段说明:
text支持string或string[]target_lang目标语言source_lang源语言model已配置的 capability 名称scene翻译场景
响应体:
{
"text": "商品名称",
"target_lang": "en",
"source_lang": "zh",
"translated_text": "Product name",
"status": "success",
"model": "opus-mt-zh-en",
"scene": "sku_name"
}
批量时:
- 返回列表和输入等长
- 单条失败返回
null
6.2 GET /health
返回示例:
{
"status": "healthy",
"service": "translation",
"default_model": "llm",
"default_scene": "general",
"available_models": ["qwen-mt", "llm", "nllb-200-distilled-600m", "opus-mt-zh-en", "opus-mt-en-zh"],
"enabled_capabilities": ["qwen-mt", "llm", "nllb-200-distilled-600m", "opus-mt-zh-en", "opus-mt-en-zh"],
"loaded_models": ["qwen-mt", "llm", "nllb-200-distilled-600m", "opus-mt-zh-en", "opus-mt-en-zh"]
}
说明:
- translator service 进程启动时会一次性初始化全部已启用 capability
- 因此本地模型加载失败、依赖缺失、配置错误会在启动阶段直接暴露,而不是拖到首个在线请求
7. 代码调用方式
业务侧统一这样调用:
from translation.client import create_translation_client
translator = create_translation_client()
result = translator.translate(
text="商品名称",
source_lang="zh",
target_lang="en",
model="opus-mt-zh-en",
scene="sku_name",
)
批量调用:
results = translator.translate(
text=["商品1", "商品2"],
source_lang="zh",
target_lang="en",
model="opus-mt-zh-en",
scene="sku_name",
)
接口 shape 约定:
translate(text="...")返回Optional[str]translate(text=[...])返回List[Optional[str]]- 批量模式始终保持“等长、同序返回”;某条失败时对应位置为
None - backend/client 可通过
supports_batch暴露是否支持原生批量;服务端会在必要时自动逐条拆分并保持返回 shape 不变
8. 具体实现说明
8.1 Qwen-MT
实现文件:
特点:
- 云端机翻
- 支持 Redis 翻译缓存
- 适合质量优先、非超高并发场景
注意:
- 当前默认
qwen-mt-flash限速较低 - 大量重复请求应依赖缓存
8.2 LLM Translation
实现文件:
特点:
- 通用大模型翻译
- 根据
scene生成内部 prompt - 更灵活,但成本和稳定性取决于上游模型
- 支持 Redis 翻译缓存
8.3 DeepL
实现文件:
特点:
- 商业翻译 API
- scene 会映射到内部上下文
- 当前默认关闭
- 支持 Redis 翻译缓存
8.4 facebook/nllb-200-distilled-600M
实现文件:
模型信息:
- Hugging Face 名称:
facebook/nllb-200-distilled-600M - 简介:多语种翻译:覆盖约 200 种语言。作为NLLB-200系列的蒸馏版本,该模型通过知识蒸馏技术将原130亿参数模型压缩至600M,同时保持了80%以上的翻译质量。
- 本地目录:
models/translation/facebook/nllb-200-distilled-600M - 当前磁盘占用:约
2.4G - 支持 Redis 翻译缓存
- 模型类型:多语种 Seq2Seq 机器翻译模型
- 来源:Meta NLLB(No Language Left Behind)系列的 600M 蒸馏版
- 结构特点:
- Transformer encoder-decoder 架构
- 12 层 encoder + 12 层 decoder
d_model=1024- 通过
source_lang + forced_bos_token_id控制翻译方向 - 语言标识采用
language_script形式,例如eng_Latn、zho_Hans - 改良 encoder-decoder(含嵌入层缩放
scale_embedding、相对位置等)
核心配置如下:
| 配置项 | 参数值 | 备注 |
|---|---|---|
隐藏层维度(d_model) |
1024 | |
| 编码器 / 解码器层数 | 12 / 12 | |
| 注意力头数 | 16 | |
| FFN 维度 | 4096 | |
| 词表大小 | 256,206 | 多语统一词表 |
| 最大序列长度 | 1024 tokens | 满足长文本翻译 |
config.json 片段(示意):
{
"d_model": 1024,
"encoder_layers": 12,
"decoder_layers": 12,
"attention_dropout": 0.1,
"use_cache": true,
"torch_dtype": "float32",
"max_length": 200
}
模型定位:
- 优势是多语覆盖面广,一个模型可以支撑很多语言方向
- 劣势是相较于 Marian 这种双语专用模型,推理更重、延迟更高
- 更适合做索引翻译(离线 / 批量),不建议作为在线 query 翻译的默认方案
显存占用情况:
- 600M 模型半精度(float16)权重约
~1.25G;推理还会叠加 CUDA context、allocator reserve、激活张量、batch、输入/生成长度等开销 - 当前这台
Tesla T4上,优化后的实际运行峰值大约在2.8-3.0 GiB
当前实现特点:
- backend 类型:
local_nllb - 运行时:
CTranslate2 Translator - 支持多语
- 调用时必须显式传
source_lang - 语言码映射定义在 <code>translation/languages.py</code>
- 当前 T4 推荐配置:
device=cuda、ct2_compute_type=float16、ct2_inter_threads=4、ct2_max_queued_batches=32、ct2_batch_type=examples、ct2_decoding_length_mode=source(+8,min=32)、batch_size=16、max_new_tokens=64
当前实现已经利用的优化:
- 已做批量分块:
translate()会按 capability 的batch_size分批进入模型 - 已切换到 CTranslate2 推理引擎:不再依赖 PyTorch
generate() - 已设置方向控制:NLLB 通过 target prefix 指定目标语言
- 已启用半精度:当前配置为
float16 - 已关闭高开销搜索:默认
num_beams=1,更接近线上低延迟设置
和你给出的批处理示例对照:
- 核心思路已经覆盖,现有实现与
tokenizer(batch) -> model.generate(...) -> batch_decode(...)一致 - 差异在于服务端额外做了语言校验、统一 chunking、输入长度约束和单条/批量 shape 保持
- “预计算 attention mask” 目前没有单独缓存层;现状是每个 batch 在 tokenizer 阶段实时生成
attention_mask,这也是 HF 常规推理路径
优化空间(按场景):
- 线上 query:优先补测
batch_size=1的真实延迟与 tail latency,而不是继续拉大 batch。 - 离线批量:可再尝试更激进的 batching / 长度分桶 / 独立批处理队列(吞吐更高,但会增加在线尾延迟风险)。
- 进一步降显存 / 提速:可在当前 CT2 方案上继续评估
int8_float16。
8.5 opus-mt-zh-en
实现文件:
模型信息:
- Hugging Face 名称:
Helsinki-NLP/opus-mt-zh-en - 本地目录:
models/translation/Helsinki-NLP/opus-mt-zh-en - 当前磁盘占用:约
1.2G - 模型类型:Marian / OPUS MT 专用双语翻译模型
- 方向约束:只支持
zh -> en
结构特点:
- encoder-decoder Seq2Seq
- 聚焦特定语言对
- 模型更小、加载更轻、吞吐更高
- 支持 Redis 翻译缓存
8.6 opus-mt-en-zh
实现文件:
模型信息:
- Hugging Face 名称:
Helsinki-NLP/opus-mt-en-zh - 本地目录:
models/translation/Helsinki-NLP/opus-mt-en-zh - 当前磁盘占用:约
1.5G - 模型类型:Marian / OPUS MT 专用双语翻译模型
- 方向约束:只支持
en -> zh
结构特点:
- encoder-decoder Seq2Seq
- 双语定向模型
- 更适合中英双向拆分部署
- 支持 Redis 翻译缓存
8.7 翻译缓存
- 所有 translation capability 都使用统一的 Redis 缓存层
- 每个 capability 通过各自的
use_cache控制是否启用缓存 - 缓存 key 格式固定为
trans:{model}:{target_lang}:{source_text[:4]}{sha256}
9. 本地模型安装与部署
9.1 准备环境
cd /data/saas-search
./scripts/setup_translator_venv.sh
9.2 下载模型
下载全部本地模型:
./.venv-translator/bin/python scripts/download_translation_models.py --all-local
下载完成后,默认目录应存在:
models/translation/facebook/nllb-200-distilled-600M
models/translation/Helsinki-NLP/opus-mt-zh-en
models/translation/Helsinki-NLP/opus-mt-en-zh
9.3 打开能力
编辑 <code>config/config.yaml</code>,把对应模型的 enabled 改成 true。
9.4 启动服务
./scripts/start_translator.sh
建议:
- 本地模型服务使用单 worker
- 避免多 worker 重复加载模型
- GPU 机器上优先使用
cuda + float16 - CPU 只建议用于功能验证或离线低频任务
- 对 NLLB,T4 上优先采用
batch_size=16 + max_new_tokens=64 + ct2_compute_type=float16 + ct2_inter_threads=4 + ct2_max_queued_batches=32 + ct2_batch_type=examples + ct2_decoding_length_mode=source(+8,min=32)
9.5 验证
健康检查:
curl http://127.0.0.1:6006/health
翻译测试:
curl -X POST http://127.0.0.1:6006/translate \
-H 'Content-Type: application/json' \
-d '{
"text": "男士偏光飞行员太阳镜",
"source_lang": "zh",
"target_lang": "en",
"model": "opus-mt-zh-en",
"scene": "sku_name"
}'
10. 性能测试与复现
说明:
- 本节现有数值是
2026-03-18的 Hugging Face / PyTorch 基线结果。 - 切换到 CTranslate2 后需要重新跑一轮基准,尤其关注
nllb-200-distilled-600m的单条延迟、并发 tail latency 和opus-mt-*的 batch throughput。
性能脚本:
- <code>benchmarks/translation/benchmark_translation_local_models.py</code>
- <code>benchmarks/translation/benchmark_translation_local_models_focus.py</code>
数据集:
最新报告:
- 摘要:<code>perf_reports/20260318/translation_local_models/README.md</code>
- 完整 Markdown:<code>perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.md</code>
- 完整 JSON:<code>perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.json</code>
- CT2 扩展总结:<code>perf_reports/20260318/translation_local_models_ct2/README.md</code>
- CT2 聚焦调优总结:<code>perf_reports/20260318/translation_local_models_ct2_focus/README.md</code>
- NLLB T4 商品标题专项调优:<code>perf_reports/20260318/nllb_t4_product_names_ct2/README.md</code>
10.1 先看哪组数据
这里把 3 类结果分开看,不再混在一张表里:
batch_sweep固定concurrency=1,只比较不同batch_size的单流批处理性能concurrency_sweep固定batch_size=1,看“单条请求”在不同并发下的延迟和吞吐batch x concurrency matrix同时看batch_size和concurrency的交互效应;本轮限制为batch_size * concurrency <= 128
建议:
- 看线上 query 翻译延迟:优先看
concurrency_sweep - 看离线批量翻译吞吐:优先看
batch_sweep - 看单 worker 服务容量边界:再看
batch x concurrency matrix
10.2 本轮补测参数
测试时间:2026-03-18
环境:
- GPU:
Tesla T4 16GB - Python env:
.venv-translator - Torch / Transformers:
2.10.0+cu128 / 5.3.0
统一参数:
- cache:关闭(
--disable-cache),避免缓存命中干扰性能结果 batch_sweep:每档256itemsconcurrency_sweep:固定batch_size=1,每档32requestsbatch x concurrency matrix:每档32requests,且只保留batch_size * concurrency <= 128- 预热:
1batch
复现命令:
cd /data/saas-search
./.venv-translator/bin/python benchmarks/translation/benchmark_translation_local_models.py
本轮扩展压测复现命令:
cd /data/saas-search
./.venv-translator/bin/python benchmarks/translation/benchmark_translation_local_models.py \
--suite extended \
--disable-cache \
--serial-items-per-case 256 \
--concurrency-requests-per-case 32 \
--concurrency-batch-size 1 \
--output-dir perf_reports/20260318/translation_local_models
单模型扩展压测示例:
./.venv-translator/bin/python benchmarks/translation/benchmark_translation_local_models.py \
--single \
--suite extended \
--model opus-mt-zh-en \
--source-lang zh \
--target-lang en \
--column title_cn \
--scene sku_name \
--disable-cache \
--batch-size-list 1,4,8,16,32,64 \
--concurrency-list 1,2,4,8,16,64 \
--serial-items-per-case 256 \
--concurrency-requests-per-case 32 \
--concurrency-batch-size 1
单条请求延迟复现:
./.venv-translator/bin/python benchmarks/translation/benchmark_translation_local_models.py \
--single \
--suite extended \
--model nllb-200-distilled-600m \
--source-lang zh \
--target-lang en \
--column title_cn \
--scene sku_name \
--disable-cache \
--batch-size-list 1 \
--concurrency-list 1,2,4,8,16,64 \
--serial-items-per-case 256 \
--concurrency-requests-per-case 32 \
--concurrency-batch-size 1
10.3 单流 batch 结果
这组只看 concurrency=1,不要把这里的 request p95 当作线上并发请求的 p95。
nllb-200-distilled-600m zh -> en
| Batch | Items/s | Avg item ms | Req p95 ms |
|---|---|---|---|
| 1 | 2.91 | 343.488 | 616.27 |
| 4 | 8.44 | 118.545 | 722.95 |
| 8 | 14.85 | 67.335 | 728.47 |
| 16 | 27.28 | 36.662 | 769.18 |
| 32 | 38.6 | 25.908 | 1369.88 |
| 64 | 58.3 | 17.152 | 1659.9 |
nllb-200-distilled-600m en -> zh
| Batch | Items/s | Avg item ms | Req p95 ms |
|---|---|---|---|
| 1 | 1.91 | 524.917 | 866.33 |
| 4 | 4.94 | 202.473 | 1599.74 |
| 8 | 8.25 | 121.188 | 1632.29 |
| 16 | 13.52 | 73.956 | 1649.65 |
| 32 | 21.27 | 47.017 | 1827.16 |
| 64 | 32.64 | 30.641 | 2031.25 |
opus-mt-zh-en zh -> en
| Batch | Items/s | Avg item ms | Req p95 ms |
|---|---|---|---|
| 1 | 6.15 | 162.536 | 274.74 |
| 4 | 15.34 | 65.192 | 356.0 |
| 8 | 25.51 | 39.202 | 379.84 |
| 16 | 41.44 | 24.129 | 797.93 |
| 32 | 54.36 | 18.397 | 1693.31 |
| 64 | 70.15 | 14.255 | 2161.59 |
opus-mt-en-zh en -> zh
| Batch | Items/s | Avg item ms | Req p95 ms |
|---|---|---|---|
| 1 | 4.53 | 220.598 | 411.57 |
| 4 | 10.12 | 98.844 | 761.49 |
| 8 | 14.63 | 68.361 | 1930.85 |
| 16 | 24.33 | 41.1 | 2098.54 |
| 32 | 33.91 | 29.487 | 2152.28 |
| 64 | 42.47 | 23.547 | 2371.85 |
批处理结论:
- 纯吞吐看,4 个方向的最佳 raw throughput 都出现在
batch_size=64 - 如果还要兼顾单个 batch 的尾延迟,
batch_size=16往往更均衡 opus-mt-zh-en是本轮 bulk 场景最快模型,nllb en->zh最慢
10.4 单条请求并发结果
这组固定 batch_size=1,可以直接理解成“单条请求在不同并发下的表现”。
nllb-200-distilled-600m zh -> en
| Concurrency | Items/s | Avg req ms | Req p50 ms | Req p95 ms |
|---|---|---|---|---|
| 1 | 4.17 | 239.99 | 226.34 | 373.27 |
| 2 | 4.1 | 477.99 | 459.36 | 703.96 |
| 4 | 4.1 | 910.74 | 884.71 | 1227.01 |
| 8 | 4.04 | 1697.73 | 1818.48 | 2383.8 |
| 16 | 4.07 | 2801.91 | 3473.63 | 4145.92 |
| 64 | 4.04 | 3714.49 | 3610.08 | 7337.3 |
nllb-200-distilled-600m en -> zh
| Concurrency | Items/s | Avg req ms | Req p50 ms | Req p95 ms |
|---|---|---|---|---|
| 1 | 2.16 | 463.18 | 439.54 | 670.78 |
| 2 | 2.15 | 920.48 | 908.27 | 1213.3 |
| 4 | 2.16 | 1759.87 | 1771.58 | 2158.04 |
| 8 | 2.15 | 3284.44 | 3658.45 | 3971.01 |
| 16 | 2.14 | 5669.15 | 7117.7 | 7522.48 |
| 64 | 2.14 | 7631.14 | 7510.97 | 14139.03 |
opus-mt-zh-en zh -> en
| Concurrency | Items/s | Avg req ms | Req p50 ms | Req p95 ms |
|---|---|---|---|---|
| 1 | 9.21 | 108.53 | 91.7 | 179.12 |
| 2 | 8.92 | 219.19 | 212.29 | 305.34 |
| 4 | 9.09 | 411.76 | 420.08 | 583.97 |
| 8 | 8.85 | 784.14 | 835.73 | 1043.06 |
| 16 | 9.01 | 1278.4 | 1483.34 | 1994.56 |
| 64 | 8.82 | 1687.08 | 1563.48 | 3381.58 |
opus-mt-en-zh en -> zh
| Concurrency | Items/s | Avg req ms | Req p50 ms | Req p95 ms |
|---|---|---|---|---|
| 1 | 3.6 | 277.73 | 145.85 | 1180.37 |
| 2 | 3.55 | 559.38 | 346.71 | 1916.96 |
| 4 | 3.53 | 997.71 | 721.04 | 2944.17 |
| 8 | 3.51 | 1644.28 | 1590.93 | 3632.99 |
| 16 | 3.5 | 2600.18 | 2586.34 | 5554.04 |
| 64 | 3.52 | 3366.52 | 2780.0 | 7950.41 |
并发结论:
- 当前本地 seq2seq backend 内部是单模型锁,单 worker 下提高客户端并发基本不会提升吞吐,主要会把等待时间堆到请求延迟上
- 线上 query 翻译如果追求稳定延迟,应优先控制在低并发;
8+并发后,4 个方向的 p95 都明显恶化 - 在线场景里,
opus-mt-zh-en延迟最稳;nllb en->zh最慢,且并发放大后尾延迟最明显
10.5 batch x concurrency 怎么看
完整矩阵见:
这张表主要回答两个问题:
- 如果已经知道自己要跑离线批处理,
batch_size拉大后,在不同并发下吞吐会不会继续涨 - 如果要拿单 worker 服务扛请求,在哪个
batch_size x concurrency组合下开始明显排队
本轮矩阵的共同特征:
- 吞吐主要由
batch_size决定,concurrency不是主要增益来源 - 在
batch_size固定时,concurrency从1升到2/4/8/...,items/s变化很小,但avg req ms / p95会持续抬升 - 因此当前实现更像“单 worker + 内部串行 GPU 推理服务”,不是一个靠客户端并发放大吞吐的服务
NLLB 性能优化经验:
- 起作用的优化点 1:
float16 + cuda- 当前本地 NLLB 由
CTranslate2在cuda:0以float16运行 - 优化后在 T4 上的峰值显存约
2.8-3.0 GiB
- 当前本地 NLLB 由
- 起作用的优化点 2:
batch_size=16- 相比
batch_size=8,吞吐提升明显 - 继续提升到
32虽然还能增吞吐,但 batch p95 和 batch max 会恶化很多
- 相比
- 起作用的优化点 3:
ct2_inter_threads=4 + ct2_max_queued_batches=32- 对
batch=1高并发商品标题场景收益最直接 - 相比默认 CT2 配置,
zh->en和en->zh的在线吞吐都能稳定提升
- 对
- 起作用的优化点 4:动态解码上限
- 推荐
ct2_decoding_length_mode=source - 推荐
ct2_decoding_length_extra=8 - 推荐
ct2_decoding_length_min=32 - 这样可以保留
max_new_tokens=64的安全上限,同时让短标题不再为长标题上限付费
- 推荐
- 起作用的优化点 5:
ct2_batch_type=examples- 在当前数据和 T4 上,比
tokens更稳 - 更适合作为线上默认
- 在当前数据和 T4 上,比
- 不建议直接作为默认的实验:
max_new_tokens=48- 大 batch 和在线吞吐都会继续变好
- 但商品标题 spot-check 已看到明显截断,尤其
en->zh
- 收益有限或不稳定的实验:
ct2_batch_type=tokensct2_max_queued_batches从16再继续拉高,收益很小ct2_decoding_length_mode=source(+4,min=24)更快,但仍有少量长标题截断风险
为什么最终没有采用其它方案:
- 当前本地最优路径已经切到
CTranslate2 + float16 - 对这个 600M 级 encoder-decoder 模型,T4 上最有效的是把 CT2 的并行和解码策略调对
- 因此这轮没有继续引入更重的服务化栈
- 如果未来目标变成“继续压缩显存”或“进一步追求更低延迟”,再评估
int8_float16或服务级微批处理队列会更合适
关键结论:
- 当前机器上,
opus-mt-zh-en是三个新增本地模型里最快的 opus-mt-en-zh大约是opus-mt-zh-en吞吐的一半nllb-200-distilled-600M在 T4 上推荐cuda + CTranslate2 float16 + batch_size=16 + ct2_inter_threads=4 + ct2_max_queued_batches=32 + dynamic decodingnllb最终可用,但吞吐仍明显低于两个 Marian 模型,更适合多语覆盖或独立资源环境
最终推荐部署方案:
- 模型:
facebook/nllb-200-distilled-600M - 设备:
cuda - 精度:
float16 - 推荐卡型:至少
Tesla T4 16GB这一级别 - 推荐 batch:
16 - 推荐
max_input_length:256 - 推荐
max_new_tokens:64 - 推荐
num_beams:1 - 推荐 CT2 并行:
ct2_inter_threads=4 - 推荐 CT2 队列:
ct2_max_queued_batches=32 - 推荐 CT2 batch 类型:
examples - 推荐动态解码:
ct2_decoding_length_mode=source、ct2_decoding_length_extra=8、ct2_decoding_length_min=32 - 运行方式:单 worker,避免重复加载
更详细的性能说明见:
- <code>perf_reports/20260318/translation_local_models/README.md</code>
- <code>perf_reports/20260318/nllb_t4_product_names_ct2/README.md</code>
11. 开发说明
如果要新增翻译 backend,最少需要做这些事:
- 在 <code>translation/backends/</code> 下新增实现
- 在 <code>translation/service.py</code> 注册 backend 创建逻辑
- 在 <code>config/config.yaml</code> 的
services.translation.capabilities中新增 capability 配置 - 如果有新的静态规则:
- scene 规则放到 <code>translation/scenes.py</code>
- 语言映射放到 <code>translation/languages.py</code>
- prompt 模板放到 <code>translation/prompts.py</code>
原则:
- 不要再引入 translation provider 兼容层
- 不要把 scene / prompt / 语言方向规则重新散落到别的目录
- 不要在代码里写隐式默认和静默兼容
12. 常见建议
- 中英商品标题双向场景,优先考虑
opus-mt-zh-en和opus-mt-en-zh - 多语种统一方案,可以考虑
nllb-200-distilled-600M - 但
nllb更适合独占资源环境 - 如果追求更高质量或更复杂语义处理,可使用
qwen-mt或llm - 如果追求稳定商业 API,可考虑
deepl
13. 相关文档
- <code>docs/翻译模块说明.md</code>(已收口到本 README,保留为跳转页)
- <code>docs/QUICKSTART.md</code>
- <code>docs/DEVELOPER_GUIDE.md</code>
- <code>docs/搜索API对接指南-00-总览与快速开始.md</code>(分册导航;微服务见
-07)