Skip to content
L2 里面是什么

预训练:模型是怎么被喂出来的

模型出厂前,先读完了半个互联网,然后才开始学说话。

像让一个人先读完图书馆再开口 ​

预训练(pretraining)——用海量文本先做一次大规模训练。

打个粗略的比方:一个孩子不会说话之前,听了三年大人聊天。他没有刻意背课文,但「苹果」这个词听过一万次以后,他知道了它跟「甜」「红」一起出现,知道了它不一定指水果。

预训练干的就是这件事。而且它只做一件事:猜下一个词。

任务听起来小到不像回事。给你前面的话,猜后面是什么。猜错了就调参数(parameter,模型训练时被调的那个数字),再猜。几十万亿次重复之后,语料里所有的统计规律都沉到了参数里。

关键在于:没人给它答案 ​

这是整件事最省力的地方,也是它能跑这么大的原因。

监督学习需要标注(annotation)—— 每句话都有人写好「标准答案」。海量的互联网文本没有标注,但每一句本身就是答案:句子本身存在,就等于「这句话之后该接什么」这个问题的正确答案已经有了。

所以预训练可以直接拿原始文本当训练材料。省掉了标注,也省掉了标注会带来的那套偏见(bias)—— 标注的人心里已有的成见。

但纯猜词是不够的 ​

这是 2018 到 2020 年之间那场热闹的核心矛盾。

只做「猜下一个词」的模型,学到的东西偏向表面。BERT 那篇论文的判断是:单方向猜词,只能看到左边的上下文,学不到「这句话整体在说什么」。他们的解法很直接——训练的时候同时看左右两边,让每一层的每个词都左右通吃,然后随机盖住一部分词让它填。填得越像,参数越往下调。

GPT 那一派走向另一个方向:坚持只从左往右猜,但赌的是规模。2020 年那篇 GPT-3 的论文报的是 1750 亿参数,梯度(gradient)—— 答错了之后,每个参数该往哪调、朝哪边调多远。GPT-3 那篇作者的说法是「不做任何梯度更新,只在提示词里给几个例子,就能干活」。这篇是本层里第一次把「不微调也能用」写成结论的论文。

T5 那篇走的是第三条路:把所有任务改写成「输入一段文字,输出一段文字」,连翻译、摘要、问答都算成翻译。这听起来像文字游戏,但实测下来泛化(generalization)—— 换一批没见过的题目还照样能干 —— 确实更整齐。

怎么判断训好了 ​

不能只看模型答得多流畅。困惑度(perplexity)—— 衡量模型对下一个词有多意外的一个指标,越低越好。如果训练语料里下一个词经常是「的」,那模型每次都猜「的」就已经很好了,困惑度就低。

训练集(training set)—— 喂给模型学的那批数据;测试集(test set)—— 留着不参与训练、只在最后拿来打分的那些数据。而困惑度有个众所周知的毛病:把训练集里的内容背下来,困惑度也能刷得很低。2020 年 GPT-3 论文特意强调了测试集的污染问题。

我自己判断一个预训练模型靠不靠谱,现在不看困惑度,只看两件事:它在没见过的任务上还能不能干活,以及它编造的时候有多自信。第二件到「会出事」那一层会展开讲。

延伸阅读 ​

  • Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer(Colin Raffel、Noam Shazeer、Adam Roberts et al.,2019,arXiv 预印本)—— T5。做法是把所有 NLP 任务统一成「文字进、文字出」,然后系统性地比预训练目标、架构、数据集、无监督数据的差别。顺带开源了 C4,那个干净的网页语料。摘要里那句话我很喜欢:迁移学习之所以有效,是因为先在数据多的任务上练过。arXiv: 1910.10683

  • Language Models are Few-Shot Learners(Tom B. Brown、Benjamin Mann、Nick Ryder et al.,2020,NeurIPS)—— GPT-3。核心结论不是模型更聪明,而是「把任务说明和几个例子直接写进输入里,就不用重新训练」。作者自己管这叫 in-context learning。1750 亿参数,比之前任何非稀疏语言模型大十倍。arXiv: 2005.14165

动手试试 ​

不用训模型,也能看出预训练在干嘛。

在 transformers 库里挑一个小模型(gpt2 只有几百兆),遮住句子里的一个词,让它补:

python
from transformers import pipeline
fill = pipeline("fill-mask", model="gpt2")
for w in ["The capital of France is [MASK]."]:
    for r in fill(w, top_k=5):
        print(r["token_str"], round(r["score"], 3))

看那五个候选。排第一的往往是对的,第二三名的差距很小——这说明模型学到的是概率分布,不是一张答案表。

再把 gpt2 换成 gpt2-large 跑一次同样的句子,看差距。这一步是我自己最直观地感受到「参数量在干什么」的地方。

下一站 ​

训完了,参数定死了。接下来是每次你按回车的那几秒里,模型到底在干什么——以及为什么这几年所有做推理的团队都在攻同一件事。

内容用 VitePress 构建,站内链接全部为相对路径。