向量化
约 2902 字大约 10 分钟
2025-04-30
这里将会从向量化开始记录,虽然这些在之前NLP的相关学习中也都了解过,并知晓。但是还是打算自己重新记录整理一下。
让计算机理解文本
计算机中对文本的记录使用过编码来实现的,比如最常见的中文的编码方案,Unicode UTF-8的编码。变长编码(1-4字节),兼容ASCII,支持全球所有语言。中文字符通常占3字节。并且成为了互联网主导编码(98.2%网页使用),解决多语言同屏显示问题。
那为什么在机器学习中和深度学习中,文本的处理和表示方式会有所不同呢?
下面以UTF-8的编码格式说明。
首先是计算效率的问题,UTF-8是变长编码,导致文本向量化结果长度不一致,难以直接输入模型。而机器学习中需要固定纬度的输入。同时UTF-8编码的字符串需先解码为Unicode码点(如U+4E25),再转换为数值向量,额外步骤会增加预处理复杂度。
其次缺少了语义信息缺失,utf-8仅仅解决了字符存储的问题。无法捕捉单词或句子的语义关系(如同义词、上下文)。而NLP任务依赖语义建模,需通过词嵌入(如Word2Vec、BERT)将文本映射到低维稠密向量空间。
最后是高纬稀疏醒的问题,如果将每个Unicode码点直接作为特征,会生成高维稀疏向量(如Unicode包含百万级码点,Unicode的编码空间是21位,理论上可支持1,114,112个码点,实际文本中,单个文档或句子仅使用极少数码点(如中文常用字约几千个),导致向量中99%以上的位置为0。),导致维度灾难和模型效率低下。
One-Hot编码
它将每个离散属性的每个类别创建一个新的二进制特征。对于每个样本,只有一个二进制特征为1,表示它属于对应的类别,其他特征为0。比如我们有三个类别老鼠,猫以及狗,那么他们对应的编码应该如下图所示
例如,对三个类别:老鼠、猫、狗,其 One-Hot 表示如下:
| 类别 | 向量表示 |
|---|---|
| 老鼠 | [1, 0, 0] |
| 猫 | [0, 1, 0] |
| 狗 | [0, 0, 1] |
Word2Vec 的论文视角:目标函数、复杂度与设计动机
相关信息
本节内容主要基于:
- Mikolov et al., Efficient Estimation of Word Representations in Vector Space (2013)
- Mikolov et al., Distributed Representations of Words and Phrases and their Compositionality (NIPS 2013)
以下整理的 Word2Vec 模型详解:主要以论文为主,另外可以参考这篇CSDN博客,写的很好。
1. 分布式假设的论文级表述
论文将语言建模问题简化为一个统计假设:
如果两个词在大量上下文中出现的概率分布相似,那么它们应具有相近的语义。
设词 w 的上下文分布为:
P(⋅∣w)
Word2Vec 的目标并非显式建模该分布,而是寻找一个低维向量:
vw∈Rd
使得向量内积能够近似反映条件概率关系。
2. Skip-gram 的概率建模本质(论文原始动机)
在 Skip-gram 中,作者假设:
给定中心词 wI,其不同上下文词的出现是条件独立的。
因此有:
P(C∣wI)=wc∈C∏P(wc∣wI)
最大化该概率即得到论文中的训练目标函数。
这一假设显著降低了建模复杂度,但也解释了 Word2Vec 不是完整语言模型 的原因。
3. Softmax 的统计意义与不可行性
Softmax 定义为:
P(wO∣wI)=∑w∈Vexp(v′w⊤vwI)exp(v′wO⊤vwI)
论文指出,该形式等价于一个对数双线性模型(log-bilinear model)。
但其计算复杂度为:
O(∣V∣)
在 ∣V∣≈106 的真实语料中,这成为主要瓶颈。
4. Hierarchical Softmax 的信息论视角(论文原意)
论文并非仅将 Hierarchical Softmax 视为“加速技巧”,而是将其解释为:
对词概率分布的一种编码方式
使用 Huffman Tree 后:
- 高频词拥有更短的编码路径
- 期望计算复杂度最小化
概率被分解为:
P(w∣wI)=j=1∏L(w)−1P(bj∣wI)
其中每个 bj 是一次二分类决策。
5. Negative Sampling 的统计重解释(论文重要思想)
论文明确指出,负采样不再试图逼近 Softmax,而是转而优化如下目标:
logσ(v′wO⊤vwI)+i=1∑kEwi∼Pn[logσ(−v′wi⊤vwI)]
这等价于:
判断一个词对 (wI,wO) 是否来自真实语料分布
即噪声对比估计(Noise Contrastive Estimation, NCE)的简化形式。
6. 噪声分布 U(w)3/4 的论文解释
论文通过实验发现:
Pn(w)∝U(w)3/4
在以下两者之间取得最优平衡:
- U(w):过度强调高频词
- 均匀分布:过度强调低频噪声
这是一个经验性但高度稳定的结论,在后续工作中被广泛沿用。
7. 高频词下采样的统计合理性
论文指出,高频功能词对区分上下文贡献极低,但主导梯度更新。
因此定义:
Pdiscard(w)=1−f(w)t
该策略等价于重新平衡经验分布与信息贡献之间的关系。
8. 线性语义结构的论文级解释
论文将向量类比现象解释为:
向量空间中的线性方向,对应于上下文分布中的一致变化模式。
例如:
vking−vman≈vqueen−vwoman
这说明 Skip-gram 学到的并非词本身,而是词在语境中的角色偏移。
9. 词向量与 PMI 的隐含联系(论文未显式但被后续工作证实)
论文的目标函数可被理解为在隐式地逼近:
PMI(w,c)=logP(w)P(c)P(w,c)
负采样的内积项可视为对 PMI 的低秩分解,这解释了 Word2Vec 向量的稳定性与可解释性。
10. 从论文角度重新理解 Word2Vec
Word2Vec 不是“浅层神经网络”,
而是一个 受计算复杂度严格约束的统计嵌入模型。
其成功来源于:
- 明确的概率假设
- 极端高效的近似优化
- 大规模语料下的统计规律涌现
总结
| 特性 | Word2Vec | 改进模型(如 BERT) |
|---|---|---|
| 训练方式 | 浅层神经网络 | 深度 Transformer |
| 上下文建模 | 局部窗口 | 全局自注意力 |
| 多义词处理 | ❌ 不支持 | ✅ 支持 |
| 计算效率 | ⚡ 高效 | 🐢 较慢 |
| 适用场景 | 小规模语料、快速训练 | 大规模预训练、复杂任务 |
Word2Vec 是 NLP 的里程碑模型,虽然已被 BERT 等取代,但其思想(如负采样、词嵌入)仍影响深远。
Bert模型
Bert可以参考这篇CSDN博客,和上文同一个作者,写的也很好。
1. BERT 核心思想
BERT 采用双向 Transformer 结构,通过预训练学习上下文相关的词表示。其核心创新是 Masked Language Model (MLM) 和 Next Sentence Prediction (NSP)。
2. 模型架构(1) 输入表示
BERT 的输入由三部分组成:
Input=Token Embedding+Segment Embedding+Position Embedding
• Token Embedding:词向量(WordPiece 分词)
• Segment Embedding:区分句子(如 [CLS] 和 [SEP])
• Position Embedding:位置编码(最大长度 512)
(2) Transformer 层
基于多头自注意力机制(Multi-Head Attention):
Attention(Q,K,V)=softmax(dkQKT)V
其中: • Q,K,V 分别是查询、键、值矩阵
• dk 是向量维度
(3) 预训练任务
Masked Language Model (MLM)
随机遮盖 15% 的 token 并预测:P(wi∣w1..i−1,wi+1..n)=softmax(Wohi)
• hi 是第 i 个 token 的隐藏层输出
• Wo 是输出权重矩阵
Next Sentence Prediction (NSP)
预测两个句子是否连续:P(IsNext)=σ(wTh[CLS])
• h[CLS] 是
[CLS]token 的向量• σ 是 sigmoid 函数
3. 关键公式
(1) 自注意力计算
MultiHead(Q,K,V)=Concat(head1,...,headh)WO
其中每个注意力头:
headi=Attention(QWiQ,KWiK,VWiV)
(2) 层归一化 (LayerNorm)
LayerNorm(x)=γσ2+ϵx−μ+β
• μ,σ 是均值和方差
• γ,β 是可学习参数
(3) 位置编码 (Positional Encoding)
PE(pos,2i)=sin(pos/100002i/dmodel)
PE(pos,2i+1)=cos(pos/100002i/dmodel)
• pos 是位置索引
• i 是维度索引
4. 与 Word2Vec 对比
| 特性 | Word2Vec | BERT |
|---|---|---|
| 上下文建模 | 局部窗口 C | 全局双向上下文 |
| 训练目标 | 词预测 | MLM + NSP |
| 多义词处理 | ❌ | ✅ |
| 计算复杂度 | O(d⋅C) | O(L2⋅d) |
5. 代码示例 (PyTorch)
from transformers import BertModel, BertTokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
inputs = tokenizer("Hello world!", return_tensors="pt")
outputs = model(**inputs)
# 获取词向量
word_vectors = outputs.last_hidden_state # [1, seq_len, 768]