深入解析 Transformers 中的 ConvBERT 模型:基于 Span 动态卷积的混合注意力架构
【免费下载链接】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
ConvBERT 是 Transformers 库中一个基于 BERT 改进的预训练语言模型,核心创新在于用**基于 Span 的动态卷积(Span-based Dynamic Convolution)**替代部分全局自注意力头,从而在建模局部依赖的同时显著降低计算成本。本指南将以 docs/source/ja/model_doc/convbert.md 为骨架,结合 src/transformers/models/convbert/modeling_convbert.py 等源码,系统讲解 ConvBERT 的架构原理、配置参数、Tokenizer 以及五大下游任务用法,帮助你快速上手这一高效混合注意力模型。
1. ConvBERT 概述:动机与核心思想
ConvBERT 模型由 Zihang Jiang、Weihao Yu、Daquan Zhou、Yunpeng Chen、Jiashi Feng 与 Shuicheng Yan 在论文《ConvBERT: Improving BERT with Span-based Dynamic Convolution》中提出,其模型卡片与源码说明位于 docs/source/en/model_doc/convbert.md 与 src/transformers/models/convbert/configuration_convbert.py。
1.1 研究动机:全局注意力存在计算冗余
论文摘要指出,以 BERT 为代表的预训练语言模型在各类自然语言理解任务上取得了显著成绩,但 BERT 高度依赖全局自注意力块,带来较大的内存占用与计算开销。虽然所有注意力头都会对整个输入序列进行查询、从全局视角生成注意力图,但研究者观察到:部分注意力头其实只需要学习局部依赖——这意味着存在计算冗余。
基于此观察,ConvBERT 提出一种新的基于 Span 的动态卷积来替换这部分自注意力头,直接建模局部依赖。新的卷积头与其余自注意力头共同组成一个混合注意力块(mixed attention block),从而在全局与局部上下文学习上都更加高效。
1.2 论文报告的成果
根据论文与文档描述:ConvBERT 在多个下游任务上以更低的训练成本和更少的模型参数显著优于 BERT 及其变体。特别地,ConvBERT-base 取得了 86.4 的 GLUE 分数,比 ELECTRA-base 高 0.7,而训练成本不到后者的四分之一。需要注意,这些为论文中报告的数据,实际效果会随数据集与训练配置变化。
1.3 模型来源与原始实现
该模型的 Transformers 实现由 abhishek 贡献,原始实现位于 yitu-opensource 的 ConvBert 仓库。仓库内还提供了 TensorFlow 1 检查点转换脚本 src/transformers/models/convbert/convert_convbert_original_tf1_checkpoint_to_pytorch.py,其中convert_orig_tf1_checkpoint_to_pytorch函数可将原始 TF1 检查点转换为 PyTorch 权重,便于复用官方预训练模型。
2. 使用技巧:训练建议与任务指南
2.1 训练提示与 BERT 一致
根据文档的 Usage tips 章节,ConvBERT 的训练技巧与 BERT 基本相同,完整的使用提示可参考 BERT 文档。这意味着熟悉 BERT 的开发者可以无缝迁移训练经验,包括学习率调度、序列长度处理、Attention Mask 的使用习惯等。
2.2 官方任务指南资源
文档列出了可直接用于实战的任务指南(以英文文档为主,另有日文等其他语言版本):
- 文本分类任务指南:docs/source/en/tasks/sequence_classification.md
- 词元分类任务指南:docs/source/en/tasks/token_classification.md
- 问答任务指南:docs/source/en/tasks/question_answering.md
- 掩码语言建模任务指南:docs/source/en/tasks/masked_language_modeling.md
- 多项选择任务指南:docs/source/en/tasks/multiple_choice.md
这些指南覆盖了 ConvBERT 所支持的全部下游任务场景。
3. 架构原理:从源码理解混合注意力
文档通过[[autodoc]]指向源码类,因此架构细节需要结合源码展开。ConvBERT 的整体结构与 BERT 类似,包含 Embeddings、Encoder(多层ConvBertLayer)与各类任务头,但每一层内部用 Span 动态卷积替换了部分注意力头。
3.1 关键子模块与文件位置
以下类均在 src/transformers/models/convbert/modeling_convbert.py 中实现:
| 模块 | 类名 | 作用 |
|---|---|---|
| 嵌入层 | ConvBertEmbeddings | 词嵌入 + 位置嵌入 + 分段嵌入,维度为embedding_size |
| 可分离卷积 | SeparableConv1D | 深度可分离卷积,depthwise + pointwise 两层 |
| 自注意力 | ConvBertSelfAttention | 混合注意力:动态卷积分支 + 缩放点积注意力分支 |
| 分组线性层 | GroupedLinearLayer | 当num_groups > 1时用于 FFN,实现分组计算 |
| 编码层 | ConvBertLayer/ConvBertEncoder | 堆叠num_hidden_layers层混合注意力块 |
3.2 混合注意力块的计算流程
从源码ConvBertSelfAttention.forward(modeling_convbert.py#L172-L251)可以看到混合注意力块的完整流程:
- 注意力头缩减:通过
head_ratio(默认 2)将注意力头数量缩减为num_attention_heads // head_ratio,attention_head_size = (hidden_size // num_attention_heads) // 2,all_head_size = num_attention_heads * attention_head_size。也就是说,一半的头部维度由自注意力承担,另一半由动态卷积承担,二者输出在 modeling_convbert.py#L243 处通过torch.cat拼接。 - 动态卷积分支:用
SeparableConv1D对 hidden states 做深度可分离卷积得到卷积注意力键(mixed_key_conv_attn_layer),再与 query 逐元素相乘(modeling_convbert.py#L200),通过conv_kernel_layer生成卷积核并做 softmax 归一化(modeling_convbert.py#L202-L204),最后用nn.functional.unfold提取局部窗口(span)并与卷积核做矩阵乘法,得到卷积输出(modeling_convbert.py#L206-L221)。 - 自注意力分支:query 与 key 做点积、按
sqrt(attention_head_size)缩放,加注意力掩码后 softmax,再对 value 加权求和(modeling_convbert.py#L224-L237)。 - 拼接输出:将卷积输出与自注意力上下文沿 head 维度拼接,reshape 后送入
ConvBertSelfOutput做残差与 LayerNorm(modeling_convbert.py#L240-L249)。
直观理解:自注意力头负责全局依赖,动态卷积头负责局部依赖(span 级别的窗口卷积),二者互补。这正对应论文中“混合注意力块在全局与局部上下文学习上都更高效”的论断。
3.3 FFN 与分组线性层
ConvBertIntermediate与ConvBertOutput(modeling_convbert.py#L313-L349)组成了每层的 FFN。当num_groups == 1时使用普通nn.Linear;当num_groups > 1时使用GroupedLinearLayer,把输入输出维度切分为num_groups组分别做矩阵乘法再拼接,以进一步降低参数量与计算量。这与论文中降低训练成本的思路一致。
3.4 解码器与交叉注意力支持
ConvBertLayer继承自GradientCheckpointingLayer(modeling_convbert.py#L352),支持梯度检查点以节省显存。当config.is_decoder=True且add_cross_attention=True时,层内还会挂载交叉注意力模块(modeling_convbert.py#L360-L392),使得 ConvBERT 也可作为编码器-解码器架构中的解码器使用。
4. ConvBertConfig 配置参数详解
文档将ConvBertConfig列为第一个可编程入口。其定义位于 src/transformers/models/convbert/configuration_convbert.py,model_type = "convbert",默认配置对齐YituTech/conv-bert-base风格。完整参数如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
vocab_size | 30522 | 词表大小(WordPiece,BERT 风格) |
hidden_size | 768 | 隐藏层维度 |
num_hidden_layers | 12 | Transformer 编码层数 |
num_attention_heads | 12 | 注意力头数量(会被head_ratio缩减) |
intermediate_size | 3072 | FFN 中间层维度 |
hidden_act | "gelu" | 隐藏层激活函数 |
hidden_dropout_prob | 0.1 | 隐藏层 Dropout 概率 |
attention_probs_dropout_prob | 0.1 | 注意力概率 Dropout |
max_position_embeddings | 512 | 最大位置编码长度 |
type_vocab_size | 2 | 分段(token type)词表大小 |
initializer_range | 0.02 | 权重初始化标准差 |
layer_norm_eps | 1e-12 | LayerNorm 的 epsilon |
pad_token_id | 1 | 填充 token id |
bos_token_id | 0 | 序列开始 token id |
eos_token_id | 2 | 序列结束 token id |
embedding_size | 768 | 嵌入维度,可与hidden_size不同(不同时会插入embeddings_project投影层) |
head_ratio | 2 | 缩减注意力头数量的比率 γ,即用多少个卷积头替换一部分自注意力头 |
conv_kernel_size | 9 | 动态卷积核大小(局部 span 的窗口宽度) |
num_groups | 1 | 分组线性层的分组数 |
classifier_dropout | None | 分类头 Dropout,为None时回退到hidden_dropout_prob |
is_decoder | False | 是否作为解码器 |
add_cross_attention | False | 是否添加交叉注意力 |
tie_word_embeddings | True | 是否绑定输入输出词嵌入 |
其中head_ratio与conv_kernel_size是 ConvBERT 区别于 BERT 的核心参数:head_ratio控制“多少个注意力头被卷积替换”(源码在 modeling_convbert.py#L141-L147 计算num_attention_heads // head_ratio),conv_kernel_size控制局部窗口大小。从源码可见,若hidden_size不能被缩减后的头数整除会抛出异常(modeling_convbert.py#L150-L153)。
4.1 从配置初始化模型
文档中的官方示例展示了如何用配置初始化随机权重的模型:
from transformers import ConvBertConfig, ConvBertModel # 初始化一个 convbert-base-uncased 风格的配置 configuration = ConvBertConfig() # 用该配置初始化一个随机权重的模型 model = ConvBertModel(configuration) # 访问模型配置 configuration = model.config5. 快速上手:加载预训练模型
由于文档主体是 API 参考风格,实际使用需要结合仓库的通用加载机制(AutoModel与AutoTokenizer定义于 src/transformers/models/auto)。ConvBERT 官方预训练检查点为YituTech/conv-bert-base(base 与 large 系列),对应ConvBertTokenizer与ConvBertModel:
from transformers import AutoTokenizer, AutoModel tokenizer = AutoTokenizer.from_pretrained("YituTech/conv-bert-base") model = AutoModel.from_pretrained("YituTech/conv-bert-base") inputs = tokenizer("Hello world!", return_tensors="pt") outputs = model(**inputs) last_hidden_state = outputs.last_hidden_state print(last_hidden_state.shape)6. ConvBertTokenizer 与 ConvBertTokenizerFast
文档列出ConvBertTokenizer与ConvBertTokenizerFast两类。
- ConvBertTokenizer:基于 WordPiece 的分词器,实现位于 src/transformers/models/convbert/tokenization_convbert.py,直接继承自
BertTokenizer,因此支持get_special_tokens_mask、save_vocabulary等 BERT 分词器全部方法,词表格式(vocab.txt)与 BERT 一致。 - ConvBertTokenizerFast:基于 HuggingFace tokenizers 库的 Rust 加速版本,速度更快,用法与
BertTokenizerFast一致。
from transformers import ConvBertTokenizer, ConvBertTokenizerFast slow = ConvBertTokenizer.from_pretrained("YituTech/conv-bert-base") fast = ConvBertTokenizerFast.from_pretrained("YituTech/conv-bert-base") print(slow.tokenize("ConvBERT is efficient.")) print(fast.tokenize("ConvBERT is efficient."))7. 五大下游任务模型用法
文档的[[autodoc]]条目给出了五个带任务头的模型类,全部位于 src/transformers/models/convbert/modeling_convbert.py。测试套件 tests/models/convbert/test_modeling_convbert.py 中的ConvBertModelTester对每类模型的前向传播、loss 计算与 decoder 模式均有覆盖,可作为用法佐证。
7.1 ConvBertModel:基础编码器
无任务头的裸编码器,forward输出BaseModelOutputWithCrossAttentions,包含last_hidden_state、hidden_states、attentions等字段。注意它会通过create_bidirectional_mask生成双向注意力掩码(modeling_convbert.py#L637-L641)。
7.2 ConvBertForMaskedLM:掩码语言建模
在编码器之上叠加ConvBertGeneratorPredictions(两层 MLP + GELU + LayerNorm)与generator_lm_head线性层(modeling_convbert.py#L670-L732)。labels中设置为-100的 token 会被忽略(掩码),损失仅对[0, ..., vocab_size-1]的 token 计算,用于预训练或完形填空:
from transformers import AutoTokenizer, ConvBertForMaskedLM import torch tokenizer = AutoTokenizer.from_pretrained("YituTech/conv-bert-base") model = ConvBertForMaskedLM.from_pretrained("YituTech/conv-bert-base") inputs = tokenizer("The capital of France is [MASK].", return_tensors="pt") labels = tokenizer("The capital of France is Paris.", return_tensors="pt")["input_ids"] outputs = model(**inputs, labels=labels) loss, logits = outputs.loss, outputs.logits mask_index = (inputs["input_ids"] == tokenizer.mask_token_id)[0].nonzero(as_tuple=True)[0] predicted_token_id = logits[0, mask_index].argmax(axis=-1) print(tokenizer.decode(predicted_token_id))7.3 ConvBertForSequenceClassification:文本分类
采用ConvBertClassificationHead(modeling_convbert.py#L735-L756),取[CLS]token(即序列第一个位置)的隐藏状态,经 Dropout → Dense → 激活 → Dropout →out_proj得到 logits。损失依据problem_type自动选择:num_labels == 1时用 MSE(回归),多标签时用BCEWithLogitsLoss,否则用CrossEntropyLoss(modeling_convbert.py#L806-L827)。适用于 GLUE 类任务,对应官方文本分类指南 docs/source/en/tasks/sequence_classification.md:
from transformers import AutoTokenizer, ConvBertForSequenceClassification import torch tokenizer = AutoTokenizer.from_pretrained("YituTech/conv-bert-base") model = ConvBertForSequenceClassification.from_pretrained("YituTech/conv-bert-base", num_labels=2) inputs = tokenizer("This movie is great!", return_tensors="pt") labels = torch.tensor([1]).unsqueeze(0) outputs = model(**inputs, labels=labels) print(outputs.loss, outputs.logits)7.4 ConvBertForTokenClassification:词元分类
对序列中每个 token 的隐藏状态加 Dropout 后过线性分类头(modeling_convbert.py#L932-L988),用CrossEntropyLoss计算损失。典型场景是命名实体识别(NER)、词性标注,对应 docs/source/en/tasks/token_classification.md:
from transformers import AutoTokenizer, ConvBertForTokenClassification import torch tokenizer = AutoTokenizer.from_pretrained("YituTech/conv-bert-base") model = ConvBertForTokenClassification.from_pretrained("YituTech/conv-bert-base", num_labels=7) inputs = tokenizer("HuggingFace is based in New York.", return_tensors="pt") labels = torch.tensor([1] * inputs["input_ids"].shape[1]).unsqueeze(0) # 示例标签 outputs = model(**inputs, labels=labels) print(outputs.logits.shape)7.5 ConvBertForQuestionAnswering:抽取式问答
用qa_outputs线性层输出每个位置的 start/end logits(modeling_convbert.py#L991-L1055)。传入start_positions与end_positions时计算二者的交叉熵损失平均值;超出序列长度的位置会被 clamp 到ignored_index忽略。典型场景是 SQuAD 类抽取式问答,对应 docs/source/en/tasks/question_answering.md:
from transformers import AutoTokenizer, ConvBertForQuestionAnswering import torch tokenizer = AutoTokenizer.from_pretrained("YituTech/conv-bert-base") model = ConvBertForQuestionAnswering.from_pretrained("YituTech/conv-bert-base") question, text = "What is the capital of France?", "The capital of France is Paris." inputs = tokenizer(question, text, return_tensors="pt") outputs = model(**inputs) start_idx = outputs.start_logits.argmax(dim=-1) end_idx = outputs.end_logits.argmax(dim=-1) print(tokenizer.decode(inputs["input_ids"][0][start_idx:end_idx + 1]))7.6 ConvBertForMultipleChoice:多项选择
输入形状为(batch_size, num_choices, seq_len),内部展平为(batch_size * num_choices, seq_len)送入编码器(modeling_convbert.py#L892-L902),经ConvBertSequenceSummary汇总序列向量(支持last/first/mean/cls_index等策略,modeling_convbert.py#L476-L572)后由线性层打分,最后 reshape 回(batch_size, num_choices)并用CrossEntropyLoss计算。适用于 SWAG、多项选择阅读理解等,对应 docs/source/en/tasks/multiple_choice.md:
from transformers import AutoTokenizer, ConvBertForMultipleChoice import torch tokenizer = AutoTokenizer.from_pretrained("YituTech/conv-bert-base") model = ConvBertForMultipleChoice.from_pretrained("YituTech/conv-bert-base") prompt = "The dog" choices = ["runs to the park.", "flies in the sky.", "swims in the sea.", "sleeps all day."] inputs = tokenizer([[prompt, c] for c in choices], return_tensors="pt", padding=True) labels = torch.tensor(0).unsqueeze(0) # 正确选项下标 outputs = model(**{k: v.unsqueeze(0) for k, v in inputs.items()}, labels=labels) print(outputs.logits)8. 模型验证与测试
仓库在 tests/models/convbert/test_modeling_convbert.py 与 tests/models/convbert/test_tokenization_convbert.py 中提供了完整测试:
- 模型测试:
ConvBertModelTester使用小型随机配置(如hidden_size=32、num_hidden_layers=2、num_attention_heads=4)验证各任务模型的前向传播、loss、梯度检查点与 decoder/交叉注意力模式;同时通过slow测试加载真实预训练权重检查输出。 - 分词器测试:验证 WordPiece 分词、特殊 token 掩码与词表保存行为。
如需本地运行测试(在仓库根目录执行):
pytest tests/models/convbert/test_modeling_convbert.py tests/models/convbert/test_tokenization_convbert.py9. 小结
ConvBERT 通过“Span 动态卷积 + 全局自注意力”的混合注意力设计,在保持全局建模能力的同时直接建模局部依赖,从而以更低训练成本与更少参数取得优于 BERT 系列的效果。在 Transformers 仓库中,你可以通过 ConvBertConfig 灵活调整head_ratio、conv_kernel_size、num_groups等核心超参,借助 BERT 兼容的 WordPiece 分词器无缝迁移使用经验,并通过六个任务模型覆盖掩码语言建模、文本分类、词元分类、问答与多项选择等典型 NLP 场景。
【免费下载链接】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),仅供参考