首页
/ Transformers 中的 M2M100:真正的多对多 100 语种机器翻译模型实战指南

Transformers 中的 M2M100:真正的多对多 100 语种机器翻译模型实战指南

2026-09-07 11:45:55作者:邬祺芯Juliet

M2M100(Many-to-Many Multilingual Machine Translation)是 Facebook AI(Meta)提出的超多语种序列到序列翻译模型,支持在 100 种语言之间直接互译,无需经过英语中转。本文以 M2M100 官方模型文档 为主线,结合 🤗 Transformers 仓库中的配置、源码与分词器实现,系统讲解 M2M100 的模型原理、语言 ID 输入格式、M2M100Config 全部参数、有监督训练与翻译生成的完整代码,以及 FlashAttention 2 与 SDPA 等注意力加速方案。读完本文,你将能够使用 facebook/m2m100_418M 检查点完成任意语言对之间的翻译与微调,并理解它区别于英语中心(English-Centric)翻译模型的核心设计。

模型总览:面向真实世界的多对多翻译

M2M100 由 Angela Fan、Shruti Bhosale、Holger Schwenk 等人在论文 Beyond English-Centric Multilingual Machine Translation 中提出(2020-10-21 发布于 HF Papers,2021-03-06 贡献至 Transformers,由 valhalla 社区成员提交)。其动机非常直白:此前的大规模多语种翻译模型大多是"英语中心"的——它们只用"与英语互译"的数据训练,因此任意两种非英语语言之间的翻译都需要经过英语中转,既损失质量也不符合全球真实翻译需求。

论文的核心贡献与设计可概括为三点:

  1. 真正意义上的 Many-to-Many:训练一个可在 100 种语言任意两两之间直接翻译的单一模型,方向覆盖数千条有监督语言对;
  2. 大规模数据挖掘与开源:通过大规模数据挖掘构建并开源覆盖大量语向的有监督训练集;
  3. 容量扩展策略:结合稠密规模扩展(dense scaling)与语言特定的稀疏参数(language-specific sparse parameters)来提升模型质量。

论文报告在非英语语向直接翻译上相对此前方案取得超过 10 BLEU 的提升,同时与 WMT 最佳单语向系统表现相当。M2M100 从模型类型上属于多语种 encoder-decoder(seq2seq)架构,主要面向翻译任务,同时也可以承担摘要等条件生成任务(模型的 M2M100ForConditionalGeneration 实现即被注释为 "Can be used for summarization")。

架构与关键设计:源码级的"为什么这样设计"

M2M100 的 PyTorch 实现位于 modeling_m2m_100.py,其中的组件类完整描述了其结构:

组件 说明 源码位置
M2M100ScaledWordEmbedding 对共享词嵌入乘上 sqrt(d_model) 缩放(受 scale_embedding=True 控制) 第 67 行起
M2M100SinusoidalPositionalEmbedding 经典正弦位置编码,可外推到任意长度 第 80 行起
M2M100Attention 多头注意力(对 query 做 1/sqrt(head_dim) 缩放后 softmax) 第 212 行起
M2M100EncoderLayer / M2M100DecoderLayer 编码器/解码器基础层 第 330 / 387 行起
M2M100Encoder / M2M100Decoder 完整编码器与解码器栈 第 501 / 593 行起
M2M100Model 组合 encoder + decoder 的裸模型,编码器与解码器共享同一份 shared 词嵌入 第 716 行起
M2M100ForConditionalGeneration M2M100Model 之上加 lm_headd_model → vocab_size 线性层,与共享词嵌入权重绑定) 第 818 行起

值得注意的实现细节:

  • 权重绑定M2M100Model 声明 decoder.embed_tokens.weightencoder.embed_tokens.weight 均绑定到 shared.weightM2M100ForConditionalGeneration 声明 lm_head.weight 绑定到 model.shared.weight,这与 config.tie_word_embeddings=True 相呼应(configuration_m2m_100.py)。
  • 位置编码外推M2M100SinusoidalPositionalEmbedding 通过 get_embedding 生成任意长度的正弦权重,offset 为 2,位置编码矩阵以非持久 nn.Buffer 保存,因此编码器输入的序列长度可以超过训练时的 max_position_embeddings
  • 标签右移:训练时 decoder 标签通过 shift_tokens_right 右移一位,首位填 decoder_start_token_id,并把标签中的 -100 替换为 pad_token_id(第 50-63 行),从而在计算交叉熵时忽略填充位。
  • 训练期特性M2M100PreTrainedModel 声明了 supports_gradient_checkpointing = True_supports_flash_attn = True_supports_sdpa = True_supports_flex_attn = True,并设置 _no_split_modules = ["M2M100EncoderLayer", "M2M100DecoderLayer"] 以便分层加载与张量并行(第 481-490 行)。即 M2M100 原生支持梯度检查点、FlashAttention、SDPA 与 FlexAttention 多种注意力路径。
  • Fairseq 转换:仓库提供了从原始 Fairseq 检查点转换的脚本 convert_m2m100_original_checkpoint_to_pytorch.py,便于复现官方权重。

语言格式约定:为什么输入输出都要带语言 ID

M2M100 是多语种模型,因此它要求输入序列遵循特殊的语言标记格式

  • 源文本格式为 [lang_code] X [eos],即源语言 ID 令牌作为前缀拼在源文本前面,[eos] 结尾;
  • 目标文本(训练时的 text_target,或解码输入)同样以目标语言 ID 开头。

语言代码会映射为形如 __en____fr____hi____zh__ 的特殊令牌(special tokens),其 ID 被安排在词汇表 vocab_size(SentencePiece 编码后的词表)之后的位置上。

tokenization_m2m_100.py 源码可以看到机制实现:set_src_lang_special_tokens(src_lang)set_tgt_lang_special_tokens(tgt_lang) 会把语言令牌设为 prefix_tokens、把 [eos] 设为 suffix_tokens;随后 build_inputs_with_special_tokensprefix_tokens + token_ids_0 + suffix_tokens 拼接(第 255-280 行)。因此:

# src_lang="hi" 时编码后的 input_ids
[__hi__]  जीवन एक चॉकलेट बॉक्स की तरह है।  [</s>]

解码端则"反向"使用:翻译生成时目标语言 ID 作为 decoder 的起始/强制首词(见下文 Generation 部分)。

M2M100Tokenizer 与 sentencepiece

M2M100Tokenizer 基于 Google SentencePiece(unigram 词表),因此运行任何示例前必须安装 sentencepiece

pip install sentencepiece

Tokenizer 的核心构造参数如下(tokenization_m2m_100.py):

参数 默认值 说明
vocab_file 词表 JSON 路径
spm_file SentencePiece 模型文件(.spm),与词表配套
src_lang None(回退为 "en" 源语言代码
tgt_lang None 目标语言代码
language_codes "m2m100" 语言代码表,可选 "m2m100""wmt21"
num_madeup_words 8 预留的合成词数量
sp_model_kwargs 空 dict 传给 SentencePieceProcessor,用于子词正则化

其中 sp_model_kwargs 支持的三个常用子词正则化参数值得展开:

  • enable_sampling:开启子词采样(subword regularization);
  • nbest_size:unigram 采样参数——nbest_size ∈ {0,1} 表示不采样;nbest_size > 1 从 nbest 结果中采样;nbest_size < 0 视作无限 nbest,使用 forward-filtering-and-backward-sampling 从整个 lattice 采样;
  • alpha:unigram 采样的平滑参数(对 BPE-dropout 则是合并操作的 dropout 概率)。

加载模型时需要在 from_pretrained 中指定语言方向(此时内部默认 src_lang="en"):

tokenizer = M2M100Tokenizer.from_pretrained("facebook/m2m100_418M", src_lang="en", tgt_lang="fr")

你也可以在之后动态修改语言方向:通过 tokenizer.src_lang = "hi"(源码中 src_lang 是带 setter 的 property,赋值即触发 set_src_lang_special_tokens),并用 tokenizer.get_lang_id("fr") 获取某个语言的 ID 用于解码强制词。

M2M100Config:默认超参数全景

配置类 M2M100Config(继承 PreTrainedConfig)位于 configuration_m2m_100.pymodel_type = "m2m_100"。为兼容通用字段,它定义了 attribute_mapnum_attention_heads → encoder_attention_headshidden_size → d_modelnum_hidden_layers → encoder_layers。其完整默认值如下:

参数 默认值 含义
vocab_size 128112 词表大小(含 100 种语言与特殊令牌后的总量)
max_position_embeddings 1024 最大位置编码长度
encoder_layers / decoder_layers 12 / 12 编码器/解码器层数
encoder_ffn_dim / decoder_ffn_dim 4096 / 4096 FFN 中间维度
encoder_attention_heads / decoder_attention_heads 16 / 16 注意力头数
encoder_layerdrop / decoder_layerdrop 0.05 / 0.05 LayerDrop 概率
d_model 1024 隐藏层维度
activation_function "relu" FFN 激活函数
dropout 0.1 全连接 dropout
attention_dropout 0.1 注意力权重 dropout
activation_dropout 0.0 激活后 dropout
init_std 0.02 参数初始化标准差
scale_embedding True 词嵌入乘以 sqrt(d_model)
is_encoder_decoder True 编码器-解码器架构标记
use_cache True 生成时是否缓存 KV
tie_word_embeddings True 是否绑定共享词嵌入
bos_token_id / pad_token_id / eos_token_id 0 / 1 / 2 特殊令牌 ID
decoder_start_token_id 2(即 eos) 解码起始令牌

decoder_start_token_ideos_token_id 相同这一点很关键:M2M100 生成时以 eos 作为 decoder 的起始令牌,目标语言 ID 会被强制为第一个真正生成的 token(详见下一节)。实例化一个随机初始化的配置与模型非常直接:

from transformers import M2M100Config, M2M100Model

# 初始化一个 facebook/m2m100_418M 风格的随机配置与模型
configuration = M2M100Config()
model = M2M100Model(configuration)
print(model.config)

有监督训练:一对平行语料的 forward/loss

直接照搬官方文档的监督训练代码,加载 418M 检查点并对一对英法平行句做一次前向、拿到翻译损失:

from transformers import M2M100ForConditionalGeneration, M2M100Tokenizer

model = M2M100ForConditionalGeneration.from_pretrained("facebook/m2m100_418M", device_map="auto")
tokenizer = M2M100Tokenizer.from_pretrained("facebook/m2m100_418M", src_lang="en", tgt_lang="fr")

src_text = "Life is like a box of chocolates."
tgt_text = "La vie est comme une boîte de chocolat."

model_inputs = tokenizer(src_text, text_target=tgt_text, return_tensors="pt").to(model.device)

loss = model(**model_inputs).loss  # forward pass

关键点:

  • src_langtgt_lang 在 tokenizer 侧决定编码时使用哪个语言前缀;text_target 用于把目标文本也编码成带目标语言前缀 + eos 的标签序列;
  • 代码会自动完成 shift_tokens_right(decoder 输入右移、首位填入 decoder_start_token_id=eos)与 -100 填充替换,因此你只需传入 labels 即可获得 Seq2SeqLMOutput.loss
  • 使用 device_map="auto" 可以自动放置权重(CPU/GPU 分片),便于在内存受限环境运行。

生成翻译:用 forced_bos_token_id 强制目标语言

推理时,由于 decoder_start_token_id 就是 eos(ID 2),解码器第一个位置会先解码出该 eos,因此必须把目标语言 ID 强制为第一个真正生成的 token,否则输出无意义。做法是在 model.generate 中传入 forced_bos_token_id=tokenizer.get_lang_id("xx")

下面的官方示例演示了印地语 → 法语中文 → 英语两条完全绕开英语中转的直译路径(facebook/m2m100_418M):

from transformers import M2M100ForConditionalGeneration, M2M100Tokenizer

hi_text = "जीवन एक चॉकलेट बॉक्स की तरह है।"
chinese_text = "生活就像一盒巧克力。"

model = M2M100ForConditionalGeneration.from_pretrained("facebook/m2m100_418M", device_map="auto")
tokenizer = M2M100Tokenizer.from_pretrained("facebook/m2m100_418M")

# translate Hindi to French
tokenizer.src_lang = "hi"
encoded_hi = tokenizer(hi_text, return_tensors="pt").to(model.device)
generated_tokens = model.generate(**encoded_hi, forced_bos_token_id=tokenizer.get_lang_id("fr"))
tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)
# "La vie est comme une boîte de chocolat."

# translate Chinese to English
tokenizer.src_lang = "zh"
encoded_zh = tokenizer(chinese_text, return_tensors="pt").to(model.device)
generated_tokens = model.generate(**encoded_zh, forced_bos_token_id=tokenizer.get_lang_id("en"))
tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)
# "Life is like a box of chocolate."

这里建议把"切换语言方向 + 强制目标语言 ID"固定为一种使用习惯:tokenizer.src_lang 决定编码前缀,get_lang_id 取得目标语言令牌 ID 并交给 forced_bos_token_id。底层的 pipeline 路径(_build_translation_inputs,第 331-339 行)也是这么做的——它校验 src/tgt_lang 都存在,把 tgt_lang_id 填入 inputs["forced_bos_token_id"] 后再调用 generate;prepare_seq2seq_batch(第 318-329 行)则用于批量地一次性设置 src/tgt 语言后返回 BatchEncoding

注意力加速:FlashAttention 2 与 SDPA

M2M100 官方文档单独列出两种注意力加速方案。需要说明的是:当前文档页头部带有 FlashAttention 与 SDPA 的支持徽标,模型类也声明了 _supports_flash_attn = True_supports_sdpa = True,意味着下列用法有原生内核路径支撑,而非通用 fallback。

使用 FlashAttention 2

FlashAttention 2 依赖 CUDA 内核,能显著加速注意力计算并降低显存占用。

安装(先确认硬件兼容性,再安装最新版):

pip install -U flash-attn --no-build-isolation

加载与推理:在 from_pretrained 中传入 attn_implementation="flash_attention_2",并使用 torch.float16torch.bfloat16 半精度:

from transformers import M2M100ForConditionalGeneration, M2M100Tokenizer

model = M2M100ForConditionalGeneration.from_pretrained(
    "facebook/m2m100_418M", attn_implementation="flash_attention_2", device_map="auto"
).eval()
tokenizer = M2M100Tokenizer.from_pretrained("facebook/m2m100_418M")

# translate Hindi to French
hi_text = "जीवन एक चॉकलेट बॉक्स की तरह है।"
tokenizer.src_lang = "hi"
encoded_hi = tokenizer(hi_text, return_tensors="pt").to(model.device)
generated_tokens = model.generate(**encoded_hi, forced_bos_token_id=tokenizer.get_lang_id("fr"))
tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)
# "La vie est comme une boîte de chocolat."

需要说明的是:FlashAttention 2 的加速收益与你的 GPU 架构直接相关,纯推理场景下相对原生实现通常能观察到吞吐提升与峰值显存下降,具体数值以自身硬件实测为准(原始文档中的 speedup 示意图即来自同族 NLLB 模型的基准测量,不同设备、批大小与序列长度下结果差异很大)。

使用 SDPA(Scaled Dot Product Attention)

PyTorch 在 torch.nn.functional 中内置了原生 scaled_dot_product_attention 算子,它会根据输入与硬件自动选择 flash attention、memory-efficient attention 或 math 等实现:

  • torch>=2.1.1 且可用实现存在时,SDPA 默认启用
  • 也可以显式传 attn_implementation="sdpa" 强制指定。
from transformers import M2M100ForConditionalGeneration

model = M2M100ForConditionalGeneration.from_pretrained(
    "facebook/m2m100_418M", attn_implementation="sdpa", device_map="auto"
)
# ...

为获得最佳加速效果,官方建议以半精度加载模型(如 torch.float16torch.bfloat16)。如果你的硬件不满足 FlashAttention 2 的条件,SDPA 是零额外依赖的更稳妥选择;若需手动关闭所有融合实现,可传 attn_implementation="eager" 回到原生数学路径以便调试。

进阶话题与后续路线

  • 任务文档:M2M100 可直接用于条件生成类任务,官方推荐的配套任务是 Translation 任务指南Summarization 任务指南,其中包含训练、评估与 pipeline 调用的完整套路。
  • 微调技巧:微调多语种翻译模型时,建议在数据加载时显式按语言切换 tokenizer.src_lang/tgt_lang,并保证 batch 内语言方向一致,避免前缀拼接混乱。
  • 回归验证:仓库测试 tests/models/m2m_100/test_modeling_m2m_100.py 覆盖了模型 forward、生成、tokenizer 语言切换等行为,可作为修改后行为对齐的参考;文档中各代码块也在 M2M100Tokenizer/M2M100ForConditionalGeneration 的 docstring 中有等价的可执行示例。
  • 动手验证路径:阅读 configuration_m2m_100.py 掌握全部超参,对照 modeling_m2m_100.py 中的缩放嵌入、正弦位置编码与权重绑定设计,再回到上文代码把 facebook/m2m100_418M 换成语料跑通训练与直译,即可完整体验"非英语中心多对多翻译"的完整链路。
登录后查看全文
热门项目推荐
相关项目推荐