# Translation Module `translation/` 是当前项目翻译能力的主目录。 如果要开发、部署、联调、压测翻译服务,优先看这份文档。 对应服务: - translator service:`http://127.0.0.1:6006` - 业务侧统一通过 [`translation/client.py`](/data/saas-search/translation/client.py) 调用 - 服务内统一通过 [`translation/service.py`](/data/saas-search/translation/service.py) 路由到具体翻译实现 相关脚本与报告: - 启动脚本:[`scripts/start_translator.sh`](/data/saas-search/scripts/start_translator.sh) - 虚拟环境:[`scripts/setup_translator_venv.sh`](/data/saas-search/scripts/setup_translator_venv.sh) - 模型下载:[`scripts/translation/download_translation_models.py`](/data/saas-search/scripts/translation/download_translation_models.py) - 本地模型压测:[`benchmarks/translation/benchmark_translation_local_models.py`](/data/saas-search/benchmarks/translation/benchmark_translation_local_models.py) - 聚焦压测脚本:[`benchmarks/translation/benchmark_translation_local_models_focus.py`](/data/saas-search/benchmarks/translation/benchmark_translation_local_models_focus.py) - 基线性能报告:[`perf_reports/20260318/translation_local_models/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models/README.md) - CT2 扩展报告:[`perf_reports/20260318/translation_local_models_ct2/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models_ct2/README.md) - CT2 聚焦调优报告:[`perf_reports/20260318/translation_local_models_ct2_focus/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models_ct2_focus/README.md) - NLLB T4 商品标题专项报告:[`perf_reports/20260318/nllb_t4_product_names_ct2/README.md`](/data/saas-search/perf_reports/20260318/nllb_t4_product_names_ct2/README.md) ## 1. 设计目标 翻译模块采用: - 一个 translator service - 多个 capability backend - 一个统一外部接口:`model + scene` 这套设计的目标是: - 翻译能力可以独立扩展、独立启停 - scene、语言码、prompt 模板、模型方向约束等翻译域知识集中在 `translation/` - 配置尽量集中在 [`config/config.yaml`](/data/saas-search/config/config.yaml) 的 `services.translation` - 配置错误应尽早报错,不做静默兼容和隐式回退 ## 2. 目录结构 核心文件: - [`translation/client.py`](/data/saas-search/translation/client.py) 业务侧 HTTP client,供 query/indexer 等模块调用 - [`translation/service.py`](/data/saas-search/translation/service.py) translator service 内部的统一编排层 - [`translation/settings.py`](/data/saas-search/translation/settings.py) 翻译配置的规范化与校验辅助函数 - [`translation/scenes.py`](/data/saas-search/translation/scenes.py) scene 规范和值校验 - [`translation/languages.py`](/data/saas-search/translation/languages.py) 语言码映射、Marian 方向映射等静态知识 - [`translation/prompts.py`](/data/saas-search/translation/prompts.py) LLM 翻译 prompt 模板 - [`translation/protocols.py`](/data/saas-search/translation/protocols.py) 输入输出协议类型 后端实现: - [`translation/backends/qwen_mt.py`](/data/saas-search/translation/backends/qwen_mt.py) Qwen-MT 云端翻译 - [`translation/backends/llm.py`](/data/saas-search/translation/backends/llm.py) 通用 LLM 翻译 - [`translation/backends/deepl.py`](/data/saas-search/translation/backends/deepl.py) DeepL 翻译 - [`translation/backends/local_ctranslate2.py`](/data/saas-search/translation/backends/local_ctranslate2.py) 本地 CTranslate2 翻译模型,包括 NLLB 和 Marian/OPUS MT ## 3. 配置约定 翻译的部署配置统一放在: - [`config/config.yaml`](/data/saas-search/config/config.yaml) -> `services.translation` 示例: ```yaml 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. 环境变量 当前翻译模块主要依赖: ```bash # Qwen / LLM DASHSCOPE_API_KEY=sk-xxx # DeepL DEEPL_AUTH_KEY=xxx ``` 服务启动端口仍可以由启动脚本环境控制: ```bash TRANSLATION_HOST=0.0.0.0 TRANSLATION_PORT=6006 ``` ## 5. Scene 规则 当前只支持 3 个标准 scene: - `general` - `sku_name` - `ecommerce_search_query` 定义位置: - [`translation/scenes.py`](/data/saas-search/translation/scenes.py) 约定: - `scene` 是公共接口字段 - 不再接受旧的 `context` - 不再对外暴露 `prompt` - LLM prompt 在服务内根据 `scene` 自动生成 ## 6. 对外 HTTP 接口 服务入口在: - [`api/translator_app.py`](/data/saas-search/api/translator_app.py) 默认地址: - `http://localhost:6006` 提供接口: - `POST /translate` - `GET /health` ### 6.1 POST /translate 请求体: ```json { "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` 翻译场景 响应体: ```json { "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 返回示例: ```json { "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. 代码调用方式 业务侧统一这样调用: ```python 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", ) ``` 批量调用: ```python 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 实现文件: - [`translation/backends/qwen_mt.py`](/data/saas-search/translation/backends/qwen_mt.py) 特点: - 云端机翻 - 支持 Redis 翻译缓存 - 适合质量优先、非超高并发场景 注意: - 当前默认 `qwen-mt-flash` 限速较低 - 大量重复请求应依赖缓存 ### 8.2 LLM Translation 实现文件: - [`translation/backends/llm.py`](/data/saas-search/translation/backends/llm.py) 特点: - 通用大模型翻译 - 根据 `scene` 生成内部 prompt - 更灵活,但成本和稳定性取决于上游模型 - 支持 Redis 翻译缓存 ### 8.3 DeepL 实现文件: - [`translation/backends/deepl.py`](/data/saas-search/translation/backends/deepl.py) 特点: - 商业翻译 API - scene 会映射到内部上下文 - 当前默认关闭 - 支持 Redis 翻译缓存 ### 8.4 `facebook/nllb-200-distilled-600M` 实现文件: - [`translation/backends/local_ctranslate2.py`](/data/saas-search/translation/backends/local_ctranslate2.py) 模型信息: - 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` 片段(示意): ```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` - 语言码映射定义在 [`translation/languages.py`](/data/saas-search/translation/languages.py) - 当前 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` 实现文件: - [`translation/backends/local_ctranslate2.py`](/data/saas-search/translation/backends/local_ctranslate2.py) 模型信息: - 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` 实现文件: - [`translation/backends/local_ctranslate2.py`](/data/saas-search/translation/backends/local_ctranslate2.py) 模型信息: - 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 准备环境 ```bash cd /data/saas-search ./scripts/setup_translator_venv.sh ``` ### 9.2 下载模型 下载全部本地模型: ```bash ./.venv-translator/bin/python scripts/translation/download_translation_models.py --all-local ``` 下载完成后,默认目录应存在: ```bash 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 打开能力 编辑 [`config/config.yaml`](/data/saas-search/config/config.yaml),把对应模型的 `enabled` 改成 `true`。 ### 9.4 启动服务 ```bash ./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 验证 健康检查: ```bash curl http://127.0.0.1:6006/health ``` 翻译测试: ```bash 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。 性能脚本: - [`benchmarks/translation/benchmark_translation_local_models.py`](/data/saas-search/benchmarks/translation/benchmark_translation_local_models.py) - [`benchmarks/translation/benchmark_translation_local_models_focus.py`](/data/saas-search/benchmarks/translation/benchmark_translation_local_models_focus.py) 数据集: - [`products_analyzed.csv`](/data/saas-search/products_analyzed.csv) 最新报告: - 摘要:[`perf_reports/20260318/translation_local_models/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models/README.md) - 完整 Markdown:[`perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.md`](/data/saas-search/perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.md) - 完整 JSON:[`perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.json`](/data/saas-search/perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.json) - CT2 扩展总结:[`perf_reports/20260318/translation_local_models_ct2/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models_ct2/README.md) - CT2 聚焦调优总结:[`perf_reports/20260318/translation_local_models_ct2_focus/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models_ct2_focus/README.md) - NLLB T4 商品标题专项调优:[`perf_reports/20260318/nllb_t4_product_names_ct2/README.md`](/data/saas-search/perf_reports/20260318/nllb_t4_product_names_ct2/README.md) ### 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`:每档 `256` items - `concurrency_sweep`:固定 `batch_size=1`,每档 `32` requests - `batch x concurrency matrix`:每档 `32` requests,且只保留 `batch_size * concurrency <= 128` - 预热:`1` batch 复现命令: ```bash cd /data/saas-search ./.venv-translator/bin/python benchmarks/translation/benchmark_translation_local_models.py ``` 本轮扩展压测复现命令: ```bash 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 ``` 单模型扩展压测示例: ```bash ./.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 ``` 单条请求延迟复现: ```bash ./.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 怎么看 完整矩阵见: - [`perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.md`](/data/saas-search/perf_reports/20260318/translation_local_models/translation_local_models_extended_221846.md) 这张表主要回答两个问题: - 如果已经知道自己要跑离线批处理,`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` - 起作用的优化点 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` 更稳 - 更适合作为线上默认 - 不建议直接作为默认的实验: - `max_new_tokens=48` - 大 batch 和在线吞吐都会继续变好 - 但商品标题 spot-check 已看到明显截断,尤其 `en->zh` - 收益有限或不稳定的实验: - `ct2_batch_type=tokens` - `ct2_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 decoding` - `nllb` 最终可用,但吞吐仍明显低于两个 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,避免重复加载 更详细的性能说明见: - [`perf_reports/20260318/translation_local_models/README.md`](/data/saas-search/perf_reports/20260318/translation_local_models/README.md) - [`perf_reports/20260318/nllb_t4_product_names_ct2/README.md`](/data/saas-search/perf_reports/20260318/nllb_t4_product_names_ct2/README.md) ## 11. 开发说明 如果要新增翻译 backend,最少需要做这些事: 1. 在 [`translation/backends/`](/data/saas-search/translation/backends) 下新增实现 2. 在 [`translation/service.py`](/data/saas-search/translation/service.py) 注册 backend 创建逻辑 3. 在 [`config/config.yaml`](/data/saas-search/config/config.yaml) 的 `services.translation.capabilities` 中新增 capability 配置 4. 如果有新的静态规则: - scene 规则放到 [`translation/scenes.py`](/data/saas-search/translation/scenes.py) - 语言映射放到 [`translation/languages.py`](/data/saas-search/translation/languages.py) - prompt 模板放到 [`translation/prompts.py`](/data/saas-search/translation/prompts.py) 原则: - 不要再引入 translation provider 兼容层 - 不要把 scene / prompt / 语言方向规则重新散落到别的目录 - 不要在代码里写隐式默认和静默兼容 ## 12. 常见建议 - 中英商品标题双向场景,优先考虑 `opus-mt-zh-en` 和 `opus-mt-en-zh` - 多语种统一方案,可以考虑 `nllb-200-distilled-600M` - 但 `nllb` 更适合独占资源环境 - 如果追求更高质量或更复杂语义处理,可使用 `qwen-mt` 或 `llm` - 如果追求稳定商业 API,可考虑 `deepl` ## 13. 相关文档 - [`docs/翻译模块说明.md`](/data/saas-search/docs/翻译模块说明.md)(已收口到本 README,保留为跳转页) - [`docs/QUICKSTART.md`](/data/saas-search/docs/QUICKSTART.md) - [`docs/DEVELOPER_GUIDE.md`](/data/saas-search/docs/DEVELOPER_GUIDE.md) - [`docs/搜索API对接指南-00-总览与快速开始.md`](/data/saas-search/docs/搜索API对接指南-00-总览与快速开始.md)(分册导航;微服务见 `-07`)