上篇回顾与本文预告在上篇文章中我们走完了 Embedding 技术的前两个阶段One-hot 编码和词袋模型BoW。我们看到了从“给词编号”到“统计词频”的演进也明白了它们共同的致命缺陷——无法理解语义。从本文开始我们将进入 Embedding 技术的核心篇章。你会看到Word2vec 如何用神经网络让机器第一次学会了“语义”。它为什么能算出“国王-男人女人≈女王”这样的关系。BERT 又是如何突破 Word2vec 的局限真正理解了“一词多义”。本文章更多作为笔者的学习记录和总结有错误地方可以提出。第四章Word2vec——词向量的真正开端4.1 从统计到预测的思想转变在上一章我们讲到词袋模型BoW及其优化版本 TF-IDF本质上都还停留在统计层面。它们知道苹果和香蕉经常出现但无法理解它们都是水果。计算机只是机械地数着词频却不懂词的含义。这种统计范式的转折点发生在 2013 年。这一年Google 的 Tomas Mikolov 等人提出了Word2vec它第一次让机器学会了语义——知道苹果和香蕉是同类而不是两个毫无关联的符号。核心转变从统计词出现了几次 → 到预测词可能出现在哪里4.2 核心思想看词的朋友圈知词的含义Word2vec 的核心思想可以用一句话概括看一个词的朋友圈就知道它是什么样的人。也就是说通过词出现的上下文邻居词来学习词的含义。句子我 喜欢 看 电影 喜欢 的朋友圈是[我, 看, 电影]→ 模型通过这些朋友来理解喜欢的含义为什么这种方法有效词经常一起出现的词能推断出的含义苹果吃、水果、甜是一种食物/水果苹果手机、公司、发布是一家科技公司银行存钱、贷款、利息金融机构河岸河流、水、钓鱼水边的陆地同一个词在不同语境中有不同的朋友圈所以会学到不同的含义——这正是 Word2vec 后来被 BERT 超越的地方我们会在第五章详细讲。4.3 两种训练方式CBOW 和 Skip-gramWord2vec 有两种训练方式就像两种不同的学习方法方式一CBOW连续词袋——完形填空任务根据上下文猜中间被遮住的词是什么。句子我 [???] 看 电影 上下文[我, 看, 电影] → 模型预测[???] 喜欢 训练过程 输入[我, 看, 电影] → 输出喜欢通俗理解就像英语考试里的完形填空给你前后文让你填中间的空。方式二Skip-gram——词语联想任务根据中心词猜它前后可能出现什么词。text句子我 喜欢 看 电影 中心词喜欢 → 模型预测[我, 看, 电影] 训练过程 输入喜欢 → 输出[我, 看, 电影]通俗理解就像老师说苹果你想到红色、甜、水果这些相关词。两种方式的对比对比CBOW完形填空Skip-gram词语联想输入上下文多个词中心词 1 个输出中心词 1 个上下文多个词训练速度快慢约为 CBOW 的 2-3 倍对罕见词的效果一般更好适用场景数据量大数据量小 / 罕见词重要实际选择如果数据量很大用 CBOW 更快如果数据量较小或需要处理罕见词Skip-gram 效果更好。一般推荐 Skip-gram。4.4 两个关键优化负采样和分层 Softmax你可能听说过深度学习的训练需要大量计算。Word2vec 的原始设计确实会面临一个巨大的计算问题词典大小 10 万个词 每次训练需要计算10 万个词 × 100 维 1000 万次运算 每句话都要算 → 太慢了神经网络训练时每看到一个训练样本比如我 喜欢 看 电影都需要计算所有词和当前词的关系。词典越大计算量越大根本无法实际使用。为了解决这个问题Word2vec 引入了两个关键优化技术。4.4.1 负采样Negative Sampling核心思想不计算所有词只算几个坏榜样。传统方法计算苹果和词典中所有 10 万个词的关系 → 10 万次计算负采样方法正样本苹果和吃一起出现 → 学负样本随机选 5 个词- 苹果和政府没一起出现过- 苹果和火车没一起出现过- 苹果和银行没一起出现过- 苹果和战争没一起出现过- 苹果和音乐没一起出现过→ 只算 6 次快了 1.6 万倍通俗理解学英语时不用背apple和所有其他词的搭配只需要知道apple和eatred这些常用搭配就够了。4.4.2 分层 SoftmaxHierarchical Softmax核心思想把词组织成一棵决策树不用遍历所有词。传统方法预测时要计算 10 万个词每个的概率 → 10 万次计算分层 Softmax把词组织成二叉树[所有词]/ \[水果类] [科技类]/ \ / \[苹果] [香蕉] [电脑] [手机]预测苹果时1. 判断在左子树还是右子树 → 1 次2. 判断在水果类下还是科技类下 → 1 次3. 具体到苹果 → 1 次→ 只需要 log2(10万) ≈ 17 次计算通俗理解不用在一堆东西里挨个找而是像查字典一样先判断是水果还是科技再继续细分。两种优化的本质优化技术解决问题效果负采样每次训练计算量太大从 10 万次降到 5-15 次分层 Softmax预测时计算量太大从 10 万次降到约 17 次⚠️这两种优化让 Word2vec 的训练从理论上可行变成了实际可用。4.5 Word2vec 的突破与局限4.5.1 Word2vec 的三个突破突破一低维稠密向量方法向量维度特点One-hot10 万维稀疏大部分是 0词袋模型10 万维稀疏Word2vec100-300 维稠密每个维度都有值突破二语义理解Word2vec 能发现词与词之间的关系苹果 和 香蕉 的向量很接近 → 它们都是水果喜欢 和 爱 的向量很接近 → 它们都是情感词突破三词向量运算国王 - 男人 女人 ≈ 王后 北京 - 中国 日本 ≈ 东京 好 - 很好 非常好 ≈ 表示程度递进这是 Word2vec 最神奇的特性词向量不仅能表示词还能进行数学运算发现词之间的逻辑关系。4.5.2 Word2vec 的局限致命缺陷静态向量Word2vec 给每个词只生成一个固定的向量不管它在什么语境中句子1我吃了苹果 → 苹果 [0.2, 0.8, 0.1, ...]句子2苹果公司发布新手机 → 苹果 [0.2, 0.8, 0.1, ...]完全一样但人类知道句子1中的苹果是水果 句子2中的苹果是科技公司 同一个词在不同语境中含义不同但 Word2vec 无法区分。这个问题正是 BERT 要解决的核心。4.6 python代码下面我们用一段完整的代码直观地体验 Word2vec 的训练过程和效果。import jieba from gensim.models import Word2Vec import numpy as np from sklearn.manifold import TSNE import matplotlib.pyplot as plt import matplotlib # # 0. 修复中文显示问题 # # 设置中文字体Windows matplotlib.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, Arial Unicode MS] matplotlib.rcParams[axes.unicode_minus] False # # 1. 准备更大的中文语料增加数据量 # print( * 60) print(1. 准备更大的语料) print( * 60) # 扩增语料重复并变化 sentences [ # 水果类 苹果很好吃, 香蕉也很好吃, 我喜欢吃苹果, 我喜欢吃香蕉, 苹果和香蕉都是水果, 水果对身体好, 每天吃水果有益健康, 苹果很甜, 香蕉很软, 水果很新鲜, # 科技公司类 苹果公司很厉害, 科技公司很赚钱, 公司发展很快, 苹果发布了新手机, 科技改变世界, 公司需要创新, 互联网公司发展迅速, 科技产品很受欢迎, 人工智能改变生活, 大数据很重要, # 电影类 我喜欢看电影, 看电影很有趣, 这部电影很好看, 这部电影很感人, 看电影可以放松, 这部影片很精彩, 电影特效很棒, 演员表演很出色, 剧情很吸引人, 值得一看的好电影, # 重复一些句子增加数据量 ] * 5 # 重复5次增加到150条 print(f共 {len(sentences)} 条文本) print(前5条:) for i, text in enumerate(sentences[:5], 1): print(f{i}. {text}) # # 2. 中文分词 # print(\n * 60) print(2. 中文分词) print( * 60) # 使用jieba分词并自定义词典确保苹果公司被拆开 jieba.suggest_freq(苹果公司, True) # 让苹果公司可以被拆开 tokenized_sentences [] for text in sentences: words list(jieba.cut(text)) tokenized_sentences.append(words) print(f分词完成共 {len(tokenized_sentences)} 条) print(示例分词:) for i in range(3): print(f{i 1}. {tokenized_sentences[i]}) # # 3. 训练 Word2vec 模型 # print(\n * 60) print(3. 训练 Word2vec 模型数据量增大) print( * 60) model Word2Vec( sentencestokenized_sentences, vector_size100, # 词向量维度 window5, # 增大窗口捕获更多上下文 min_count2, # 至少出现2次才保留过滤罕见词 workers1, sg1, # Skip-gram epochs100 ) print(训练完成) print(f词典大小: {len(model.wv)} 个词) print(f词向量维度: {model.wv.vector_size}) print(f\n词典中的词前20个: {list(model.wv.index_to_key)[:20]}) # # 4. 找相似词现在应该更准了 # print(\n * 60) print(4. 寻找相似词更好的结果) print( * 60) # 找苹果的相似词 if 苹果 in model.wv: similar model.wv.most_similar(苹果, topn8) print(与 苹果 最相似的词:) for word, score in similar: print(f {word}: {score:.4f}) print() # 找电影的相似词 if 电影 in model.wv: similar model.wv.most_similar(电影, topn8) print(与 电影 最相似的词:) for word, score in similar: print(f {word}: {score:.4f}) print() # 找公司的相似词 if 公司 in model.wv: similar model.wv.most_similar(公司, topn8) print(与 公司 最相似的词:) for word, score in similar: print(f {word}: {score:.4f}) # # 5. 词相似度计算现在应该更高了 # print(\n * 60) print(5. 计算词相似度) print( * 60) pairs [ (苹果, 香蕉), (电影, 影片), (公司, 科技), (好吃, 很甜), (喜欢, 爱) ] for word1, word2 in pairs: if word1 in model.wv and word2 in model.wv: sim model.wv.similarity(word1, word2) print(f{word1} 和 {word2} 的相似度: {sim:.4f}) else: print(f{word1} 或 {word2} 不在词典中) # # 6. 找出不匹配词 # print(\n * 60) print(6. 找出不匹配的词) print( * 60) test_lists [ [苹果, 香蕉, 公司, 电影], [喜欢, 爱, 很甜, 科技], ] for word_list in test_lists: available [w for w in word_list if w in model.wv] if len(available) 3: odd model.wv.doesnt_match(available) print(f在 {available} 中不匹配的词是: {odd}) # # 7. 词向量运算 # print(\n * 60) print(7. 词向量运算) print( * 60) if all(w in model.wv for w in [苹果, 好吃, 好看]): try: result model.wv.most_similar( positive[苹果, 好看], negative[好吃], topn3 ) print(苹果 - 好吃 好看 ≈ ?) for word, score in result: print(f {word}: {score:.4f}) except: print(词向量运算需要更多数据) # # 8. 词向量可视化修复中文显示 # print(\n * 60) print(8. 词向量可视化) print( * 60) words_to_plot [苹果, 香蕉, 公司, 电影, 好吃, 喜欢, 科技, 水果] available_words [w for w in words_to_plot if w in model.wv] if len(available_words) 3: print(f将可视化 {len(available_words)} 个词: {available_words}) # 提取词向量 word_vectors np.array([model.wv[word] for word in available_words]) # t-SNE降维 tsne TSNE(n_components2, random_state42, perplexitymin(4, len(available_words) - 1)) vectors_2d tsne.fit_transform(word_vectors) # 绘制现在能显示中文 plt.figure(figsize(12, 8)) # 不同类别用不同颜色 colors [red, red, blue, blue, green, green, blue, red] for i, word in enumerate(available_words): x, y vectors_2d[i] plt.scatter(x, y, s100, ccolors[i % len(colors)]) plt.annotate(word, (x, y), fontsize14, weightbold) plt.title(Word2vec 词向量可视化t-SNE降维, fontsize16) plt.xlabel(维度1, fontsize12) plt.ylabel(维度2, fontsize12) plt.grid(True, alpha0.3) plt.tight_layout() plt.show() else: print(可用词太少无法可视化) # # 9. 总结 # print(\n * 60) print(9. 总结) print( * 60) print(f词典大小: {len(model.wv)} 个词) print(f每个词用 {model.wv.vector_size} 维向量表示) if 苹果 in model.wv and 香蕉 in model.wv: sim model.wv.similarity(苹果, 香蕉) print(f苹果和香蕉的相似度: {sim:.4f}应该0.5才算好) if sim 0.5: print(模型效果不错相似词聚集在一起了) else: print(相似度偏低需要更多训练数据) print(\n * 60)一句话总结Word2vec 让机器第一次学会了语义但它给每个词只发一张固定证件照无法区分苹果是水果还是公司。这个缺陷催生了 BERT 的诞生。第五章BERT——真正理解上下文的革命在上一章我们讲到Word2vec 让机器第一次学会了语义但它有一个致命缺陷静态向量。Word2vec 给每个词只生成一个固定的向量不管它出现在什么语境中。让我们再看一次这个问题句子1我吃了一个苹果 → 苹果 [0.2, 0.8, 0.1, ...] 句子2苹果公司发布了新手机 → 苹果 [0.2, 0.8, 0.1, ...] ↑ 完全一样但人类知道句子1中的苹果是水果句子2中的苹果是科技公司。同一个词在不同语境中含义不同但 Word2vec 无法区分。这就是 NLP 领域著名的一词多义Polysemy问题。5.1.1 静态向量的更多局限问题说明示例一词多义同一词在不同语境含义不同苹果 水果 / 公司上下文无关不考虑词在句子中的角色狗咬人 vs 人咬狗近义词混淆无法区分细微差别高兴 vs 兴奋 vs 愉快长距离依赖只能看窗口内的词句子开头的词影响结尾的含义5.1.2 从静态到动态思想的跨越Word2vec 到 BERT 的核心演进是Word2vec一个词 → 一个向量固定 BERT一个词 上下文 → 一个向量动态本质区别Word2vec 是查字典查了就有固定答案BERT 是读句子边读边理解。5.2 BERT 的核心思想BERTBidirectional Encoder Representations from Transformers由 Google 在 2018 年提出它的核心思想可以概括为三个关键词双向 上下文感知 预训练微调5.2.1 双向理解Word2vec 虽然同时看前后文但它是浅层的看只有1层网络。BERT 是深层的看12-24层网络。Word2vec只看窗口内的词句子我 昨天 在 超市 买了 很多 水果其中 最 喜欢 的 是 苹果以苹果为中心窗口5看前后各5个“苹果”只能看到左边5个词水果其中 最 喜欢 的看不到开头我 昨天 在 超市 买了 很多不知道前面提到“水果”这个类别信息被弱化了Word2vec 的局限性只能看一小段看不到远处的上下文。BERT看整个句子句子我 昨天 在 超市 买了 很多 水果其中 最 喜欢 的 是 苹果苹果同时关注所有词我 ← 弱关联谁买的昨天 ← 弱关联什么时候超市 ← 中等关联在哪里买买了 ← 强关联动作很多 ← 中等关联数量水果 ← 强关联类别其中 ← 中等关联指代最 ← 弱关联程度喜欢 ← 强关联情感的 ← 弱关联助词是 ← 中等关联系动词苹果 ← 自身→ 苹果知道自己是水果类别因为看到了开头的水果5.2.2 上下文感知同一个词在不同句子中BERT 会生成不同的向量句子1我吃了一个苹果 → BERT 给苹果的向量 偏向水果方向 句子2苹果公司发布了新手机 → BERT 给苹果的向量 偏向科技方向这就是动态向量向量随上下文变化。5.2.3 预训练 微调BERT 的训练分为两个阶段阶段做什么用多少数据需要多少时间预训练在海量文本上学习语言几十亿词几周用大量 GPU微调在特定任务上调整几千到几万条几小时用少量 GPU预训练是 Google 做的我们只需要直接下载预训练好的模型然后在自己的任务上微调即可。5.3 BERT 的两种预训练任务BERT 的预训练由两个任务组成5.3.1 任务一MLMMasked Language Model掩码语言模型核心思想随机遮住句子中 15% 的词让模型预测被遮住的是什么。原句我喜欢看电影 遮住我 [MASK] 看电影 任务预测 [MASK] 处是什么词 → 喜欢为什么叫完形填空这就像英语考试里的完形填空给你前后文让你填中间的空。但 BERT 比传统的完形填空更强对比传统完形填空BERT 的 MLM看的方向通常只看左边同时看左右两边理解方式基于规则基于深层神经网络MLM 让 BERT 学会了双向理解——同时看左边和右边的词来预测中间的词。5.3.2 任务二NSPNext Sentence Prediction下一句预测核心思想给模型两句话判断第二句是否是第一句的下一句。句子A我喜欢看电影 句子B这部电影很好看 任务判断 B 是否是 A 的下一句 → 是 ✅ 句子A我喜欢看电影 句子B今天天气很好 任务判断 B 是否是 A 的下一句 → 否 ❌为什么需要 NSP很多 NLP 任务如问答、对话需要理解句子之间的关系。NSP 让 BERT 学会了句子级别的理解。5.4 BERT 与 Word2vec 的本质区别维度Word2vecBERT网络深度1 层12-24 层看多远窗口内5-10 个词整个句子512 个词词向量静态永远不变动态随上下文变化一词多义❌ 无法处理✅ 完全能处理训练方式从零训练预训练 微调推理速度快慢需要 GPU模型大小几十 MB几百 MB - 几 GB一句话总结Word2vec 给每个词发一张固定证件照无论何时何地都一样BERT 给每个词拍一张生活照根据语境动态变化。5.5 python代码示例import os os.environ[HF_ENDPOINT] https://hf-mirror.com import torch from transformers import BertTokenizer, BertModel print(*60) print(BERT 基础使用示例使用镜像源) print(*60) # # 1. 加载预训练模型和分词器 # print(\n1. 加载 BERT 模型和分词器) print(-*40) model_name bert-base-chinese # 现在可以顺利下载了 tokenizer BertTokenizer.from_pretrained(model_name) model BertModel.from_pretrained(model_name) print(✅ 模型加载成功) print(f模型名称: {model_name}) print(f模型层数: {model.config.num_hidden_layers}) print(f隐藏层维度: {model.config.hidden_size}) # # 2. 测试基本功能 # print(\n *60) print(2. 测试基本功能) print(*60) sentence 我喜欢看电影 inputs tokenizer(sentence, return_tensorspt) print(f句子: {sentence}) print(fToken IDs: {inputs[input_ids][0].tolist()}) print(fToken: {tokenizer.convert_ids_to_tokens(inputs[input_ids][0])}) with torch.no_grad(): outputs model(**inputs) print(f词向量维度: {outputs.last_hidden_state.shape}) print(✅ 测试成功) print(\n *60) print(训练完成) print(*60)一句话总结BERT 通过深层双向 Transformer让机器第一次真正理解了词的上下文含义解决了 Word2vec 无法处理的一词多义问题。第六章简单总结从 One-hot 到 BERTEmbedding 技术走过了从“给词编号”到“理解词义”的完整进化。One-hot 解决了文字数字化的问题却带来了维度灾难词袋模型用计数降低了维度却丢失了语义Word2vec 让机器第一次学会了“苹果≈香蕉”却无法区分“苹果”是水果还是公司BERT 用深层双向网络实现了上下文感知让同一个词在不同语境中有了不同含义。每一次突破都是在解决上一步的缺陷。技术演进从未停止但理解这条脉络就抓住了 NLP 的核心。希望这篇文章能帮你理清 Embedding 的来龙去脉。上篇回顾从 One-hot 到 BERT搞懂Embedding上-CSDN博客本文参考一文搞懂one-hot和embedding_one hot-CSDN博客AI词袋模型Bag of Words, BoW详解-CSDN博客【Transformer系列】深入浅出理解Embedding词嵌入_transformer embedding-CSDN博客