GPT-2 技术报告
GPT-2 是传统NLP技术和大模型的衔接,其在1.5B参数规模下,以无监督学习完成多类传统NLP任务。
原文《Language Models are Unsupervised Multitask Learners》, 2019 OpenAI, Cited by 18785
- https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf
1.简介
1.1 摘要
- 传统方法局限
以往的 NLP 任务依赖于有监督学习 和 特定任务数据集 。仅针对单一任务,模型的泛化和跨任务能力有限。
NLP 任务如:问答(QA)、机器翻译、阅读理解、摘要生成等。
-
GPT-2 的核心发现
-
若在大规模通用语料(如 WebText)上预训练语言模型,它会自然地学会完成多种nlp任务,即使没有显式监督信号。
-
模型容量与任务迁移效果呈 log-linear 增长规律,说明更大的模型更善于 zero-shot 学习。
-
最大模型 GPT-2(1.5B 参数) 在 8 个语言建模数据集中有 7 个达到 SOTA(state-of-the-art)效果,尽管仍未充分拟合 WebText。
-
例如,在 CoQA(对话式问答)上,仅通过 prompt(给定文档 + 问题)推理,模型能达到 55 F1,优于 3/4 的有监督基线模型。
-
GPT-2结果启示
-
语言理解和生成能力可以通过从自然语言中“无监督地”自回归生成涌现,而非依赖人工标注的任务式训练。
-
大模型不仅学到了统计模式,也掌握了更深层次的语言生成能力。模型规模的扩大是实现这种 zero-shot 任务迁移 的关键;
-
GPT-2 展示了大规模自监督语言模型的“涌现多任务能力”。无需显式监督即可在多种 NLP 任务上取得SOTA性能,标志着从监督学习到通用语言理解的范式转变。
-
1.2 核心论点
- 当前系统局限性:依赖单一任务和单一领域数据集的监督学习导致机器学习系统泛化能力不足,表现为对数据分布或任务变化的脆弱性。
- 零样本学习的突破:语言模型通过大规模预训练(如WebText),无需参数或架构修改即可在零样本设置下执行多种下游任务,展现了通用系统的潜力。
- 模型规模与性能:零样本任务性能随语言模型规模增加而提升,验证了高容量模型在通用任务处理中的重要性。
2. 方法
2.1 语言建模
核心方法是语言建模(Language Modeling),通常被视为无监督分布估计,从一组示例 (x1, x2, …, xn)中估计符号序列 (s1, s2, …, sn)`的联合概率
-
联合概率分解为条件概率乘积:
p(x)=∏i=1np(si∣s1,...,si−1)p(x) = \prod_{i=1}^{n} p(s_i \mid s_1, ..., s_{i-1})p(x)=i=1∏np(si∣s1,...,si−1)
这种方法使得 p(x) 以及任何形式为 p(sn∣s1,…,sn−1)p(s_n ∣s_1, …,s_{n−1} )p(sn∣s1,…,sn−1) 的条件概率的采样和估计变得易于处理(tractable)。
2.2 任务建模与多任务扩展
-
单任务学习可以表达为条件概率估计 p( output | input )
-
通用系统应能执行多任务,即不仅条件化输入,还需条件化任务 p( output |input, task),类似元学习(meta-learning)
-
自然语言作为任务描述
语言提供了一种灵活方式,将任务、输入、输出都表示为符号序列。即用这种格式训练单一模型执行多任务,例如:
- 翻译训练示例可写作 (translate to french, english text, french text)- 阅读理解可写作 (answer the question, document, question, answer)
2.3 大模型与多任务
大规模文本数据杂乱无章,与标准化的监督任务(如问答、翻译)差距巨大,其天然存在“任务演示”(task demonstrations)的提示词;
如果大型语言模型若容量足够,会学会这些任务的结构与逻辑。
因此,大规模语言模型在自然语言预训练中,会隐式地学习任务执行能力,从而在无监督条件下表现出多任务迁移能力。
总结出以下趋势:
- 从监督到无监督:不再依赖人工标注或奖励信号,模型在预测自然语言时,隐式学习各种任务。
- 从交互到被动学习:不局限于对话,而是利用互联网中大量存在的文本。
- 验证路径:通过 zero-shot 评测多种任务,检验语言模型是否具备这种“内生任务理解能力”。
3. 数据
3.1 数据源
以往的语言模型只在单一领域的数据上训练,例如:
- 新闻文章(Jozefowicz et al., 2016)
- Wikipedia(Merity et al., 2016)
- 小说文本(Kiros et al., 2015)
GPT2用尽可能大且多样化的数据集,从而收集不同领域和语境。
3.2 数据处理
数据源是 网页抓取数据(如 Common Crawl),其规模远超以往语料,存在严重的数据质量问题,大量文档“几乎无法理解”。
为此,GPT2的数据采集步骤为:
- 从 Reddit 中抓取所有被用户点赞(karma ≥ 3)的外部链接;这些链接通常指向高质量、教育性或有趣的内容;
- 最终得到的 WebText 数据集 包含约 4500 万个网页链接 的文本子集;
- 经过文本抽取(使用 Dragnet 和 Newspaper 工具)、去重和启发式清洗后,保留 约 800 万篇文档,总计 40GB 文本;
- 移除 Wikipedia 文档,以避免训练集与评测任务重叠。
这一数据集体现了高质量 + 多样性 + 无监督的特点。
4.输入表示
4.1 字节与字符
理解 char(字符) 与 byte(字节) 的区别与联系,是彻底搞懂语言模型输入表示(如 byte-level BPE) 的基础。
一、直观定义
名称 | 意义 | 举例 |
---|---|---|
char | character(字符),一个“可读的符号”—人类层面的文本单位 | 'A' 、'中' 、'🙂' ,各类标点符号 |
byte | 计算机的最小可寻址存储单位,用于存储数据 | 8位二进制,如 01000001 |
-
char 是“文字”的概念,是抽象的。
-
byte 是“存储”的概念,是表示字符的二进制编码。
-
常见字符类别:
字符(char) | 举例 |
---|---|
字母(letters) | a–z, A–Z |
数字(digits) | 0–9 |
标点(punctuation) | ! ? . , … |
空格(space) | ' ' |
其他符号 | emoji, 数学符号等 |
二、举例
- 字符 ‘A’
字符 | Unicode 码点 | UTF-8 编码 | 占用字节数 |
---|---|---|---|
'A' | U+0041 | 0x41 | 1 byte |
- 字符 ‘中’
字符 | Unicode 码点 | UTF-8 编码 | 占用字节数 |
---|---|---|---|
'中' | U+4E2D | 0xE4 0xB8 0xAD | 3 bytes |
- 字符 ‘🙂’
字符 | Unicode 码点 | UTF-8 编码 | 占用字节数 |
---|---|---|---|
'🙂' | U+1F642 | 0xF0 0x9F 0x99 0x82 | 4 bytes |
4.2 字符编码方法
语言模型自回归式计算(生成)任何字符串概率。
大型语言模型在预处理阶段:
- 小写化
- 分词
- OOV (Out-of-Vocabulary, 词汇表外标记/未登录词)
4.2.1 Unicode
Unicode不是编码方法,是一个字符集,以统一全球的字符标准。
Unicode用唯一编号(码点)表示世界上所有文字和符号。是现代字符编码体系的基础。
Unicode不关心存储(那是编码方法的功能), 只定义:“字符” 在唯一ID:
概念 | 含义 | 类比 |
---|---|---|
字符(Character) | 抽象的文字符号,如 “A”、“中”、“😊” | 文字本身 |
码点(Code Point) | 分配给每个字符的唯一编号,如 U+0041、U+4E2D | ID号 |
编码(Encoding Form) | 把码点表示成一串数字的方式,如 UTF-8 | ID的字节表示方法 |
- Unicode 编号系统(码点空间)
,共可容纳约 111 万个字符(1,114,112 个码点), 这 111 万个位置被划分成 17 个「平面」(Plane):
平面编号 | 范围(16进制) | 名称 | 内容示例 |
---|---|---|---|
0 | U+0000 – U+FFFF | 基本多文种平面(Basic Multilingual Plane, BMP) | 常用语言:中英日、符号、表情基础,共65536个字符 |
1 | U+10000 – U+1FFFF | 多文种补充平面 | 罕见文字、历史文字 |
2 | U+20000 – U+2FFFF | 表意文字补充平面 | CJK 扩展 B 区(更多汉字) |
3–13 | 保留 | 未分配 | |
14 | 特殊用途平面 | 隐藏注释、控制符 | |
15–16 | 私用区平面(PUA) | 由用户/厂商自定义符号 |
Unicode的字符范围:U+0000 ~ U+10FFFF, 共需21位二进制编号空间表示。剩余位数用于扩展(3-16平面)
每个十六进制数字 = 4 个二进制位,如: 0x10 = 0001 0000。
- 不同写法
写法 | 实际意思 | 位长 | 示例解释 |
---|---|---|---|
Plane 16 | 平面号是 16 | 用 5 位存平面号 | 16 → 二进制 10000 |
0x10 | 十六进制表示的 16 | 对应二进制 0001 0000 | 是平面号的十六进制写法 |
0x10000 | 码点 U+10000 | 二进制共 17 位 | 是 Plane 1 的第一个字符,不是 Plane 16 |
- 二进制位置关系
位段 | 位数 | 含义 | 二进制示例 (0x10FFFF ) |
---|---|---|---|
[20–16] | 5 位 | 平面号(Plane number) | 10000 (Plane 16) |
[15–0] | 16 位 | 平面内偏移 | 1111 1111 1111 1111 |
- 编码过程:
┌──────────────────────────────┐
│ Character (中) │ ← 抽象文字
└──────────────┬───────────────┘↓
┌──────────────────────────────┐
│ Unicode Code Point: U+4E2D │ ← 全局唯一编号
└──────────────┬───────────────┘↓
┌──────────────────────────────┐
│ UTF-8 : E4 B8 AD (3 bytes) │
│ UTF-16 : 4E2D (2 bytes) │
│ UTF-32 : 00004E2D (4 bytes) │
└──────────────────────────────┘
4.2.2 ASCII
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码) 是最早广泛应用的字符编码标准之一(1960年代),
最初用于在计算机、电传打字机和通信设备之间表示文字。
- 编码长度:7 位 (bit)
- 可表示字符数:2⁷ = 128 个符号(从 0 到 127)
- 覆盖键盘上的 字母区、数字区、符号区。
| 范围 | 含义 | 示例 |
| ------- | ------------- | ---------------------------------------------------- | ---- |
| 0–31 | 控制字符 | 回车(CR, 13)、换行(LF, 10)、制表符(TAB, 9) | |
| 32–47 | 标点与空格 | 空格(32)、!
, "
, #
, $
, %
, &
, '
, (
, )
等 | |
| 48–57 | 数字 | '0'
~'9'
| |
| 58–64 | 符号 | : ; < = > ? @
| |
| 65–90 | 大写字母 | 'A'
~'Z'
| |
| 91–96 | 符号 | [ \ ] ^ _ `` | | | 97–122 | **小写字母** |
‘a’~
’z’ | | | 123–126 | **符号** |
{ | } ~` |
| 127 | DEL(删除控制) | 删除键控制符 | |
4.2.3 UTF-8
早期的 ASCII 只有 128 个字符(7 bit),仅能覆盖英文字符与控制字符;不同国家为了支持自己的语言(如中文、日文、俄文)各自定义了本地编码:
- 中国:GB2312、GBK、GB18030
- 日本:Shift-JIS
- 欧洲:ISO-8859-x
以上编码互不兼容、会造成乱码,于是产生了统一方案 —— Unicode(统一字符集)。
Unicode 给每个字符一个唯一编号(码点,Code Point),但Unicode 只是“编号系统”,它没规定具体存储方式。
UTF (Unicode Transformation Format)是 一种 把 Unicode码点转成字节序列的方法。
UTF 通过字节头(二进制前几位)的定义,使得可用变长字节(1-3)表示不同字符,在计算机内存和网络中高效、兼容地存储和传输Unicode编号。
名称 | 每个字符长度 | 特点 |
---|---|---|
UTF-32 | 固定 4 字节 | 简单但浪费内存 |
UTF-16 | 可变 2 或 4 字节 | Windows 内部使用 |
UTF-8 | 可变 1–4 字节 | 最节省空间、兼容 ASCII、广泛使用 |
UTF兼容所有 ASCII 字符(U+0000~U+007F,在UTF-8中使用单字节表示
- 编码规则(UFT-8)
第 1 字节 的前导位(1 的个数)表示总字节数;之后每个 续字节 都以 10 开头;其余位用于存放真正的字符二进制数据。
字节数 | Unicode 码点范围 | 格式 | 示例 |
---|---|---|---|
1 | U+0000 ~ U+007F | 0xxxxxxx | 英文、数字、标点、控制符 |
2 | U+0080 ~ U+07FF | 110xxxxx 10xxxxxx | 欧洲字符:带重音的拉丁字母、希腊字母、西里尔字母,部分符号 |
3 | U+0800 ~ U+FFFF | 1110xxxx 10xxxxxx 10xxxxxx | 中文、日文、韩文、阿拉伯文等 |
4 | U+10000 ~ U+10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | Emoji、古文字、罕见文字等 |
- 示例
字符 | Unicode | 二进制 | UTF-8 字节序列 | 十六进制表示 |
---|---|---|---|---|
A | U+0041 | 01000001 | 01000001 | 41 |
¢ | U+00A2 | 0000 0000 1010 0010 | 11000010 10100010 | C2 A2 |
中 | U+4E2D | 0100 1110 0010 1101 | 11100100 10111000 10101101 | E4 B8 AD |
😀 | U+1F600 | 0001 1111 0110 0000 0000 | 11110000 10011111 10011000 10000000 | F0 9F 98 80 |
Unicode值(二进制) 转 UTF-8 字节的填充方式 (码点转换):
01001110 00101101 # '中'字符 → Unicode = U+4E2D → 二进制数值 = 01001110 00101101
1110xxxx 10xxxxxx 10xxxxxx # 三字节 的 UTF-8 填充位置
11100100 10111000 10101101 # 二进制 对应 填充结果
- 语音的联系
文字编码体系 与 语音/语言学结构 存在根本区别,因此UTF-8 不直接表示“音素”,它表示“字符(grapheme)”,而不是“语音单位(phoneme)”。
但 Unicode 可以间接编码带“音标”或“拼音符号”的字符,如下:
概念 | 属于哪一层 | 例子 | 谁负责编码? |
---|---|---|---|
音素(phoneme) | 语音层(声音最小单位) | /p/, /b/, /ʃ/, /ɑː/ | 不在计算机文本编码范围 |
字符(character / grapheme) | 书写层(文字符号) | p, b, sh, a | Unicode / UTF-8 负责编码 |
Unicodeb包含国际音标(IPA, International Phonetic Alphabet)字符:
音标 | 含义 | Unicode 码点 | UTF-8 编码 |
---|---|---|---|
ʃ | 英语 sh 音 | U+0283 | CCA3 |
ɪ | 短 i 音 | U+026A | C9AA |
ŋ | 鼻音 ng | U+014B | C58B |
ð | 英语 th 音 | U+00F0 | C3B0 |
也可以用 “基础字母 + 音调符号”动态生成新字形,比如:
e + ́(U+0065 + U+0301)→ é, n + ̃(U+006E + U+0303)→ ñ
这类“组合音调符号”(Combining Diacritical Marks)常见于越南语、拼音、IPA 等场景。
4.3 BPE
Byte Pair Encoding (BPE) 是一种数据压缩算法,后来被引入到 NLP 领域作为一种子词(Subword)分词方法。它通过迭代将语料中最频繁的连续字符或子词对合并成一个新的子词,来构建训练语料(数据集)的词汇表。
BPE 的思想可应用于不同的编码层次,主要有三种:
Word-level BPE → Unicode-level BPE → Byte-level BPE。
4.3.1 Unicode-level BPE
Unicode-level BPE(也常被称为字符级 BPE,但更精确的说法是在 Unicode 码点上操作)是 BPE 在 NLP 中应用的一种早期和常见形式,例如最初在 WMT 2016 机器翻译任务中使用的 BPE 实现。
核心思想和过程
基本词汇表(Base Vocabulary): 初始词汇表包含所有在语料中出现的单个 Unicode 字符(码点)。
分词单位: 模型的输入单元是这些 Unicode 字符。
合并: 算法不断寻找语料中最常出现的相邻字符对(或子词对),并将它们合并成一个新的子词符号,添加到词汇表中。
最终词汇表: 最终的词汇表由所有初始字符和所有合并产生的新子词组成。
4.3.2 Byte-level BPE
Byte-level BPE 是为了解决 Unicode-level BPE 带来的巨大基准词汇表问题而提出的,也是 GPT-2/GPT-3/Llama 等现代大型语言模型普遍采用的分词技术(通常带有改进,如限制合并)。
核心思想和过程
- 输入转换: 文本首先被编码成原始字节序列(通常是 UTF-8 编码)。
- 基本词汇表(Base Vocabulary): 初始词汇表固定包含所有可能的1个单位字节取值,即 0 到 255。大小: 恒定为 256。
- 分词单位: 模型的输入单元是这些原始字节。
- 合并: BPE 算法在字节序列上运行,寻找最频繁的相邻字节对进行合并。
例如,如果字节序列 0x63 0x61 0x74 频繁出现(对应 ‘c’, ‘a’, ‘t’),它可能会被合并为一个新的子词符号。
- 最终词汇表: 256 个基础字节 + 所有合并得到的子词。
4.3.3 对比
方法 | OOV / 通用性处理 | 核心工作单位 | 示例(“basketball”) | 说明 |
---|---|---|---|---|
纯词级 (Word-level) | 无法处理 OOV(词表外词替换为 <unk> ) | 完整单词 | [basketball] 或 [basket] , [ball] (若词表中有) | 统计训练语料中最频繁的前 N 个词;N 越大,OOV 越少,通常 50k – 1M+ |
Unicode-level BPE(或 BPE over Code Points) | 罕见词可拆为已知子词或字符组合;仍需完整 Unicode 码点集。 | Unicode 码点(character code point) | [bas] , [ket] , [ball] | 理论需 ≥130k 个起始符号(BMP + 部分扩展平面),但一般只选用训练语料中出现的子集(约 100k)。 |
纯字符级 (Char-level) | 完全通用(每个字符都是合法 token) | 单个 Unicode 字符 | [b] , [a] , [s] , [k] , … | 理论无 OOV,序列过长,效率低。以 Unicode 字符为基本单位(例如英文字母+标点约 128,中文需 5k+)。 |
字节级 BPE (Byte-level BPE) | 绝对通用:任何 Unicode 字符均能拆为 ≤4 个字节(256 种基本符号)。 | 字节 (Byte, 0–255) | [byte_for_b] , [byte_for_a] , … 合并为子词 | 通用且紧凑,无需 Unicode 表;字节数量为256,位宽为(8位二进制);BPE 通过高频字节对合并构造子词,最终约 32k–50k token。 |
模型可以处理的字符串空间有限
-
纯字节级 LM (Byte-level LMs):
- 优点:可以处理所有 Unicode 字符串(因为所有字符串都可以表示为 UTF-8 字节序列),满足通用性要求。
- 缺点:在大型数据集(如 One Billion Word Benchmark)上,性能不如词级别 LM。作者在 WebText 上的尝试也发现同样的性能差距。
-
纯词级别 LM (Word-level LMs):
- 优点:在大型数据集上性能更强,更具竞争力。
- 缺点:通用性不足,受限于预处理和词汇表。
-
BPE (Byte Pair Encoding)
- 介于字符和词级别之间的实用方法,为高频序列使用词级输入,为低频序列使用字符级输入。
- 在 Unicode 码点 (Unicode code points),而非字节序列,需将所有 Unicode 符号都纳入基础词汇表,这将导致基础词汇量超过 130,000,远超常规32k-64k词汇表。
- 在 UTF-8 字节序列上应用 BPE,基础词汇表(0–255 字节序列,对应 2^8 个字节值),这解决传统 BPE 词汇表爆炸问题。
- 阻止 BPE 跨越字符类别 (character categories) 进行合并。例如,阻止字母和数字的合并。
- 为空格 (spaces) 添加例外,允许其进行跨类别合并,以提高压缩效率,同时只对单词造成最小程度的碎片化 (minimal fragmentation)。
5.模型结构
GPT-2 延续了 Transformer Decoder-only 架构(即 GPT 风格,不含 encoder 部分),核心特征:
- masked self-attention;
- 输入为 token embeddings + positional embeddings;
- 输出同样是预测下一个 token 的分布。
这一部分完全继承自 Vaswani et al. (2017) 和 GPT-1 (Radford et al., 2018)。
5.1 参数规模
4个参数规模(Scales). 第一个版本规模等同于GPT1, 第二个版本等同于Bert
模型规模 (参数量) | 层数 (L) | 隐层维度 (d_model) |
---|---|---|
117M | 12 | 768 |
345M | 24 | 1024 |
762M | 36 | 1280 |
1542M (GPT-2) | 48 | 1600 |
5.2 关键架构修改 (Modifications)
- 层归一化 (Layer Normalization, LN) 的移动:
- 变化: 将层归一化从子块(sub-block)的输出移到了子块的输入。
- 类比: 这类似于预激活残差网络 (pre-activation residual network)(He et al., 2016)的设计,有助于更深层网络的训练。
- 额外的层归一化:
- 增加: 在最终的自注意力块之后额外增加了一个层归一化。
- 改进的初始化 (Modified Initialization):
- 目的: 考虑到残差路径上随着模型深度的累积 (accumulation)效应,添加缩放因子,在网络加深时保持信号的合理方差。
- 方法: 在初始化时,将残差层的权重乘以一个因子 N−1/2N^{-1/2}N−1/2, 其中 N 是残差层的数量(即模型中的层数)。
- 模型规模的扩大:
- 词汇表 (Vocabulary): 扩大到 50,257 个词元。
- 上下文大小 (Context Size): 从 512 增加到 1024 个词元,允许模型处理更长的依赖关系。
- 批次大小 (Batchsize): 增加到 512。
Ref
- 《Attention is All you Need》, https://arxiv.org/abs/1706.03762, Cited by 197804, 2017.
- gpt1 = Improving language understanding by generative pre-training.
- gpt2 = Language Models are Unsupervised Multitask Learners
- gpt3 = Language Models are Few-Shot Learners