unilm 仓库中的 fairseq.modules:构建 InfoXLM 系列模型的独立模块库全景解析
【免费下载链接】unilmLarge-scale Self-supervised Pre-training Across Tasks, Languages, and Modalities项目地址: https://gitcode.com/GitHub_Trending/un/unilm
infoxlm/fairseq/docs/modules.rst是 InfoXLM 所基于的 fairseq 分支文档中“Library Reference”一员:它声明了一组可独立使用的torch.nn.Module类,作为实现新的BaseFairseqModel时的现成构件。本文以该文档指向的 fairseq.modules 包 为主体,结合包内每个关键模块的真实源码,讲清这些构件的接口、参数、实现细节(如增量解码 KV 缓存、band 式自适应词表、beam 搜索矩阵乘优化、apex FusedLayerNorm 回退逻辑),并给出它们在 InfoXLM / XLM-Align / RoBERTa 模型代码中的实际引用点,帮助你在阅读或扩展该仓库的模型实现时,能准确定位每个构件的底层行为。
modules.rst 文档定位与 fairseq.modules 包结构
modules.rst 的全文只有一张 Sphinx 指令:
.. automodule:: fairseq.modules :members: :undoc-members:它属于 index.rst 定义的 “Library Reference” 目录树之一(与tasks、models、criterions、optim、lr_scheduler、data并列),其作用是自动抓取 fairseq/modules 包中所有公开成员的 docstring 生成 API 参考页。因此要真正理解这份文档,核心是读懂包__init__.py中__all__声明的 28 个公开符号。按功能可归为五类:
| 类别 | 导出符号 | 源码文件 |
|---|---|---|
| 注意力 | MultiheadAttention、DownsampledMultiHeadAttention、SparseMultiheadAttention(间接) | multihead_attention.py、downsampled_multihead_attention.py、sparse_multihead_attention.py |
| Transformer 层 | TransformerEncoderLayer、TransformerDecoderLayer、TransformerSentenceEncoder、TransformerSentenceEncoderLayer | transformer_layer.py、transformer_sentence_encoder.py |
| 词表/位置嵌入 | AdaptiveInput、AdaptiveSoftmax、PositionalEmbedding、LearnedPositionalEmbedding、SinusoidalPositionalEmbedding | adaptive_input.py、adaptive_softmax.py、positional_embedding.py 等 |
| 归一化/激活/门控 | LayerNorm、gelu、gelu_accurate、Highway、LogSumExpMoE、MeanPoolGatingNetwork | layer_norm.py、gelu.py、logsumexp_moe.py |
| 推断优化/卷积 | BeamableMM、ConvTBC、DynamicConv、LightweightConv、LinearizedConvolution、VGGBlock | beamable_mm.py、dynamic_convolution.py 等 |
这个包是 InfoXLM 仓库内模型代码的公共依赖层。例如 InfoXLM 系列的核心模型 xlm_align.py 中from fairseq.modules import LayerNorm;fairseq 内置的 RoBERTa 模型 roberta/model.py 则导入LayerNorm、TransformerSentenceEncoder与init_bert_params。InfoXLM、XLM-Align、mBERT、XLM 等预训练脚本(见 infoxlm/README.md)训练出的 checkpoint 正是由这些构件拼成的编码器堆叠而成。
MultiheadAttention:支持增量解码与 KV 缓存的自/交叉注意力
MultiheadAttention 是“Attention Is All You Need”式的多头注意力实现,也是仓库内所有 seq2seq 模型的注意力基座。构造函数签名为:
MultiheadAttention(embed_dim, num_heads, kdim=None, vdim=None, dropout=0., bias=True, add_bias_kv=False, add_zero_attn=False, self_attention=False, encoder_decoder_attention=False)关键设计点(见 multihead_attention.py):
- q/k/v 独立投影:
q_proj、k_proj、v_proj为三个独立nn.Linear,并允许kdim/vdim与embed_dim不同(交叉注意力场景)。self_attention=True时强制qkv_same_dim(L39-L40)。 - 缩放与初始化:
self.scaling = head_dim ** -0.5;reset_parameters在 qkv 同维时使用gain=1/sqrt(2)的 Xavier 均匀初始化(L69-L82),注释写明这是经验上收敛更好的初始化。 - 可选机制:
add_bias_kv会在 K/V 末尾拼接可学习的bias_k/bias_v(L168-L176);add_zero_attn追加一个全零位置,使模型总能获得“零输出”选项(L223-L231)。 - 两条执行路径:
forward优先判断是否满足F.multi_head_attention_forward条件(非 ONNX、无增量状态、无 static_kv),满足则走 PyTorch 内置融合实现(L123-L135);否则走手写 bmm 路径,支持attn_mask、key_padding_mask(以-inf填充,见 L244-L251)与注意力权重返回(可取各 head 均值或每 head 权重)。 - 增量解码 KV 缓存:
_get_input_buffer/_set_input_buffer借助utils的 incremental state 存取prev_key/prev_value(L317-L330);解码时新 K/V 与历史拼接(L184-L210),static_kv=True(编码器输出作 KV)时跳过重算;reorder_incremental_state支持 beam search 下的批量重排(L308-L315)。 apply_sparse_mask钩子:基类为空实现(L332-L333),供SparseMultiheadAttention等子类覆写注入稀疏模式。- checkpoint 兼容:
upgrade_state_dict_named会把旧版单一in_proj_weight拆分为q/k/v_proj.weight(L335-L362)。
TransformerEncoderLayer / TransformerDecoderLayer:可切换 pre-norm 的层积木
transformer_layer.py 提供从命令行参数args直接构造的层块,是 fconv、fconv_self_att 等旧模型的默认层。
- normalize_before 双模式:
maybe_layer_norm用before ^ after断言保证每层子模块只归一化一次,normalize_before为真时采用 tensor2tensor 风格的 pre-norm(L110-L115、L295-L300),注释说明这是为了训练更稳健。 - attn_mask 用 -1e8 而非 -inf:
TransformerEncoderLayer.forward中显式注释——用-inf会在个别 padding 边界情形让注意力权重出现 NaN 并污染模型参数,因此改为masked_fill(attn_mask.bool(), -1e8)(L86-L94)。 - Decoder 层:默认构造 self-attn(可选
add_bias_kv/add_zero_attn)与 encoder-decoder attention(kdim/vdim取编码器维度,encoder_decoder_attention=True,L135-L180);交叉注意力前向以static_kv=True传入,利用上文提到的 KV 缓存避免重复投影;并支持cross_self_attention(将 encoder_out 拼入 self-attn 的 K/V,L228-L237)。 - 线性层初始化:文件底部的
Linear()工厂函数对nn.Linear做 Xavier 均匀初始化并将 bias 置零(L306-L311)。 TransformerDecoderLayer还实现了make_generation_fast_(need_attn=...)(L302-L303),供生成阶段决定是否计算注意力权重。
另外,TransformerSentenceEncoder/TransformerSentenceEncoderLayer(transformer_sentence_encoder.py)是 BERT 风格双向编码器堆叠,init_bert_params用于 RoBERTa/InfoXLM 类模型的参数初始化——这正是 roberta/model.py 所引用者,也是 InfoXLM 继续训练 XLM-R 基座的模型骨架来源。
位置编码:PositionalEmbedding 工厂与正弦/学习式两条路线
positional_embedding.py 是一个轻量工厂函数:
def PositionalEmbedding(num_embeddings, embedding_dim, padding_idx, learned=False): if learned: if padding_idx is not None: num_embeddings = num_embeddings + padding_idx + 1 m = LearnedPositionalEmbedding(num_embeddings, embedding_dim, padding_idx) nn.init.normal_(m.weight, mean=0, std=embedding_dim ** -0.5) if padding_idx is not None: nn.init.constant_(m.weight[padding_idx], 0) else: m = SinusoidalPositionalEmbedding( embedding_dim, padding_idx, init_size=num_embeddings + padding_idx + 1) return mlearned=False走正弦路线;learned=True时若指定padding_idx,位置 id 会整体偏移padding_idx + 1,使位置 0 从第一个真实 token 开始计数,并把 padding 位置权重恒置 0。- LearnedPositionalEmbedding 继承
nn.Embedding,forward接受外部传入的positions(与padding_idx互斥)或经utils.make_positions由 token 序列推导;max_positions返回num_embeddings - padding_idx - 1。它同样实现了 ONNX trace 分支(prepare_for_onnx_export_)。 - SinusoidalPositionalEmbedding 的
get_embedding与 tensor2tensor 一致:half_dim组正弦/余弦拼接,奇数维时零填充,padding 行置零;forward中若max_pos > weights.size(0)会按需重建并扩展权重表(L58-L65),因此天然支持任意长度,max_positions()直接返回1e5(L83-L85)。增量解码单步时只取padding_idx + pos一行并 expand(L68-L73)。
InfoXLM 预训练中--tokens-per-sample 512的 token block 长度上限,就是由这类位置编码的容量与max_positions约束共同决定的。
大词表效率构件:AdaptiveInput 与 AdaptiveSoftmax
这一对模块实现了频率分 band 的自适应输入/输出,常用于 LSTM 类语言模型(仓库内 lstm.py 即from fairseq.modules import AdaptiveSoftmax)。
- AdaptiveInput:按
cutoff把词表切成若干 band,第i个 band 用initial_dim // factor ** i维嵌入再经无偏线性升维到output_dim(L37-L45);forward对每个 band 用input.lt/ge掩码筛选出该 band 的词并查表(L61-L72)。weights_for_band(band)返回该 band 的嵌入权重与投影权重,供输出层复用。 - AdaptiveSoftmax:头部线性层输出
cutoff[0] + len(cutoff) - 1维(前cutoff[0]个高频词 + 各尾部 band 的门控类),_make_tail为每个 band 构造“降维投影 → Dropout → band 内线性”的串联,且当adaptive_inputs与tie_proj给定时用 TiedLinear 共享嵌入权重(TiedHeadModule处理输入维度不匹配的桥接)。 - 按需计算:
adapt_target先把目标 id 改写为“band 头 id + band 内偏移”的两级目标(L122-L145);forward只对被命中的 band 执行index_select后过尾层,未命中的 band 返回None(L147-L168),从而避免为整个词表打分。 get_log_prob则走完整概率树:头部 LogSoftmax 后,各 band 的 tail 输出加上“进入该 band 的先验对数概率”(L170-L205),用于推理与评估。
BeamableMM:利用 beam 复制结构加速推理的矩阵乘
BeamableMM 是一个只有一行构造参数的小模块,但它封装了 beam search 解码中的一个重要观察:
源侧输入被复制 beam 份、目标侧宽度为 1 时,
(bsz x 1 x nhu) @ (bsz x sz2 x nhu)可重排为(bsz/beam x beam x nhu) @ (bsz/beam x sz2 x nhu),批量矩阵乘的计算量近似降为 1/beam。
forward仅在同时满足test 模式、beam_size已设置、输入 3 维且时间步为 1 时启用优化路径(L22-L42),用unfold(0, beam, beam)完成重排,bsz == beam时退化为torch.mm;否则原样bmm。生成流程需先调用set_beam_size(beam_size)注入 beam 宽度。这是该仓库 seq2seq 生成路径中注意力打分的隐藏加速点。
LayerNorm 工厂:apex FusedLayerNorm 的透明回退
layer_norm.py 全文仅 8 行,却是仓库内所有 Transformer 层的归一化入口:
def LayerNorm(normalized_shape, eps=1e-5, elementwise_affine=True, export=False): if not export and torch.cuda.is_available(): try: from apex.normalization import FusedLayerNorm return FusedLayerNorm(normalized_shape, eps, elementwise_affine) except ImportError: pass return torch.nn.LayerNorm(normalized_shape, eps, elementwise_affine)逻辑是:非导出环境且 CUDA 可用时优先返回 apex 的融合FusedLayerNorm(访存融合、训练更快),导入失败或 ONNX 导出(export=True)时静默回退到torch.nn.LayerNorm。这解释了为什么TransformerDecoderLayer里对char_inputs导出场景显式传export=...(transformer_layer.py L160-L161)——融合核在 ONNX trace 下不可用。InfoXLM 的 xlm_align.py 直接依赖这一工厂,说明其编码器各层归一化行为同样受该回退机制支配。
同目录的 gelu.py 提供gelu(优先F.gelu,缺失时回退x * 0.5 * (1 + erf(x/sqrt(2))))与gelu_accurate(tanh 近似式),供utils.get_activation_fn等按名称选用。
从文档入口到源码:检索与延伸阅读路径
- API 参考页本身:modules.rst(由 index.rst 的 Library Reference toctree 挂载)。
- 包导出清单:fairseq/modules/init.py(28 个公开符号,是核对“文档覆盖哪些类”的权威列表)。
- 实际调用点示例:xlm_align.py 中导入 LayerNorm、roberta/model.py 中导入 LayerNorm 与 TransformerSentenceEncoder、lstm.py 中导入 AdaptiveSoftmax。
- 训练侧上下文:InfoXLM / XLM-Align / MLM / MLM+TLM 的预训练命令与参数(
--tokens-per-sample 512、--roberta-model-path、--xlco_layer等)见 infoxlm/README.md,这些命令产出的模型即由本文各模块拼装而成。
综上,modules.rst这一页看似极简的 automodule 文档,背后对应的是一个覆盖“注意力—层结构—嵌入—归一化—词表—推理加速”五个层面、且深度绑定 ONNX 导出与增量解码路径的构件库;理解其中每个类的接口与回退逻辑,是读懂 InfoXLM 仓库内模型代码(乃至在其 fairseq 分支上实现新BaseFairseqModel)的直接前提。
【免费下载链接】unilmLarge-scale Self-supervised Pre-training Across Tasks, Languages, and Modalities项目地址: https://gitcode.com/GitHub_Trending/un/unilm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考