2026/3/28 15:28:03
网站建设
项目流程
优质做网站哪家好,网址推荐你会感谢我的,ui设计页面布局和功能,室外建筑网站词向量深度笔记#xff1a;从 OneHot 到 Word2Vec#xff08;逻辑链 代码#xff09;
前言
这是一篇关于 NLP 基石—— 词向量#xff08;Word Embeddings#xff09; 的系统笔记#xff0c;内容来源于课程讲义、教材阅读和个人实践整理。
本文的核心目标是讲清楚逻辑链…词向量深度笔记从 OneHot 到 Word2Vec逻辑链 代码前言这是一篇关于 NLP 基石——词向量Word Embeddings的系统笔记内容来源于课程讲义、教材阅读和个人实践整理。本文的核心目标是讲清楚逻辑链条为什么需要词向量OneHot 哪里不够Word2Vec 如何改进FastText 又解决了什么新问题每个技术点都会按照问题 → 朴素方案 → 局限 → 改进思路 → 公式/定义 → 直觉解释 → 工程实践的路径展开。掌握这些你就理解了现代 NLP 模型是如何读懂文字的第一步。1. 词向量基础为什么需要向量化逻辑链条问题计算机只认识数字无法直接处理苹果香蕉这种符号。需求必须把文本转化为计算机可处理的向量形式。目标让模型理解文字的含义而不仅仅是把它们当成编号。词向量的作用将离散的单词映射为低维、稠密、可学习的实数向量用于刻画词的语义与句法特征。为什么重要让自然语言变成机器可处理的向量形式是几乎所有 NLP 神经模型的输入基础。通过向量距离与方向表达词间相似度与类比关系如king−manwoman≈queen \text{king} - \text{man} \text{woman} \approx \text{queen}king−manwoman≈queen。显著优于 OneHot 等手工编码是现代 NLP 性能提升的关键一步。2. OneHot 编码最简单的词表示2.1 什么是 OneHot逻辑链条朴素方案用数字编号1红色2蓝色3绿色。局限这会让模型误以为3 2 1即绿色比红色大这在分类问题中是错误的。改进方案OneHot独热编码——每个类别占据独立的一维彼此正交。定义将每个词转换为一个只包含0 和 1的二进制向量。向量中只有一个位置为 1对应该词在词表中的索引其余位置均为0。直觉理解想象一个巨大的开关面板词表里有多少个词就有多少个开关。对于红色这个词只有第 1 个开关是开的对于蓝色只有第 2 个开关是开的。它们在空间里是互相垂直的完全独立。示例代码importnumpyasnpfromsklearn.preprocessingimportOneHotEncoder# 示例数据包含三个类别的列表categories[红色,蓝色,绿色,蓝色,红色]categoriesnp.array(categories).reshape(-1,1)# 创建 OneHotEncoder 对象encoderOneHotEncoder(sparse_outputFalse)one_hot_encodedencoder.fit_transform(categories)print(One-Hot 编码结果)print(one_hot_encoded)# 输出# [[0. 0. 1.] -- 红色# [0. 1. 0.] -- 蓝色# [1. 0. 0.] -- 绿色# [0. 1. 0.] -- 蓝色# [0. 0. 1.]] -- 红色print(类别顺序,encoder.categories_)### 2.2 为什么需要 OneHot三大理由|理由|解释||:---|:---||不排队|不用1、2、3这种数字避免让本来没大小的类别假装有顺序||好算账|变成0/1向量就能在坐标系里算距离、点积让模型听得懂这些特征||能一起算|和别的数值特征放在一起做归一化、训练模型都很自然、很方便|### 2.3 OneHot 的优点与缺点#### 优点-解决了分类器不好处理**属性数据**的问题。-在一定程度上起到了**扩充特征**的作用。-值只有0和1不同类型存储在**垂直的空间**几何上等距。#### 缺点-**维度灾难**词表有10万个词每个词就是长度10万的向量99,999个0极度稀疏。-**无法表达语义关系**任意两个不同词的余弦相似度都是0。 $$ \text{similarity}(\text{麦当劳},\text{肯德基})0$$ 模型无法知道麦当劳和肯德基是相似的。---## 3. Word2Vec从符号到语义的飞跃### 逻辑链条-**问题**OneHot 太稀疏且完全不懂语义。-**核心假设****分布假设**——一个词的含义由它周围的词上下文决定。-**方案**通过预测上下文或中心词训练出稠密的词向量。### Word2Vec 定义一种用于生成**词向量Embedding**的模型通过训练**浅层神经网络**来学习词向量表示。### 两种架构1.**CBOW**Continuous Bag of Words用上下文预测中心词。2.**Skip-Gram**用中心词预测上下文。---### 3.1 CBOW 模型用周围词猜中间词#### 逻辑链条-**任务**做完形填空——给你前后文猜中间的空是什么词。-**输入**上下文词左右窗口内的词。-**处理**把上下文词的向量**平均化**。-**输出**预测中心词。#### CBOW 的网络结构1.**输入层**上下文词的 OneHot 向量或直接用索引查表。2.**隐藏层**查询嵌入矩阵 $W_1$得到每个词的向量然后**求平均**$h\frac{1}{k}\sum_{i}W_1[c_i]$。3.**输出层**用矩阵 $W_2$ 计算分数 $uW_2^T h$经过 Softmax 得到概率分布。#### 代码实现带详细注释pythonimportnumpyasnp# 第一步准备数据 corpus[[I,love,NLP],[NLP,is,fun]]# 语料库vocablist(set(wforsincorpusforwins))# 去重得到词表word_to_idx{w:ifori,winenumerate(vocab)}# 词→索引Vlen(vocab)# 词汇表大小比如 6 个词D3# 词向量维度3 维空间# 第二步随机初始化两个矩阵 W1np.random.randn(V,D)*0.1# 输入矩阵最终的词向量W2np.random.randn(D,V)*0.1# 输出矩阵工具人矩阵lr0.01# 学习率window1# 窗口大小左右各看 1 个词# 第三步Softmax 函数 defsoftmax(x):enp.exp(x-np.max(x))# 防溢出先减最大值returne/e.sum()# 归一化成概率# 第四步训练循环 forepochinrange(50):# 训练 50 轮forsentincorpus:# 遍历每个句子foriinrange(len(sent)):# 遍历句子里每个词targetsent[i]# 中心词要预测的t_idxword_to_idx[target]# 收集上下文词左右窗口内的词ctx[]forjinrange(max(0,i-window),min(len(sent),iwindow1)):ifj!i:# 排除中心词自己ctx.append(word_to_idx[sent[j]])ifnotctx:continue# 没上下文就跳过# 【核心】前向传播上下文词向量的平均hnp.mean(W1[ctx],axis0)# shape: (D,)# 预测中心词h 乘以 W2 得到每个词的分数uW2.T h# shape: (V,)y_predsoftmax(u)# 变成概率分布# 计算误差真实标签 - 预测概率y_truenp.zeros(V)y_true[t_idx]1# one-hot 向量ey_pred-y_true# 误差向量# 【核心】反向传播更新权重dW2np.outer(h,e)# W2 的梯度dhW2 e# 传回 h 的梯度# 更新两个矩阵W2-lr*dW2forcinctx:W1[c]-lr*dh/len(ctx)# 平均分配给每个上下文词# 第五步使用词向量 word_vecW1[word_to_idx[NLP]]print(NLP 的词向量,word_vec)口诀式总结中心词做主角逐个预测邻居每个邻居单独更新。3.3 CBOW vs Skip-Gram如何选择维度CBOWSkip-Gram输入 → 输出上下文 → 中心词中心词 → 上下文隐藏层处理上下文词向量平均化直接取中心词向量训练样本数每个窗口 1 个样本每个窗口2k2k2k个样本kkk为窗口大小训练速度快一次预测一个词慢一次预测kkk个词对高频词效果好语义平滑也好对低频词较弱被平均稀释强单独建模更细粒度适用场景大规模语料快速训练关注语义精度尤其是罕见词选择建议数据量巨大、追求训练速度 →CBOW关注低频实体名词质量如专业术语、人名、地名→Skip-Gram实践中可两者都训练比较下游任务性能后再做选择4. 训练优化解决 Softmax 的瓶颈逻辑链条问题Word2Vec 的输出层是 Softmax分母要对全词表比如 100 万个词求和escore∑w1Vescore\frac{e^{score}}{\sum_{w1}^{V} e^{score}}∑w1Vescoreescore。每算一个样本都要算 100 万次指数速度太慢朴素方案能不能不算所有词改进方案 1负采样Negative Sampling——把多分类变成二分类。改进方案 2层次化 SoftmaxHierarchical Softmax——用二叉树组织词表。4.1 负采样Negative Sampling核心思想我们不再做从 100 万个词里找正确的那 1 个这种多分类任务而是改成做是非题中心词正确上下文 → 标签 1正样本中心词随机抽的噪声词 → 标签 0负样本这样我们只需要算1 个正样本和kkk个负样本通常k5k5k5复杂度从O(V)O(V)O(V)降到了O(k)O(k)O(k)。目标函数对于中心词wcw_cwc与真实上下文词wow_owo最大化logσ(vwo⊤vwc)∑i1kEwi∼Pn(w)[logσ(−vwi⊤vwc)] \log \sigma(v_{w_o}^\top v_{w_c}) \sum_{i1}^{k} \mathbb{E}_{w_i \sim P_n(w)}\left[\log \sigma(-v_{w_i}^\top v_{w_c})\right]logσ(vwo⊤vwc)i1∑kEwi∼Pn(w)[logσ(−vwi⊤vwc)]第一项让正样本的 sigmoid 概率接近 1。第二项让负样本的 sigmoid 概率接近 0即−v-v−v的 sigmoid 接近 1。代码实现代码实现核心区别importnumpyasnp# 准备数据和 CBOW 一样corpus[[I,love,NLP]]vocablist(set(wforsincorpusforwins))word_to_idx{w:ifori,winenumerate(vocab)}V,Dlen(vocab),3# 初始化两个矩阵 W1np.random.randn(V,D)*0.1W2np.random.randn(D,V)*0.1lr0.01window1defsoftmax(x):enp.exp(x-np.max(x))returne/e.sum()# 训练循环核心区别在这里forepochinrange(50):forsentincorpus:foriinrange(len(sent)):center_wordsent[i]# 中心词是主角center_idxword_to_idx[center_word]hW1[center_idx]# 【关键】直接拿出主角的向量不用平均# 收集上下文词邻居们context_words_indices[]forjinrange(max(0,i-window),min(len(sent),iwindow1)):ifj!i:context_words_indices.append(word_to_idx[sent[j]])ifnotcontext_words_indices:continue# 【关键】对每一个邻居进行一次预测和更新fortarget_ctx_idxincontext_words_indices:# 前向传播用主角 h 去预测邻居uW2.T h y_predsoftmax(u)# 真实答案是邻居的 one-hoty_truenp.zeros(V)y_true[target_ctx_idx]1# 计算误差ey_pred-y_true# 反向传播 更新权重dW2np.outer(h,e)dhW2 e W2-lr*dW2 W1[center_idx]-lr*dh# 只更新主角的向量# 最终词向量就是 W1word_vecW1[word_to_idx[love]]print(love 的词向量,word_vec)实战要点负样本数 (k)常用 2–10语料越大可以越小。噪声分布 (P_n(w))实践常用词频的 (3/4) 次幂归一化兼顾高频与长尾。优点每次更新只动很少一部分向量非常适合向量化和并行。4.2 层次化 SoftmaxHierarchical Softmax核心结构用一棵二叉树表示整个词表每个叶子是一个词。预测一个词的概率 从根走到该叶子路径上所有二分类概率的乘积。计算复杂度与树高成正比一般是 (O(\log V))。霍夫曼树Huffman Tree建树思路按词频从小到大构建二叉树高频词路径短靠近根。低频词路径长离根较远。预测思路从根到叶子每一次用 sigmoid 做“向左 / 向右”的二分类决策。走完整条路径大约需要 (\log V) 次二分类。和普通 Softmax 对比普通 Softmax每次要算 (V) 个得分。层次化 Softmax只算 (\log V) 个节点速度可快几百倍。霍夫曼树代码实现建树importheapq# 节点类定义 classNode:def__init__(self,word_idNone,freq0):self.word_idword_id# 词的编号叶子节点才有self.freqfreq# 频率用来排序self.leftNoneself.rightNonedef__lt__(self,other):returnself.freqother.freq# 构建霍夫曼树 defbuild_huffman_tree(word_freq_dict): word_freq_dict: {词id: 出现次数} 返回: 霍夫曼树的根节点 # 1. 为每个词创建叶子节点nodes[Node(word_id,freq)forword_id,freqinword_freq_dict.items()]# 2. 扔进最小堆按频率排序heapq.heapify(nodes)# 3. 循环合并每次取最小的两个造一个新 parentwhilelen(nodes)1:child1heapq.heappop(nodes)child2heapq.heappop(nodes)parentNode(freqchild1.freqchild2.freq)parent.leftchild1 parent.rightchild2 heapq.heappush(nodes,parent)# 4. 最后剩一个节点就是树根returnnodes口诀式总结建树按频率从小到大建二叉树高频词路径短近根低频词路径长。预测从根到叶子每层用 sigmoid 二分类去左 or 去右总共 (\log(V)) 次计算。对比Softmax 算 (V) 次Huffman 只算 (\log(V)) 次速度快几百倍。4.3 负采样 vs 层次化 Softmax维度负采样层次化 Softmax复杂度(O(k))(k) 通常为 5(O(\log V))概率分布近似不是真正 Softmax精确可恢复真正概率实现难度简单易实现易并行稍复杂需要建树与路径编码工程特点非常适合大规模词向量训练适合类别极多的分类任务常见用途Word2Vec、推荐、图嵌入等文本分类、大标签空间任务5. FastText子词信息的引入逻辑链条问题Word2Vec 把每个词当作原子单位不知道 “apple” 和 “apples” 有关系对未登录词OOV也无能为力。思路词是由字符或子词组成的子词蕴含形态与语义词根、前后缀等。方案FastText引入 n-gram 子词特征让词向量 词本身向量 子词向量之和。5.1 FastText 的定义与特点定义FastText 是 Facebook 提出的高效文本分类与词向量学习工具核心是利用子词subword和 n-gram 特征来表示词。与 CBOW 的相同点都是三层结构输入层、隐藏层、输出层。输入都是一堆向量词或 n-gram 的 embedding。隐藏层通过对这些向量做求和 / 平均得到句子或文档表示。与 CBOW 的不同点维度CBOWFastText输出目标中心词词预测文档类别标签文本分类输入单位词词 n-gram 子词编码方式OneHot 或索引查表直接用 embedding 查表OOV 能力弱只能用 UNK强子词组合出未见词的向量5.2 FastText 的技术细节损失函数文本分类部分使用交叉熵损失L−∑cyclogpc \mathcal{L} - \sum_{c} y_c \log p_cL−c∑yclogpc等价于最大化正确类别的对数概率。输出层的层次化 Softmax按类别频率构建霍夫曼树。复杂度从 (N) 降到 (\log N)。每个非叶节点对应一个带参数的 sigmoid 单元。负类沿左子树编码为 0正类沿右子树编码为 1。N-gram 特征将文本按字符或词为单位用长度为 (N) 的窗口滑动生成一系列 n-gram 片段。例如对单词 “apple”字符 3-gram 可能包括ap、app、ppl、ple、le等。FastText 为每个 n-gram 分配一个向量最终词向量 词本身向量 所有 n-gram 向量的和或平均。工程优化过滤出现次数过少的词和 n-gram减少噪声与内存消耗。用哈希技巧存储大量 n-gram 特征控制参数量。在保持精度的前提下保证训练和推理速度都非常快。6. 语言模型基础从 N-gram 到自回归6.1 自回归语言模型定义自回归语言模型假设当前 token 的分布只依赖于之前已经生成的历史序列。整个句子的概率分解为P(x1,…,xT)∏t1TP(xt∣x1,…,xt−1) P(x_1, \dots, x_T) \prod_{t1}^{T} P(x_t \mid x_1, \dots, x_{t-1})P(x1,…,xT)t1∏TP(xt∣x1,…,xt−1)生成时先根据 (P(x_1)) 采样第一个 token。再根据 (P(x_2 \mid x_1)) 采样第二个 token。如此循环直到生成终止符。训练目标最小化负对数似然L−∑t1TlogP(xt∣x1,…,xt−1) \mathcal{L} -\sum_{t1}^{T} \log P(x_t \mid x_1, \dots, x_{t-1})L−t1∑TlogP(xt∣x1,…,xt−1)等价于最大化训练语料的似然让真实句子更“有可能”。采样温度的作用对 logits (z_i) 进行缩放zi′ziT z_i \frac{z_i}{T}zi′Tzi再喂入 softmax当 (T \to 0) 时分布极尖锐接近贪心选最大概率词。当 (T) 较大时分布变平采样更随机多样性提高。6.2 N-gram 语言模型定义N-gram 模型是自回归模型的一个有限记忆特例只看最近的 (n-1) 个词P(xt∣x1,…,xt−1)≈P(xt∣xt−n1,…,xt−1) P(x_t \mid x_1, \dots, x_{t-1}) \approx P(x_t \mid x_{t-n1}, \dots, x_{t-1})P(xt∣x1,…,xt−1)≈P(xt∣xt−n1,…,xt−1)概率估计示例二元模型BigramP(xt∣xt−1)≈count(xt−1,xt)count(xt−1) P(x_t \mid x_{t-1}) \approx \frac{\text{count}(x_{t-1}, x_t)}{\text{count}(x_{t-1})}P(xt∣xt−1)≈count(xt−1)count(xt−1,xt)三元模型TrigramP(xt∣xt−2,xt−1)≈count(xt−2,xt−1,xt)count(xt−2,xt−1) P(x_t \mid x_{t-2}, x_{t-1}) \approx \frac{\text{count}(x_{t-2}, x_{t-1}, x_t)}{\text{count}(x_{t-2}, x_{t-1})}P(xt∣xt−2,xt−1)≈count(xt−2,xt−1)count(xt−2,xt−1,xt)实际中必须结合平滑如加一、Kneser-Ney避免零概率。工程特点训练阶段主要是大规模 n-gram 计数和归一化。存储代价高需要裁剪和压缩。推理阶段依赖查表和缓存优化。6.3 自回归神经 LM vs N-gram维度N-gram神经 LMRNN / Transformer条件依赖只看最近 (n-1) 个词理论上可看全局历史表示方式离散计数连续向量表示数据稀疏严重未见组合概率为 0可泛化到未见组合长距离依赖几乎无能为力可以建模长依赖甚至整篇文档可以把 N-gram 看作自回归模型的一种有限阶马尔可夫近似。7. 词向量知识架构总览基础词向量NLP 根基├── OneHot 编码├── Word2VecCBOW / Skip-Gram└── FastText子词信息演进RNN早期序列模型核心Attention 机制和 Transformer深入KV Cache、位置编码、归一化应用BERT、GPT、LLaMA、DeepSeek优化训练、微调、RLHF实战推理部署、RAG 技术总结逻辑链与记忆口诀技术问题方案口诀OneHot计算机不认识文字每个词一个独立维度不排队、好算账、能一起算CBOWOneHot 无语义用上下文预测中心词上文求平均预测中间误差回传Skip-GramCBOW 对低频词弱用中心词预测上下文中心做主角逐个预测邻居负采样Softmax 太慢多分类变二分类1 个正样本 (k) 个负样本层次化 SoftmaxSoftmax 太慢用二叉树组织词表建霍夫曼树(\log(V)) 次计算FastTextWord2Vec 不懂子词引入 n-gram 特征词向量 n-gram 向量平均核心脉络回顾OneHot把离散符号嵌入欧式空间但维度高且没有语义。Word2Vec利用“上下文预测”任务学习稠密词向量CBOW 偏快Skip-Gram 偏精细。负采样 / 层次化 Softmax解决大词表下的训练效率问题。FastText把子词信息也嵌入进来显著增强对未登录词和形态变化的处理能力。在更高一层语言模型LM用这些向量建模整句概率从 N-gram 发展到自回归神经网络再到 Transformer 大模型。把这条线记住再看任何 NLP 模型的 embedding 层就都会更顺畅。