预测「明天穿不穿外套」
假设输入只有两个数(已经是你手工算好的特征):
x1x1:今天最高气温(摄氏度)
x2x2:降雨概率(0~1)
模型极简:穿外套分数 = w1 * x1 + w2 * x2 + b
这里 (w1, w2, b) 就是三个参数(三个浮点数)。
它们背后的含义可以很直白:
w1w1 通常是负数:越热越不想穿外套。
w2w2 通常是正数:越容易下雨越想穿。
bb:整体偏「穿」还是「不穿」。
训练就是用很多天的真实数据,把 (w1,w2,b) 调到让预测和历史选择尽量一致。
这时每个浮点数有一点「可讲故事」的含义(和特征挂钩)。
稍微真一点:nn.Linear 里每个数是什么意思?
一层:y = x @ W + b
输入向量 xx:比如 4 个数,表示 4 个特征。
WW 里第 ii 行第 jj 列那个浮点数:第 (j) 个输入对第 (i) 个输出贡献多少权重。
bb 里第 ii 个数:第 (i) 个输出的偏置。
所以:单个浮点 =「某个输入通道对某个输出通道的影响力系数」(在纯线性层里相对最好讲)。
大模型里层数深、还有非线性,单独一个权重的人话解释会变弱,但本质仍是:这些数在定义「怎么把上一层的表示混合成下一层」。
3. 词 embedding:每个浮点「像什么」?
词表里的「猫」可能对应一条 4096 维的向量,4096 个浮点。
严格说:第 37 维等于 0.12 一般不对应「可爱度 0.12」这种固定标签;维度是学出来的,常不可逐个命名。
整体说:整条向量在空间里把「猫」放在和狗近、和汽车远的位置,语义靠相对位置体现。
所以这类浮点:单个维度多半不讲故事;整段向量表示「这个词在语义空间里的位置」。
4. 和 LLM 的关系(一句话)
LLM 里几亿个浮点参数,大部分是 attention / FFN 里的权重,含义类似:
- 「当前这一层,该把哪些过去的 token、哪些通道上的信号,以多大比例混在一起」。
仍然是系数,只是从「两个特征」变成了「几千维 × 很多层 × 很多头」,人脑无法逐个解释,但不是玄学:每个都是参与矩阵运算的实数系数。
5. 帮你收一句
张量:装这些浮点的盒子(带形状)。
浮点(参数):在简单模型里常像 (w1,w2) 那种「影响力」;在深层网络里多数是 「怎么混合、传递、过滤信息」的系数。
不抽象的例子:气温 + 降雨概率 → 穿不穿外套 那三个数,就是最小的「参数即浮点、且有直观含义」的样板。
若你愿意,可以再问:「那训练是怎么知道该把 0.12 改成 0.11 的?」 那就接 loss + 梯度 用同一套「外套」例子讲一步。
语言模型 (Language Model, LM):计算一个 词序列*(一个句子)*出现的 概率。
大语言模型 (Large Language Model, LLM):由语言模型与统计学逐步演进到 Transformer 架构而诞生的。
语言模型 (Language Model, LM):计算一个 词序列(一个句子)出现的 概率。
大语言模型 (Large Language Model, LLM):由语言模型与统计学逐步演进到 Transformer 架构而诞生的。
**1.**N-gram 模型
在深度学习兴起前,统计是主流
相关信息
核心思想: 算出一个句子出现的概率,等于该句子中 每个词 出现的条件概率的连乘。
概率的链式法则:
但直接计算上述公式几乎是不可能的:
- 每个词的条件概率没法算
- 词可能从未在训练数据中出现过
因此提出了 马尔可夫假设 (Markov Assumption)
相关信息
核心思想: 不必回溯一个词的全部历史,可以近似认为,一个词的出现概率只与它前面有限的 n−1 个词有关
即:只看最近的,不看全部历史
相当于 将链式法则缩小了范围:
1.1 N-gram 模型是什么
基于这个假设建立的语言模型称之为 N-gram 模型。
- "N" 代表范围(上下文窗口大小)。让我们来看几个最常见的例子来理解这个概念:
假设你在预测下一个词。没有马尔可夫假设时,你需要考虑从第一个词到当前词的全部历史,这很复杂。
马尔可夫假设说:下一个词只和最近的几个词有关,不用看更早的历史。
用生活例子理解
若没有马尔可夫假设(复杂):
- 预测"今天吃什么",需要知道:昨天吃了什么、前天吃了什么、上周吃了什么、上个月吃了什么...
有马尔可夫假设(简单):
- 预测"今天吃什么",只需要知道:昨天吃了什么(只看最近1个)
在语言模型中的应用
Bigram(二元模型,N=2)
只看前1个词:
预测"学习"的概率,只看前一个词"agent"
公式:P(学习 | agent) = 在"agent"后面出现"学习"的次数 / "agent"出现的总次数
Trigram(三元模型,N=3)
只看前2个词:
预测"学习"的概率,看前两个词"datawhale agent"
公式:P(学习 | datawhale agent) = 在"datawhale agent"后面出现"学习"的次数 / "datawhale agent"出现的总次数
为什么需要这个假设?
问题: 如果考虑所有历史,计算量太大,数据也不够。
解决: 马尔可夫假设把问题简化,只考虑最近的几个词,计算更可行,也能用有限数据训练。
简单总结
马尔可夫假设 = "只看最近的,忽略更早的"
就像预测天气:通常只看最近几天的,而不是看过去一年的每一天
就像预测下一个词:通常只看前面1-2个词,而不是看整句话的开头
这样既简化了计算,又保留了大部分有用信息。
N-gram 模型的根本缺陷
问题:词被当作“孤立的符号”
在 N-gram 模型中,每个词只是一个独立的符号,没有语义关系。
举个例子:
"苹果" 和 "水果" 在 N-gram 看来是完全不同的符号
"学习" 和 "study" 也是完全不同的符号
模型不知道它们之间有任何关系
就像:
把每个词当作一个独立的"身份证号"
身份证号之间没有关系,即使"苹果"和"水果"语义相关,模型也看不到
解决方案:用向量表示词
核心思想:把词变成“坐标点”
把词映射到高维空间中的点(向量),语义相近的词在空间中距离更近。
生活化理解:
想象一个“语义地图”:
每个词是地图上的一个点
相近的词靠得近,不相关的词离得远
例如:
"agent" 和 "robot" 靠得很近(都是“代理/机器人”)
"agent" 和 "apple" 离得很远(语义无关)
为什么用向量?
向量可以表示位置和方向
可以计算距离和相似度
可以捕捉语义关系
词嵌入(Word Embedding)是什么?
简单说: 词嵌入就是词的“向量身份证”
传统方式:词 = "agent"(字符串)
词嵌入方式:词 = [0.2, -0.5, 0.8, ..., 0.1](一个数字向量)
关键特点:
语义相近的词,向量也相近
向量是连续的,可以计算相似度
向量维度通常很高(如 100、300、768 维)
前馈神经网络语言模型的两步
第一步:构建语义空间(词嵌入层)
做什么:
创建一个高维空间(如 300 维)
把每个词映射成这个空间中的一个向量
让语义相近的词在空间中靠近
类比:
就像给每个词分配一个“地址”
相关词的地址在附近,不相关的词地址离得远
第二步:学习映射函数(神经网络层)
做什么:
输入:前 n-1 个词的词向量
输出:下一个词的概率分布(每个词出现的概率)
类比:
输入:前几个词的“地址”
输出:下一个词最可能是哪个(及其概率)
神经网络的作用:
学习从上下文到下一个词的映射规律
通过大量数据训练,找到这个规律
完整流程示例
假设要预测句子 "datawhale agent [下一个词]"
N-gram 方式(旧方法):
1. 统计 "agent" 后面出现过哪些词
2. 计算每个词出现的频率
3. 选择频率最高的词
❌ 问题:不知道 "agent" 和 "robot" 有什么关系
神经网络方式(新方法):
1. 把 "datawhale" 和 "agent" 转换成向量
- "datawhale" → [0.1, 0.3, -0.2, ...]
- "agent" → [0.5, -0.1, 0.8, ...]
2. 把这两个向量输入神经网络
3. 神经网络经过计算,输出所有词的概率:
- "learns": 0.35
- "works": 0.25
- "runs": 0.15
- ... 其他词的概率
4. 选择概率最高的词作为预测结果
✅ 优势:模型能理解语义关系,因为相近的词向量也相近
为什么这样更好?
| 方面 | N-gram | 神经网络语言模型 |
|---|---|---|
| 词表示 | 离散符号(字符串) | 连续向量(数字) |
| 语义理解 | ❌ 无法理解 | ✅ 可以理解 |
| 泛化能力 | ❌ 只能记住见过的 | ✅ 可以泛化到相似词 |
| 数据需求 | 需要大量数据统计 | 通过训练学习规律 |
简单总结
N-gram 的问题:把词当作孤立符号,看不到语义关系
解决方案:用向量表示词,让语义相近的词在空间中靠近
词嵌入:词的向量表示,是词的“语义坐标”
神经网络:学习从上下文向量到下一个词概率的映射
核心思想: 从“统计频率”转向“学习语义”,让模型理解词的含义,而不只是记住组合。
1. 什么叫"拟合"?
简单理解:找规律
简单理解:找规律
生活例子:
你观察:下雨时温度下降,晴天时温度上升
你总结:温度 ≈ 30 - 雨量×2
这就是“拟合”:从数据中找到规律,用公式描述
在神经网络中:
输入:前 n-1 个词
输出:下一个词的概率
拟合:通过大量句子,让网络自动找到从输入到输出的规律
类比:
就像教小孩学语言,给他看很多句子,他慢慢学会“什么词后面通常跟什么词”
神经网络也是通过看大量数据,自动学会这个规律
2. 词向量是怎么来的?不是人工设计的
关键:词向量是自动学习出来的,不是人工标注的
错误理解:
人工定义 "agent" = [0.5, 0.2, ...]
人工定义 "robot" = [0.6, 0.3, ...](让它靠近 agent)
实际情况:
词向量是训练过程中自动生成的
初始是随机值,通过训练逐步调整
具体过程(简化版)
第一步:随机初始化
开始时,所有词的向量都是随机生成的:
"agent" → [0.1, -0.3, 0.7, ...] (随机数字)
"robot" → [-0.2, 0.5, -0.1, ...] (随机数字)
"apple" → [0.4, 0.1, -0.5, ...] (随机数字)
此时这些向量没有意义,只是随机数字。
第二步:通过训练自动调整
神经网络看大量句子,比如:
- "The agent works"
- "The robot works"
- "I eat an apple"
神经网络发现:
- "agent" 和 "robot" 经常出现在相似的上下文中
- 所以它们的向量应该靠近一些
- 自动调整向量,让它们更接近
经过大量训练后:
"agent" → [0.5, -0.1, 0.8, ...]
"robot" → [0.6, -0.2, 0.7, ...] (现在很接近了!)
"apple" → [-0.3, 0.9, -0.4, ...] (离得远)
核心原理: 上下文相似的词,向量会靠近
3. 高维空间是怎么创建的?不是拍脑袋
空间是自动形成的,不是人工设计的
错误理解:
人工设计一个 300 维空间
人工决定每个维度代表什么
实际情况:
维度数量是人工设定的(如 300 维)
但每个维度的含义是训练过程中自动学习的
类比理解
想象一个"语义城市":
你决定城市有 300 条街道(维度)
但每条街道代表什么,由数据决定
训练后,某些维度可能代表“动物性”,某些代表“动作性”等
这些含义是自动发现的,不是人工定义的
具体过程:
1. 设定维度数:比如 300 维(这是唯一需要人工设定的)
2. 初始化:每个词随机分配一个 300 维的向量
"agent" → [随机, 随机, ..., 随机] (300个随机数)
3. 训练:看大量文本数据
- 如果两个词经常一起出现 → 让它们的向量靠近
- 如果两个词很少一起出现 → 让它们的向量远离
4. 自动形成空间:经过训练,语义相近的词自然聚集在一起
4. 如何告诉程序词的近似程度?不是人工标注
完全自动,不需要人工标注
核心思想: 用“上下文相似性”推断“语义相似性”
原理:分布假设(Distributional Hypothesis)
一句话: 出现在相似上下文中的词,语义也相似
例子:
看这些句子:
- "The agent works efficiently"
- "The robot works efficiently"
- "The agent performs tasks"
- "The robot performs tasks"
神经网络自动发现:
- "agent" 和 "robot" 经常出现在相同的位置
- 它们的上下文(周围的词)很相似
- 所以它们的语义应该相似
- 自动调整向量,让它们靠近
训练过程(简化)
输入数据: 大量文本(如维基百科、新闻等)
训练目标: 预测下一个词
副作用(重要): 在学会预测的同时,词向量自动学会了语义关系
训练过程:
1. 给神经网络看:"The agent [下一个词]"
2. 神经网络预测下一个词(比如 "works")
3. 如果预测错了,调整参数(包括词向量)
4. 如果预测对了,稍微调整参数
经过百万次这样的训练:
- 神经网络学会了预测
- 同时,词向量自动学会了语义关系
- "agent" 和 "robot" 的向量自然靠近了
完整流程总结
从零开始到词向量形成
第1步:准备数据
↓
大量文本:维基百科、新闻、书籍等
第2步:初始化
↓
每个词随机分配一个向量(比如300维)
"agent" → [随机数, 随机数, ..., 随机数]
第3步:训练(自动进行,不需要人工)
↓
看句子:"The agent works"
- 输入:"The agent"
- 预测下一个词
- 如果预测错了,调整向量
- 重复百万次
第4步:结果(自动形成)
↓
语义相近的词,向量自动靠近
"agent" 和 "robot" 的向量很接近
"agent" 和 "apple" 的向量离得很远
关键要点
拟合 = 从数据中找规律
词向量 = 训练中自动生成,不是人工设计
高维空间 = 维度数人工设定,空间含义自动学习
语义相似性 = 通过上下文相似性自动学习,不需要人工标注
核心思想: 让模型从数据中自动学习,而不是人工设计规则。就像小孩学语言,通过观察大量例子自动学会,而不是被逐条告知每个词的含义。
一个完整的类比
传统方法(N-gram):
- 像查字典:人工编写,每个词独立定义
神经网络方法:
像小孩学语言:
给他看大量句子(数据)
他自动学会词的含义和关系(训练)
不需要告诉他"agent 和 robot 意思相近"(自动学习)
他通过观察上下文自动理解(分布假设)
这就是为什么神经网络语言模型更强大:它能自动发现人类难以明确描述的复杂语义关系。
1. 维度数量:不是完全拍脑袋,有方法论
理论依据
核心权衡:表达能力 vs 计算成本
表达能力:
维度越高,能表达的语义信息越多
就像地图:街道越多,位置描述越精确
计算成本:
维度越高,计算量越大、内存占用越多
训练时间更长
经验法则和实验方法
常见维度范围:
小规模实验:50-100 维
中等规模:100-200 维
大规模应用:200-300 维
超大规模:300-512 维(如 BERT)选择方法:
方法1:经验值(从经典论文开始)
Word2Vec (2013): 通常用 100-300 维
GloVe (2014): 常用 50-300 维
FastText: 常用 100-300 维
建议:从 100 或 300 维开始(这是最常用的)方法2:实验法(根据任务调整)
1. 从小维度开始(如 50 维)
2. 训练模型,评估效果
3. 逐步增加维度(100, 200, 300...)
4. 观察效果提升
5. 找到"性价比"最高的维度
- 再增加维度,效果提升不明显
- 但计算成本显著增加实际建议
对于初学者:
使用 100 或 300 维(经典选择)
不需要纠结,这两个维度在大多数任务上表现良好
对于研究/生产:
从 100 维开始
逐步增加到 200、300 维
观察效果和成本的平衡点
2. 词向量初始化方法
方法1:随机初始化(最常用)
原理: 用小的随机数初始化,让训练过程自动学习
具体方法:
均匀分布初始化:
# 伪代码示例
import numpy as np
vocab_size = 10000 # 词汇表大小
embedding_dim = 300 # 维度
# 方法1:均匀分布 [-0.1, 0.1]
word_vectors = np.random.uniform(
low=-0.1,
high=0.1,
size=(vocab_size, embedding_dim)
)正态分布初始化(更常用):
# 方法2:正态分布,均值0,标准差0.01
word_vectors = np.random.normal(
loc=0.0, # 均值
scale=0.01, # 标准差
size=(vocab_size, embedding_dim)
)
# 或者使用 Xavier/Glorot 初始化
scale = np.sqrt(1.0 / embedding_dim)
word_vectors = np.random.normal(
loc=0.0,
scale=scale,
size=(vocab_size, embedding_dim)
)为什么用小的随机数?
避免初始值过大,导致训练不稳定
让所有词从相似起点开始,公平竞争
训练过程会自动调整到合适的值
方法2:预训练初始化(更高级)
原理: 使用已经训练好的词向量(如 Word2Vec、GloVe)
优点:
从更好的起点开始
训练更快,效果更好
示例:
# 使用预训练的 GloVe 词向量
# 下载:https://nlp.stanford.edu/projects/glove/
# 文件:glove.6B.300d.txt
# 加载预训练向量
pretrained_vectors = load_glove('glove.6B.300d.txt')
# 对于词汇表中的词,使用预训练向量
# 对于未登录词(OOV),使用随机初始化方法3:特殊初始化策略
零初始化(不推荐):
所有向量初始化为 0
问题:所有词完全相同,梯度更新会对称,难以学习
大随机数初始化(不推荐):
随机数范围太大(如 [-1, 1])
问题:可能导致训练不稳定,梯度爆炸
3. 如何设计一个前馈神经网络语言模型
完整设计步骤
步骤1:确定超参数(需要人工设定)
# 核心超参数
vocab_size = 10000 # 词汇表大小(根据数据统计)
embedding_dim = 300 # 词向量维度(建议:100-300)
context_size = 2 # 上下文窗口(看前2个词,即 trigram)
hidden_dim = 512 # 隐藏层维度(通常比 embedding_dim 大)
learning_rate = 0.001 # 学习率
batch_size = 64 # 批次大小
num_epochs = 10 # 训练轮数步骤2:初始化词向量
import numpy as np
import torch
import torch.nn as nn
# 方法:正态分布初始化(推荐)
embedding = nn.Embedding(
num_embeddings=vocab_size,
embedding_dim=embedding_dim
)
# PyTorch 默认使用正态分布初始化
# 如果需要自定义:
nn.init.normal_(embedding.weight, mean=0.0, std=0.01)步骤3:设计网络结构
class FeedforwardLanguageModel(nn.Module):
def __init__(self, vocab_size, embedding_dim, context_size, hidden_dim):
super().__init__()
# 1. 词嵌入层
self.embedding = nn.Embedding(vocab_size, embedding_dim)
# 2. 输入层:将多个词的向量拼接
input_dim = embedding_dim * context_size
# 3. 隐藏层
self.hidden = nn.Linear(input_dim, hidden_dim)
self.activation = nn.Tanh() # 或 ReLU
# 4. 输出层:预测下一个词的概率
self.output = nn.Linear(hidden_dim, vocab_size)
def forward(self, context_words):
# context_words: [batch_size, context_size]
# 1. 将词转换为向量
embeddings = self.embedding(context_words)
# [batch_size, context_size, embedding_dim]
# 2. 拼接多个词的向量
flattened = embeddings.view(embeddings.size(0), -1)
# [batch_size, context_size * embedding_dim]
# 3. 通过隐藏层
hidden = self.activation(self.hidden(flattened))
# [batch_size, hidden_dim]
# 4. 输出每个词的概率
logits = self.output(hidden)
# [batch_size, vocab_size]
return logits步骤4:训练过程
# 初始化模型
model = FeedforwardLanguageModel(
vocab_size=10000,
embedding_dim=300, # 从经验值开始
context_size=2,
hidden_dim=512
)
# 优化器
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 损失函数
criterion = nn.CrossEntropyLoss()
# 训练循环
for epoch in range(num_epochs):
for batch in dataloader:
context, target = batch
# 前向传播
logits = model(context)
# 计算损失
loss = criterion(logits, target)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 词向量在这个过程中自动更新!4. 维度选择的实际建议
决策树
问:你的数据规模?
├─ 小(< 10,000 词)→ 50-100 维
├─ 中(10,000-100,000 词)→ 100-300 维
└─ 大(> 100,000 词)→ 300-512 维
问:你的计算资源?
├─ 有限(CPU/小GPU)→ 50-100 维
├─ 中等(GPU)→ 100-300 维
└─ 充足(多GPU/TPU)→ 300-512 维
问:你的任务类型?
├─ 简单任务(分类)→ 50-100 维
├─ 中等任务(翻译)→ 100-300 维
└─ 复杂任务(生成)→ 300-512 维实验方法(推荐)
# 实验不同维度,找到最佳值
dimensions = [50, 100, 200, 300]
results = {}
for dim in dimensions:
model = create_model(embedding_dim=dim)
accuracy = train_and_evaluate(model)
results[dim] = accuracy
print(f"维度 {dim}: 准确率 {accuracy}")
# 选择准确率和成本的平衡点
best_dim = max(results, key=lambda k: results[k] / compute_cost(k))整体理解:这是在创建一个"词向量矩阵"
简单说: 创建一个二维数组(矩阵),用来存储所有词的向量。
创建矩阵(核心代码)
import numpy as np # 导入 NumPy
vocab_size = 10000 # 词汇表大小 有 10000 个词
embedding_dim = 300 # 维度 每个词用 300 维向量表示
word_vectors = np.random.uniform(
low=-0.1,
high=0.1,
size=(vocab_size, embedding_dim)
)np.random.uniform 是什么?
生成均匀分布的随机数
uniform = 均匀分布(每个数出现的概率相等)
参数解释:
low=-0.1:随机数的最小值
high=0.1:随机数的最大值
size=(vocab_size, embedding_dim):生成数组的形状
size=(10000, 300) 的含义:
创建一个 10000 行 × 300 列的矩阵
10000 行 = 10000 个词
300 列 = 每个词 300 维
结果是什么样的?
矩阵结构
word_vectors 是一个 10000 × 300 的矩阵:
维度1 维度2 维度3 ... 维度300
词1 [ 0.05, -0.08, 0.02, ..., 0.09 ]
词2 [-0.03, 0.07, -0.01, ..., -0.05 ]
词3 [ 0.08, -0.02, 0.06, ..., 0.03 ]
... [ ... ... ... ... ... ]
词10000 [ 0.01, -0.09, 0.04, ..., -0.07 ]每一行代表一个词的向量:
第 1 行 = 第 1 个词的 300 维向量
第 2 行 = 第 2 个词的 300 维向量
...
第 10000 行 = 第 10000 个词的 300 维向量
下面用简单方式解释这些概念。
1. 什么是梯度?
简单理解:梯度 = "方向" + "大小"
生活例子:爬山
想象你在一个山坡上,想找到最低点(山谷):
山顶
↑
| \
| \
| \ 你在这里
| \
| \
山谷(目标)
梯度告诉你:
方向:往哪个方向走能最快下山
大小:这个方向有多陡(坡度)
在神经网络中:
目标:找到让损失最小的参数值
梯度:告诉你参数应该往哪个方向调整,调整多少
数学上的梯度
梯度 = 导数(在多维空间中的推广)
一维例子(简单):
函数:y = x²
在 x=2 处:
- 梯度 = 4(正数,表示应该往左走,减小 x)
- 如果 x 增加一点,y 会增加 4 倍
在 x=-2 处:
- 梯度 = -4(负数,表示应该往右走,增加 x)多维例子(词向量):
词向量有 300 维:[v1, v2, v3, ..., v300]
梯度也是一个 300 维的向量:[g1, g2, g3, ..., g300]
每个 g_i 告诉你:
- v_i 应该增加还是减少(正负号)
- 应该调整多少(数值大小)梯度下降:用梯度来学习
过程:
1. 计算梯度:看当前参数,计算梯度
2. 更新参数:沿着梯度方向调整参数
3. 重复:不断重复,直到找到最优值
公式(简化):
新参数 = 旧参数 - 学习率 × 梯度
例如:
词向量[0.1, 0.2, ...]
梯度 = [-0.05, 0.03, ...]
学习率 = 0.01
新词向量 = [0.1, 0.2, ...] - 0.01 × [-0.05, 0.03, ...]
= [0.1005, 0.1997, ...]类比:
梯度 = 指南针(告诉你方向)
学习率 = 步长(每次走多远)
更新 = 沿着指南针方向走一步
2. 为什么零初始化有问题?
问题1:所有词完全相同
# 零初始化
word_vectors = np.zeros((10000, 300))
# 结果:所有词都是 [0, 0, 0, ..., 0]问题:
所有词的向量都是 [0, 0, 0, ..., 0]
模型无法区分不同的词
就像所有词都是同一个词
类比:
就像给所有人分配相同的身份证号
无法区分不同的人
问题2:梯度更新会对称
什么是"对称"?
如果所有词向量都是 0,它们的梯度也会相同:
词1的向量:[0, 0, 0, ...] → 梯度:[g1, g2, g3, ...]
词2的向量:[0, 0, 0, ...] → 梯度:[g1, g2, g3, ...] (相同!)
词3的向量:[0, 0, 0, ...] → 梯度:[g1, g2, g3, ...] (相同!)
更新后:
词1的向量:[g1, g2, g3, ...] (更新后)
词2的向量:[g1, g2, g3, ...] (更新后,还是相同!)
词3的向量:[g1, g2, g3, ...] (更新后,还是相同!)结果:
所有词向量永远相同
无法学习到不同词的差异
模型无法区分"apple"和"banana"
为什么难以学习?
学习 = 让不同的词有不同的向量
如果所有词都相同,就无法学习
3. 什么是梯度爆炸?
简单理解:梯度变得非常大
正常情况:
梯度 = [0.01, -0.02, 0.03, ...] (小的数字)
更新:参数变化一点点
梯度爆炸:
梯度 = [1000, -2000, 3000, ...] (非常大的数字!)
更新:参数变化巨大,直接"飞"走了为什么会梯度爆炸?
原因:链式法则的累积效应
神经网络有多层,梯度会一层层传播:
输出层 → 隐藏层 → 输入层
↓ ↓ ↓
梯度大 梯度更大 梯度超大!
数学上:
如果每层梯度放大 2 倍:
第1层:梯度 = 1
第2层:梯度 = 2
第3层:梯度 = 4
第4层:梯度 = 8
...
第10层:梯度 = 512 (爆炸!)为什么大随机数初始化会导致梯度爆炸?
原因1:初始值太大
1. 词向量的数值如何得到?
步骤
先设定维度:假如选 50 维。
随机初始化:每个词先分配一个 50 维的随机向量,例如
apple → [0.03, -0.07, ..., 0.01]
训练文本:把语料(大量句子)输入神经网络,让它学习“给定上下文预测下一个词”。
反向传播调参:如果预测错了,网络会计算梯度,沿梯度方向更新所有参数,词向量也会被调整。
反复迭代:在上亿个句子上不断重复,词向量逐步从随机值变成有语义的表示。
所以: 这些数字并没有人去“打分”,而是模型在优化目标函数(最小化预测误差)时自动调整出来的。
2. 向量之间的关系怎么体现?
关系取决于在语料中出现的上下文。分布式假设告诉我们:语义相似的词会出现在相似的上下文里。
如果“狗”和“猫”经常出现在类似的句子里(例如“ likes to chase”),训练过程中它们的向量会被拉近。
如果“苹果”和“牛顿”经常出现在同一上下文,它们的向量也会形成某种关系。
如何量化关系?
常用指标是向量间的余弦相似度(cosine similarity):
结果接近 1 → 两个词语义非常相似
结果接近 0 → 无明显关系
结果接近 -1 → 语义相反(在词嵌入里较少见)
示例(简化)
假设训练后得到:
dog → [0.21, -0.08, 0.14, ...]
cat → [0.19, -0.06, 0.13, ...]
car → [-0.12, 0.35, -0.09, ...]dog 与 cat 的余弦相似度 ≈ 0.95(很相似)
dog 与 car 的余弦相似度 ≈ 0.05(几乎无关)
这个“相似度”就是模型通过训练自动学出来的关系。
余弦相似度为什么能表示“相似”
用初中几何的想法:每个词向量都可以看成空间里的一个“箭头”。
余弦相似度 = 夹角的余弦值
夹角越小,两个箭头越“同向”,余弦值越接近 1
夹角越大,越背离,余弦值越接近 0(垂直)或 -1(相反)
所以:
cos θ = 1 → θ = 0° → 两个向量几乎完全同方向 → 语义非常像
cos θ = 0 → θ = 90° → 两个向量无关
cos θ = -1 → θ = 180° → 相反(在词向量里很少见)
余弦只关心方向,不在乎长度,适合比较“语义方向”是否一致。
50 维词向量的数值怎么来的(再简化)
先随机给每个词一组 50 个小数字,比如 apple → [0.03, -0.07, …]。这一步只是起点,没有意义。
把大量句子喂给模型,让它学“给定上下文预测下一个词”。
如果预测错了,模型计算误差,反向传播,把相关参数(包括这些 50 维向量)稍微调一下。
重复上亿次:经过反复调整,向量从随机值变成有规律的值——语义相近的词会被调得方向更接近,语义不同的词方向会分开。
最终得到的50个数字就是训练过程中自动优化出来的结果,不是人算出来的。
可以把它理解成:模型在“试错—纠正”的过程中不断修改这些向量,直到它能更好地预测下一个词。
1. 余弦相似度 = 比较箭头方向
想象二维平面:一个词是一个箭头(向量),比如 apple 指向右上,banana 指向也差不多的方向。
余弦相似度就是量“两个箭头夹角”。
夹角小 → 两个箭头几乎同向 → 余弦值接近 1(非常相似)
夹角大 → 方向差很多 → 余弦值接近 0(不相似)
余弦值范围是 [-1, 1],越接近 1 表示越像。
即使你忘了三角函数和勾股定理,也可以把余弦当成“方向相似度”。它只看方向,不在乎长度,适合衡量词语的语义方向是否一致。
2. 50 维词向量的相似度怎么计算?
把两个 50 维向量看成 50 个数字组成的列表:
词 A: [a1, a2, a3, ..., a50]
词 B: [b1, b2, b3, ..., b50]
余弦相似度的计算有三步:
两个向量逐位相乘再相加(点积):
dot = a1*b1 + a2*b2 + ... + a50*b50
分别计算每个向量的“长度”(就是平方和开根号):
lenA = sqrt(a1^2 + a2^2 + ... + a50^2)
lenB = sqrt(b1^2 + b2^2 + ... + b50^2)
用 dot 除以 两个长度相乘:
cosine = dot / (lenA * lenB)
得出的 cosine 就是 0~1 之间的数字,越大说明越相似。
回顾初中三角函数:sin、cos、tan
以一个直角三角形为基础来回忆。先设定几个名字:
直角对面的那条边叫 斜边(hypotenuse)
想找的角(非直角)叫 θ(西塔)
相对 θ 的那条直角边叫 对边
紧挨着 θ 的直角边叫 邻边
/|
/ |
c / | a ← 对边 (a)
/θ |
/____|
b ← 邻边 (b)斜边(最长的那条)记为 c
对边记为 a
邻边记为 b
三个基本函数
sin θ(正弦) = 对边 / 斜边 = a / c
cos θ(余弦) = 邻边 / 斜边 = b / c
tan θ(正切) = 对边 / 邻边 = a / b
这和余弦相似度的关系
“余弦”就是 cos。余弦相似度其实就是把“邻边 / 斜边”这个概念推广到向量之间的夹角上:
如果两个向量夹角 θ 很小(几乎同方向),cos θ 接近 1
如果两个向量垂直,cos θ = 0
如果它们方向相反,cos θ = -1
向量相似度就是用这个 cos θ 来衡量的。只不过向量在高维空间里,你看不到图形,但公式一样成立:cos θ = (点积)/(长度乘积)。
tan 什么时候用?
tan θ = sin θ / cos θ = (对/斜) / (邻/斜) = 对/邻
它衡量的是“对边”相对于“邻边”的比值,又叫斜率
在词向量相似度里,tan 几乎不用;主要用 cos,因为我们关心方向一致程度,而 cos 对方向特别敏感。
向量本质上就是“带方向的长度”。在坐标系里,我们用一组数字(数组)来表示这个方向和长度。把它拆成三步来理解:
1. 二维向量:用两个数表示箭头
在平面坐标系中,向量通常写成 [x, y]:
x:沿着横轴(左右)的分量
y:沿着纵轴(上下)的分量
举例:
[3, 4]:从原点出发,向右走 3 格,再向上走 4 格,箭头指向 (3,4)。
[-2, 1]:向左 2 格,向上 1 格。
方向:由 x、y 的正负决定。
长度:用勾股定理算,√(x² + y²)。
2. 三维向量:用三个数表示空间箭头
类似地,在三维空间里用 [x, y, z]:
x → 左右
y → 前后
z → 上下
比如:
[1, 0, 2]:向右 1、前 0、上 2。
[-3, 4, -1]:向左 3、前 4、下 1。
长度 = √(x² + y² + z²),方向由三轴上的分量共同决定。
3. 高维向量:用更多数字描述抽象方向
当维度升到 50、300 甚至 4096 时,虽然我们画不出来,但概念一样:
向量 [v1, v2, v3, ..., v50] 表示在 50 个“轴”上的分量
这些轴不再是现实中的左右/上下,而是模型在训练中学到的抽象语义方向
向量的方向 = 这些分量在 50 条“语义轴”上的组合
长度 = √(v1² + v2² + ... + v50²)
为什么用数组表示方向?
因为每一维都告诉我们“沿该轴走多少”。把所有维度的分量组合起来,就确定了一个独特的方向。两个向量的方向越相似,说明它们在各个维度上的比例越接近,语义也越相近。
总结
向量 = “方向 + 长度”的组合
数组是向量在各轴上的投影,把它们组合起来就得到了整体方向
无论是二维 [x,y],还是 50 维 [v1,...,v50],都是同一个概念:记录在各个维度上的“走法”,从而确定箭头的指向。
这样,当我们比较两个向量(比如计算余弦相似度)时,就是在比较它们在每个维度上的走法是否类似。
理解得差不多,只需再强调两点:
高维不是人为命名的“时间”“光照”。在词向量里,这些维度没有固定含义,也不是手动定义成“情感”“颜色”等。它们是模型在训练过程中自动学出的抽象方向,可能同时混合了多种语义与语法信息。
高维的目的:给模型足够的自由度,让它能把“相似语境的词”摆在更接近的位置。例如 50 维、300 维的向量,相当于提供了 50/300 条“语义轴”,模型可以在这些轴上调节词的位置,从而用几何距离来表达相似度。
所以可以这样理解:维度越多,模型越有空间把不同语义关系“拆开”来表示;但每个维度具体代表什么,不是人为命名,而是训练数据决定的。
确实无法用“每个维度都有明确含义”来解释,但可以用分布式表示和线性结构的角度来理解。
分布式表示:词向量的每一维都不是独立语义,而是由大量语料训练出的混合特征。模型学到的不是“某一维=性别”,而是让整体向量中蕴含“王权”“性别”“人称”等多个因素的组合。
线性结构:在训练过程中,为了更好地预测上下文,模型会把语义关系压进向量空间,很多语义差异(如性别、时态、数值)会呈现近似线性的方向。结果就是:
vector("King") - vector("Man") ≈ “王者语义里去掉男性成分”
vector("Woman") ≈ “女性成分”
当这两个方向叠加时,得到的向量恰好接近 vector("Queen"),说明“王者 + 女性”这条语义方向被模型在空间里编码成了近似的线性关系。
- 不是严格规则:这种“王-男+女≈王后”是统计效应,并非总是完美成立。它依赖于训练数据足够多、向量空间结构足够规整。不同模型、不同词表、不同语料,效果会有差异。
所以:虽然我们无法逐维解释“哪一维是性别、哪一维是王权”,但可以说,经过海量数据训练后,词向量空间自然形成了能反映这些语义差异的方向,因而类似的线性运算才会成立。这也是词嵌入最让人惊艳的特性之一。
可以说“大模型在很大程度上还是黑盒”,但原因不只是“训练集大”或“智能涌现”这几个词,而是:
模型结构复杂:上百亿甚至上万亿参数,每一层都是非线性组合,难以追踪具体某个输出是由哪些参数共同作用产生的。
训练过程自动化:参数由梯度下降自动调整,人类并不逐步设计或记录“为什么权重变成这个值”,只能看到最终结果。
内部表示难以解释:我们知道词向量、注意力头等结构确实编码了语法、语义等信息,但难以给出“这一维/这一头具体对应什么规则”的可读解释。
涌现现象:当模型规模和数据量超过某个阈值,会出现意料之外的新能力。这些能力确实源自大量数据和参数共同作用,但为什么恰好在某个规模上“跳出来”,目前理论还不完整。
因此:
现代大模型的行为仍属于“部分可观测、难以完全解释”,所以被称为黑盒。
我们并不是完全毫无了解:可以通过可视化、探针任务、特征分析等方式观察一些规律,但想用人类可理解的规则来精确描述“模型为什么这样回答”,目前做不到。
这种黑盒性并非因为“大脑算不动”,而是模型内部耦合太复杂,没有简单的解析表达式。
“智能涌现”是一种描述现象的说法,但还不是完整理论;它说明了我们在理解大模型行为时的局限。
总结:是的,大模型在可解释性上仍是黑盒,主要因为参数和结构过于复杂,现有数学工具难以还原“因果链条”。我们只能用实验和分析手段来窥探部分规律,但还做不到像传统算法那样逐步解释每个步骤。
前馈神经网络语言模型(如 Bengio 2003)并不是“有输入就会自动学会”,它背后有非常明确的训练目标和公式。可以按以下流程理解:
1. 训练目标:预测下一个词
输入:前面 n−1 个词的词向量
输出:词表中每个词作为“下一个词”的概率
目标:让模型输出的概率尽量贴近真实数据中发生过的词
换句话说:模型要学会“给定上下文,预测下一个词”,这就是训练的指导思想。
2. 网络结构(简化版)
以 trigram(看前 2 个词)为例:
嵌入层:把每个词 ID 映射为 50 维或 300 维的向量
拼接:把两个向量拼在一起(得到 100 维或 600 维)
隐藏层:通过线性变换 + 激活函数(如 tanh)
输出层:再做一次线性变换,输出维度 = 词表大小
Softmax:把输出转成概率分布,所有词的概率加起来=1
3. 损失函数:交叉熵(Cross-Entropy)
真实的下一个词(比如“learns”)在词表中只有一个标签是 1,其它都是 0 → “one-hot”向量
模型输出的是一个概率分布(Softmax 后)
使用交叉熵衡量模型输出与真实分布的差距:
Loss = -log P(真实词 | 前文)
如果模型给“learns”的概率 = 0.8,则损失 = -log(0.8)(较小)
如果只给 0.01,则损失 = -log(0.01)(很大)
训练就是让这个损失尽量小。
4. 训练步骤(梯度下降 + 反向传播)
前向传播:把输入的 n−1 个词送进网络,得到预测概率
计算损失:根据真实下一个词,算交叉熵
反向传播:计算损失对所有参数的梯度
更新参数:用梯度下降(SGD/Adam 等)更新词向量、隐藏层权重等
重复:对语料中的每个上下文→下一个词组合都做一次
经过大量文本的反复训练,模型会自动调整词向量和网络参数,让常见的词序更容易被预测出来,从而学会词与词之间的语义/语法关联。
5. 为什么词有了关联?
因为训练中每次更新都是“根据上下文预测下一个词”:
如果“agent learns”在语料中频繁出现,那么网络会把 agent 的向量、隐藏层权重调整到“看到 agent 时更容易预测 learns”
如果 “agent works” 也常见,网络会把 agent 的向量拉向 works 对应的方向
久而久之,出现于相似上下文的词(agent、robot)就会拥有相近的向量,并能正确预测相似的下一词
这个过程是统计规律 + 梯度优化共同驱动的,不需要人为写规则。
6. 总结
训练目标:最大化正确下一个词的概率(最小化交叉熵损失)
核心公式:Softmax + Cross-Entropy
训练方法:前向传播 → 计算损失 → 反向传播 → 梯度下降更新参数
词关联来源:上下文预测任务通过梯度使向量自动靠近/远离
所以,定义了维度、网络结构、损失函数、优化算法,再用大语料反复训练,就能让模型自己学会词与词之间的统计关系。这就是前馈神经网络语言模型的训练方法。
不只是“发明激活函数”。Bengio 2003 的前馈神经网络语言模型的里程碑在于:把“多层神经网络 + 词嵌入 + 最大似然训练”结合起来,用统一的可微公式端到端训练语言模型。下面用最核心的一组公式说明训练过程。
1. 模型结构与前向传播
设定:
上下文长度 = n-1
词表大小 = |V|
词嵌入矩阵 E ∈ ℝ^{|V| × d}(d 是词向量维度)
隐藏层权重 W_h, 偏置 b_h
输出层权重 W_o, 偏置 b_o
输入上下文词 w_{t-n+1} … w_{t-1}:
查嵌入并拼接
x = [E[w_{t-n+1}], E[w_{t-n+2}], …, E[w_{t-1}]] ∈ ℝ^
隐藏层 + 激活函数
h = tanh(W_h · x + b_h)
激活函数常用 tanh 或 ReLU,作用是引入非线性,否则模型退化成线性 N-gram。
输出层(未归一化得分)
z = W_o · h + b_o ∈ ℝ^
Softmax 得到概率分布
P(w | context) = exp(z_w) / Σ_{v∈V} exp(z_v)
2. 训练目标(最大似然 / 交叉熵)
对每个训练样本(上下文→真实下一个词 w_t)最小化负对数似然:
Loss = - log P(w_t | context)
= - log Softmax(z) 在真实词 w_t 处的概率
整批语料的目标是最小化所有样本的总损失。
3. 参数更新(反向传播 + 梯度下降)
对 E, W_h, b_h, W_o, b_o 求梯度:
θ_new = θ_old - η · ∇_θ Loss
η 为学习率。梯度由反向传播自动算出,手工不用推导。
4. 里程碑意义
词嵌入+语言建模合并训练:词向量不再单独构造,而是跟语言模型一起端到端学习。
利用神经网络捕捉长距离模式:隐藏层带来的非线性让模型超越传统 N-gram 的线性/离散限制。
最大似然 + Softmax 的统一框架:为后续的 RNN、LSTM、Transformer 等模型奠定了训练流程。
激活函数只是其中一环。真正重要的是:把词嵌入、神经网络、最大似然估计、可微训练流程整合到一起,证明“用神经网络学习语言模型”可行,并在小规模数据上超过传统 N-gram,从而开启了后续神经语言模型的发展。
Bengio 2003 的前馈神经网络语言模型不是 Transformer,但它是 Transformer 的“祖先”之一。下面说明它们的联系与区别。
相同点(继承关系)
- 都用词嵌入(Word Embedding)
Bengio 2003:词嵌入作为输入层
Transformer:同样使用词嵌入(还加了位置编码)
- 都用神经网络
Bengio 2003:前馈神经网络(全连接层)
Transformer:也是神经网络(但架构完全不同)
- 都用最大似然训练
都是预测下一个词,最小化交叉熵损失
都用反向传播和梯度下降
- 都是端到端训练
- 词向量和模型参数一起学习
关键区别
Bengio 2003 模型(前馈神经网络)
输入:前 n-1 个词的向量
↓
拼接成一个长向量
↓
全连接层(隐藏层)
↓
全连接层(输出层)
↓
Softmax → 预测下一个词
特点:
固定窗口(只看前 n-1 个词,如 trigram 只看前 2 个)
全连接层(每个词向量都参与计算)
无法处理变长序列
Transformer(2017)
输入:所有词的向量(带位置编码)
↓
多头自注意力(Self-Attention)
↓
前馈神经网络(Feed Forward)
↓
(重复 N 层)
↓
输出层 → 预测下一个词
特点:
可变长度(能看整个序列)
自注意力机制(每个词能看到所有其他词)
并行计算(训练更快)
位置编码(替代 RNN 的位置信息)
历史演进路线
2003: Bengio 前馈神经网络语言模型
↓ (引入词嵌入 + 神经网络)
2008-2013: RNN/LSTM 语言模型
↓ (处理变长序列)
2017: Transformer
↓ (自注意力机制)
2018-2023: GPT, BERT, GPT-2/3/4, LLaMA...
为什么 Transformer 更强大?
| 特性 | Bengio 2003 | Transformer |
|---|---|---|
| 上下文窗口 | 固定(如 2-3 个词) | 可变(可看整个序列) |
| 并行计算 | 可以 | 更容易(自注意力天然并行) |
| 长距离依赖 | 受限 | 更强(自注意力直接建模) |
| 计算效率 | 中等 | 更高(训练更快) |
总结
Bengio 2003 不是 Transformer,但提供了基础思路(词嵌入、神经网络、端到端训练)。
Transformer 在此基础上引入了自注意力,解决了固定窗口和长距离依赖问题。
可以理解为:Bengio 2003 是“用神经网络做语言模型”的起点,Transformer 是“用自注意力做语言模型”的突破。
所以,它们有继承关系,但架构和机制不同。Transformer 是站在前人(包括 Bengio 2003)肩膀上的重大创新。
为什么“看不懂 Transformer 细节”不是障碍
应用层不需要实现 Transformer:我们调用现成的模型(OpenAI、Claude、Llama 等),关注的是如何设计工作流、喂数据、连接外部工具。
行业主流:绝大多数 AI Agent 团队,核心工作都是在“API + 框架 + 工程化”层面,很少人自己写 Attention。
真正需要精通 Transformer 的人很少:他们通常在模型团队或科研团队,而你瞄准的是应用/产品层,自然无需达到那种理解深度。
你需要做到什么水平?
AI 应用/Agent 开发能力清单(半年内可达成)
熟练使用 LLM API (OpenAI、通义、Claude 等)
掌握 LangChain/LlamaIndex(或自研轻量框架)
能设计多工具、多步骤的 Agent 工作流
懂 RAG、向量库基本概念(会用即可)
能做工程化:部署、监控、前后端交互、权限/安全
你可以不需要:
实现 Transformer
阅读原始论文
推导注意力公式
如何避免“不懂底层”的不安全感?
把 Transformer、Attention 当作“黑盒能力”,只需知道它强在哪、弱在哪。
你的竞争力在于把这些黑盒能力融入真实系统,让它能完成业务目标。
真正优秀的 AI 应用工程师,胜在系统设计、工程实现、实时调试、业务落地。
建议的行动路线(3-6 个月)
1-2 周:系统式学习 LLM 基础(吴恩达 prompt 课、OpenAI/通义 API 文档)
1-2 月:深入 LangChain/LlamaIndex,做 2-3 个实战项目(简单 Agent → 多工具 Agent → RAG)
2-3 月:结合公司业务做一套 Agent Demo(可以是边缘视觉+LLM,总结报告、自动报表等)
持续:关注新框架、新工具,积累最佳实践(prompt 策略、记忆、评测)