深入解析 Transformers 中的 XLM 模型:跨语言预训练原理、配置与任务头实战
【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
XLM(Cross-lingual Language Model)是 Facebook AI 提出、并已在 Hugging Face Transformers 中完整落地的跨语言预训练模型,它通过因果语言建模(CLM)、掩码语言建模(MLM)与翻译语言建模(TLM)三种目标,让单一模型可以同时服务多种语言的编码与理解任务。本篇以 XLM 官方文档 为骨架,结合当前仓库中 XLM 的 配置类、分词器 与 模型实现 源码,讲解如何用 Pipeline、AutoModel 快速上手,并逐项解读XLMConfig、XLMTokenizer与各类任务头(分类、抽取式问答、多项选择、序列标注、语言模型)的用法与底层原理。
一、XLM 模型概览:从跨语言预训练到多语言任务
XLM 论文于 2019-01-22 发布在 HF papers 上,并在 2020-11-16 被贡献到 Hugging Face Transformers。其核心思路是用跨语言预训练目标打破语言边界,具体包含两条技术路线:
- 无监督的单语预训练:只使用单语数据,通过掩码语言建模(MLM)等目标学习语言内部的表示;
- 有监督的多语预训练:使用两种及以上语言的平行语料,通过翻译语言建模(TLM)目标训练,TLM 是 BERT 掩码语言建模目标向多语言输入的扩展——模型会看到来自不同语言的平行片段,并依据其他语言提供的上下文预测被掩码的词,从而让语言间共享上下文知识。
因此 XLM 模型原生支持三类目标:因果语言建模(causal language modeling,对应causal配置项)、掩码语言建模(masked language modeling)以及翻译语言建模(translation language modeling,即 MLM 的多语言扩展)。
所有原始 XLM 检查点托管在 Facebook AI 社区,典型模型包括FacebookAI/xlm-mlm-en-2048(英语、2,048 维)等。在 Transformers 中,XLM 的完整代码位于 src/transformers/models/xlm/ 目录,包含:
| 文件 | 职责 |
|---|---|
| configuration_xlm.py | XLMConfig配置类 |
| tokenization_xlm.py | XLMTokenizer(BPE 分词器) |
| modeling_xlm.py | XLMModel及全部任务头模型 |
| convert_xlm_original_pytorch_checkpoint_to_pytorch.py | 官方原始检查点转换脚本 |
在下文中,凡涉及跨语言分类、翻译、问答等任务的更多用法,都可以围绕上述两个任务族的官方模型(xlm-mlm-*与xlm-clm-*)展开。
二、快速上手:三分钟完成掩码词预测
文档给出了三种使用路径:Pipeline、AutoModel(AutoTokenizer+AutoModelForMaskedLM)以及命令行。下面先复现文档示例,再做源码级解读。
2.1 方式一:使用 Pipeline
from transformers import pipeline pipeline = pipeline( task="fill-mask", model="facebook/xlm-roberta-xl", device=0 ) pipeline("Bonjour, je suis un modèle <mask>.")device=0表示将模型放到第一块 GPU 上运行。fill-mask任务要求输入的句子中包含掩码 token,pipeline 会输出多个候选词的分数与预测。
2.2 方式二:使用 AutoModel 与 AutoTokenizer
import torch from transformers import AutoModelForMaskedLM, AutoTokenizer tokenizer = AutoTokenizer.from_pretrained( "FacebookAI/xlm-mlm-en-2048", ) model = AutoModelForMaskedLM.from_pretrained( "FacebookAI/xlm-mlm-en-2048", device_map="auto", ) inputs = tokenizer("Hello, I'm a <mask> model.", return_tensors="pt").to(model.device) with torch.no_grad(): outputs = model(**inputs) predictions = outputs.logits.argmax(dim=-1) predicted_token = tokenizer.decode(predictions[0][inputs["input_ids"][0] == tokenizer.mask_token_id]) print(f"Predicted token: {predicted_token}")几个值得注意的细节:
device_map="auto"会自动将模型分配到可用设备(CPU/GPU),因此后续需要把输入inputs通过.to(model.device)移到与模型相同的设备上;- 解码预测词时,先通过
tokenizer.mask_token_id定位input_ids中掩码 token 所在的位置,再对该位置的 logits 做argmax; - XLM 的掩码 token 默认是
"<special1>"(见下文XLMTokenizer部分),XLMTokenizer会自动在词表中识别它。
2.3 方式三:命令行
文档同时提示,可直接点击右侧模型列表中的 XLM 模型,查看将 XLM 应用于分类、翻译、问答等跨语言任务的更多示例,也可以通过transformers-cli环境运行同类命令。
三、XLMConfig:逐项拆解配置参数
XLMConfig继承自PreTrainedConfig,源码定义于 configuration_xlm.py。它的model_type = "xlm",并定义了到通用命名空间的attribute_map,例如把hidden_size映射到emb_dim、num_attention_heads映射到n_heads、num_hidden_layers映射到n_layers、n_words映射到vocab_size、bos_index/eos_index/pad_index分别映射到bos_token_id/eos_token_id/pad_token_id——这意味着在外部以通用参数名(如hidden_size)传参时,会被自动映射到 XLM 的原生参数。
从 配置类 可以看到完整默认值,下面分四类说明。
3.1 结构与激活
| 参数 | 默认值 | 说明 |
|---|---|---|
vocab_size | 30145 | 词表大小(原生名n_words) |
emb_dim | 2048 | 词嵌入与隐层维度(即通用hidden_size) |
n_layers | 12 | Transformer 层数(通用num_hidden_layers) |
n_heads | 16 | 注意力头数(通用num_attention_heads),要求emb_dim可被n_heads整除 |
dropout | 0.1 | 全连接层与残差路径的 dropout 比例 |
attention_dropout | 0.1 | 注意力权重上的 dropout 比例 |
gelu_activation | True | 是否使用 GELU 激活;为False时使用 ReLU |
layer_norm_eps | 1e-12 | LayerNorm 的 epsilon |
init_std | 0.02 | 权重初始化标准差 |
max_position_embeddings | 512 | 位置嵌入最大长度 |
3.2 编码器/解码器与注意力形态
| 参数 | 默认值 | 说明 |
|---|---|---|
causal | False | 是否以因果方式运行。若为True,使用三角注意力掩码,即每个位置只能看到左侧上下文而非双向上下文 |
is_encoder | True | 初始化为 Transformer 编码器还是解码器。注意:当前 XLMModel 在构造解码器时会直接抛出NotImplementedError,即现阶段 XLM 只能作为编码器使用 |
sinusoidal_embeddings | False | 是否使用正弦位置嵌入(Vaswani et al. 方式)替代可学习绝对位置嵌入。仓库测试中有专门用例(test_modeling_xlm.py 中XLMConfig(sinusoidal_embeddings=True))验证该开关 |
3.3 多语言相关
| 参数 | 默认值 | 说明 |
|---|---|---|
n_langs | 1 | 模型支持的语言数量,单语模型设为 1 |
use_lang_emb | True | 是否使用额外的语言嵌入。当n_langs > 1时,模型会维护一张n_langs × emb_dim的lang_embeddings表;单语场景不会创建该表 |
lang_id | 0 | 生成文本时指定的目标语言 ID(配合mask_token_id使用,见 3.4) |
在模型前向实现中可以看到,词嵌入之后按顺序叠加:位置嵌入、语言嵌入(当传入langs且n_langs > 1且use_lang_emb为真时)、token type 嵌入,最后过 LayerNorm 与 dropout。语言嵌入由词级张量langs(形状(batch_size, sequence_length))逐位置索引——这意味着同一句话里可以按 token 粒度混合多种语言。
3.4 特殊 token 索引、初始化与序列摘要(summary)
| 参数 | 默认值 | 说明 |
|---|---|---|
unk_index | 3 | 词表中未知 token 的索引 |
mask_index | 5 | 词表中掩码 token 的索引 |
mask_token_id | 0 | 模型无关参数,用于在 MLM 语境中生成文本时标记被掩码的位置 |
pad_token_id | 2 | padding token ID |
bos_token_id | 0 | 序列开始 token ID |
eos_token_id | 1 | 序列结束 token ID |
embed_init_std | 2048^-0.5 | 初始化嵌入矩阵时截断正态分布的标准差 |
tie_word_embeddings | True | 是否将输出预测层与输入嵌入权重绑定 |
summary_type | "first" | 序列摘要方式:"last"(取最后 token,XLNet 风格)、"first"(取首 token,BERT 风格)、"mean"(所有 token 均值)、"cls_index"(按给定分类 token 位置取值)、"attn"(预留,用多头注意力,暂未实现) |
summary_use_proj | True | 向量提取后是否再接一层投影 |
summary_activation | 无 | 传"tanh"时对输出做 tanh 激活,其它值不激活 |
summary_proj_to_labels | True | 投影输出维度取num_labels还是hidden_size |
summary_first_dropout | 0.1 | 投影与激活之后的 dropout 比例 |
start_n_top | 5 | SQuAD 评估脚本中用到的起始位置 beam 数 |
end_n_top | 5 | SQuAD 评估脚本中用到的结束位置 beam 数 |
其中start_n_top/end_n_top与 SQuAD 风格的抽取式问答输出直接相关(见第六节XLMForQuestionAnswering的输出字段)。summary_*系列参数则由分类与多项选择模型的XLMSequenceSummary模块消费。
四、XLMTokenizer:BPE 与多语言分词管线
XLMTokenizer基于 Byte-Pair Encoding(BPE),其内部使用vocab.json(词表)与merges.txt(合并规则)两个文件(见 tokenization_xlm.py)。它继承自PreTrainedTokenizer,绝大多数主方法由父类提供,自身重点实现了分词细节与特殊 token 逻辑。
4.1 完整分词管线
分词过程遵循以下步骤(源码注释):
- 对大部分受支持语言使用Moses 预处理与分词(通过
sacremoses库,必须先pip install sacremoses,否则会抛出 ImportError); - 对中(
zh)、日(ja)、泰(th)语使用语言专属分词器:中文默认使用rjieba,日文使用Mykytea(KyTea 的 Python 封装),泰文使用pythainlp; - 可选地对全部输入执行小写化与去重音(
do_lowercase_and_remove_accent=True时,通过 NFD 归一化丢弃Mn类别字符,即组合变音符); - 对已分词结果执行BPE 子词切分(词末用
</w>标记词边界); - 支持用
special_tokens参数与set_special_tokens方法向词表追加额外符号(例如__classify__)。
在_tokenize方法中(源码)可以看到,语言代码不是zh/th/ja时走 Moses 管线,其中罗马尼亚语(ro)会额外应用romanian_preprocessing(对应FacebookAI/xlm-mlm-enro-1024检查点)。bypass_tokenizer=True时只按空格切分并直接进入 BPE,方便用户使用 XLM 原始预处理脚本在外部完成分词后接入。
4.2 语言映射与特殊 token
lang2id:把语言名映射为 ID 的字典;id2lang:反向映射。两者对多语预训练词表会自动加载,例如FacebookAI/xlm-mlm-100的config.lang2id/config.id2lang会完整覆盖其支持的 100 种语言。构造 tokenizer 时两者必须等长(源码中有assert len(lang2id) == len(id2lang))。- 默认特殊 token 约定:
unk_token="<unk>"、bos_token="<s>"、sep_token="</s>"、pad_token="<pad>"、cls_token="</s>"、mask_token="<special1>",并预置<special0>~<special9>十个额外特殊 token。这也是为什么预训练模型把<special1>当作掩码符号——模型配置中的mask_index=5与分词器的掩码 token 定义在加载预训练词表后保持一致。
4.3 特殊 token 拼装格式
build_inputs_with_special_tokens定义了两类序列格式(源码):
- 单序列:
<s> X </s> - 序列对:
<s> A </s> B </s>
对应地,get_special_tokens_mask会为上述格式返回1/0掩码(1表示特殊 token),save_vocabulary则会把vocab.json与merges.txt保存到指定目录,供再次加载或共享使用。
五、XLMModel 与 XLMWithLMHeadModel:模型结构与前向原理
5.1 XLMModel:编码器主体
XLMModel(源码)在__init__中按如下结构搭建:
- 嵌入层:
position_embeddings(max_position_embeddings × emb_dim)、可选的lang_embeddings(n_langs × emb_dim,仅n_langs > 1且use_lang_emb=True时创建)、embeddings(词嵌入,n_words × emb_dim,以pad_index作为 padding index)以及嵌入后的layer_norm_emb; - Transformer 层堆叠:每层依次为
MultiHeadAttention(后接layer_norm1)与TransformerFFN(后接layer_norm2),采用 Pre-LN 风格残差;FFN 隐藏维度取emb_dim * 4; - 前向时默认按
(input_ids != pad_index)求和得到各句长度lengths,再由工具函数get_masks生成掩码:causal=False时注意力掩码与 padding 掩码一致,causal=True时生成形状(batch, slen, slen)的下三角掩码(get_masks)。
前向输入除了input_ids/attention_mask/inputs_embeds等通用字段外,还有两个 XLM 特色参数:
langs:形状(batch_size, sequence_length)的语言 ID 张量,按词级为每个 token 指定语言;ID 可由model.config.lang2id(语言名 → ID)与model.config.id2lang(ID → 语言名)互转;lengths:形状(batch_size,)的句长张量,可避免在 padding 位置计算注意力;attention_mask可以达到同样效果,此参数仅为兼容原版 XLM 保留。
从结构还可以推断:XLM 的注意力单元支持 K/V 缓存(前向签名包含cache),这为以掩码生成方式做解码加速预留了接口。默认返回BaseModelOutput(last_hidden_state、可选hidden_states、attentions),也可以return_dict=False得到元组。
5.2 XLMWithLMHeadModel:语言模型头
XLMWithLMHeadModel(源码)在编码器之上叠加XLMPredLayer作为预测头:
- 当
config.asm=False(默认)时,预测层是一个线性投影nn.Linear(emb_dim, n_words),且通过_tied_weights_keys与输入嵌入权重绑定(共享词嵌入,tie_word_embeddings); - 当
config.asm=True时,使用AdaptiveLogSoftmaxWithLoss(自适应 log-softmax 投影)替代线性层,用于超大词表场景的高效 softmax。
因为 XLM 生成属于掩码填充式(masked 自编码式)而非自回归式,prepare_inputs_for_generation(源码)会在输入末尾自动追加一个mask_token_id的掩码 token,并用config.lang_id填充对应位置的langs——这样解码器只需预测该掩码位置即可,这也是配置中mask_token_id与lang_id的用武之地。
前向时labels会在模型内部完成移位,也就是说可以直接传labels=input_ids;-100位置的 label 会被忽略、不计入 loss。logits_to_keep参数允许只对最后若干位置计算 logits,以节约显存。
六、XLM 的下游任务头族
文档中还列出了覆盖常见 NLP 任务的各任务头模型,它们共享同一个XLMModel主干,只是顶部分别换成序列摘要、分类或指针式抽取头。测试文件 tests/models/xlm/test_modeling_xlm.py 同时验证了这些类与 pipeline 的映射关系:
| 模型类 | 对应 Pipeline 任务 | 头结构说明 |
|---|---|---|
XLMModel | feature-extraction | 纯编码器,输出 token 级隐状态 |
XLMWithLMHeadModel | fill-mask、text-generation | 掩码预测线性头 / 自适应 softmax |
XLMForSequenceClassification | text-classification、zero-shot | XLMSequenceSummary池化 + 线性分类层,适配 GLUE 类任务 |
XLMForMultipleChoice | — | 对每个选项独立编码后经序列摘要打分 |
XLMForTokenClassification | token-classification | 直接在最后一层隐状态上接线性分类层 |
XLMForQuestionAnsweringSimple | — | 简单的起止位置 pointer 抽取头 |
XLMForQuestionAnswering | — | SQuAD 2.0 风格头,输出束搜索式的 top-k 起止位置与is_impossible判断 |
6.1 序列分类(GLUE 风格)
XLMForSequenceClassification(源码)把编码器输出交给XLMSequenceSummary(其行为完全由summary_type、summary_use_proj、summary_activation、summary_first_dropout等配置决定),再做线性投影。文档注释明确指出它适配 GLUE 类任务,支持num_labels个分类类别;传入labels时用交叉熵(或 MSE,用于回归)计算 loss。
6.2 抽取式问答:Simple 与 SQuAD 两种形态
仓库中保留了两套问答头:
XLMForQuestionAnsweringSimple:输入start_positions/end_positions计算标准抽取式问答 loss(输出为通用QuestionAnsweringModelOutput);XLMForQuestionAnswering:为 SQuAD 2.0 设计的带束搜索 top-k 变体,输出专用结构XLMForQuestionAnsweringOutput(源码)。当不提供起止位置标注时,该输出包含以下字段(这也是 XLM 独有的 output 类型):start_top_log_probs:形状(batch, start_n_top),束搜索得到的 top-k 起始位置对数概率;start_top_index:top-k 起始位置索引;end_top_log_probs:形状(batch, start_n_top * end_n_top),top-k 结束位置对数概率;end_top_index:top-k 结束位置索引;cls_logits:形状(batch,),答案is_impossible(问题无答案)的对数概率;loss:当同时提供start_positions、end_positions(以及可选的is_impossible)时,为起始、结束(以及不可回答)分类损失之和。
6.3 Token 分类与多项选择
XLMForTokenClassification:对每个 token 做分类,适合命名实体识别(NER)、词性标注等序列标注任务,输出TokenClassifierOutput;XLMForMultipleChoice:forward接收多个选项的输入与labels,输出MultipleChoiceModelOutput,适合多项选择阅读理解。
七、从源码与测试中验证的工程要点
- Pipeline 支持面:测试文件 test_modeling_xlm.py 中把
feature-extraction、fill-mask、text-classification、text-generation、token-classification、zero-shot六类任务与上述模型一一对应,说明 XLM 可以直接在这些标准 pipeline 中使用; - 端到端可用性:同文件还包含
XLMModelLanguageGenerationTest(使用FacebookAI/xlm-mlm-en-2048实测语言生成),以及XLMModelTest对正弦位置嵌入等配置开关的专项用例; - 社区检查点导入:若持有官方 XLM 原始 PyTorch 检查点,可借助 convert_xlm_original_pytorch_checkpoint_to_pytorch.py 中的
convert_xlm_checkpoint_to_pytorch函数完成格式转换; - 配置项对照:
XLMConfig的attribute_map使模型可以兼容 BERT 等模型通用的hidden_size/num_hidden_layers/num_attention_heads/vocab_size参数名,降低从其他架构迁移的成本。
八、小结与使用建议
回顾全篇,XLM 在 Transformers 中的正确打开方式可归纳为四点:
- 选模型:纯单语(如
xlm-mlm-en-2048)直接用于英语的 MLM/分类;多语(如xlm-mlm-100)进行跨语言迁移或零样本评测; - 配分词:使用多语模型时注意让
XLMTokenizer自动加载lang2id/id2lang,并按需传入词级langs张量才能吃到语言嵌入; - 调结构:通过
XLMConfig控制激活函数、位置嵌入、注意力形态与summary_type,并按需开启asm自适应 softmax 处理超大词表; - 选任务头:文本分类走
XLMForSequenceClassification(配 zero-shot pipeline 可做零样本分类),序列标注走XLMForTokenClassification,抽取式问答按是否需要 SQuAD 束搜索输出在Simple与完整版之间选择,生成类任务则由XLMWithLMHeadModel承担。
如果要在多语环境下微调或推理,请记住一个前提:当前仓库中的XLMModel只支持编码器形态(构造解码器会触发NotImplementedError),因此凡是需要自回归解码的用法都应把 XLM 当作"掩码填充式"语言模型来使用,这也正是prepare_inputs_for_generation会自动补mask_token_id的原因。
【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考