更加鲁棒的 RoBERTa 模型

更加鲁棒的 RoBERTa 模型

BERT 2018 年发布之后,很多团队按论文里的配方复现,发现怎么都追不上论文成绩。大家逐渐意识到,BERT 那套预训练设置里藏着不少随手定下来的超参数,未必是最优的。RoBERTa 是 Facebook AI 2019 年交出来的答卷,它把 BERT 的网络结构原封不动保留,只把预训练的数据、任务、掩码方式、批量大小挨个重做对照实验,最终成绩全面超过 BERT。这个工作的价值不在新结构,而在证明了预训练配方对结果的影响有多大。

接下来,我们逐条讲 RoBERTa 改了哪几处,每处为什么有效,最后给一段加载现成模型的代码。读这篇之前,你需要知道 BERT 的 MLM 和 NSP 两个预训练任务分别在做什么。

1. 动机由来

我们先看 BERT 公布的原始配方。MLM 加 NSP 两个任务,在约 16GB 文本上训练,批量 256,掩码在数据预处理时一次性定好。问题是这套配方当年时间紧,很多选择是拍脑袋定的。RoBERTa 做的事,就是把每个选择单独拎出来做消融实验,一次只改一个变量,看成绩涨还是跌,最后把所有涨点的项合到一起。

2. 预训练任务

第一刀砍向 NSP。BERT 让模型判断第二句是不是第一句的下文,本意是让模型学会句子间关系。RoBERTa 把带 NSP 和不带 NSP 的模型并排训练对比,发现去掉 NSP 之后多数下游任务反而变好。原因和 ALBERT 的判断一致,这个任务太容易,模型光靠话题一致性就能蒙对,学不到真正的句间逻辑。砍掉之后,RoBERTa 只保留 MLM 这一个任务。

配套的输入格式也跟着变。BERT 拼两句,中间夹 [SEP]。RoBERTa 干脆把多篇文档的句子连续塞进一个 512 长度的窗口,不做句子边界标记,填到满为止。模型在更长的连贯文本里做掩码,看到的上下文更完整。

3. 动态掩码

第二处改的是掩码方式。BERT 在预处理阶段就把每个句子的掩码位置算好,之后反复训练都用同一张掩码表。RoBERTa 改成动态掩码,数据每次送进模型时现场重新随机选位置。区别看似微小,影响却很实际。静态掩码下,同一句话在训练全程被遮的都是同样几个词,模型等于反复做同一张卷子。动态掩码下,这句话这次遮这里下次遮那里,同样的文本被利用出更多花样,泛化更好。这里要注意,遮的比例仍然是 15%,变的是位置重新随机,不是遮得更多。

4. 数据与批量

配方改不动结构,剩下的就是堆量和调参。RoBERTa 把训练文本从约 16GB 扩到约 160GB,新增 CC-News、OpenWebText、Stories 几个语料,是原来的 10 倍。批量从 256 提到 8192,大梯度让训练更稳,也允许配更大的学习率。训练步数上,论文从 10 万步一路加到 30 万、50 万,成绩持续上涨,没有过拟合迹象,最终用 50 万步。分词器也从 WordPiece 换成字节级 BPE,词表扩到 5 万,遇到未登录词不再直接退化成一个陌生符号。我们把两边的配方并排看:

项目BERT-baseRoBERTa-base
网络结构12 层 Transformer完全相同
预训练任务MLM 加 NSP仅 MLM
掩码方式预处理时定死每步重新采样
训练数据约 16GB约 160GB
批量大小2568192
训练步数约 100 万50 万
分词器WordPiece 约 3 万字节级 BPE 约 5 万

批量大了 32 倍,步数反而少一半,可吃进去的 token 总量仍然远超 BERT,这才是成绩提升的物质基础。RoBERTa 能做的文本分类、命名实体识别、问答、自然语言推理这些任务,BERT 也都能接,它的意义是在同等结构下把上限抬高了一截。

5. 加载使用

RoBERTa 权重在 Hugging Face 上公开,调用方式和 BERT 几乎一样,只是类名换成 Roberta。我们以文本分类为例:

from transformers import RobertaTokenizer, RobertaForSequenceClassification

tokenizer = RobertaTokenizer.from_pretrained('roberta-base')
model = RobertaForSequenceClassification.from_pretrained('roberta-base', num_labels=2)

text = 'This movie is great'
inputs = tokenizer(text, return_tensors='pt')
out = model(**inputs)                 # out.logits 是两个类别的得分
label = out.logits.argmax().item()

常用权重的地址列在这。英文版 roberta-base 和 roberta-large 见 https://huggingface.co/FacebookAI/roberta-base ,多语言版 xlm-roberta-base 和 xlm-roberta-large 见 https://huggingface.co/FacebookAI/xlm-roberta-base ,在 MNLI 上微调过的 roberta-large-mnli 可直接做零样本分类,中文场景常用哈工大的 chinese-roberta-wwm-ext,地址是 https://huggingface.co/hfl/chinese-roberta-wwm-ext 。

6. 误区澄清

第一个坑,以为 RoBERTa 改了模型结构。它和 BERT-base 的层数、隐藏维度、注意力头数完全一样,差别全在预训练配方上。

第二个坑,以为去掉 NSP 是因为它有害。实验显示它更多是没用,占了训练信号却帮不上下游任务,删掉后把算力省给 MLM。

第三个坑,以为 RoBERTa 只是把 BERT 训得更久。步数其实砍了一半,真正起作用的是批量放大、数据翻十倍和掩码动态化这几处配合,单靠加步数复制不出同样的效果。

我们回到开头的问题。RoBERTa 没发明新结构,它把 BERT 预训练的每个默认值重新验证了一遍。去掉没用的 NSP,掩码改成动态,数据扩到 10 倍,批量提到 8192,配合字节级 BPE,同等结构下成绩稳超 BERT。这个工作留下的方法论很实在,效果不达预期时,先把训练配方逐个变量拆开看。原论文见 https://arxiv.org/pdf/1907.11692.pdf