人工智能(AI)全体系保姆级学习------系列三
文章目录
- 人工智能(AI)全体系保姆级学习------系列三
- [第10章 Attention机制](#第10章 Attention机制)
-
- [10.1 注意力机制本质](#10.1 注意力机制本质)
-
- [1. 什么是注意力机制](#1. 什么是注意力机制)
- [2. 先用直观方式理解](#2. 先用直观方式理解)
- [3. 数学本质是什么](#3. 数学本质是什么)
- [4. 权重是怎么来的](#4. 权重是怎么来的)
- [5. Query、Key、Value 到底是什么](#5. Query、Key、Value 到底是什么)
- [6. 为什么注意力机制有效](#6. 为什么注意力机制有效)
- [7. 一个具体例子](#7. 一个具体例子)
- [8. 在哪些地方会用到注意力机制](#8. 在哪些地方会用到注意力机制)
- [9. 一个最基础的注意力分类模型](#9. 一个最基础的注意力分类模型)
- [10. 完整可运行代码](#10. 完整可运行代码)
- [11. 核心思想总结](#11. 核心思想总结)
- [10.2 Scaled Dot-Product](#10.2 Scaled Dot-Product)
-
- [1. 这一节在解决什么问题](#1. 这一节在解决什么问题)
- [2. 核心公式](#2. 核心公式)
- [3. 每一项在干什么](#3. 每一项在干什么)
-
- (1)计算相似度
- (2)为什么要除以 (\sqrt{d})
- [(3)Softmax 归一化](#(3)Softmax 归一化)
- [(4)加权 Value](#(4)加权 Value)
- [4. 一句话理解](#4. 一句话理解)
- [5. 一个直观例子](#5. 一个直观例子)
- [6. 矩阵角度理解(很重要)](#6. 矩阵角度理解(很重要))
- [7. PyTorch完整实现(从0实现)](#7. PyTorch完整实现(从0实现))
- [8. 加入 Mask(真实模型必须)](#8. 加入 Mask(真实模型必须))
- [9. 实际应用在哪里](#9. 实际应用在哪里)
- [10. 核心总结](#10. 核心总结)
- [10.3 Multi-Head Attention](#10.3 Multi-Head Attention)
- [第11章 Transformer架构](#第11章 Transformer架构)
-
- [11.1 Encoder-Decoder](#11.1 Encoder-Decoder)
- [10.3 Multi-Head Attention](#10.3 Multi-Head Attention)
-
-
- [1. 为什么需要 Multi-Head](#1. 为什么需要 Multi-Head)
- [2. 核心思想](#2. 核心思想)
- [3. 数学定义(核心公式)](#3. 数学定义(核心公式))
- [4. 从计算流程理解](#4. 从计算流程理解)
- [5. 为什么效果更好](#5. 为什么效果更好)
- [6. 维度变化(必须理解)](#6. 维度变化(必须理解))
- [7. 一个直观例子](#7. 一个直观例子)
- [8. 完整代码(手写 Multi-Head)](#8. 完整代码(手写 Multi-Head))
- [9. 实际意义](#9. 实际意义)
- [10. 核心总结](#10. 核心总结)
- [11.1 Encoder-Decoder 结构](#11.1 Encoder-Decoder 结构)
-
- [1. Transformer在做什么](#1. Transformer在做什么)
- [2. 一个非常直观的类比](#2. 一个非常直观的类比)
- [3. Encoder做了什么(本质)](#3. Encoder做了什么(本质))
- [4. Encoder内部结构(带理解)](#4. Encoder内部结构(带理解))
-
- (1)Self-Attention(信息交流)
- (2)前馈网络(信息加工)
- [5. 为什么要多层 Encoder](#5. 为什么要多层 Encoder)
- [6. Decoder做了什么(通俗理解)](#6. Decoder做了什么(通俗理解))
- [7. 为什么不能"看到未来"(关键点)](#7. 为什么不能“看到未来”(关键点))
- [8. Decoder最关键的一步](#8. Decoder最关键的一步)
- [9. 一个完整流程(带理解)](#9. 一个完整流程(带理解))
- [10. 一个最小Encoder实现](#10. 一个最小Encoder实现)
- [11. 核心总结(这一节一定要记住)](#11. 核心总结(这一节一定要记住))
- [11.2 Position Encoding(位置编码)](#11.2 Position Encoding(位置编码))
-
- [1. 问题从哪里来](#1. 问题从哪里来)
- [2. 核心解决思路](#2. 核心解决思路)
- [3. 为什么用"加法"](#3. 为什么用“加法”)
- [4. 数学定义(核心公式)](#4. 数学定义(核心公式))
- [5. 为什么用正弦和余弦](#5. 为什么用正弦和余弦)
- [6. 更直观理解](#6. 更直观理解)
- [7. 一个小例子](#7. 一个小例子)
- [8. PyTorch实现(位置编码)](#8. PyTorch实现(位置编码))
- [9. 实际意义](#9. 实际意义)
- [10. 核心总结](#10. 核心总结)
- [11.3 Transformer整体流程](#11.3 Transformer整体流程)
-
- [1. 这一节在解决什么问题](#1. 这一节在解决什么问题)
- [2. 一句话理解Transformer](#2. 一句话理解Transformer)
- [3. 整体流程(从输入到输出)](#3. 整体流程(从输入到输出))
-
- (1)输入阶段
- (2)进入Encoder(理解阶段)
- (3)进入Decoder(生成阶段)
- (4)Decoder内部结构
- [第一步:Masked Self-Attention](#第一步:Masked Self-Attention)
- (5)输出阶段
- [4. 用一个完整例子串起来](#4. 用一个完整例子串起来)
- [5. Transformer的核心特点](#5. Transformer的核心特点)
- [6. 一个极简Transformer实现](#6. 一个极简Transformer实现)
- [7. 为什么这个结构这么重要](#7. 为什么这个结构这么重要)
- [8. 核心总结](#8. 核心总结)
-
- [第12章 大语言模型(LLM)](#第12章 大语言模型(LLM))
-
- [12.1 GPT系列(生成模型)](#12.1 GPT系列(生成模型))
-
- [1. GPT在做什么](#1. GPT在做什么)
- [2. 一种更直观的理解方式](#2. 一种更直观的理解方式)
- [3. 数学本质](#3. 数学本质)
- [4. GPT和Transformer的关系](#4. GPT和Transformer的关系)
- [5. 为什么必须限制"看未来"](#5. 为什么必须限制“看未来”)
- [6. GPT是如何学会语言的](#6. GPT是如何学会语言的)
- [7. 为什么GPT可以写文章](#7. 为什么GPT可以写文章)
- [8. 一个简单生成过程](#8. 一个简单生成过程)
- [9. 一个最小GPT实现(简化版)](#9. 一个最小GPT实现(简化版))
- [10. 本节核心总结](#10. 本节核心总结)
- [12.2 BERT(理解)](#12.2 BERT(理解))
-
- [1. BERT在做什么](#1. BERT在做什么)
- [2. 和GPT最核心的区别](#2. 和GPT最核心的区别)
- [3. 数学本质(核心目标)](#3. 数学本质(核心目标))
- [4. Mask机制的不同](#4. Mask机制的不同)
- [5. BERT是如何理解语言的](#5. BERT是如何理解语言的)
- [6. 为什么BERT不能直接写文章](#6. 为什么BERT不能直接写文章)
- [7. 一个简单理解例子](#7. 一个简单理解例子)
- [8. 一个最小BERT实现(简化版)](#8. 一个最小BERT实现(简化版))
- [9. GPT vs BERT(关键区别)](#9. GPT vs BERT(关键区别))
- [10. 本节核心总结](#10. 本节核心总结)
- [12.3 Prompt Engineering](#12.3 Prompt Engineering)
-
- [1. Prompt在做什么](#1. Prompt在做什么)
- [2. 为什么一句话能改变结果](#2. 为什么一句话能改变结果)
- [3. Prompt的数学本质](#3. Prompt的数学本质)
- [4. Prompt如何影响模型行为](#4. Prompt如何影响模型行为)
- [5. Prompt为什么可以"变强"](#5. Prompt为什么可以“变强”)
- [6. Chain-of-Thought(思维链)](#6. Chain-of-Thought(思维链))
- [7. 一个实际应用变化](#7. 一个实际应用变化)
- [8. Prompt在产品中的作用](#8. Prompt在产品中的作用)
- [9. 一个简单代码示例](#9. 一个简单代码示例)
- [10. 本节核心总结](#10. 本节核心总结)
- [12.4 Fine-tuning(模型微调)](#12.4 Fine-tuning(模型微调))
-
- [1. 为什么需要Fine-tuning](#1. 为什么需要Fine-tuning)
- [2. Fine-tuning在做什么](#2. Fine-tuning在做什么)
- [3. 数学本质](#3. 数学本质)
- [4. 和Prompt的区别](#4. 和Prompt的区别)
- [5. Fine-tuning带来的变化](#5. Fine-tuning带来的变化)
- [6. 一个实际应用场景](#6. 一个实际应用场景)
- [7. 一个简单微调示意代码](#7. 一个简单微调示意代码)
- [8. Fine-tuning的局限](#8. Fine-tuning的局限)
- [9. 什么时候该用Fine-tuning](#9. 什么时候该用Fine-tuning)
- [10. 本节核心总结](#10. 本节核心总结)
- [第13章 大模型训练技术](#第13章 大模型训练技术)
-
- [13.1 RLHF(人类反馈强化学习)](#13.1 RLHF(人类反馈强化学习))
-
- [1. RLHF在解决什么问题](#1. RLHF在解决什么问题)
- [2. RLHF在做什么](#2. RLHF在做什么)
-
- [3. 数学本质](#3. 数学本质)
- [4. RLHF的三步流程](#4. RLHF的三步流程)
- [5. 一个直观例子](#5. 一个直观例子)
- [6. RLHF带来的变化](#6. RLHF带来的变化)
- [7. 简单强化学习示意代码](#7. 简单强化学习示意代码)
- [8. RLHF的意义](#8. RLHF的意义)
- [9. 本节核心总结](#9. 本节核心总结)
- [13.2 LoRA / PEFT](#13.2 LoRA / PEFT)
-
- [1. 为什么需要高效微调](#1. 为什么需要高效微调)
- [2. PEFT在做什么](#2. PEFT在做什么)
- [3. LoRA的核心思想](#3. LoRA的核心思想)
- [4. 为什么LoRA有效](#4. 为什么LoRA有效)
- [5. 一个直观理解](#5. 一个直观理解)
- [6. LoRA带来的优势](#6. LoRA带来的优势)
- [7. 一个简单LoRA示意代码](#7. 一个简单LoRA示意代码)
- [8. LoRA的应用场景](#8. LoRA的应用场景)
- [9. 本节核心总结](#9. 本节核心总结)
- [13.3 模型蒸馏](#13.3 模型蒸馏)
- [第13章 大模型训练技术](#第13章 大模型训练技术)
-
- [13.3 模型蒸馏(Model Distillation)](#13.3 模型蒸馏(Model Distillation))
-
- [1. 为什么需要模型蒸馏](#1. 为什么需要模型蒸馏)
- [2. 模型蒸馏在做什么](#2. 模型蒸馏在做什么)
- [3. 数学本质](#3. 数学本质)
- [4. 为什么蒸馏有效](#4. 为什么蒸馏有效)
- [5. 一个直观理解](#5. 一个直观理解)
- [6. 蒸馏带来的变化](#6. 蒸馏带来的变化)
- [7. 一个简单蒸馏示意代码](#7. 一个简单蒸馏示意代码)
- [8. 蒸馏的应用场景](#8. 蒸馏的应用场景)
- [9. 本节核心总结](#9. 本节核心总结)
- 系列三总结:Transformer与大模型(第10章---第13章)
-
- [1. 从Attention到理解与生成](#1. 从Attention到理解与生成)
- [2. Transformer:统一的建模框架](#2. Transformer:统一的建模框架)
- [3. GPT与BERT:两条分化路径](#3. GPT与BERT:两条分化路径)
- [4. Prompt:从"用模型"到"控制模型"](#4. Prompt:从“用模型”到“控制模型”)
- [5. Fine-tuning与参数控制](#5. Fine-tuning与参数控制)
- [6. RLHF:引入人类标准](#6. RLHF:引入人类标准)
- [7. 模型蒸馏:走向实际落地](#7. 模型蒸馏:走向实际落地)
- [8. 整体核心逻辑](#8. 整体核心逻辑)
- [9. 一句话总总结](#9. 一句话总总结)
-
- [7. 一个简单蒸馏示意代码](#7. 一个简单蒸馏示意代码)
- [8. 蒸馏的应用场景](#8. 蒸馏的应用场景)
- [9. 本节核心总结](#9. 本节核心总结)
- 系列三总结:Transformer与大模型(第10章---第13章)
-
- [1. 从Attention到理解与生成](#1. 从Attention到理解与生成)
- [2. Transformer:统一的建模框架](#2. Transformer:统一的建模框架)
- [3. GPT与BERT:两条分化路径](#3. GPT与BERT:两条分化路径)
- [4. Prompt:从"用模型"到"控制模型"](#4. Prompt:从“用模型”到“控制模型”)
- [5. Fine-tuning与参数控制](#5. Fine-tuning与参数控制)
- [6. RLHF:引入人类标准](#6. RLHF:引入人类标准)
- [7. 模型蒸馏:走向实际落地](#7. 模型蒸馏:走向实际落地)
- [8. 整体核心逻辑](#8. 整体核心逻辑)
- [9. 一句话总总结](#9. 一句话总总结)
第10章 Attention机制
10.1 注意力机制本质
1. 什么是注意力机制
注意力机制(Attention Mechanism)本质上是在解决一个很现实的问题:
当输入信息很多时,模型不应该平均对待所有内容,而应该把更多计算资源放到更重要的信息上。
例如一句话里,并不是每个词都同样重要。
在做情感分析时,"很好""失望""喜欢""差"这类词通常比"这个""东西""真的"更关键。
注意力机制做的事情,就是让模型自己学会:当前任务下,哪里更值得关注。
所以,注意力机制不是"记住全部",而是"有重点地看"。
2. 先用直观方式理解
可以把它理解成"阅读时划重点"。
人看一段文字时,不会每个字都花同样精力,而是会自然把注意力集中在关键词、转折词、结论句上。
神经网络中的注意力机制,其实就是把这个过程数学化了:
① 先比较"当前目标"和"各部分输入"的相关性
② 再给每个部分一个权重
③ 最后把重要部分加权汇总
也就是说,注意力机制最后输出的不是"原封不动的全部信息",而是经过筛选后的重点信息表示。
3. 数学本质是什么
注意力机制最核心的形式,就是一个加权求和。
设输入中有 (n) 个向量:
v 1 , v 2 , ... , v n v_1, v_2, \dots, v_n v1,v2,...,vn
模型会为每个向量分配一个权重:
α 1 , α 2 , ... , α n \alpha_1, \alpha_2, \dots, \alpha_n α1,α2,...,αn
最终输出为:
Attention ( V ) = ∑ i = 1 n α i v i \text{Attention}(V)=\sum_{i=1}^{n}\alpha_i v_i Attention(V)=i=1∑nαivi
这里的含义非常直接:
- (v_i) 表示第 (i) 个位置携带的信息
- (\alpha_i) 表示第 (i) 个位置的重要程度
- 权重越大,说明该位置越值得关注
通常这些权重满足:
∑ i = 1 n α i = 1 , α i ≥ 0 \sum_{i=1}^{n}\alpha_i = 1,\quad \alpha_i \ge 0 i=1∑nαi=1,αi≥0
这表示它们像一个概率分布。
模型会把注意力"分配"到不同位置,只不过有的位置分得多,有的位置分得少。
4. 权重是怎么来的
注意力机制最关键的地方,不是加权求和,而是:这些权重怎么计算出来。
通常会先计算每个位置的"相关性分数":
e i = f ( q , k i ) e_i=f(q,k_i) ei=f(q,ki)
其中:
- (q) 表示当前查询目标(Query)
- (k_i) 表示第 (i) 个位置的特征(Key)
- (f) 表示相似度函数
然后再通过 Softmax 把分数归一化,变成权重:
α i = exp ( e i ) ∑ j = 1 n exp ( e j ) \alpha_i=\frac{\exp(e_i)}{\sum_{j=1}^{n}\exp(e_j)} αi=∑j=1nexp(ej)exp(ei)
所以注意力机制完整过程是:
相似度计算 → 归一化 → 加权聚合 \text{相似度计算} \rightarrow \text{归一化} \rightarrow \text{加权聚合} 相似度计算→归一化→加权聚合
这一步非常重要,因为它说明注意力不是凭空来的,而是通过"比较当前目标和所有候选信息的关系"自动得到的。
5. Query、Key、Value 到底是什么
注意力机制里最经典的三个概念是:
Query(查询)、Key(键)、Value(值)
它们经常一起写成:
Attention ( Q , K , V ) \text{Attention}(Q,K,V) Attention(Q,K,V)
初看会有点抽象,但本质不难。
可以这样理解:
(1)Query :我现在到底想找什么
(2)Key :每条信息用来被匹配的"标签"
(3)Value:真正要取出来参与计算的内容
打个比方:
你在图书馆找"深度学习相关书籍"。
- Query:你脑子里"深度学习"这个需求
- Key:每本书的标签、标题、分类信息
- Value:书本真正的内容
模型就是拿 Query 去和所有 Key 比较,看看谁更相关;
然后把相关的那些 Value 按权重聚合起来。
因此,注意力机制可以理解成:
用 Query 去检索 Key,再对对应的 Value 做加权汇总。
6. 为什么注意力机制有效
(1)它能抓住重点信息
传统方法常常会把所有输入压缩成一个固定表示,这样容易把关键信息冲淡。
注意力机制允许模型在每次处理时动态挑重点,因此表达能力更强。
(2)它能处理长距离依赖
一句很长的话里,前后很远的两个词可能有很强关系。
注意力机制可以直接计算任意两个位置之间的关联,而不需要一步一步传递。
这一点是 Transformer 能替代 RNN 的关键原因之一。
(3)它更适合并行计算
RNN 按顺序处理输入,而注意力机制可以通过矩阵运算一次性比较多个位置,天然适合 GPU 并行。
这也是它后来成为大模型核心组件的重要工程原因。
7. 一个具体例子
看这句话:
这个产品外观不错,但是续航很差。
如果任务是做情感分析,那么模型不能只看到"不错",因为后面"但是续航很差"对整体评价影响更大。
注意力机制会自动学到:在这句话里,"差""续航""但是"这些部分更值得关注。
也就是说,注意力机制并不是单纯找"积极词"或"消极词",而是在具体上下文中判断:哪部分对当前任务最关键。
8. 在哪些地方会用到注意力机制
注意力机制已经不是某个单独任务的小技巧,而是一种通用的信息建模方式。
在自然语言处理中,它可以用于:
机器翻译、文本分类、阅读理解、文本摘要、问答系统
在计算机视觉中,它可以用于:
图像分类、目标检测、图像生成、图像区域建模
在推荐系统中,它可以用于:
用户历史行为建模、商品兴趣加权、序列推荐
在大语言模型中,它几乎就是核心中的核心。
因为模型需要在很长的上下文中决定:当前生成下一个词时,到底该参考前文的哪些部分。
9. 一个最基础的注意力分类模型
下面给你一个最小但完整的例子:
用 PyTorch 实现一个带注意力机制的文本分类模型。
这个模型做的事情是:
① 把每个字转成向量
② 给每个字计算一个注意力分数
③ 对所有字做加权求和
④ 用聚合后的句子向量做分类
数学形式如下。
先得到每个位置的向量表示:
h i = Embedding ( x i ) h_i=\text{Embedding}(x_i) hi=Embedding(xi)
然后计算每个位置的注意力得分:
e i = w T h i + b e_i=w^T h_i+b ei=wThi+b
用 Softmax 得到权重:
α i = exp ( e i ) ∑ j = 1 n exp ( e j ) \alpha_i=\frac{\exp(e_i)}{\sum_{j=1}^{n}\exp(e_j)} αi=∑j=1nexp(ej)exp(ei)
用权重对所有向量做加权求和:
s = ∑ i = 1 n α i h i s=\sum_{i=1}^{n}\alpha_i h_i s=i=1∑nαihi
最后分类输出:
y ^ = Softmax ( W s + b ) \hat{y}=\text{Softmax}(Ws+b) y^=Softmax(Ws+b)
这个结构虽然简单,但已经完整体现了注意力机制的本质。
10. 完整可运行代码
下面这份代码可以直接运行。
功能包括:
- 构建简单训练数据
- 建立词表
- 训练带注意力的分类模型
- 输出预测结果
- 输出每个字对应的注意力权重
python
import torch
import torch.nn as nn
import torch.optim as optim
# =========================
# 1. 准备训练数据
# =========================
texts = [
"这个产品很好",
"这个东西很差",
"我非常喜欢",
"我特别失望",
"质量真的不错",
"体验非常糟糕",
"效果很好",
"效果太差"
]
# 1 表示正向,0 表示负向
labels = [1, 0, 1, 0, 1, 0, 1, 0]
# =========================
# 2. 构建字符级词表
# =========================
vocab = {"<PAD>": 0}
for text in texts:
for ch in text:
if ch not in vocab:
vocab[ch] = len(vocab)
# 句子最大长度
max_len = max(len(text) for text in texts)
def encode_text(text, vocab, max_len):
"""
将文本转为索引序列,不足部分用 PAD 补齐
"""
ids = [vocab[ch] for ch in text]
if len(ids) < max_len:
ids += [0] * (max_len - len(ids))
return ids
X = torch.tensor([encode_text(text, vocab, max_len) for text in texts], dtype=torch.long)
y = torch.tensor(labels, dtype=torch.long)
# =========================
# 3. 定义带注意力机制的分类模型
# =========================
class AttentionClassifier(nn.Module):
def __init__(self, vocab_size, embed_dim, num_classes):
super().__init__()
# 字符嵌入层:把每个字符映射成稠密向量
self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
# 注意力打分层:对每个位置输出一个分数
self.attention_fc = nn.Linear(embed_dim, 1)
# 分类层:用聚合后的句子向量做分类
self.classifier = nn.Linear(embed_dim, num_classes)
def forward(self, x):
"""
x: [batch_size, seq_len]
"""
# 1. 嵌入表示
emb = self.embedding(x) # [batch_size, seq_len, embed_dim]
# 2. 计算注意力分数
scores = self.attention_fc(emb).squeeze(-1) # [batch_size, seq_len]
# 3. 对 PAD 位置做掩码,避免其参与注意力计算
mask = (x != 0) # PAD位置为False
scores = scores.masked_fill(~mask, -1e9)
# 4. Softmax 归一化得到注意力权重
attn_weights = torch.softmax(scores, dim=1) # [batch_size, seq_len]
# 5. 加权求和,得到句子表示
sentence_vector = torch.sum(emb * attn_weights.unsqueeze(-1), dim=1) # [batch_size, embed_dim]
# 6. 分类输出
logits = self.classifier(sentence_vector) # [batch_size, num_classes]
return logits, attn_weights
# =========================
# 4. 初始化模型、损失函数、优化器
# =========================
model = AttentionClassifier(
vocab_size=len(vocab),
embed_dim=32,
num_classes=2
)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
# =========================
# 5. 模型训练
# =========================
epochs = 300
for epoch in range(epochs):
model.train()
optimizer.zero_grad()
logits, attn_weights = model(X)
loss = criterion(logits, y)
loss.backward()
optimizer.step()
if (epoch + 1) % 50 == 0:
preds = torch.argmax(logits, dim=1)
acc = (preds == y).float().mean().item()
print(f"Epoch {epoch + 1:03d} | Loss = {loss.item():.4f} | Acc = {acc:.4f}")
# =========================
# 6. 定义预测函数
# =========================
def predict(text):
"""
对单条文本进行预测,并打印注意力权重
"""
model.eval()
ids = encode_text(text, vocab, max_len)
x = torch.tensor([ids], dtype=torch.long)
with torch.no_grad():
logits, attn_weights = model(x)
probs = torch.softmax(logits, dim=1)[0]
pred = torch.argmax(probs).item()
print("=" * 50)
print("输入文本:", text)
print("预测类别:", "正向" if pred == 1 else "负向")
print("类别概率:", probs.tolist())
print("注意力权重:")
# 只打印真实字符部分,不打印 PAD
for ch, w in zip(text, attn_weights[0][:len(text)]):
print(f"{ch} -> {w.item():.4f}")
# =========================
# 7. 测试模型
# =========================
predict("这个产品不错")
predict("这个体验很差")
predict("我很喜欢")
predict("我很失望")
11. 核心思想总结
注意力机制最重要的思想不是公式本身,而是它带来的处理方式变化:
过去的模型更像"平均处理全部输入",
而注意力机制更像"先判断谁重要,再重点处理谁"。
所以它的本质可以概括为一句话:
Attention = 相关性建模 + 权重分配 + 信息聚合 \text{Attention} = \text{相关性建模} + \text{权重分配} + \text{信息聚合} Attention=相关性建模+权重分配+信息聚合
再进一步压缩,就是:
Attention ( Q , K , V ) = Softmax ( f ( Q , K ) ) V \text{Attention}(Q,K,V)=\text{Softmax}(f(Q,K))V Attention(Q,K,V)=Softmax(f(Q,K))V
这也是后面 Transformer 和大模型的基础出发点。
10.2 Scaled Dot-Product
1. 这一节在解决什么问题
上一节已经知道:注意力机制核心是
Attention = Softmax ( Score ) ⋅ V \text{Attention} = \text{Softmax}(\text{Score}) \cdot V Attention=Softmax(Score)⋅V
关键在于:Score(相关性)怎么计算?
最简单的一种方式就是:
👉 用向量点积来衡量相似度
这就是 Scaled Dot-Product Attention。
2. 核心公式
给定:
Q ∈ R n × d , K ∈ R n × d , V ∈ R n × d Q \in \mathbb{R}^{n \times d},\quad K \in \mathbb{R}^{n \times d},\quad V \in \mathbb{R}^{n \times d} Q∈Rn×d,K∈Rn×d,V∈Rn×d
注意力计算为:
Attention ( Q , K , V ) = Softmax ( Q K T d ) V \text{Attention}(Q,K,V) = \text{Softmax}\left(\frac{QK^T}{\sqrt{d}}\right)V Attention(Q,K,V)=Softmax(d QKT)V
3. 每一项在干什么
(1)计算相似度
Q K T QK^T QKT
表示:
👉 每一个 Query 与所有 Key 的点积
结果是:
∈ R n × n \in \mathbb{R}^{n \times n} ∈Rn×n
含义:
- 第 (i,j) 个元素表示:第 (i) 个位置对第 (j) 个位置的关注程度
(2)为什么要除以 (\sqrt{d})
latex
\frac{QK^T}{\sqrt{d}}
作用是:防止数值过大导致 Softmax 失效
原因:
如果维度 (d) 很大,点积结果会变大:
Q ⋅ K ∼ O ( d ) Q \cdot K \sim O(d) Q⋅K∼O(d)
会导致:
Softmax → 极端分布(接近 o n e − h o t ) \text{Softmax} \rightarrow 极端分布(接近 one-hot) Softmax→极端分布(接近one−hot)
这样模型就学不动了(梯度消失)
所以需要缩放:
1 d \frac{1}{\sqrt{d}} d 1
👉 本质:控制方差,稳定训练
(3)Softmax 归一化
Softmax ( Q K T d ) \text{Softmax}\left(\frac{QK^T}{\sqrt{d}}\right) Softmax(d QKT)
作用:
👉 把"相似度矩阵"变成"注意力分布"
每一行满足:
∑ j α i j = 1 \sum_j \alpha_{ij} = 1 j∑αij=1
(4)加权 Value
Output = α V \text{Output} = \alpha V Output=αV
本质:
每一行 = ∑ j α i j v j \text{每一行} = \sum_j \alpha_{ij} v_j 每一行=j∑αijvj
👉 每个位置重新聚合全局信息
4. 一句话理解
Scaled Dot-Product Attention 可以理解为:
👉 用 Query 去"检索" Key,再用权重去聚合 Value
或者更数学一点:
每一行 = ∑ j α i j v j \text{每一行} = \sum_j \alpha_{ij} v_j 每一行=j∑αijvj
5. 一个直观例子
句子:
我 很 喜欢 这个 产品
当模型处理"喜欢"这个词时:
- Query = "喜欢"的向量
- Key = 所有词的向量
- Value = 所有词的语义信息
模型会计算:
喜欢 ⋅ { 我, 很, 喜欢, 这个, 产品 } \text{喜欢} \cdot \{\text{我, 很, 喜欢, 这个, 产品}\} 喜欢⋅{我, 很, 喜欢, 这个, 产品}
结果可能是:
- 喜欢 ↔ 喜欢(最高)
- 喜欢 ↔ 产品(较高)
- 喜欢 ↔ 我(较低)
然后:
👉 "喜欢"这个位置,会融合"产品""喜欢"等信息
6. 矩阵角度理解(很重要)
注意力可以完全用矩阵表示:
Q K T → 相似度矩阵Softmax ( Q K T ) → 权重矩阵Softmax ( Q K T ) V → 新表示 QK^T \rightarrow \text{相似度矩阵} \text{Softmax}(QK^T) \rightarrow \text{权重矩阵} \text{Softmax}(QK^T)V \rightarrow \text{新表示} QKT→相似度矩阵Softmax(QKT)→权重矩阵Softmax(QKT)V→新表示
所以本质是:
👉 一次矩阵乘法完成"全局信息交互"
这也是 Transformer 可以并行的核心原因。
7. PyTorch完整实现(从0实现)
下面代码手写 Scaled Dot-Product Attention(不调用现成API)
python
import torch
import torch.nn as nn
# =========================
# 1. 构造输入
# =========================
batch_size = 2
seq_len = 5
d_model = 16
# 随机输入(模拟Embedding输出)
X = torch.randn(batch_size, seq_len, d_model)
# =========================
# 2. 定义Q K V映射
# =========================
W_Q = nn.Linear(d_model, d_model)
W_K = nn.Linear(d_model, d_model)
W_V = nn.Linear(d_model, d_model)
Q = W_Q(X)
K = W_K(X)
V = W_V(X)
# =========================
# 3. 计算Attention
# =========================
# Step1: 点积
scores = torch.matmul(Q, K.transpose(-2, -1))
# Step2: 缩放
d_k = Q.size(-1)
scores = scores / (d_k ** 0.5)
# Step3: Softmax
attn_weights = torch.softmax(scores, dim=-1)
# Step4: 加权求和
output = torch.matmul(attn_weights, V)
print("输出形状:", output.shape)
print("注意力矩阵:", attn_weights)
8. 加入 Mask(真实模型必须)
在实际中(比如语言模型),不能看到"未来信息"。
例如:
我 今天 去 吃饭
当预测"今天"时,不能看到"去 吃饭"
所以需要 Mask:
Masked Attention \text{Masked Attention} Masked Attention
代码实现:
python
# 创建mask(上三角为True)
mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1).bool()
# 应用mask
scores = scores.masked_fill(mask, float('-inf'))
attn_weights = torch.softmax(scores, dim=-1)
9. 实际应用在哪里
Scaled Dot-Product Attention 是:
Transformer 的最底层核心算子
直接应用于:
- GPT(生成模型)
- BERT(理解模型)
- ChatGPT(对话系统)
- 代码生成模型
- 多模态模型(文本+图像)
可以说:
所有大模型 = Attention 的堆叠
10. 核心总结
最重要的一行公式:
Attention ( Q , K , V ) = Softmax ( Q K T d ) V \text{Attention}(Q,K,V) = \text{Softmax}\left(\frac{QK^T}{\sqrt{d}}\right)V Attention(Q,K,V)=Softmax(d QKT)V
最终本质:
让每个位置都能"看见全局",并按重要性选择信息
10.3 Multi-Head Attention
第11章 Transformer架构
11.1 Encoder-Decoder
、好,这一版我帮你再精修到可直接发博客级别:
- 公式只集中在数学部分
- 每个公式配简洁解释
- 文字比刚才稍微更充实,但不过度
- 不使用多余符号(👉最多不出现)
- 结构清晰、专业但易理解
10.3 Multi-Head Attention
1. 为什么需要 Multi-Head
在上一节中,单个 Attention 已经可以实现:
让每个位置根据相关性,从整个序列中获取信息。
但它有一个本质限制:
只能在一个特征空间里建模所有关系。
而现实数据(尤其是语言)具有多种不同类型的依赖关系。例如一句话:
小明在北京工作
模型在理解时,往往需要同时处理:
(1)人物与动作的关系(小明 → 工作)
(2)动作与地点的关系(工作 → 北京)
(3)句子整体语义结构
如果只用一个 Attention,这些关系会被压在同一表示空间中,容易互相干扰,表达能力受限。
因此,引入 Multi-Head Attention,让模型可以同时从多个子空间建模不同关系。
2. 核心思想
Multi-Head Attention 的核心思想可以概括为:
将一次注意力计算拆分为多个并行的子计算,每个子计算在不同的特征空间中进行,最后再进行融合。
从直观上看,可以理解为:
- 单头 Attention:用一种方式理解信息
- 多头 Attention:用多种方式同时理解信息,再进行综合
这样可以显著提升模型对复杂结构的表达能力。
3. 数学定义(核心公式)
首先,对输入进行线性变换,得到不同 head 的 Query、Key、Value:
Q i = Q W i Q , K i = K W i K , V i = V W i V Q_i = QW_i^Q,\quad K_i = KW_i^K,\quad V_i = VW_i^V Qi=QWiQ,Ki=KWiK,Vi=VWiV
这里的含义是:
每个 head 都有自己的一组参数 (W_i^Q, W_i^K, W_i^V),
它们会把原始表示映射到不同的子空间中。
接下来,每个 head 独立计算注意力:
head i = Softmax ( Q i K i T d k ) V i \text{head}_i = \text{Softmax}\left(\frac{Q_i K_i^T}{\sqrt{d_k}}\right)V_i headi=Softmax(dk QiKiT)Vi
这一公式可以拆开理解为三步:
- (Q_i K_i^T):计算当前 head 中各位置之间的相似度
- (\frac{1}{\sqrt{d_k}}):对相似度进行缩放,防止数值过大
- Softmax:将相似度转为权重分布
- 乘以 (V_i):根据权重对信息进行加权聚合
最后,将所有 head 的结果拼接并线性变换:
MultiHead ( Q , K , V ) = Concat ( head 1 , ... , head h ) W O \text{MultiHead}(Q,K,V) = \text{Concat}(\text{head}_1,\dots,\text{head}_h)W^O MultiHead(Q,K,V)=Concat(head1,...,headh)WO
这一步的作用是:
- Concat:整合多个子空间的信息
- (W^O):重新映射回统一表示空间
4. 从计算流程理解
整个 Multi-Head Attention 可以分为五个步骤:
第一步:输入经过线性映射,生成 Q、K、V
第二步:将 Q、K、V 按维度拆分成多个 head
第三步:每个 head 独立计算注意力
第四步:将所有 head 的输出拼接
第五步:通过线性层融合结果
这个过程的关键变化在于:
原本一次注意力计算,被扩展为多次并行计算,并在最后统一整合。
5. 为什么效果更好
(1)表达能力提升
单头 Attention 只能在一个空间中学习关系,而 Multi-Head 将表示拆分为多个子空间,每个子空间可以学习不同模式,使整体表达更加丰富。
(2)信息解耦
不同类型的关系被分配到不同 head 中建模,避免了在同一空间中的信息冲突,提高了建模质量。
(3)结构更稳定
多个 head 的结果在最后进行融合,相当于从多个角度进行综合判断,有助于模型训练更加稳定。
6. 维度变化(必须理解)
设模型维度为:
text
d_model = 512
头数为:
text
h = 8
则每个 head 的维度为:
text
d_k = 64
这意味着:
- 每个 head 在低维空间中进行注意力计算
- 最终通过拼接恢复到原始维度
这种设计既保证了表达能力,又控制了计算复杂度。
7. 一个直观例子
句子:
我今天在图书馆学习
不同 head 可能关注不同内容:
一个 head 关注"我 → 学习"(行为关系)
一个 head 关注"学习 → 图书馆"(地点关系)
一个 head 关注"今天 → 学习"(时间关系)
最终输出是这些关系的综合结果,而不是单一关系。
8. 完整代码(手写 Multi-Head)
python
import torch
import torch.nn as nn
class MultiHeadAttention(nn.Module):
def __init__(self, d_model=32, num_heads=4):
super().__init__()
assert d_model % num_heads == 0
self.d_model = d_model
self.num_heads = num_heads
self.d_k = d_model // num_heads
# Q K V 映射
self.W_Q = nn.Linear(d_model, d_model)
self.W_K = nn.Linear(d_model, d_model)
self.W_V = nn.Linear(d_model, d_model)
# 输出映射
self.W_O = nn.Linear(d_model, d_model)
def forward(self, x):
"""
x: [batch_size, seq_len, d_model]
"""
batch_size, seq_len, _ = x.size()
# 1. 线性映射得到 Q K V
Q = self.W_Q(x)
K = self.W_K(x)
V = self.W_V(x)
# 2. 切分为多头
def split_heads(t):
# [B, L, D] -> [B, H, L, d_k]
return t.view(batch_size, seq_len, self.num_heads, self.d_k)\
.transpose(1, 2)
Q = split_heads(Q)
K = split_heads(K)
V = split_heads(V)
# 3. 注意力计算
scores = torch.matmul(Q, K.transpose(-2, -1)) # 相似度
scores = scores / (self.d_k ** 0.5) # 缩放
attn_weights = torch.softmax(scores, dim=-1)
out = torch.matmul(attn_weights, V) # 加权聚合
# 4. 拼接多头
out = out.transpose(1, 2).contiguous()
out = out.view(batch_size, seq_len, self.d_model)
# 5. 输出映射
out = self.W_O(out)
return out, attn_weights
# 测试
x = torch.randn(2, 5, 32)
model = MultiHeadAttention(d_model=32, num_heads=4)
out, attn = model(x)
print("输出形状:", out.shape)
print("注意力形状:", attn.shape)
9. 实际意义
Multi-Head Attention 是 Transformer 能够成功的关键结构之一。
它使模型具备:多角度理解能力,更强的上下文建模能力,更丰富的语义表达能力
在 GPT、BERT 等模型中,每一层都会使用 Multi-Head Attention,层层叠加后形成强大的建模能力。
10. 核心总结
Multi-Head Attention 的本质不是简单的"多次计算",而是:
在多个子空间中并行建模不同关系,并进行融合。
它解决了单一注意力表达能力不足的问题,使模型能够更全面地理解复杂数据结构。
好,这一版我帮你补强"通俗理解"部分,但不降低专业性,同时保持你现在的整体风格(结构清晰 + 公式少 + 有代码)。
11.1 Encoder-Decoder 结构
1. Transformer在做什么
前面学的 Attention,本质是在解决:
一句话内部,谁和谁有关。
但一个完整模型,不只是"理解一句话",还要:
- 理解输入
- 生成输出
Transformer 就是把这两件事拆开来做:
Encoder 负责理解,Decoder 负责生成
可以把它想成一个流程:
输入 → 理解 → 表示 → 再生成输出
2. 一个非常直观的类比
可以把 Transformer 理解成两个人协作:
Encoder:阅读者
负责把原文读懂,并整理成一份"笔记"
Decoder:写作者
根据这份笔记,一点一点写出新的内容
例如翻译:
原句:
I love AI
过程变成:
- Encoder:理解这句话的意思(不是逐词,而是整体语义)
- Decoder:根据理解,生成"我喜欢人工智能"
关键点在于:
👉 Decoder 并不是直接看原句,而是看 Encoder 给出的"理解结果"
3. Encoder做了什么(本质)
Encoder 的作用可以总结为一句话:
把"原始输入"变成"带上下文的语义表示"。
通俗一点说:
原始词向量:
- "银行" → 只是一个词
经过 Encoder 后:
- "银行" → 会结合上下文,变成"金融机构"或"河岸"
也就是说:
Encoder 让每个词"理解了周围环境"。
4. Encoder内部结构(带理解)
每一层 Encoder 做两件事:
(1)Self-Attention(信息交流)
作用:
让每个词看到整个句子
通俗理解:
一句话中,每个词都会问:
"我应该参考哪些词?"
然后根据相关性,把别的信息融合进来。
(2)前馈网络(信息加工)
作用:
对融合后的信息进行进一步处理
可以理解为:
Attention 是"收集信息"
FFN 是"加工信息"
类似:
收集资料 → 思考整理
5. 为什么要多层 Encoder
Transformer 通常会堆很多层(比如 12 层、24 层)
原因是:
第一层:学基础关系
第二层:学更复杂关系
第三层:学抽象语义
可以理解为:
逐层提炼信息
就像:
- 第1层:看字
- 第2层:看词
- 第3层:看句子结构
- 第N层:理解语义
6. Decoder做了什么(通俗理解)
Decoder 的作用是:
一步一步生成结果
例如生成句子:
我 今天 去 吃饭
Decoder 实际是这样工作的:
第一步:生成"我"
第二步:基于"我",生成"今天"
第三步:基于"我 今天",生成"去"
它是一个逐步预测的过程。
7. 为什么不能"看到未来"(关键点)
在生成过程中,有一个非常重要的限制:
模型不能看到未来的词。
比如:
在预测"今天"时:
不能提前看到"去 吃饭"
否则模型会"作弊"。
所以引入:
Masked Self-Attention
作用:
👉 只允许看左边(历史信息)
8. Decoder最关键的一步
Decoder 和 Encoder 最大区别在这里:
Decoder会去"参考Encoder的输出"
这一步可以理解为:
- Encoder:提供"理解结果"
- Decoder:在生成时不断查阅这份理解
例如翻译:
生成"喜欢"时:
Decoder 会自动关注输入中的 "love"
所以这一步的本质是:
生成过程与输入语义对齐
9. 一个完整流程(带理解)
整个 Transformer 实际流程是:
① 输入文本 → 转为向量
② 进入 Encoder → 逐层理解
此时得到:
👉 "整句话的语义表示"
③ Decoder 开始生成
每一步都做:
- 看已经生成的内容(历史)
- 看 Encoder 的输出(理解结果)
- 预测下一个词
不断循环,直到结束。
10. 一个最小Encoder实现
python
import torch
import torch.nn as nn
class EncoderBlock(nn.Module):
def __init__(self, d_model=32, num_heads=4, hidden_dim=64):
super().__init__()
# 多头注意力(信息交流)
self.attn = nn.MultiheadAttention(d_model, num_heads, batch_first=True)
# 前馈网络(信息加工)
self.ffn = nn.Sequential(
nn.Linear(d_model, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, d_model)
)
# 归一化
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
def forward(self, x):
# 1. 自注意力
attn_out, _ = self.attn(x, x, x)
# 残差连接 + 归一化
x = self.norm1(x + attn_out)
# 2. 前馈网络
ffn_out = self.ffn(x)
# 残差连接 + 归一化
x = self.norm2(x + ffn_out)
return x
# 测试
x = torch.randn(2, 5, 32)
model = EncoderBlock()
out = model(x)
print("输出形状:", out.shape)
11. 核心总结(这一节一定要记住)
Transformer 的核心结构可以记成一句话:
Encoder负责"理解",Decoder负责"生成"。
更具体一点:
- Encoder:让每个词理解上下文
- Decoder:基于理解,一步步生成结果
整个模型的关键在于:
用 Attention 让信息在序列中自由流动。
11.2 Position Encoding(位置编码)
1. 问题从哪里来
在前面的结构中,Transformer 有一个非常"反直觉"的特点:
它本身并不具备顺序信息。
不像 RNN 按时间一步步处理,也不像 CNN 有局部滑动窗口,
Transformer 的 Attention 是这样工作的:
- 所有位置同时输入
- 每个位置可以直接"看到"所有位置
这带来一个问题:
如果输入是:
我 喜欢 你
你 喜欢 我
在没有位置信息的情况下,这两句话的"词集合"是一样的:
text
我 喜欢 你
模型是无法区分顺序的。
也就是说:
Attention 只关心"谁和谁相关",但不知道"谁在前谁在后"。
2. 核心解决思路
为了解决这个问题,Transformer 引入:
Position Encoding(位置编码)
它的核心思想非常简单:
给每个位置一个"位置信号",让模型知道顺序。
具体做法是:
在词向量中加入位置信息:
text
最终输入 = 词向量 + 位置编码
这样一来:
- "我在第1个位置"
- "你在第3个位置"
这些信息会被显式编码进去。
3. 为什么用"加法"
这里有一个设计选择:
为什么是"相加",而不是拼接?
原因是:
- 保持维度不变(方便计算)
- 让模型可以同时利用"语义信息"和"位置信息"
可以理解为:
词向量表示"是什么",位置编码表示"在哪里"。
相加之后:
👉 每个向量同时包含"内容 + 位置"
4. 数学定义(核心公式)
Transformer 使用的是一种固定的函数编码方式 :
P E ( p o s , 2 i ) = sin ( p o s 10000 2 i d ) P E ( p o s , 2 i + 1 ) = cos ( p o s 10000 2 i d ) PE(pos, 2i) = \sin\left(\frac{pos}{10000^{\frac{2i}{d}}}\right) PE(pos, 2i+1) = \cos\left(\frac{pos}{10000^{\frac{2i}{d}}}\right) PE(pos,2i)=sin(10000d2ipos)PE(pos,2i+1)=cos(10000d2ipos)
这里的含义是:
- (pos):位置(第几个词)
- (i):维度索引
- (d):向量维度
解释一下这两个公式在干什么:
- 偶数维用 sin
- 奇数维用 cos
- 不同维度使用不同频率
这样每个位置都会得到一个独特的编码向量。
5. 为什么用正弦和余弦
这个设计看起来很"数学",但其实有两个很重要的工程意义:
(1)可以表示相对位置
由于 sin 和 cos 具有周期性:
不同位置之间的关系可以通过函数变化体现出来。
模型可以通过这些规律,学习:
- 两个词之间距离远还是近
- 顺序关系
(2)支持更长序列
这种编码是"函数生成"的,而不是查表:
- 可以推广到任意长度
- 不依赖训练数据
相比之下,如果用"可学习位置向量",长度受限。
6. 更直观理解
可以把位置编码理解为:
给每个位置贴一个"身份证"
例如:
第1个词 → 编码A
第2个词 → 编码B
第3个词 → 编码C
然后和词向量融合:
原本:
"我" → 含义向量
现在变成:
"我 + 第1位"
"喜欢 + 第2位"
"你 + 第3位"
这样模型在计算 Attention 时,就能区分:
谁在前
谁在后
7. 一个小例子
句子:
我 爱 你
加入位置编码后:
- "我" → 向量 + 位置1
- "爱" → 向量 + 位置2
- "你" → 向量 + 位置3
如果换成:
你 爱 我
虽然词一样,但位置不同:
👉 输入向量完全不同
模型自然可以区分这两句话。
8. PyTorch实现(位置编码)
下面是一个标准实现(可直接运行):
python
import torch
import torch.nn as nn
import math
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=100):
super().__init__()
pe = torch.zeros(max_len, d_model)
# 位置索引
position = torch.arange(0, max_len).unsqueeze(1)
# 频率项
div_term = torch.exp(
torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model)
)
# 偶数维:sin
pe[:, 0::2] = torch.sin(position * div_term)
# 奇数维:cos
pe[:, 1::2] = torch.cos(position * div_term)
pe = pe.unsqueeze(0) # [1, max_len, d_model]
self.register_buffer('pe', pe)
def forward(self, x):
"""
x: [batch_size, seq_len, d_model]
"""
seq_len = x.size(1)
return x + self.pe[:, :seq_len]
# 测试
x = torch.zeros(2, 5, 32)
pe = PositionalEncoding(d_model=32)
out = pe(x)
print("输出形状:", out.shape)
9. 实际意义
位置编码解决了 Transformer 的一个核心缺陷:
Attention 本身没有顺序感。
通过加入位置编码:
- 模型可以感知顺序
- 可以建模前后关系
- 可以处理语言结构
这是 Transformer 能用于 NLP 的关键条件之一。
10. 核心总结
Position Encoding 的本质可以总结为:
给序列加入顺序信息,让无序模型具备"顺序感"。
它解决的问题是:
- Attention 无法区分顺序
它带来的能力是:
- 建模位置关系
- 支持长序列
- 提供结构信息
可以简单记为一句话:
Attention 负责"关系",Position Encoding 负责"顺序"。
11.3 Transformer整体流程
1. 这一节在解决什么问题
前面我们已经分别学了:
- Attention:信息如何流动
- Multi-Head:如何多角度建模
- Encoder-Decoder:结构如何组织
- Position Encoding:如何表示顺序
现在需要解决的是:
这些模块是如何组合起来,形成一个完整可工作的模型?
这一节的目标就是:
把 Transformer 从"模块理解"升级为"整体理解"。
2. 一句话理解Transformer
Transformer 的整体流程可以用一句话概括:
输入 → 加位置 → Encoder理解 → Decoder生成 → 输出结果
但这句话还太抽象,下面一步步展开。
3. 整体流程(从输入到输出)
(1)输入阶段
原始文本:
我 喜欢 AI
首先会变成:
- 词向量(Embedding)
- 加上位置编码(Position Encoding)
得到:
text
带有语义 + 顺序信息的向量序列
这一阶段解决两个问题:
- 每个词"是什么"
- 每个词"在哪个位置"
(2)进入Encoder(理解阶段)
输入进入 Encoder(通常是多层叠加)
每一层都做:
① Self-Attention(信息交流)
② 前馈网络(信息加工)
经过多层后,每个词都会变成:
融合全局信息的表示
通俗理解:
- 每个词已经"看懂整句话"
- 表示中包含上下文
输出结果可以理解为:
text
整句话的语义表示(上下文编码)
(3)进入Decoder(生成阶段)
Decoder 是一个"逐步生成"的过程。
它的输入有两部分:
(1)已经生成的内容
(2)Encoder的输出(理解结果)
(4)Decoder内部结构
每一层 Decoder 包含三步:
第一步:Masked Self-Attention
作用:
只看已经生成的内容
例如生成到第3个词时:
- 可以看前2个词
- 不能看后面的词
这是为了保证生成是"逐步"的。
第二步:Encoder-Decoder Attention
作用:
在生成时参考输入内容
可以理解为:
- Encoder:提供"理解结果"
- Decoder:不断查阅这个结果
例如翻译时:
生成"喜欢"时,会关注输入中的"love"。
第三步:前馈网络
作用:
对当前信息做非线性变换
类似 Encoder 中的 FFN,负责增强表达能力。
(5)输出阶段
Decoder 最后输出:
text
每个词的概率分布
例如:
text
P(我) = 0.7
P(你) = 0.2
P(他) = 0.1
然后选择概率最大的词,作为当前输出。
不断重复这个过程,直到生成结束。
4. 用一个完整例子串起来
以翻译为例:
输入:
I love AI
流程:
第一步:Embedding + 位置编码
第二步:Encoder理解 → 得到语义表示
第三步:Decoder开始生成:
- 第1步:生成"我"
- 第2步:基于"我",生成"喜欢"
- 第3步:基于"我 喜欢",生成"人工智能"
整个过程:
👉 一边生成,一边参考输入
5. Transformer的核心特点
(1)完全基于Attention
Transformer 不依赖:
- RNN(顺序处理)
- CNN(局部卷积)
而是完全用 Attention:
直接建模全局关系
(2)并行计算能力强
Encoder 可以一次性处理整个序列:
text
不需要一步一步循环
这使得训练速度大幅提升。
(3)长距离依赖能力强
任意两个位置之间:
都可以直接建立联系。
不像 RNN:
需要多步传递。
6. 一个极简Transformer实现
下面给你一个"最简版结构理解代码"(可运行):
python
import torch
import torch.nn as nn
class SimpleTransformer(nn.Module):
def __init__(self, d_model=32, num_heads=4):
super().__init__()
# 编码器
self.encoder = nn.TransformerEncoder(
nn.TransformerEncoderLayer(d_model=d_model, nhead=num_heads),
num_layers=2
)
# 解码器
self.decoder = nn.TransformerDecoder(
nn.TransformerDecoderLayer(d_model=d_model, nhead=num_heads),
num_layers=2
)
self.fc = nn.Linear(d_model, 10)
def forward(self, src, tgt):
"""
src: 输入序列
tgt: 已生成序列
"""
# Encoder
memory = self.encoder(src)
# Decoder
output = self.decoder(tgt, memory)
# 输出
out = self.fc(output)
return out
# 测试
src = torch.randn(5, 2, 32) # [seq_len, batch, d_model]
tgt = torch.randn(5, 2, 32)
model = SimpleTransformer()
out = model(src, tgt)
print("输出形状:", out.shape)
7. 为什么这个结构这么重要
Transformer 的这个结构,带来了三个关键变化:
第一:统一建模方式
→ 所有任务都可以用同一套结构
第二:可扩展
→ 层数越多,能力越强
第三:成为大模型基础
→ GPT、BERT、本质都是它的变体
8. 核心总结
Transformer整体可以总结为:
Encoder负责理解,Decoder负责生成
完整流程:
- 输入 → 编码 → 理解
- 输出 → 逐步生成
其中最核心的机制是:
Attention驱动信息流动
整个模型的本质可以理解为:
用注意力构建一个"信息交互网络",再在这个网络上进行生成
接下来进入下一章:
第12章 大语言模型(LLM)
12.1 GPT系列(生成模型)
1. GPT在做什么
GPT 的核心任务其实非常简单:根据已有文本,预测下一个词。这个过程本身并不复杂,但当它不断重复时,就可以逐步生成完整的句子甚至文章。
例如输入一句话开头:
我今天很
模型不会去理解"情绪"这个概念,而是基于训练中学到的语言规律,去计算在这种语境下哪个词最可能出现。它可能认为"开心"出现的概率更高,于是生成:
我今天很开心
接着,它再基于"我今天很开心"继续预测下一个词。这个过程不断循环,最终形成一段完整文本。从本质上看,GPT 并不是在写作,而是在进行连续的概率预测。
2. 一种更直观的理解方式
可以把 GPT 理解为一个能力非常强的自动补全系统。当你输入一句话的开头时,它会根据过去见过的大量文本,去判断这个句子通常会如何继续。
例如输入:
我喜欢
模型并不会分析"喜欢"的情感含义,而是会去匹配类似表达在数据中的出现方式,从而推断"我喜欢"后面通常接什么。它可能生成:
我喜欢学习
随后再继续补全:
我喜欢学习人工智能
这个过程并不是整体规划出来的,而是一步一步延续出来的。模型每次只做一个局部决策,但由于这种决策足够准确,最终就能形成结构合理的文本。
3. 数学本质
GPT 的训练目标可以用下面这个公式表示:
P ( x 1 , x 2 , . . . , x n ) = ∏ t = 1 n P ( x t ∣ x 1 , . . . , x t − 1 ) P(x_1, x_2, ..., x_n) = \prod_{t=1}^{n} P(x_t \mid x_1, ..., x_{t-1}) P(x1,x2,...,xn)=t=1∏nP(xt∣x1,...,xt−1)
这个公式表达的含义是,一句话的生成过程可以拆解为多个连续步骤:先生成第一个词,然后基于第一个词生成第二个词,再基于前两个词生成第三个词,之后不断向后递推,直到整句话完成。
因此,模型在每一步只解决一个问题,就是在当前已有上下文的条件下,下一个词最可能是什么。整个语言生成能力,其实就是这种"局部预测"不断叠加的结果。
4. GPT和Transformer的关系
GPT 并不是一个全新的模型结构,它本质上是 Transformer 的一个特定使用方式。更准确地说,它只使用了 Transformer 中的 Decoder 部分。
这样设计的原因在于,生成任务本身就是从左到右逐步展开的过程,并不需要像 Encoder 那样进行双向理解。因此 GPT 的结构具有明显特点:只能看到前面的内容,并且一步步向后生成。
这种结构使得模型非常适合处理写作、对话、代码生成等任务,因为这些任务本身就是顺序展开的。
5. 为什么必须限制"看未来"
在训练 GPT 时,有一个非常关键的约束条件,就是模型不能看到未来的词。
例如在处理句子:
我今天很开心
如果模型在预测"开心"时已经提前看到了这个词,那么训练就变成了简单的"复制答案",模型无法真正学到语言规律。
因此在 Attention 中必须加入限制,使得每个位置只能访问它之前的内容。这种机制保证了模型在训练时的行为和实际生成时保持一致,也让每一步预测都具有真实意义。
6. GPT是如何学会语言的
GPT 并不会学习传统意义上的语法规则或逻辑推理,它的能力来自于对大量文本数据的统计学习。
在训练过程中,模型会逐渐掌握:
哪些词经常一起出现,哪些表达更符合语言习惯,以及不同上下文中常见的句子结构。随着数据规模和模型规模的不断扩大,这种统计能力会变得越来越强。
最终表现出来的效果是,模型生成的内容看起来像是经过理解和思考,但本质上仍然是基于概率的预测。
7. 为什么GPT可以写文章
当模型足够大时,它不仅学到了词之间的关系,还学到了更高层次的结构信息。例如句子如何衔接,段落如何展开,以及不同类型文本的表达方式。
因此它可以生成连贯的句子,组织合理的段落,甚至模仿特定风格进行写作。
需要注意的是,这种能力并不是来自"理解世界",而是来自对大量文本模式的学习。模型并没有真正的思考过程,它只是把预测能力发挥到了极致。
8. 一个简单生成过程
给定一句开头:
人工智能的发展
模型会先预测下一个词,例如"非常迅速",然后再基于这个结果继续预测后续内容,例如"对社会产生了深远影响"。这个过程会不断重复,直到生成完整文本。
整个过程中,模型不会回头修改已经生成的内容,也不会提前规划全文结构,而是始终基于当前上下文做出最优预测。
9. 一个最小GPT实现(简化版)
下面是一个简化版 GPT 结构代码,可以直接运行,用于理解其核心机制:
python
import torch
import torch.nn as nn
class MiniGPT(nn.Module):
def __init__(self, vocab_size=100, d_model=32, num_heads=4):
super().__init__()
self.embedding = nn.Embedding(vocab_size, d_model)
self.decoder_layer = nn.TransformerDecoderLayer(
d_model=d_model,
nhead=num_heads
)
self.decoder = nn.TransformerDecoder(self.decoder_layer, num_layers=2)
self.fc = nn.Linear(d_model, vocab_size)
def forward(self, x):
emb = self.embedding(x)
seq_len = x.size(0)
mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1).bool()
out = self.decoder(emb, emb, tgt_mask=mask)
logits = self.fc(out)
return logits
x = torch.randint(0, 100, (5, 2))
model = MiniGPT()
out = model(x)
print("输出形状:", out.shape)
10. 本节核心总结
GPT 的核心思想可以概括为:通过不断预测下一个词,逐步构建完整文本。它基于 Transformer 的 Decoder 结构,采用自回归方式进行生成,并通过 Mask 机制保证预测过程的合理性。
可以用一句话总结:
GPT 是一个基于注意力机制的自回归语言生成模型。
12.2 BERT(理解)
1. BERT在做什么
如果说 GPT 的目标是"生成文本",那么 BERT 的目标就是:
理解文本。
它不会去预测"下一个词是什么",而是试图理解一句话中每个词的含义,以及它们之间的关系。
例如一句话:
我在银行工作
这里的"银行"可能表示金融机构,也可能表示河岸。
BERT 的作用就是根据上下文判断:这里的"银行"到底是什么意思。
因此,它更擅长的任务是:
- 分类
- 判断
- 信息抽取
- 语义理解
而不是生成内容。
2. 和GPT最核心的区别
GPT 是从左到右逐步生成的模型,而 BERT 的最大特点是:
它可以同时看到整个句子。
例如:
我喜欢这个电影,因为它很精彩
在理解"精彩"时,BERT 不仅可以看前面的"电影",还可以看后面的内容。
这意味着:
它的理解是"全局的",而不是"单向的"。
换句话说:
GPT 是一边读一边写,
BERT 是一次性把整句话读完再理解。
3. 数学本质(核心目标)
BERT 的核心训练任务之一是 Masked Language Model(掩码语言模型):
P ( x i ∣ x 1 , . . . , x i − 1 , x i + 1 , . . . , x n ) P(x_i \mid x_1, ..., x_{i-1}, x_{i+1}, ..., x_n) P(xi∣x1,...,xi−1,xi+1,...,xn)
这个公式表示:
模型在预测某个词时,可以利用它前后的所有上下文信息。
也就是说:
不是预测"下一个词",而是预测"被遮住的词"。
4. Mask机制的不同
GPT 的 Mask 是:
遮住未来
而 BERT 的 Mask 是:
随机遮住部分词
例如:
我喜欢[MASK]电影
模型需要预测:
精彩
这种训练方式的意义在于:
模型必须理解整个句子,而不是依赖局部信息。
5. BERT是如何理解语言的
BERT 的训练过程,本质是在不断学习:
一个词在不同上下文中的含义变化。
例如"苹果":
- 在"我吃了一个苹果"中是水果
- 在"苹果发布了新产品"中是公司
通过大量数据训练,模型逐渐学会:
根据上下文动态调整词的表示。
这也是它比传统词向量(如Word2Vec)更强的原因。
6. 为什么BERT不能直接写文章
BERT 虽然理解能力很强,但它不适合生成任务。
原因在于:
它的训练方式不是"从左到右生成",而是"填空"。
它并不知道:
下一步应该写什么,只知道"这个位置最合理的词是什么"。
因此:
- GPT 擅长写作
- BERT 擅长理解
这是由训练目标决定的,而不是模型大小。
7. 一个简单理解例子
句子:
这部电影非常[MASK]
BERT 会根据上下文判断:
最可能的词是"好看"或"精彩"。
这里它不是在"写句子",而是在做:
语义补全。
这种能力使它在很多任务中表现很好,例如:
情感分析、文本分类、问答系统等。
8. 一个最小BERT实现(简化版)
下面是一个简化版 BERT 编码结构,用来理解其核心机制:
python
import torch
import torch.nn as nn
class MiniBERT(nn.Module):
def __init__(self, vocab_size=100, d_model=32, num_heads=4):
super().__init__()
# 词向量
self.embedding = nn.Embedding(vocab_size, d_model)
# Encoder结构
self.encoder_layer = nn.TransformerEncoderLayer(
d_model=d_model,
nhead=num_heads
)
self.encoder = nn.TransformerEncoder(self.encoder_layer, num_layers=2)
# 输出层(预测词)
self.fc = nn.Linear(d_model, vocab_size)
def forward(self, x):
"""
x: [seq_len, batch_size]
"""
emb = self.embedding(x)
# 编码(双向注意力)
out = self.encoder(emb)
logits = self.fc(out)
return logits
# 测试
x = torch.randint(0, 100, (5, 2))
model = MiniBERT()
out = model(x)
print("输出形状:", out.shape)
9. GPT vs BERT(关键区别)
可以从本质上这样理解两者:
GPT 是顺序生成模型,它关注的是"接下来写什么";
BERT 是上下文理解模型,它关注的是"这句话是什么意思"。
两者的差异来自三个方面:
第一,信息流方向不同。GPT 是单向的,而 BERT 是双向的。
第二,训练目标不同。GPT 预测下一个词,BERT 预测被遮住的词。
第三,应用场景不同。GPT 更适合生成任务,BERT 更适合理解任务。
10. 本节核心总结
BERT 的核心可以总结为:
它通过双向上下文建模,实现对语言的深度理解。
相比 GPT,它不擅长生成,但在理解类任务中更有优势。
可以用一句话概括:
BERT 是一个基于双向注意力的语言理解模型。
12.3 Prompt Engineering
1. Prompt在做什么
在使用大模型时,我们表面上是在"提问",但本质上是在设计输入结构。这个输入就是 Prompt。
模型的输出并不是完全随机生成的,而是严格依赖于输入条件的结果。换句话说,模型并不会主动理解你的真实意图,它只能根据你提供的 Prompt 去推测你想要什么。
因此可以这样理解:
Prompt 并不是一句普通的话,而是模型行为的"控制信号"。
同一个模型,如果 Prompt 不同,输出结果可能完全不同。这种差异,往往比模型本身的差异还要大。
2. 为什么一句话能改变结果
来看一个非常直观的变化。
当输入是:
什么是注意力机制
模型通常会给出一个比较泛化的解释,内容可能正确,但结构松散。
如果换成:
请用通俗语言分三段解释注意力机制,并给出一个生活中的类比
模型输出会明显变得更清晰,结构更稳定,表达更符合阅读习惯。
这里并没有改变模型,只是改变了输入方式。
但结果却发生了明显变化。
这说明一个核心问题:
你不是在问问题,而是在定义输出的形态。
3. Prompt的数学本质
从模型角度看,大语言模型始终在做一件事:
P ( 输出 ∣ 输入 ) P(\text{输出} \mid \text{输入}) P(输出∣输入)
这里的"输入",就是 Prompt。
也就是说,Prompt 决定了条件概率的范围。不同的输入,会让模型在不同的"可能答案空间"中进行选择。
因此可以理解为:
Prompt 并不是描述问题,而是在限定答案的生成路径。
4. Prompt如何影响模型行为
Prompt 的作用,不只是提供信息,更是在约束模型。
首先,它决定模型如何理解任务。模型需要通过输入判断你是在做解释、分类,还是生成内容。如果表达不清,模型就会走偏。
其次,它会影响输出形式。是否需要结构化表达,是否需要代码,是否需要分段,这些都可以通过 Prompt 明确。
更关键的是,它会影响推理方式。不同的表达,会让模型走不同的推理路径,从而得到完全不同的结果。
换句话说:
Prompt 的本质,是在不断缩小模型的"自由度"。
5. Prompt为什么可以"变强"
随着模型规模的提升,一个重要现象开始出现:
模型本身已经具备大量潜在能力,但这些能力是否能被激发,取决于输入方式。
当 Prompt 设计得足够清晰时,模型更容易进入正确的输出轨道;当 Prompt 模糊时,模型会在多个可能方向中摇摆。
因此,一个好的 Prompt,其实是在做一件事情:
让模型"少走弯路"。
6. Chain-of-Thought(思维链)
在 Prompt 设计中,有一个非常重要的技巧,叫做 Chain-of-Thought。
它的核心思路不是让模型直接给出答案,而是引导模型一步一步展开推理。
例如一个简单问题:
23 × 17 等于多少
模型可能直接给出结果,但不一定稳定。
如果改成:
请一步一步计算 23 × 17,并给出中间过程
模型会先拆分问题,再逐步计算,最后得到结果。
这种方式的关键在于:
模型被引导进入"推理模式",而不是"猜答案模式"。
在复杂任务中,这种方法往往能显著提升准确率。
7. 一个实际应用变化
来看一个更贴近实际的例子。
任务是生成健康建议。
如果输入:
给我一些健康建议
模型通常会给出比较泛泛的回答,内容缺乏针对性。
如果改为:
请针对大学生作息,生成5条具体可执行的健康建议,每条不少于20字,并包含运动和饮食两个方面
输出会明显发生变化,建议更具体,结构更清晰,也更容易落地。
这个变化并不是模型变强了,而是输入更精确了。
8. Prompt在产品中的作用
在实际产品开发中,Prompt 并不是一个"辅助工具",而是核心能力的一部分。
它直接影响:
输出是否稳定,结果是否符合预期,以及用户体验是否一致。
在很多 AI 产品中,功能的好坏并不取决于模型大小,而取决于 Prompt 设计是否合理。
因此可以说:
Prompt 是连接"模型能力"和"用户需求"的关键桥梁。
9. 一个简单代码示例
下面是一个基础调用示例,可以直观看到 Prompt 的作用:
python
from openai import OpenAI
client = OpenAI()
prompt = "请用通俗语言解释什么是Transformer,并用一个生活中的例子说明"
response = client.chat.completions.create(
model="gpt-4.1-mini",
messages=[
{"role": "user", "content": prompt}
]
)
print(response.choices[0].message.content)
只需要修改 prompt 的内容,就可以明显改变模型输出的风格和结构。
10. 本节核心总结
Prompt Engineering 的本质,不是提问技巧,而是输入设计能力。
它通过改变输入,控制模型的输出范围、表达方式和推理路径,从而让模型更稳定地完成任务。
可以用一句话概括:
Prompt 是对模型行为的显式控制方式。
12.4 Fine-tuning(模型微调)
1. 为什么需要Fine-tuning
在很多场景下,仅仅通过 Prompt 已经可以完成大部分任务。但当需求变得更复杂时,会出现一个问题:
模型"能理解",但"不稳定"。
例如:
同样的输入,有时候回答很好,有时候偏离目标;
或者输出风格不统一,难以在产品中长期使用。
这时就会发现一个限制:
Prompt 只能"引导模型",但不能从根本上改变模型能力。
因此需要一种更深层的方法,让模型在特定任务上表现更稳定,这就是 Fine-tuning。
2. Fine-tuning在做什么
Fine-tuning 的本质,是在已有大模型基础上,继续用特定数据进行训练。
它不是从头训练模型,而是:
在已有能力之上,进行"定向强化"。
可以这样理解:
大模型已经学会了"语言",
而 Fine-tuning 是让它学会"特定领域的表达方式"。
例如:
- 医疗问答
- 法律文本分析
- 产品客服回复
通过针对性数据训练后,模型会更倾向输出符合该领域风格的内容。
3. 数学本质
Fine-tuning 本质上仍然是在优化模型参数,使预测更符合目标数据分布:
θ ∗ = arg max θ ∑ ( x , y ) log P θ ( y ∣ x ) \theta^* = \arg\max_\theta \sum_{(x,y)} \log P_\theta(y \mid x) θ∗=argθmax(x,y)∑logPθ(y∣x)
这个公式表示:
通过不断调整参数,使模型在输入 (x) 时,更容易输出目标结果 (y)。
换句话说:
模型原本的概率分布被"重新拉向"特定任务。
4. 和Prompt的区别
Prompt 和 Fine-tuning 解决的是两个不同层面的问题。
Prompt 是在输入层控制模型行为,它不改变模型本身,只是引导输出方向。
Fine-tuning 是在模型层改变参数,使模型在特定任务上形成"偏好"。
因此可以这样理解:
Prompt 是"短期控制",
Fine-tuning 是"长期改变"。
在实际应用中,两者往往结合使用。
5. Fine-tuning带来的变化
经过 Fine-tuning 后,模型会发生几个明显变化。
首先,输出更加稳定。同样输入,不会出现大幅波动。
其次,风格更加统一。模型会倾向于使用训练数据中的表达方式。
更重要的是,对特定任务的理解更强。例如客服场景中,模型会更准确识别用户意图。
这些变化,使模型更适合在真实产品中使用。
6. 一个实际应用场景
以智能客服为例。
如果只用 Prompt:
模型可能回答得不错,但风格不统一,有时偏离业务规范。
如果使用 Fine-tuning:
可以用历史客服对话数据进行训练,使模型输出更加符合企业要求。
例如:
- 语气统一
- 回答规范
- 避免不合适表达
这样模型就从"通用助手",变成"专用客服"。
7. 一个简单微调示意代码
下面是一个简化的训练示例,用于理解 Fine-tuning 的基本流程:
python
import torch
import torch.nn as nn
import torch.optim as optim
# 简化模型
model = nn.Linear(10, 2)
# 假数据(输入x,对应标签y)
x = torch.randn(100, 10)
y = torch.randint(0, 2, (100,))
# 损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练过程
for epoch in range(10):
outputs = model(x)
loss = criterion(outputs, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f"Epoch {epoch}, Loss: {loss.item():.4f}")
这个过程本质上就是:
不断调整模型参数,让输出更接近目标结果。
8. Fine-tuning的局限
虽然 Fine-tuning 很强,但也存在一些问题。
首先,成本较高。需要数据、算力和时间。
其次,数据质量非常关键。如果数据不好,模型反而会变差。
另外,一旦微调完成,模型会更偏向某个任务,通用能力可能下降。
因此在实际使用中,需要权衡是否真的需要微调。
9. 什么时候该用Fine-tuning
当出现以下情况时,通常需要考虑使用 Fine-tuning:
模型输出不稳定,难以通过 Prompt 修复;
需要长期保持固定风格;
任务具有明显领域特征,例如医疗、金融等。
如果只是简单优化输出格式,一般使用 Prompt 就足够。
10. 本节核心总结
Fine-tuning 的核心,是通过调整模型参数,让模型在特定任务上表现更稳定、更符合需求。
它不是替代 Prompt,而是对 Prompt 的补充。
可以用一句话概括:
Fine-tuning 是对模型能力的定向强化。
第13章 大模型训练技术
13.1 RLHF(人类反馈强化学习)
1. RLHF在解决什么问题
当模型规模越来越大时,会出现一个明显问题:
模型"会说话",但不一定"说得对"。
例如:
回答可能逻辑通顺,但不符合人类习惯;
或者语气不合适,甚至出现偏差内容。
这说明一个问题:
模型优化的是"概率正确",而不是"人类满意"。
RLHF 的出现,就是为了弥补这一点。
2. RLHF在做什么
RLHF 的核心思想是:
让模型的输出,更符合人类偏好。
它不是单纯依赖数据训练,而是引入"人类评价"。
可以这样理解:
模型先生成多个答案,然后由人类判断哪个更好,
再用这些评价去反向优化模型。
本质上是:
把"人类标准"加入训练过程。
3. 数学本质
RLHF 可以看作是在最大化奖励函数:
max θ E y ∼ P θ ( ⋅ ∣ x ) [ R ( x , y ) ] \max_\theta \mathbb{E}{y \sim P\theta(\cdot \mid x)} [R(x, y)] θmaxEy∼Pθ(⋅∣x)[R(x,y)]
这里的 (R(x, y)) 表示人类对结果的评分。
也就是说:
模型不再只追求概率最大,而是追求"评分最高"。
4. RLHF的三步流程
整个过程可以理解为三个阶段。
首先是监督微调,让模型具备基础能力;
然后训练奖励模型,用来模拟人类打分;
最后通过强化学习,让模型不断优化输出。
这三个步骤结合起来,使模型逐渐向"人类偏好"靠近。
5. 一个直观例子
假设模型回答问题:
如何提高学习效率
模型可能给出多个版本。
人类会选择:
更清晰、更具体、更有逻辑的那个。
经过大量这样的反馈,模型就会逐渐学会:
什么样的回答更"好"。
6. RLHF带来的变化
引入 RLHF 后,模型会发生明显变化。
回答更自然,更符合人类表达习惯;
逻辑更清晰,更有层次;
不合适内容减少。
这些变化,使模型从"会说话"变成"会交流"。
7. 简单强化学习示意代码
下面是一个简化示例,用来理解强化学习更新过程:
python
import torch
# 假设模型输出概率
log_probs = torch.tensor([0.2, 0.5, 0.3], requires_grad=True)
# 假设奖励(人类反馈)
reward = torch.tensor([1.0, 2.0, 0.5])
# 强化学习目标(简单形式)
loss = - (log_probs * reward).mean()
loss.backward()
print("梯度:", log_probs.grad)
这个过程本质上是在:
让高奖励的输出更容易被模型选择。
8. RLHF的意义
RLHF 的最大价值在于:
它让模型从"数据驱动"转变为"人类驱动"。
模型不再只是模仿文本,而是逐渐学会:
什么是更符合人类期望的表达。
9. 本节核心总结
RLHF 的核心可以总结为:
通过引入人类反馈,让模型输出更加符合人类标准。
可以用一句话概括:
RLHF = 用人类偏好重新塑造模型行为。
13.2 LoRA / PEFT
1. 为什么需要高效微调
在实际应用中,Fine-tuning 虽然有效,但会遇到一个非常现实的问题:
成本太高。
大模型参数规模通常在亿级甚至百亿级,如果对整个模型进行微调,需要:
大量显存、较长训练时间,以及较高的工程复杂度。
更重要的是,每一个任务都单独微调一份完整模型,在实际系统中几乎不可维护。
因此就产生了一个核心需求:
能不能只改一小部分参数,就达到类似效果?
这就是 LoRA 和 PEFT 出现的背景。
2. PEFT在做什么
PEFT(Parameter Efficient Fine-Tuning)的核心思想是:
不改变大模型主体,只调整少量参数。
可以理解为:
模型的大部分能力已经足够强,不需要重新学习,只需要在关键位置进行轻量调整。
这种方式有两个直接好处:
一是训练成本大幅下降,二是可以快速适配多个任务。
因此在工业界中,PEFT 已经成为主流方案。
3. LoRA的核心思想
LoRA 是 PEFT 中最常用的一种方法,它的思路非常巧妙。
在原始模型中,一个线性层通常表示为:
W x W x Wx
LoRA 并不会直接修改 (W),而是引入一个"增量矩阵":
W ′ = W + A B W' = W + A B W′=W+AB
其中 (A) 和 (B) 是低秩矩阵,维度远小于原始参数。
这意味着:
模型的大部分参数保持不变,只额外学习一个小的"调整项"。
从效果上看,相当于对模型进行微调;
但从计算上看,成本却大幅降低。
4. 为什么LoRA有效
LoRA 能起作用的关键在于一个假设:
模型的更新不需要覆盖整个参数空间。
很多任务的差异,其实只需要在少量方向上进行调整,就可以完成适配。
低秩矩阵 (A B) 正好提供了这种"有限调整能力"。
可以这样理解:
原模型已经掌握了通用知识,而 LoRA 只是在某些方向上进行"微调校正"。
5. 一个直观理解
可以把大模型想象成一个已经写好的系统。
传统 Fine-tuning 是:
直接修改整个系统代码。
而 LoRA 更像是:
在系统外加一个"插件",通过少量修改改变整体行为。
这种方式不仅更轻量,而且更安全,不会破坏原始能力。
6. LoRA带来的优势
使用 LoRA 后,会带来几个明显优势。
训练成本显著降低,因为只需要训练小规模参数;
显存占用减少,可以在普通设备上运行;
模型复用性增强,可以针对不同任务加载不同 LoRA 权重。
这使得一个基础模型可以支持多个应用场景,而不需要重复训练。
7. 一个简单LoRA示意代码
下面是一个简化版本,用来理解 LoRA 的结构:
python
import torch
import torch.nn as nn
class LoRALinear(nn.Module):
def __init__(self, in_features, out_features, rank=2):
super().__init__()
# 原始权重(冻结)
self.weight = nn.Linear(in_features, out_features)
for param in self.weight.parameters():
param.requires_grad = False
# LoRA参数
self.A = nn.Linear(in_features, rank, bias=False)
self.B = nn.Linear(rank, out_features, bias=False)
def forward(self, x):
original = self.weight(x)
lora_update = self.B(self.A(x))
return original + lora_update
# 测试
x = torch.randn(4, 10)
model = LoRALinear(10, 5)
out = model(x)
print("输出形状:", out.shape)
这个结构体现了核心思想:
原始模型不动,只叠加一个可训练的低秩变化。
8. LoRA的应用场景
在实际项目中,LoRA 非常适合以下场景。
当需要快速适配多个业务任务时,可以为每个任务训练独立 LoRA;
当算力资源有限时,可以在较低成本下完成微调;
当需要保持基础模型能力时,可以避免过度修改模型参数。
因此,它在 AI 产品开发中非常常见。
9. 本节核心总结
LoRA 和 PEFT 的核心思想,是用最小的参数调整,实现最大的效果变化。
它不追求"重新训练模型",而是:
在已有能力基础上进行轻量优化。
可以用一句话概括:
LoRA = 用低成本实现高效微调。
13.3 模型蒸馏
继续保持完全一致风格👇
第13章 大模型训练技术
13.3 模型蒸馏(Model Distillation)
1. 为什么需要模型蒸馏
大模型的能力很强,但在实际使用中会遇到一个现实问题:
太大、太慢、成本太高。
例如,在服务器上运行还可以接受,但如果要部署到移动端、网页端,或者需要高并发服务时,大模型的计算开销会变得不可承受。
这就带来一个核心问题:
能不能保留大模型的能力,同时让模型变小、变快?
模型蒸馏正是为了解决这个问题。
2. 模型蒸馏在做什么
模型蒸馏的核心思想是:
用一个大模型去"教"一个小模型。
大模型被称为 Teacher(教师模型),
小模型被称为 Student(学生模型)。
训练时,小模型不仅学习真实数据,还会学习大模型的输出结果。
可以理解为:
不是直接学答案,而是学"老师是怎么判断的"。
3. 数学本质
蒸馏的核心,是让学生模型去拟合教师模型的输出分布:
latex
L = \alpha L_{\text{真实标签}} + (1 - \alpha) L_{\text{蒸馏}}
其中蒸馏损失通常表示为:
latex
L_{\text{蒸馏}} = KL(p_{\text{teacher}} \parallel p_{\text{student}})
这表示:
学生模型不仅要接近真实答案,还要接近教师模型的"概率分布"。
这种分布中包含了更多信息,例如哪些选项更接近正确答案。
4. 为什么蒸馏有效
传统训练中,模型只知道"正确答案是什么"。
但在蒸馏中,模型还能学到:
不同类别之间的"相似程度"。
例如一个分类任务中:
正确答案是"猫",但教师模型可能给出:
- 猫:0.8
- 狗:0.15
- 车:0.05
学生模型通过学习这个分布,可以理解:
"狗"比"车"更接近"猫"。
这种信息,被称为"软标签",它比单一标签更有价值。
5. 一个直观理解
可以把蒸馏理解为学习方式的变化。
传统训练更像是:
老师只告诉你答案对不对。
而蒸馏更像是:
老师告诉你为什么这个答案更合理,以及其他选项差在哪里。
因此学生模型不仅学结果,还学判断过程。
6. 蒸馏带来的变化
经过蒸馏后,小模型通常会表现出明显提升。
相比直接训练的小模型,它的效果更接近大模型;
推理速度更快,资源消耗更低;
在实际部署中更加灵活。
这种方式,使得"强能力 + 低成本"成为可能。
7. 一个简单蒸馏示意代码
下面是一个简化版本,用来理解蒸馏过程:
python
import torch
import torch.nn.functional as F
# teacher输出(已经训练好)
teacher_logits = torch.tensor([[2.0, 1.0, 0.1]])
# student输出
student_logits = torch.tensor([[1.5, 0.8, 0.3]], requires_grad=True)
# 转为概率分布
teacher_probs = F.softmax(teacher_logits, dim=1)
student_log_probs = F.log_softmax(student_logits, dim=1)
# KL散度(蒸馏损失)
loss = F.kl_div(student_log_probs, teacher_probs, reduction='batchmean')
loss.backward()
print("梯度:", student_logits.grad)
这个过程的核心在于:
学生模型通过模仿教师模型的输出分布,不断逼近其能力。
8. 蒸馏的应用场景
在实际项目中,模型蒸馏非常常见。
当需要部署到移动端或边缘设备时,可以用蒸馏得到轻量模型;
在高并发服务中,可以用小模型替代大模型以降低成本;
在多模型系统中,可以用蒸馏统一模型表现。
因此,它是从"研究模型"走向"工程落地"的关键技术之一。
9. 本节核心总结
模型蒸馏的核心思想,是通过教师模型指导学生模型训练,使小模型获得接近大模型的能力。
它实现了一个重要目标:
在降低计算成本的同时,尽量保持性能。
可以用一句话概括:
模型蒸馏 = 用大模型指导小模型学习。
至此,这一部分完成了核心闭环:
- RLHF 解决"符合人类"
- LoRA 解决"高效微调"
- 蒸馏解决"模型变小"
好,这一部分我给你做一个真正"能写进博客/申报书"的总结:
- 不罗列碎点
- 有结构但不死板
- 有概念、有逻辑、有理解
直接可用👇
系列三总结:Transformer与大模型(第10章---第13章)
1. 从Attention到理解与生成
从第10章开始,整个体系的核心建立在一个关键思想上:
模型不再"逐个处理信息",而是"动态关注重要信息"。
Attention 机制本质上是在做一件事:
在当前任务中,判断"哪些信息更重要"。
Scaled Dot-Product Attention 通过相似度计算,让模型自动选择关注对象;
Multi-Head Attention 则进一步扩展,让模型可以从多个角度同时理解信息,例如语义关系、位置关系和上下文关联。
这一阶段的关键变化在于:
模型从"固定计算流程",转变为"动态信息选择"。
2. Transformer:统一的建模框架
第11章将 Attention 上升为完整结构,形成 Transformer。
Transformer 的核心突破,不在于某一个公式,而在于整体设计:
它完全抛弃了传统的 RNN 顺序结构,转而使用 Attention 实现全局建模。
Position Encoding 的引入,使模型在没有顺序结构的情况下,仍然可以理解"先后关系";
Encoder-Decoder 结构,则分别承担"理解"和"生成"的角色。
这一阶段的本质是:
把"信息选择机制"升级为"统一建模框架"。
Transformer 不再是一个技巧,而成为后续所有大模型的基础架构。
3. GPT与BERT:两条分化路径
进入第12章后,模型开始沿着两个方向发展:
一个是生成,一个是理解。
GPT 采用单向结构,通过自回归方式逐步生成文本,本质是连续的概率预测过程;
BERT 则采用双向结构,通过 Mask 训练,实现对上下文的整体理解。
两者的差异,并不是能力强弱,而是目标不同:
GPT 关注"接下来写什么",
BERT 关注"这句话是什么意思"。
这一阶段的关键认知是:
模型能力由训练目标决定,而不是结构本身。
4. Prompt:从"用模型"到"控制模型"
随着大模型能力增强,一个重要变化出现:
模型本身已经很强,但如何使用它,变得更加关键。
Prompt Engineering 的出现,使人可以通过输入设计,直接影响模型输出。
模型不再只是工具,而变成一种"可被引导的系统"。
不同的 Prompt,可以改变任务理解、输出格式,甚至推理路径。
这一阶段的核心转变是:
人与模型的关系,从"调用工具"变为"控制行为"。
5. Fine-tuning与参数控制
当 Prompt 无法满足需求时,就需要进一步深入模型层。
Fine-tuning 通过调整模型参数,使其在特定任务上更加稳定;
LoRA 和 PEFT 则进一步优化,使这种调整变得高效、低成本。
这一阶段解决的是:
如何在不破坏原有能力的情况下,让模型适应具体应用。
可以理解为:
从"临时控制"走向"长期能力塑造"。
6. RLHF:引入人类标准
在模型能力不断增强后,一个新的问题出现:
模型的输出是否符合人类预期。
RLHF 的核心,是把"人类评价"引入训练过程,使模型优化目标从"数据概率"转向"人类偏好"。
这一阶段的关键变化在于:
模型不再只是模仿数据,而开始对齐人类标准。
7. 模型蒸馏:走向实际落地
当模型能力、控制方式都具备之后,最后一个问题是:
如何真正落地使用。
模型蒸馏通过"教师---学生"机制,使小模型获得接近大模型的能力,从而降低计算成本,提高部署效率。
这一阶段解决的是:
从"能用"走向"可规模化使用"。
8. 整体核心逻辑
从第10章到第13章,其实形成了一条非常清晰的技术演进路径:
从 Attention 的信息选择机制出发,构建 Transformer 框架;
在此基础上分化出 GPT(生成)与 BERT(理解);
再通过 Prompt 实现对模型行为的直接控制;
进一步通过 Fine-tuning 和 LoRA 调整模型能力;
引入 RLHF 使模型符合人类偏好;
最后通过蒸馏实现工程落地。
这条路径可以总结为一个闭环:
建模能力 → 行为控制 → 能力优化 → 人类对齐 → 工程落地
9. 一句话总总结
整个系列可以用一句话概括:
Transformer提供结构,GPT/BERT定义能力,Prompt负责控制,Fine-tuning与RLHF优化行为,蒸馏实现落地。
模型通常会表现出明显提升。
相比直接训练的小模型,它的效果更接近大模型;
推理速度更快,资源消耗更低;
在实际部署中更加灵活。
这种方式,使得"强能力 + 低成本"成为可能。
7. 一个简单蒸馏示意代码
下面是一个简化版本,用来理解蒸馏过程:
python
import torch
import torch.nn.functional as F
# teacher输出(已经训练好)
teacher_logits = torch.tensor([[2.0, 1.0, 0.1]])
# student输出
student_logits = torch.tensor([[1.5, 0.8, 0.3]], requires_grad=True)
# 转为概率分布
teacher_probs = F.softmax(teacher_logits, dim=1)
student_log_probs = F.log_softmax(student_logits, dim=1)
# KL散度(蒸馏损失)
loss = F.kl_div(student_log_probs, teacher_probs, reduction='batchmean')
loss.backward()
print("梯度:", student_logits.grad)
这个过程的核心在于:
学生模型通过模仿教师模型的输出分布,不断逼近其能力。
8. 蒸馏的应用场景
在实际项目中,模型蒸馏非常常见。
当需要部署到移动端或边缘设备时,可以用蒸馏得到轻量模型;
在高并发服务中,可以用小模型替代大模型以降低成本;
在多模型系统中,可以用蒸馏统一模型表现。
因此,它是从"研究模型"走向"工程落地"的关键技术之一。
9. 本节核心总结
模型蒸馏的核心思想,是通过教师模型指导学生模型训练,使小模型获得接近大模型的能力。
它实现了一个重要目标:
在降低计算成本的同时,尽量保持性能。
可以用一句话概括:
模型蒸馏 = 用大模型指导小模型学习。
至此,这一部分完成了核心闭环:
- RLHF 解决"符合人类"
- LoRA 解决"高效微调"
- 蒸馏解决"模型变小"
好,这一部分我给你做一个真正"能写进博客/申报书"的总结:
- 不罗列碎点
- 有结构但不死板
- 有概念、有逻辑、有理解
直接可用👇
系列三总结:Transformer与大模型(第10章---第13章)
1. 从Attention到理解与生成
从第10章开始,整个体系的核心建立在一个关键思想上:
模型不再"逐个处理信息",而是"动态关注重要信息"。
Attention 机制本质上是在做一件事:
在当前任务中,判断"哪些信息更重要"。
Scaled Dot-Product Attention 通过相似度计算,让模型自动选择关注对象;
Multi-Head Attention 则进一步扩展,让模型可以从多个角度同时理解信息,例如语义关系、位置关系和上下文关联。
这一阶段的关键变化在于:
模型从"固定计算流程",转变为"动态信息选择"。
2. Transformer:统一的建模框架
第11章将 Attention 上升为完整结构,形成 Transformer。
Transformer 的核心突破,不在于某一个公式,而在于整体设计:
它完全抛弃了传统的 RNN 顺序结构,转而使用 Attention 实现全局建模。
Position Encoding 的引入,使模型在没有顺序结构的情况下,仍然可以理解"先后关系";
Encoder-Decoder 结构,则分别承担"理解"和"生成"的角色。
这一阶段的本质是:
把"信息选择机制"升级为"统一建模框架"。
Transformer 不再是一个技巧,而成为后续所有大模型的基础架构。
3. GPT与BERT:两条分化路径
进入第12章后,模型开始沿着两个方向发展:
一个是生成,一个是理解。
GPT 采用单向结构,通过自回归方式逐步生成文本,本质是连续的概率预测过程;
BERT 则采用双向结构,通过 Mask 训练,实现对上下文的整体理解。
两者的差异,并不是能力强弱,而是目标不同:
GPT 关注"接下来写什么",
BERT 关注"这句话是什么意思"。
这一阶段的关键认知是:
模型能力由训练目标决定,而不是结构本身。
4. Prompt:从"用模型"到"控制模型"
随着大模型能力增强,一个重要变化出现:
模型本身已经很强,但如何使用它,变得更加关键。
Prompt Engineering 的出现,使人可以通过输入设计,直接影响模型输出。
模型不再只是工具,而变成一种"可被引导的系统"。
不同的 Prompt,可以改变任务理解、输出格式,甚至推理路径。
这一阶段的核心转变是:
人与模型的关系,从"调用工具"变为"控制行为"。
5. Fine-tuning与参数控制
当 Prompt 无法满足需求时,就需要进一步深入模型层。
Fine-tuning 通过调整模型参数,使其在特定任务上更加稳定;
LoRA 和 PEFT 则进一步优化,使这种调整变得高效、低成本。
这一阶段解决的是:
如何在不破坏原有能力的情况下,让模型适应具体应用。
可以理解为:
从"临时控制"走向"长期能力塑造"。
6. RLHF:引入人类标准
在模型能力不断增强后,一个新的问题出现:
模型的输出是否符合人类预期。
RLHF 的核心,是把"人类评价"引入训练过程,使模型优化目标从"数据概率"转向"人类偏好"。
这一阶段的关键变化在于:
模型不再只是模仿数据,而开始对齐人类标准。
7. 模型蒸馏:走向实际落地
当模型能力、控制方式都具备之后,最后一个问题是:
如何真正落地使用。
模型蒸馏通过"教师---学生"机制,使小模型获得接近大模型的能力,从而降低计算成本,提高部署效率。
这一阶段解决的是:
从"能用"走向"可规模化使用"。
8. 整体核心逻辑
从第10章到第13章,其实形成了一条非常清晰的技术演进路径:
从 Attention 的信息选择机制出发,构建 Transformer 框架;
在此基础上分化出 GPT(生成)与 BERT(理解);
再通过 Prompt 实现对模型行为的直接控制;
进一步通过 Fine-tuning 和 LoRA 调整模型能力;
引入 RLHF 使模型符合人类偏好;
最后通过蒸馏实现工程落地。
这条路径可以总结为一个闭环:
建模能力 → 行为控制 → 能力优化 → 人类对齐 → 工程落地
9. 一句话总总结
整个系列可以用一句话概括:
Transformer提供结构,GPT/BERT定义能力,Prompt负责控制,Fine-tuning与RLHF优化行为,蒸馏实现落地。