ChatGPT大模型技术谱系:从N-gram到RLHF的阅读与选型指南
简介:这份PDF综述面向自然语言处理学习者、AI从业者及关注大模型技术脉络的读者,系统梳理ChatGPT从深度学习、语言模型、语义表示到预训练技术的发展轨迹,并讨论其在对话、问答、文本生成与翻译等场景中的应用潜力。全文围绕N-gram到神经网络语言模型、Transformer自注意力机制、Word2Vec与ELMo/BERT/GPT-2的预训练框架展开,进一步分析GPT-3的生成能力与幻觉问题,以及ChatGPT通过指令学习、监督微调和基于人类反馈的强化学习实现与人类对齐的路径。压缩包内为1个PDF文件,大小约2.15MB,便于在电脑或平板上直接阅读与检索。已有526人学习,适合希望建立大模型技术演进全景、理解ChatGPT关键机制与局限,并为进一步阅读论文或开展应用实践打基础的读者。
1. 一份把 ChatGPT 技术谱系讲透的综述 PDF:它到底能帮你解决什么
很多人第一次接触大模型,是从调 API 开始的:写几行请求、拼一段提示词、拿到一段回复,跑通了就算会了。可一旦模型答非所问、编造事实、多轮对话越聊越偏,就只能靠反复改 prompt 试运气,说不清问题到底出在预训练、微调还是对齐环节。这份《ChatGPT 大模型技术发展与应用》综述 PDF 恰好补的就是这一段:它把 N-gram 到 RNN/LSTM/GRU、再到 Transformer,把 TF-IDF、pLSA、LDA 到 Word2Vec、ELMo、BERT、GPT-2,最后到 GPT-3 与 ChatGPT 的指令学习、监督微调、基于人类反馈的强化学习(RLHF)串成一条完整演进线,并逐段分析幻觉、生成不可控、逻辑推理弱这些问题的技术来源。适合要写技术方案、做模型选型、准备组内分享或课程报告的从业者与高年级学生,读完能对「大模型为什么长这样」形成一张可复述的结构图。
2. 先把技术脉络读成一张可复述的图:综述的章节拆法与阅读顺序
这份 PDF 的价值不在于结论,而在于它给出的推导链条是连续的——每一个模型都在解决上一个模型的具体缺陷。所以读法很关键:按原文章节顺序正读,会比跳着看摘要有效率得多。下面把它拆成可以照着走的阅读路径,并说明每段该抓什么。
2.1 四个技术阶段与各自要抓的核心矛盾
综述主体可以归并为四个阶段,每个阶段对应一个明确的瓶颈,读的时候只抓那个瓶颈,其余细节可以后补。
| 阶段 | 代表方法 | 要解决的核心矛盾 | 遗留问题 |
|---|---|---|---|
| 统计语言模型 | N-gram | 参数空间过大,用马尔科夫假设只依赖前 n-1 个词 | 数据稀疏、n 增大后参数爆炸 |
| 神经语言模型 | NNLM、RNN、LSTM、GRU | 词离散表示与长距离依赖建模 | 梯度消失/爆炸,串行计算慢 |
| 注意力架构 | Transformer | 全局依赖与并行训练效率 | 缺少大规模预训练范式 |
| 预训练与对齐 | ELMo、BERT、GPT 系列、RLHF | 通用语义知识获取与人类意图对齐 | 幻觉、知识谬误、推理不稳 |
这张表建议手抄一遍。后面所有章节内容都是在这四行上做展开,比如 N-gram 的平滑方法(拉普拉斯、Good-Turing、Kneser-Ney、插值)解决的是第一行的稀疏问题,LSTM 的三个门解决的是第二行的梯度问题,Transformer 的 Self-attention 解决的是第三行的并行与全局依赖问题。抓住「谁在替谁擦屁股」,整篇文章就不再是一串模型名的罗列。
2.2 给出公式的段落,用纸笔推一遍再往下读
综述里出现的公式不多,但都是关键节点,值得慢读。
第一个是语言模型的链式分解,即整句概率按条件概率逐项相乘:
P(S) = P(W1, W2, …, Wk) = P(W1)·P(W2|W1)…P(Wk|W1, W2, …, Wk-1)
这一行是后面所有模型的目标函数来源。N-gram 把条件项截断成前 n-1 个词,这就是马尔科夫假设;RNN 用一个隐状态 h 代替全部历史,本质是在压缩这个条件项;Transformer 则用注意力直接对全序列加权,不再压缩。看懂这一行,后面三种架构的取舍就自然成立了。
第二个是 N-gram 的极大似然估计:
P(Wk|Wk-1, …, Wk-n+1) = count(Wk, Wk-1, …, Wk-n+1) / count(Wk-1, …, Wk-n+1)
分子分母都是语料中的片段计数。自己动手算一下就能直观看到稀疏问题:当 n 取到 4 或 5,绝大多数片段在语料中出现 0 次,分子为 0,整句概率直接归零。这就是为什么后面必须引入平滑,以及为什么神经网络方案(连续向量、参数共享)能从根上绕开计数为零。
第三个是 Transformer 的缩放点积注意力:
Attention(Q, K, V) = softmax(QKᵀ / √dk) · V
重点看两个地方:QKᵀ 算的是当前位置与全序列所有位置的相关程度,除以 √dk 是为了防止维度增大后点积数值过大把 softmax 推到饱和区;再乘 V 完成加权求和。因为每个位置的计算相互独立,所以能并行——这一点正是它能取代 RNN、支撑更大规模模型的关键。读到 Masked Self-attention 时注意它只改了一处:把未来位置的权重置为负无穷,从而保证语言模型只能看前文。
2.3 顺着引用往回补:把综述当成索引而不是终点
综述的参考文献给的是这条路线上最原始的论文坐标,这是它最实用的地方。建议的用法是:第一遍只读综述正文,把每个模型的一句话定位记下来;第二遍挑两三个和你工作直接相关的点回溯原文。
具体操作可以按这个顺序走:
第一步,通读第 1 章和第 2 章,把 N-gram、NNLM、LSTM、Transformer、Word2Vec、ELMo、BERT、GPT-2 各自的「创新点 + 解决的问题」写在便签上,一模型一张。
第二步,标出你实际会用到的环节。做检索增强的,重点回看第 2 章关于语义表示的部分,理解 Word2Vec 的静态词向量为什么无法处理多义,这直接决定你为什么要用上下文相关的嵌入。做对话产品的,重点看第 3 章指令学习与监督微调的组织方式。
第三步,针对标出的环节去找对应原文,读引言和实验设置即可,不必逐行推公式。综述已经帮你完成了「为什么这条线是连续的」这层工作,原文补的是「具体怎么做的、参数是多少」。
提示:读综述最容易犯的错是把它当百科从头背模型名。正确姿势是把它当路线图,先建立因果链,再把链条上你关心的两三个节点展开。
3. 把演进逻辑落到选型上:N-gram 到 Transformer 的取舍怎么判断
读懂历史不是为了怀旧,是为了在新项目里做判断:什么时候用轻量方案就够,什么时候必须上预训练模型,参数规模和效果之间是什么关系。这一章把综述里的技术演进翻译成选型判断,并给出可以直接跑的对照实验。
3.1 参数量、数据量与能力之间的经验关系
综述在讲 GPT-2 时给了一组很有说服力的数据:最大规模约 1.5B 参数,预训练语料 WebText 约 40GB 高质量文本;BERT 的 Base 是 12 层、约 110M 参数,Large 是 24 层、约 340M 参数,预训练数据包括约 800M 词的 Books Corpus 和约 2.5B 词的 Wikipedia。把这些数字放在一起看,规律很清楚:模型规模和语料规模同步放大,能力才会上台阶,只放大一边往往收效有限。
这对选型有三条直接结论:
- 任务简单、领域封闭、数据量小(几百到几千条标注),微调一个中小规模的开源模型通常就够,不必追大。
- 任务是开放域生成或复杂问答,且对稳定性要求高,模型规模、指令数据质量、对齐策略三者缺一不可,缺哪一环都会在线上暴露。
- 不要只看参数量。综述特意强调 GPT-2 展示了不微调直接做 zero-shot 的能力,这意味着预训练数据的覆盖面有时比参数数量更能决定泛化表现。
还有一点容易被忽略:综述明确指出 ChatGPT 官方未公布详细方案,文中涉及 InstructGPT 与 ChatGPT 原理的部分属于基于公开信息的合理推论。读的时候要带着这个边界意识,不要把推论当定论抄进技术方案里。
3.2 用一份可运行脚本做 N-gram 与神经网络方案的手感对照
概念看再多,不如亲手跑一次。下面这段 Python 用最朴素的方式实现 N-gram 语言模型,目的是让你直观看到稀疏问题与平滑的作用,而不是做一个能上线的模型。
from collections import defaultdict, Counter import math def train_ngram(corpus_tokens, n=3): """统计 n-gram 频次,corpus_tokens 为已分词的 token 列表""" ngram_count = defaultdict(Counter) # 前缀 -> {下一个词: 次数} for i in range(len(corpus_tokens) - n + 1): prefix = tuple(corpus_tokens[i:i + n - 1]) # 前 n-1 个词作为条件 next_word = corpus_tokens[i + n - 1] # 第 n 个词作为预测目标 ngram_count[prefix][next_word] += 1 return ngram_count def prob_with_smoothing(model, prefix, word, vocab_size, alpha=1.0): """加一平滑(拉普拉斯平滑),alpha 为平滑系数""" counter = model.get(tuple(prefix), Counter()) numerator = counter.get(word, 0) + alpha # 未见过的片段补 alpha denominator = sum(counter.values()) + alpha * vocab_size # 分母加上全部词表的平滑量 return numerator / denominator # 玩具语料,真实场景请替换为你的领域文本 corpus = "大模型 预训练 需要 大规模 语料 大模型 微调 需要 高质量 标注".split() model = train_ngram(corpus, n=3) vocab = set(corpus) # 对比:平滑前与平滑后的概率 prefix = ["大模型", "预训练"] for w in ["需要", "跳舞"]: p = prob_with_smoothing(model, prefix, w, len(vocab), alpha=1.0) print(f"P({w} | {prefix}) = {p:.6f}")逻辑说明:train_ngram按滑动窗口切出「前 n-1 词 → 第 n 词」的映射并计数,这就是极大似然估计的分子分母来源。prob_with_smoothing里alpha=1.0即拉普拉斯平滑,把未出现过的片段从概率 0 抬到一个极小正数,避免整句概率被清零。vocab_size必须传全词表大小而不是局部 counter 的长度,否则分母算错,这是手写实现最常见的翻车点。把n从 3 调到 5,你会明显看到大部分 prefix 的 counter 为空,稀疏问题立刻显形——这正是综述里那段稀疏性论述的现场版。
想再往前一步看神经方案的差别,思路是:把离散 token 换成 embedding,用一个循环结构压缩历史,输出层做 softmax。综述里 NNLM 的流程描述得很清楚——one-hot 经投影矩阵降维成词向量、拼接、过 tanh 隐层、线性层打分、softmax 出概率。按这个流程复现一个小模型,重点体会两点:词向量让相似词共享统计强度,从而缓解稀疏;隐状态让历史信息被压缩而不是被截断,从而缓解 n 增大带来的参数爆炸。
3.3 从 Seq2Seq 到注意力:为什么长句翻译必须先解决信息压缩
综述第 1 章后半段讲了一条很重要的支线:RNN 做 Encoder-Decoder 时,解码阶段只能用编码阶段最后时刻的那一个特征向量,前面所有时刻的信息都被压进这一个向量里,句子一长就丢。Bahdanau 等人引入 Attention,让解码器在每一步针对性地关注编码器不同时刻的输出,实现动态对齐——这是注意力机制最早的说服力来源。
这个逻辑对今天做 RAG 或长文档问答的人依然成立:把长文档压成单个向量再做检索或生成,本质上就是当年 Seq2Seq 的那个瓶颈。区别只是现在用向量库做近似检索替代了注意力权重,但「不能只靠一个压缩表示承载全部信息」这条教训没变。理解这段历史,你在设计方案时就会自然地想到分块、重排、多路召回这些手段,而不是把所有内容塞进一次请求。
4. 对齐三件套:指令学习、监督微调与 RLHF 在工程上怎么理解
GPT-3 已经展示了规模带来的能力,但综述明确指出它仍有幻觉、生成不可控、知识谬误、逻辑推理差等问题。ChatGPT 的关键增量不是换了更大的底座,而是在 GPT-3.5 上补了对齐环节。这一章把这套机制拆成工程上能理解的动作,并给出可复现的指令数据构造与评测流程。
4.1 三个阶段各自解决什么问题
把综述第 3 章的内容按目标拆开,三个阶段的分工是清晰的:
| 阶段 | 输入 | 目标 | 数据形态 | 典型失败表现 |
|---|---|---|---|---|
| 指令学习 | 大量任务指令与示例 | 让模型理解「按要求做事」而非只续写 | 多任务指令集合,任务类型尽量分散 | 只认训练里见过的任务句式 |
| 监督微调 | 高质量「指令-理想回答」对 | 教会模型输出符合人类期望的格式与语气 | 人工撰写或筛选的示范回答 | 回答风格对了但事实仍错 |
| 基于人类反馈的强化学习 | 同一问题的多个回答排序 | 让模型偏好人类更认可的输出 | 成对或成组的偏好比较数据 | 奖励模型被钻空子,输出变长变空 |
三者的关系不是替代而是叠加:指令学习搭广度,监督微调定风格与格式,偏好学习调价值排序。工程上最容易出错的是跳过前两步直接上偏好学习,结果奖励模型学到的是一堆风格偏好,事实性没有任何改善。
4.2 构造一份最小可用的指令数据集
这一段给的是可抄作业的流程。假设你要做一个领域问答助手,手里有一批业务文档,想把通用底座调成能按规范作答的样子。
第一步,先把任务类型列全。常见做法是至少覆盖:直接问答、要点抽取、格式转换(如转成表格或 JSON)、拒答与澄清、多轮指代消解。每类先写 20~50 条种子指令,注意指令措辞要多样,不要所有条目都用同一个句式开头。
第二步,准备参考答案。这里有个血泪经验:参考答案的质量比数量重要得多。一批 500 条精写的样本,效果通常好过 5000 条从旧系统日志里扒出来、格式混乱的回答,因为模型会忠实模仿你给的示范,包括其中的坏习惯。
第三步,把数据组织成统一的对话格式,用脚本做校验,避免脏数据混入。
import json def build_sft_sample(instruction, input_text, output): """按统一的指令微调格式组装单条样本""" return { "messages": [ {"role": "system", "content": "你是某业务领域的问答助手,回答需基于给定资料,不确定时明确说明。"}, {"role": "user", "content": f"{instruction}\n\n资料:{input_text}"}, {"role": "assistant", "content": output}, ] } def validate(samples, max_len=2048): """基础校验:字段完整、角色顺序正确、长度可控""" cleaned = [] for idx, s in enumerate(samples): msgs = s.get("messages", []) roles = [m["role"] for m in msgs] if roles != ["system", "user", "assistant"]: print(f"[跳过] 第 {idx} 条角色顺序异常: {roles}") continue if not msgs[-1]["content"].strip(): print(f"[跳过] 第 {idx} 条回答为空") continue total = sum(len(m["content"]) for m in msgs) if total > max_len: print(f"[截断] 第 {idx} 条过长: {total}") msgs[-1]["content"] = msgs[-1]["content"][:max_len // 3] cleaned.append({"messages": msgs}) return cleaned raw = [ build_sft_sample("请从资料中提取关键指标并转成表格", "本季度交付周期为 12 天……", "| 指标 | 数值 |\n| --- | --- |\n| 交付周期 | 12 天 |"), build_sft_sample("资料里没提到的内容怎么处理", "本季度交付周期为 12 天……", "资料中未提及该信息,建议补充相关文档后再确认。"), ] data = validate(raw) with open("sft_train.jsonl", "w", encoding="utf-8") as f: for item in data: f.write(json.dumps(item, ensure_ascii=False) + "\n") print(f"有效样本 {len(data)} 条")逻辑说明:build_sft_sample把 system 角色固定为行为约束,这样训练时模型会把「基于资料、不确定就说不知道」当成默认行为,而不是每次靠用户提醒。validate做了三件事——检查角色顺序、剔除空回答、对超长样本做截断。角色顺序这一条看着啰嗦,但实际非常必要:不同训练框架对消息序列的校验严格程度不同,格式错位往往不报错,只是静默学坏。max_len按你的底座上下文长度设,留出生成余量,不要顶满。
参数上要注意的是样本长度分布:如果九成样本都在 200 字以内,少数两三千字的长样本会被大量填充浪费算力,建议按长度分桶后再组 batch。
4.3 用偏好比较数据做轻量对齐
监督微调之后模型能答得像样了,但同一个问题它可以给出多个都「能看」的回答,需要从中挑出更符合人类预期的那个,这一步就是偏好学习的用武之地。
数据构造的关键是「同一输入、多个输出、人工排序」。实操中常见做法是每次给标注者一个问题加两条回答,让他们二选一或判定平局,成本低且一致性较好。整理成如下结构即可交给训练框架:
def build_preference_pair(prompt, chosen, rejected): """chosen 为人类更认可的回答,rejected 为较差的回答""" return {"prompt": prompt, "chosen": chosen, "rejected": rejected} pairs = [ build_preference_pair( "交付周期数据从哪里看?", "该项数据在季度运营报表的交付章节,指标名为平均交付周期。", "你可以去系统里找找,应该在某个报表里。" ), build_preference_pair( "这个指标比上季度如何?", "资料中仅提供本季度数据,未包含上季度对比,无法判断变化趋势。", "应该是变好了。" ), ]逻辑说明:chosen要体现两个特征——有依据、边界清楚;rejected则典型地表现为含糊、臆测、无出处。这两类对照样本喂得足够多,模型才会学会「不确定时说不确定」,而这恰恰是抑制幻觉最有效的手段之一。参数上,偏好数据量通常比监督微调数据少一个量级就有效果,但每条的质量要求更高,标注一致性差的数据会把奖励信号带偏。
注意:综述里对 InstructGPT 与 ChatGPT 的具体实现标注为基于公开信息的推论。工程上照搬「三阶段」这个骨架是安全的,但任何具体的超参数、数据配比都不要当作官方结论引用。
5. 避坑与排查:读这份综述以及照着它做方案时最容易翻的五个车
这一章是从实际使用中攒下来的,现象都真实出现过,按「现象 → 原因 → 解决」写,照着对号入座能省不少时间。
5.1 把推论当定论,方案里写了不存在的事实
现象:技术方案里出现「某模型使用多少万条指令数据训练」这类精确数字,评审时被追问出处,答不上来。
原因:综述本身明确写了官方未公布详细方案,部分内容是学术视角的合理推测。读的时候如果跳过这段声明,很容易把推测性描述当成既定事实吸收。
解决:凡是要写进方案的数字,回到原始论文或官方技术报告核实;核实不了的,改写成定性描述,比如「通过指令学习与偏好对齐提升与人类意图的一致性」,并注明信息来源与推断性质。这条习惯比记住任何一个数字都值钱。
5.2 只盯着模型结构,忽略了数据与对齐
现象:照着综述把 Transformer 结构复现了一遍,训出来的模型生成质量依然很差,于是怀疑架构理解有误。
原因:综述的叙事重心确实在架构演进,但它同样用了大段篇幅讲语义表示、预训练范式和对齐策略。只看第 1 章的架构线,会误以为能力全来自结构,实际上预训练数据的覆盖面与对齐数据的质量往往更决定最终表现。
解决:把第 2 章和第 3 章至少各读一遍,明确「底座能力来自预训练、行为规范来自对齐」这条分工。做方案时把数据准备单独列一个阶段,给足工时。
5.3 N-gram 复现时概率全为零
现象:手写 N-gram 跑出来,稍微长一点的句子概率就是 0,或者 log 概率变成负无穷。
原因:没有做平滑,语料中未出现的 n-gram 计数为 0,链式相乘后整句归零;另外分母用错(用了局部 counter 长度而不是全词表大小)也会让结果失真。
解决:先加平滑把零概率抬起来,再检查分母口径。工程上更稳的做法是全程用对数概率相加代替概率相乘,避免浮点下溢。真要做文本生成或打分,直接上现成的统计语言模型工具,别手搓。
5.4 指令微调数据格式错位却没有任何报错
现象:训练 loss 正常下降,但模型上线后经常答非所问,或者把 system 里的话当作用户问题回答。
原因:不同框架对 messages 序列的角色顺序和特殊 token 处理方式不同,格式错位时往往不抛异常,只是静默地学到错误的输入输出对应关系。
解决:训练前跑一遍格式校验脚本,检查角色顺序、空内容、超长样本;训练后抽 20 条样本做人工对拍,看模型是否复现了示范回答的格式。这个校验步骤建议固化进流水线,别靠肉眼。
5.5 偏好数据标注不一致,模型学会钻空子
现象:做完偏好对齐后,输出变长了、语气变客气了,但事实错误反而增多。
原因:标注者对「好回答」的判断标准不统一,或者被长度、语气这些表面特征带偏,导致模型学到的是「越长越客气越好」,而不是「越准确越好」。
解决:先写一份标注规范,明确「有依据、边界清楚、不臆测」三条优先于语言风格;安排一定比例的双人交叉标注计算一致性,一致性低的批次重标。偏好数据宁可少而一致,不要多而混乱。
6. 把综述用成工具箱:一份可复述的技术演进对照表与三处验证技巧
读完之后怎么确认自己真的读懂了,而不是产生了「我都看过了」的错觉?我自己的做法是强制输出一遍对照表,再做三处小验证,能过就说明这层知识已经落地了。
先给对照表。这张表建议你自己填一遍再和综述核对,比直接抄有效得多:
| 演进节点 | 上一代的具体痛点 | 本代的关键手段 | 代价/新增问题 |
|---|---|---|---|
| N-gram | 统计语言模型参数空间过大 | 马尔科夫假设,只看前 n-1 词 | 数据稀疏,需平滑 |
| NNLM | 离散表示的稀疏与泛化差 | 词向量 + 前馈网络 | 只能用固定窗口历史 |
| RNN/LSTM/GRU | 长距离依赖建模 | 循环结构 + 门控机制 | 串行计算慢,极长序列仍吃力 |
| Transformer | 全局依赖与训练效率 | Self-attention + 并行 | 计算与显存随序列长度增长 |
| ELMo/BERT | 静态词向量无法处理多义 | 上下文相关表示、双向预训练 | 预训练成本高 |
| GPT 系列 | 通用语义知识获取 | 单向预训练 + 规模扩展 | 生成不可控、幻觉 |
| ChatGPT | 输出与人类期望不一致 | 指令学习 + 监督微调 + 偏好对齐 | 对齐数据成本高、易被表面特征带偏 |
填表时如果某一行的「代价」写不出来,回去重读那一段,通常是因为只记住了它解决了什么,没注意它引入了什么。
再说三处验证。
第一处验证,用自己的话解释注意力。不看原文,说出 Q、K、V 分别承担什么角色、为什么除以 √dk、Masked 版本改了什么。讲不清「除以 √dk 是为了防止点积过大导致 softmax 饱和」,说明只记住了公式形状。这一步我一般会写在纸上,写完再翻回第 1.3 节对照。
第二处验证,跑通一次小规模对照实验。拿你手头的一份领域文本,分别用 N-gram 加平滑和一个小型神经语言模型算几个句子的困惑度,观察两者在未见组合上的差异。不用追求效果,追求的是你能亲眼看到稀疏和平滑的实际影响。跑之前记得先做分词和去重,语料里混入重复段落会让统计结果虚高。
第三处验证,把对齐三阶段映射到自己项目上。写下你的场景需要哪一类指令、参考答案从哪来、谁来做偏好排序。如果发现偏好排序找不到合适的标注者,说明这个环节在当前阶段可以延后,先把指令数据和监督微调做扎实。这条判断非常实用——很多团队在数据基础还很薄的时候就急着上偏好学习,结果钱花了,事实性一点没改善。
还有一个容易被忽略的技巧:读综述时把每一处「某某等人在某年提出」都当成一个可追溯的锚点,但只对你关心的两三个锚点回溯原文。全追一遍性价比极低,因为综述已经把演进逻辑梳理清楚了,你要补的只是自己要用到的那一小块细节。我当时就是贪多,把参考文献从头翻了一遍,最后真正用上的只有语义表示和指令微调那几篇,其余全成了沉没成本。
从那以后我每次读这类综述,都强制走一遍「填对照表 → 讲清一个公式 → 跑一次最小实验」的流程,宁可慢一点,也不让阅读停留在「好像看懂了」的层面。这份 PDF 篇幅不长,但信息密度高,按这个流程走一遍,你对大模型技术路线的理解会从名词记忆变成因果推理。希望帮到你。
本文还有配套的精品资源,点击获取