README.md 21 KB

Translation Module

translation/ 是当前项目翻译能力的主目录。
如果要开发、部署、联调、压测翻译服务,优先看这份文档。

对应服务:

相关脚本与报告:

1. 设计目标

翻译模块采用:

  • 一个 translator service
  • 多个 capability backend
  • 一个统一外部接口:model + scene

这套设计的目标是:

  • 翻译能力可以独立扩展、独立启停
  • scene、语言码、prompt 模板、模型方向约束等翻译域知识集中在 translation/
  • 配置尽量集中在 <code>config/config.yaml</code>services.translation
  • 配置错误应尽早报错,不做静默兼容和隐式回退

2. 目录结构

核心文件:

后端实现:

3. 配置约定

翻译的部署配置统一放在:

示例:

services:
  translation:
    service_url: "http://127.0.0.1:6006"
    default_model: "llm"
    default_scene: "general"
    timeout_sec: 10.0
    cache:
      enabled: true
      key_prefix: "trans:v2"
      ttl_seconds: 62208000
      sliding_expiration: true
      key_include_scene: true
      key_include_source_lang: 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
      deepl:
        enabled: false
        backend: "deepl"
        api_url: "https://api.deepl.com/v2/translate"
        timeout_sec: 10.0
      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"
        device: "cuda"
        torch_dtype: "float16"
        batch_size: 16
        max_input_length: 256
        max_new_tokens: 64
        num_beams: 1
        attn_implementation: "sdpa"
      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"
        device: "cuda"
        torch_dtype: "float16"
        batch_size: 16
        max_input_length: 256
        max_new_tokens: 256
        num_beams: 1
      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"
        device: "cuda"
        torch_dtype: "float16"
        batch_size: 16
        max_input_length: 256
        max_new_tokens: 256
        num_beams: 1

配置边界:

  • config.yaml 只放部署和运行参数 例如 service_urldefault_modeldefault_sceneenabledbase_urlapi_urlmodel_dirdevice
  • translation 目录内部放翻译静态知识 例如 scene 规则、语言码映射、prompt 模板、Marian 方向约束

说明:

  • service_urldefault_modeldefault_scene 只从 YAML 读取
  • 不再通过环境变量静默覆盖翻译行为配置
  • 密钥仍通过环境变量提供

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:

  • general
  • sku_name
  • ecommerce_search_query

定义位置:

约定:

  • scene 是公共接口字段
  • 不再接受旧的 context
  • 不再对外暴露 prompt
  • LLM prompt 在服务内根据 scene 自动生成

6. 对外 HTTP 接口

服务入口在:

默认地址:

  • http://localhost:6006

提供接口:

  • POST /translate
  • GET /health

6.1 POST /translate

请求体:

{
  "text": "商品名称",
  "target_lang": "en",
  "source_lang": "zh",
  "model": "opus-mt-zh-en",
  "scene": "sku_name"
}

字段说明:

  • text 支持 stringstring[]
  • 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": ["llm"]
}

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
  • 更灵活,但成本和稳定性取决于上游模型

8.3 DeepL

实现文件:

特点:

  • 商业翻译 API
  • scene 会映射到内部上下文
  • 当前默认关闭

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
  • 模型类型:多语种 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_Latnzho_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
  • 支持多语
  • 调用时必须显式传 source_lang
  • 语言码映射定义在 <code>translation/languages.py</code>
  • 当前 T4 推荐配置:device=cudatorch_dtype=float16batch_size=16max_new_tokens=64attn_implementation=sdpa

当前实现已经利用的优化:

  • 已做批量分块:translate() 会按 capability 的 batch_size 分批进入模型
  • 已做动态 padding:tokenizer 使用 padding=Truetruncation=True
  • 已传入 attention_mask:由 tokenizer 生成并随 generate() 一起送入模型
  • 已设置方向控制:NLLB 通过 tokenizer.src_langforced_bos_token_id 指定语言对
  • 已启用推理态:torch.inference_mode() + model.eval()
  • 已启用半精度和更优注意力实现:当前配置为 float16 + sdpa
  • 已关闭高开销搜索:默认 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 / 长度分桶 / 独立批处理队列(吞吐更高,但会增加在线尾延迟风险)。
  • 进一步降显存 / 提速:可评估 ctranslate2 / int8;当前仓库尚未引入该运行栈。

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
  • 聚焦特定语言对
  • 模型更小、加载更轻、吞吐更高

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
  • 双语定向模型
  • 更适合中英双向拆分部署

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 + attn_implementation=sdpa

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. 性能测试与复现

性能脚本:

数据集:

复现命令:

cd /data/saas-search
./.venv-translator/bin/python scripts/benchmark_translation_local_models.py

单模型复现示例:

./.venv-translator/bin/python scripts/benchmark_translation_local_models.py \
  --single \
  --model opus-mt-zh-en \
  --source-lang zh \
  --target-lang en \
  --column title_cn \
  --scene sku_name

单条请求延迟复现:

./.venv-translator/bin/python scripts/benchmark_translation_local_models.py \
  --single \
  --model nllb-200-distilled-600m \
  --source-lang zh \
  --target-lang en \
  --column title_cn \
  --scene sku_name \
  --batch-size 1 \
  --limit 100

说明:

  • 对当前脚本和本地 backend 来说,“单条请求”可以直接等价理解为 batch_size=1
  • 此时脚本里的 batch_latency_*,就可以直接视为“单次请求延迟”指标
  • 线上搜索 query 翻译更应该关注这组数据,而不是大 batch 吞吐

当前单条请求实测(Tesla T4limit=100):

  • nllb-200-distilled-600m zh->en:p50 约 292.54 ms,p95 约 624.12 ms,平均约 321.91 ms
  • nllb-200-distilled-600m en->zh:p50 约 481.61 ms,p95 约 1171.71 ms,平均约 542.47 ms

当前压测环境:

  • GPU:Tesla T4 16GB
  • Python env:.venv-translator
  • 数据量:18,576 条商品标题

最终性能结果:

Model Direction Device Rows Load s Translate s Items/s Avg item ms Batch p50 ms Batch p95 ms
opus-mt-zh-en zh -> en cuda 18,576 3.1435 497.7513 37.32 26.795 301.99 1835.81
opus-mt-en-zh en -> zh cuda 18,576 3.1867 987.3994 18.81 53.155 449.14 2012.12
nllb-200-distilled-600m zh -> en cuda 500 7.3397 25.9577 19.26 51.915 832.64 1263.01
nllb-200-distilled-600m en -> zh cuda 500 7.4152 42.0405 11.89 84.081 1093.87 2107.44

NLLB 性能优化经验:

  • 起作用的优化点 1:float16 + cuda
    • 模型确认以 torch.float16 实际加载到 cuda:0
    • 优化后在 T4 上的峰值显存约 2.8-3.0 GiB
  • 起作用的优化点 2:batch_size=16
    • 相比 batch_size=8,吞吐提升明显
    • 继续提升到 32 虽然还能增吞吐,但 batch p95 和 batch max 会恶化很多
  • 起作用的优化点 3:max_new_tokens=64
    • 商品标题翻译通常不需要 256 的生成上限
    • 收紧生成长度后,zh->enen->zh 都有明显收益
  • 起作用的优化点 4:attn_implementation=sdpa
    • 对当前 PyTorch + T4 环境有效
    • 配合半精度和较合理 batch size 后,整体延迟进一步下降
  • 已有但不需要单独开关的点:attention_mask
    • 当前实现会在 tokenizer 阶段自动生成并传入 generate()
    • 它属于标准推理路径,不是一个额外的“高级优化开关”

为什么最终没有采用其它方案:

  • 当前 HF 原生方案已经能在 T4 上稳定跑通
  • 10G+ 可用显存下,原生 float16 已足够支撑 NLLB-600M
  • 因此暂时不需要为这个模型额外引入 GGUF 或 CT2 的新运行栈
  • 如果未来目标变成“继续压缩显存”或“进一步追求更低延迟”,再评估 ct2-int8 会更合适

关键结论:

  • 当前机器上,opus-mt-zh-en 是三个新增本地模型里最快的
  • opus-mt-en-zh 大约是 opus-mt-zh-en 吞吐的一半
  • nllb-200-distilled-600M 在显存充足时可以用 cuda + float16 + batch_size=16 + max_new_tokens=64 + sdpa 正常运行
  • nllb 最终可用,但吞吐仍明显低于两个 Marian 模型,更适合多语覆盖或独立资源环境

最终推荐部署方案:

  • 模型:facebook/nllb-200-distilled-600M
  • 设备:cuda
  • 精度:float16
  • 推荐卡型:至少 Tesla T4 16GB 这一级别
  • 推荐 batch:16
  • 推荐 max_input_length256
  • 推荐 max_new_tokens64
  • 推荐 num_beams1
  • 推荐注意力实现:sdpa
  • 运行方式:单 worker,避免重复加载

更详细的性能说明见:

11. 开发说明

如果要新增翻译 backend,最少需要做这些事:

  1. <code>translation/backends/</code> 下新增实现
  2. <code>translation/service.py</code> 注册 backend 创建逻辑
  3. <code>config/config.yaml</code>services.translation.capabilities 中新增 capability 配置
  4. 如果有新的静态规则:

原则:

  • 不要再引入 translation provider 兼容层
  • 不要把 scene / prompt / 语言方向规则重新散落到别的目录
  • 不要在代码里写隐式默认和静默兼容

12. 常见建议

  • 中英商品标题双向场景,优先考虑 opus-mt-zh-enopus-mt-en-zh
  • 多语种统一方案,可以考虑 nllb-200-distilled-600M
  • nllb 更适合独占资源环境
  • 如果追求更高质量或更复杂语义处理,可使用 qwen-mtllm
  • 如果追求稳定商业 API,可考虑 deepl

13. 相关文档