Ascend-SACT/M2M100
模型介绍文件和版本Pull Requests讨论分析
下载使用量0

M2M100模型部署与优化总结

1. 背景

1.1 验证范围

本文记录 M2M100 翻译模型在 Ascend NPU 上的 WMT19 精度验证、Hugging Face Transformers 推理性能、FastAPI 服务化部署、API 调用、服务性能测试,以及长文本翻译处理方式。

其中 HF 推理指使用 Hugging Face Transformers 直接加载模型并在 NPU 上执行 generate()。

本次验证覆盖两类模型:

模型用途模型来源
M2M100 418M精度基线验证、HF 性能测试https://huggingface.co/facebook/m2m100_418M
M2M100 1.2B精度补充验证、HF 性能测试、服务化验证https://huggingface.co/facebook/m2m100_1.2B

1.2 硬件环境

设备信息:

项目规格
服务器Atlas 800T A2
加速卡Ascend 910B3

1.3 软件环境

项目版本 / 规格
操作系统 / 架构Ubuntu 22.04.5 LTS / aarch64
驱动 / 固件25.2.0 / 7.0.3.220
CANN8.5.1
Python3.11.14
torch / torch_npu2.9.0+cpu / 2.9.0.post1+gitee7ba04

1.4 fairseq 代码仓目录

占位符含义
<PROJECT_ROOT>本项目根目录
<MODEL_ROOT>模型目录根路径
<FAIRSEQ_ROOT>已完成 M2M100 适配的 fairseq 目录
<RUN_DIR>精度或性能测试输出目录
<SERVICE_ENDPOINT>FastAPI 服务地址

服务启动、API 调用和 HF direct 性能测试只依赖当前项目代码与 Hugging Face 模型目录,不需要 fairseq 代码仓。只有运行 WMT19 精度评测时,才需要准备 <FAIRSEQ_ROOT>。

如果本地还没有 fairseq 代码仓,可以先克隆代码并切换到项目使用的 fairseq 目录:

git clone https://github.com/facebookresearch/fairseq.git <FAIRSEQ_ROOT>
cd <FAIRSEQ_ROOT>

<FAIRSEQ_ROOT> 需要包含完整的 M2M100 tokenizer 资源,至少应存在:

<FAIRSEQ_ROOT>/examples/m2m_100/tokenizers/thirdparty/mosesdecoder/scripts/tokenizer/tokenizer.perl

该文件来自 Moses SMT 的 mosesdecoder 仓库。若当前 fairseq 目录缺少该文件,在 M2M100 示例目录执行:

cd <FAIRSEQ_ROOT>/examples/m2m_100
bash install_dependecies.sh

该脚本会将 Moses tokenizer 下载到 tokenizers/thirdparty/mosesdecoder,并固定到 M2M100 示例使用的 revision。

1.5 官方参考

  • M2M100 论文:Beyond English-Centric Multilingual Machine Translation,arXiv:2010.11125。
  • 论文 Table 10 报告 Chinese-English WMT’19 的 M2M-100 BLEU 为 29.0,Published best single model 为 29.1。

2. 精度评测

2.1 评测口径

评测方向:

zh -> en

评测数据:

项目内容
数据集WMT19 Chinese-English
splitwmt19
语言对zh-en
样本数2000
数据获取python -m sacrebleu -t wmt19 -l zh-en --echo src/ref
指标detokenized BLEU
计分方式tok.sh en 后使用 sacrebleu -tok none
beam5
最大生成长度200

这里的计分方式与 M2M100 README 计分口径保持一致:先使用 M2M100 示例目录中的 tok.sh en 对 hypothesis 和 reference 做英文 tokenization,再使用 sacrebleu -tok none 计分。

2.2 官方论文结果

论文 Table 10 中 Chinese-English WMT’19 结果:

系统BLEU
Published best single model29.1
M2M-10029.0

论文说明 Table 10 使用 WMT test set 上的 detokenized BLEU,并使用 sacreBLEU 计分。

2.3 统一 HF 精度评测入口

418M 与 1.2B 使用同一个 HF 精度评测入口:

hf_m2m100_api/evaluate_hf_wmt19.py

该脚本在未指定 --devices 时使用 --device 做单卡生成;指定 --devices 时会自动按行号切分 WMT19 样本、多进程加载模型、每卡生成一个 shard,最后按原始顺序合并 hypothesis 并统一计分。

2.4 418M 精度结果

为了缩短全量评测时间,实际采用 8 卡分片生成,每张卡处理连续 250 条样本:

cd <PROJECT_ROOT>

python hf_m2m100_api/evaluate_hf_wmt19.py \
  --model-dir <MODEL_ROOT>/m2m100_418M \
  --fairseq-dir <FAIRSEQ_ROOT> \
  --output-dir <RUN_DIR>/wmt19-zh-en-hf-418m-sharded \
  --devices npu:0,npu:1,npu:2,npu:3,npu:4,npu:5,npu:6,npu:7 \
  --source-lang zh \
  --target-lang en \
  --beam 5 \
  --batch-size 1 \
  --max-new-tokens 200

实测结果:

模型链路样本数BLEU与官方 M2M-100 29.0 差值设备wall time
M2M100 418MHF generate sharded200022.6-6.48 x NPU319.259 s

418M 与论文 M2M-100 结果差距较大。该差异符合预期:论文 Table 10 的 M2M-100 并不是 418M 小模型的公开精度基线,而是更大容量的 M2M-100 系统结果。因此继续验证 1.2B 模型。

2.5 1.2B 精度结果

1.2B 使用同一脚本进行完整 WMT19 zh-en 评测,只需要替换模型目录与输出目录:

cd <PROJECT_ROOT>

python hf_m2m100_api/evaluate_hf_wmt19.py \
  --model-dir <MODEL_ROOT>/m2m100_1.2B \
  --fairseq-dir <FAIRSEQ_ROOT> \
  --output-dir <RUN_DIR>/wmt19-zh-en-hf-1.2b \
  --devices npu:0,npu:1,npu:2,npu:3,npu:4,npu:5,npu:6,npu:7 \
  --source-lang zh \
  --target-lang en \
  --beam 5 \
  --batch-size 1 \
  --max-new-tokens 200

实测结果:

模型链路样本数BLEU与官方 M2M-100 29.0 差值
M2M100 1.2BHF generate200027.8-1.2

1.2B 结果明显接近论文 Table 10 的 M2M-100 29.0,比 418M 高 5.2 BLEU。后续服务化部署选择 1.2B 作为主服务模型。

2.6 精度结论

模型BLEU结论
论文 M2M-10029.0官方 Chinese-English WMT’19 参考值。
M2M100 418M22.6与官方差距 6.4 BLEU,不适合作为官方精度复现模型。
M2M100 1.2B27.8与官方差距 1.2 BLEU,适合作为服务化主模型。

需要注意:论文结果、HF 418M 路径和 HF 1.2B 路径在模型容量、实现细节、tokenization 和 checkpoint 来源上不完全一致,因此这里存在一定差异。

3. 性能评测

3.1 评测配置

短句性能测试使用:

New Delhi is the capital of India.

公共参数:

项目值
翻译方向en -> zh
beam5
max new tokens128
batch size1
warmup3
iterations20

natural 表示按 tokenizer 输出的真实输入长度进行推理。

3.2 418M HF 性能

cd <PROJECT_ROOT>

python hf_m2m100_api/benchmark_hf_pad_compare.py \
  --model-dir <MODEL_ROOT>/m2m100_418M \
  --device npu:1 \
  --warmup 3 \
  --iterations 20
模型输入 shapegenerate only 平均耗时tokenize+generate 平均耗时P50 generate
418M[1,10]0.2324 s0.2332 s0.2336 s

3.3 1.2B HF 性能

1.2B 使用相同短句测试输入进行 direct HF 性能测试:

cd <PROJECT_ROOT>

python hf_m2m100_api/benchmark_hf_pad_compare.py \
  --model-dir <MODEL_ROOT>/m2m100_1.2B \
  --device npu:1 \
  --warmup 3 \
  --iterations 20

实测结果:

模型输入 shapegenerate only 平均耗时tokenize+generate 平均耗时P50 generate
418M[1,10]0.2324 s0.2332 s0.2336 s
1.2B[1,10]0.4162 s0.4171 s0.4155 s

1.2B 相比 418M 具备更好的 WMT19 zh-en 精度,但短句 direct 推理延迟约为 418M 的 1.8 倍。

4. 服务化部署与服务性能验证

4.1 服务代码结构

<PROJECT_ROOT>/
  hf_m2m100_api/
    hf_m2m100_fastapi.py
    hf_m2m100_npu_infer.py
    text_splitter.py
    evaluate_hf_wmt19.py
    benchmark_api.py
    benchmark_hf_concurrency.py
    benchmark_hf_multiprocess.py
    benchmark_hf_pad_compare.py
    requirements.txt
    start.sh
  tests/
    test_benchmark_hf_pad_compare.py
    test_hf_m2m100_fastapi.py
    test_hf_m2m100_npu_infer.py
    test_start_sh.py
    test_text_splitter.py
  pytest.ini

模块职责:

文件说明
hf_m2m100_api/hf_m2m100_fastapi.pyFastAPI 应用、服务生命周期、请求校验和同步推理锁。
hf_m2m100_api/hf_m2m100_npu_infer.pyHF 模型加载、设备解析、复用已加载模型实例推理。
hf_m2m100_api/text_splitter.py长文本句子切分、超长单句兜底拆分、翻译 chunk 并按原输入拼回。
hf_m2m100_api/evaluate_hf_wmt19.pyWMT19 zh-en HF 精度评测,覆盖数据获取、单卡或多卡分片生成、分词和 sacrebleu 计分。
hf_m2m100_api/benchmark_api.pyHTTP 服务端到端性能测试。
hf_m2m100_api/benchmark_hf_concurrency.py不经过 HTTP 的 HF 多线程并发测试。
hf_m2m100_api/benchmark_hf_multiprocess.py不经过 HTTP 的 HF 多进程并发测试。
hf_m2m100_api/benchmark_hf_pad_compare.py不经过 HTTP 的 HF direct 短句性能测试。
tests/HF 服务、HF 推理、长文本拆分和 benchmark 参数构造的回归测试。

代码验证:

cd <PROJECT_ROOT>

python -m py_compile hf_m2m100_api/*.py
bash -n hf_m2m100_api/start.sh
pytest -q

4.2 安装依赖

cd <PROJECT_ROOT>/hf_m2m100_api
pip install -r requirements.txt

4.3 启动服务

cd <PROJECT_ROOT>/hf_m2m100_api

M2M100_MODEL_DIR=<MODEL_ROOT>/m2m100_1.2B \
M2M100_DEVICE=npu:1 \
M2M100_HOST=0.0.0.0 \
M2M100_PORT=8000 \
M2M100_WORKERS=1 \
./start.sh

等价 uvicorn 命令:

M2M100_MODEL_DIR=<MODEL_ROOT>/m2m100_1.2B \
M2M100_DEVICE=npu:1 \
uvicorn hf_m2m100_fastapi:app \
  --host 0.0.0.0 \
  --port 8000 \
  --workers 1

启动参数:

参数作用
M2M100_MODEL_DIR / --model-dir指定 HF 模型目录。
M2M100_DEVICE / --device指定模型加载和推理使用的 NPU。
M2M100_HOST / --host指定 HTTP 监听地址。
M2M100_PORT / --port指定 HTTP 监听端口。
M2M100_WORKERS / --workers指定 uvicorn worker 进程数。每个 worker 会独立加载一份模型实例。

4.4 API 调用

健康检查:

curl http://<SERVICE_ENDPOINT>/health

翻译请求:

curl -X POST http://<SERVICE_ENDPOINT>/translate \
  -H 'Content-Type: application/json' \
  -d '{
    "texts": ["New Delhi is the capital of India."],
    "src_lang": "en",
    "tgt_lang": "zh"
  }'

请求参数:

参数默认值说明
texts无待翻译文本列表。
src_lang无源语言代码,调用时需要显式填写。完整语言列表可查看模型目录下的 special_tokens_map.json,字段为 additional_special_tokens;请求参数使用去掉前后双下划线后的代码,例如 __zh__ 对应 zh。
tgt_lang无目标语言代码,调用时需要显式填写。该参数用于指定模型输出哪种语言。完整语言列表可查看模型目录下的 special_tokens_map.json,字段为 additional_special_tokens;请求参数使用去掉前后双下划线后的代码,例如 __en__ 对应 en。
num_beams5beam search 的候选路径数量。值越大,生成时保留并比较的候选翻译越多,通常有利于翻译质量,但会增加推理耗时。
split_long_texttrue是否启用服务端长文本拆分。开启后服务按句子边界拆分输入文本,再按原始顺序拼回翻译结果。
max_input_tokens_per_chunk100单个句子过长时的兜底拆分长度。正常情况下服务按句子切分,一句作为一个 chunk;只有某一句本身超过模型可接受的输入长度时,才会按该值继续拆小。

请求示例:

curl -X POST http://<SERVICE_ENDPOINT>/translate \
  -H 'Content-Type: application/json' \
  -d '{
    "texts": ["New Delhi is the capital of India."],
    "src_lang": "en",
    "tgt_lang": "zh"
  }'

响应格式:

{
  "translations": ["新德里是印度的首都。"],
  "src_lang": "en",
  "tgt_lang": "zh",
  "elapsed_seconds": 0.4092
}

4.5 长文本翻译问题与处理

M2M100 的 HF generate() 可以接收接近模型上限的长输入,但模型训练和常规测试数据通常按单条语句组织。直接把多句或整段长文本作为一条输入时,实测容易出现漏翻、重复或提前结束。这里不是服务接口把输入截短了,而是模型把多句长文本当成一条很长的句子翻译时,生成过程可能提前认为已经翻译完成,导致后面的内容被跳过,或者前面的内容被重复生成。

服务端处理策略:

  1. 启用 split_long_text=true。
  2. 按句子边界切分文本,一句作为一个 chunk。
  3. 只有单句超过模型输入上限时,才按 max_input_tokens_per_chunk 做兜底硬拆,避免请求超过 tokenizer/model 可处理范围。
  4. 当前服务串行翻译拆分后的 chunk,并按原始输入顺序拼回,优先保证稳定性和覆盖率。

4.6 服务化性能测试

服务 benchmark:

cd <PROJECT_ROOT>

python hf_m2m100_api/benchmark_api.py \
  --url http://<SERVICE_ENDPOINT> \
  --warmup 3 \
  --iterations 20

服务实测:

模型链路client end-to-end 平均耗时service elapsed 平均耗时P50 clientP50 service
418MFastAPI 预加载模型推理0.2464 s0.2439 s0.2469 s0.2445 s
1.2BFastAPI 预加载模型推理0.4323 s0.4294 s0.4336 s0.4306 s

当前服务默认启用长文本拆分。短句请求只会拆出一个句子 chunk,因此端到端耗时仍接近 direct HF natural;1.2B 的 HTTP 端到端平均耗时为 0.4323 s,主要延迟来自模型生成。

4.7 多 worker 并发验证

当前 start.sh 默认启动单个服务进程。如果希望在同一张 NPU 上启动多个模型实例,可以通过 M2M100_WORKERS 或直接向 start.sh 传入 --workers:

cd <PROJECT_ROOT>/hf_m2m100_api

M2M100_MODEL_DIR=<MODEL_ROOT>/m2m100_1.2B \
M2M100_DEVICE=npu:1 \
M2M100_HOST=127.0.0.1 \
M2M100_PORT=18183 \
M2M100_WORKERS=<WORKERS> \
./start.sh

每个 worker 是一个独立进程,会各自加载一份模型实例到同一张 NPU。该方式可以提高短句并发吞吐,但显存占用会随 worker 数增加,单请求延迟也可能随并发数升高。

测试口径:

项目值
模型M2M100 1.2B
设备单张 Ascend 910B3
请求文本New Delhi is the capital of India.
源语言 / 目标语言en -> zh
beam5
最大生成长度128
每档正式请求数40
每档预热请求数4

实测结果:

workers并发数QPSclient 平均耗时client P95service 平均耗时service P95
112.260.4423 s0.4560 s0.4397 s0.4533 s
122.290.6556 s0.8832 s0.6512 s0.8788 s
142.241.1188 s1.8058 s1.1123 s1.7998 s
182.261.9866 s3.5171 s1.9767 s3.5098 s
212.260.4411 s0.4481 s0.4384 s0.4453 s
224.470.4413 s0.4577 s0.4383 s0.4548 s
244.100.6803 s0.9031 s0.6754 s0.8952 s
283.901.1238 s2.1623 s1.1157 s2.1544 s
312.130.4684 s0.4606 s0.4654 s0.4578 s
324.450.4371 s0.4917 s0.4342 s0.4886 s
344.590.5426 s0.8877 s0.5383 s0.8825 s
385.210.8441 s1.4382 s0.8374 s1.4339 s
412.100.4753 s0.4707 s0.4727 s0.4685 s
424.150.4535 s0.4525 s0.4506 s0.4499 s
446.410.5034 s0.9180 s0.4997 s0.9113 s
485.960.7475 s1.3605 s0.7421 s1.3548 s

测试结果显示,单 worker 下提高并发数不会提升吞吐,只会增加排队延迟;多 worker 会在同卡上加载多个模型实例,可以提升短句并发吞吐。

5. 结论与建议

  • 如果优先考虑翻译质量,建议使用 M2M100 1.2B;M2M100 418M 精度较低。
  • 对于多句或整段长文本,建议在服务端开启句子级拆分,减少漏翻、重复和提前结束的风险。
  • 短句高并发场景可以通过同卡多 worker 提升吞吐,但 worker 数和请求并发数需要结合 QPS 与 P95 延迟实测确定。