AI-For-Beginners 语言建模实战:无监督训练 CBoW 与 Skip-Gram 词向量
教程人工智能机器学习深度学习【免费下载链接】AI-For-Beginners12 Weeks, 24 Lessons, AI for All!项目地址https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners点击查看免费下载本文是 AI-For-Beginners 课程 NLP 专题中「语言建模Language Modeling / Sprachmodellierung」一课的完整技术指南。你将理解为什么语义嵌入Word2Vec、GloVe本质上就是一种语言建模掌握 N-Gram、连续词袋CBoW与 Skip-Gram 三种嵌入训练方法的核心思想并基于仓库内的两个 Jupyter Notebook用 TensorFlow 与 PyTorch 从零训练出属于你自己的 Word2Vec 词向量空间最后通过近义词查询close words实验与 Skip-Gram 改造作业完成实战闭环。语言建模从「预测缺失词」到理解语言在课程前文14-Embeddings 一课中我们已经知道嵌入层Embedding Layer能把词映射为稠密低维向量Word2Vec 与 GloVe 这类语义嵌入则进一步让语义相近的词在向量空间中彼此靠近。而它们本质上是走向**语言建模Language Modeling**的第一步——即创建某种能够理解或表示语言本质的模型。语言建模的核心思想是在无标签数据集上进行无监督训练。这一点至关重要因为我们拥有海量未标注文本几乎取之不尽而有标签文本的数量永远受限于我们愿意投入的标注人力成本。最常见的做法是构建能够预测文本中缺失词的语言模型只需要在文本中随机遮掉一个词把它当作训练样本即可数据标注成本几乎为零。这种「填空」式的自监督任务正是词向量得以大规模训练的基础。三种嵌入训练方法训练嵌入有若干种思路它们的共同点都是利用上下文共现关系学习词与词之间的关联方法训练目标数学描述N-Gram 语言建模依据前 N 个词预测下一个 token用前 N 个 token 预测第 N1 个 token连续词袋CBoW依据上下文预测中间词在 token 序列 $W_{-N}, \dots, W_N$ 中预测中间 token $W_0$Skip-Gram依据中间词预测一组相邻词从中间 token $W_0$ 预测邻域集合 ${W_{-N},\dots,W_{-1},W_1,\dots,W_N}$两种 Word2Vec 架构各有取舍课程文档明确指出CBoW 训练更快而 Skip-Gram 较慢但对低频词infrequent words的表示效果更好。理解这一权衡是后续作业中选择方案时的关键依据。✍️ 实战一用 TensorFlow 训练 CBoW 模型仓库中的 CBoW-TF.ipynb 完整展示了在 TensorFlow/Keras 中从零训练 CBoW 模型的流程数据集选用 AG News新闻分类数据集。以下代码片段均取自该 Notebook 的实际实现。1. 加载数据集与定义词表from tensorflow import keras import tensorflow as tf import tensorflow_datasets as tfds import numpy as np ds_train, ds_test tfds.load(ag_news_subset).values()2. 构建 CBoW 模型CBoW 的核心任务是依据 $2N$ 个相邻词预测中间词。例如当 $N1$ 时从句子I like to train networks可以得到如下训练对第一个词是输入第二个词是要预测的目标(like,I)、(I,like)、(to,like)、(like,to)、(train,to)、(to,train)、(networks,train)、(train,networks)。模型架构非常简洁输入词经过嵌入层——这个嵌入层本身就是要训练得到的 Word2Vec 词向量因此单独定义为embedder变量本示例嵌入维度取30真实 Word2Vec 常为 300可自行调大实验嵌入向量再传入一个稠密层预测输出词神经元数量为vocab_size。vocab_size 5000 vectorizer keras.layers.experimental.preprocessing.TextVectorization(max_tokensvocab_size, input_shape(1,)) embedder keras.layers.Embedding(vocab_size, 30, input_length1) model keras.Sequential([ embedder, keras.layers.Dense(vocab_size, activationsoftmax) ]) model.summary()几点实现细节值得注意Keras 嵌入层能自动把数值输入转换为 one-hot 编码无需手动 one-hot 编码输入词input_length1表示输入序列只含一个词嵌入层通常设计用于更长序列输出层配合sparse_categorical_crossentropy损失函数时标签只需提供词的编号同样无需 one-hot 编码从model.summary()的输出可以看到参数量构成Embedding 层(None, 1, 30)共 150,000 参数Dense 层(None, 1, 5000)共 155,000 参数总计 305,000 个可训练参数vocab_size设为 5000 是为了限制计算量随后用vectorizer.adapt()从训练集前 500 条新闻中构建词表并通过get_vocabulary()取出。3. 生成 CBoW 训练样本下面这个to_cbow函数是整条数据流水线的核心给定一个句子可以是词列表也可以是已编码的向量/张量它会按窗口大小生成「上下文词 → 中心词」的样本对def to_cbow(sent, window_size2): res [] for i, x in enumerate(sent): for j in range(max(0, i-window_size), min(iwindow_size1, len(sent))): if i ! j: res.append([sent[j], x]) return res print(to_cbow([I,like,to,train,networks])) print(to_cbow(vectorizer(I like to train networks)))运行输出既展示了原始词对也展示了经由vectorizer编码后的整数编号对证明该函数可以同时作用于「词」与「张量」两种输入形式。4. 组装训练数据集并训练先遍历前 10,000 条新闻去掉该限制可训练出更好的嵌入但等待时间更长调用to_cbow生成样本对再打包为 batchX [] Y [] for i, x in zip(range(10000), ds_train.map(extract_text).as_numpy_iterator()): for w1, w2 in to_cbow(vectorizer(x), window_size1): X.append(tf.expand_dims(w1, 0)) Y.append(tf.expand_dims(w2, 0)) ds tf.data.Dataset.from_tensor_slices((X, Y)).batch(256) model.compile(optimizerkeras.optimizers.SGD(lr0.1), losssparse_categorical_crossentropy) model.fit(ds, epochs200)训练使用SGD 优化器、学习率 0.1也鼓励尝试 Adam 等其他优化器每轮 2156 个 batch、batch size 256共训练 200 个 epoch可重复执行该单元格以继续降低损失Notebook 中的实际训练日志显示 loss 从首轮约 5.61 逐步降至第 200 轮约 5.02损失持续收敛注意该 Notebook 是在 Colab 环境Python 3.8.12中运行代码中使用的keras.layers.experimental.preprocessing.TextVectorization与SGD(lr...)属于较老版本的 Keras API新版本中分别对应keras.layers.TextVectorization与SGD(learning_rate...)。5. 提取词向量并做近义词查询训练完成后embedder就是我们的 Word2Vec 嵌入。把词表中所有词的向量提取出来需要 reshape 去掉多余的维度vectors embedder(vectorizer(vocab)) vectors tf.reshape(vectors, (-1, 30))查看单词Paris被编码成的 30 维向量paris_vec embedder(vectorizer(paris))[0] print(paris_vec)再实现一个近义词查询函数计算输入词向量 $v$ 与词表中每个词向量 $w_i$ 的范数 $|w_i - v|$排序后取前 n 个最接近的词def close_words(x, n5): vec embedder(vectorizer(x))[0] top5 np.linalg.norm(vectors-vec, axis1).argsort()[:n] return [ vocab[x] for x in top5 ]Notebook 中的实际查询结果close_words(paris)→[paris, philippines, seoul, jakarta, zoo]close_words(china)→[china, russia, pakistan, israel, turkey]close_words(official)→[official, military, office, police, sources]可以看到即便只用 10,000 条新闻和 30 维嵌入训练 200 轮模型也已经捕获了「国家名聚集」「官方/权威类词聚集」等明显的语义结构。✍️ 实战二用 PyTorch 训练 CBoW 模型仓库中的 CBoW-PyTorch.ipynb 提供了同一实验的 PyTorch 版本架构与数据处理思路一致可对照学习两个框架的 API 差异。1. 加载数据集与构建词表import torch import torchtext import os import collections import builtins import random import numpy as np device torch.device(cuda if torch.cuda.is_available() else cpu) def load_dataset(ngrams1, min_freq1, vocab_size5000, lines_cnt500): tokenizer torchtext.data.utils.get_tokenizer(basic_english) test_dataset, train_dataset torchtext.datasets.AG_NEWS(root./data) train_dataset list(train_dataset) test_dataset list(test_dataset) classes [World, Sports, Business, Sci/Tech] counter collections.Counter() for i, (_, line) in enumerate(train_dataset): counter.update(torchtext.data.utils.ngrams_iterator(tokenizer(line), ngramsngrams)) if i lines_cnt: break vocab torchtext.vocab.Vocab(collections.Counter(dict(counter.most_common(vocab_size))), min_freqmin_freq) return train_dataset, test_dataset, classes, vocab, tokenizer这里同样把vocab_size限制为 5000 以控制计算量分词器为basic_english词表基于出现频率最高的 token 构建。2. 构建 CBoW 模型PyTorch 版本将嵌入层与输出线性层封装在Sequential中逻辑与 TensorFlow 版本完全对应vocab_size len(vocab) embedder torch.nn.Embedding(num_embeddingsvocab_size, embedding_dim30) model torch.nn.Sequential( embedder, torch.nn.Linear(in_features30, out_featuresvocab_size), )print(model)输出Embedding(5002, 30)与Linear(in_features30, out_features5002)——这里的 5002 是 PyTorch 词表在 5000 之外自动加入特殊 token 后的实际大小。3. 编码与训练数据准备与 TensorFlow 版一样先实现句子编码函数再复用同一个to_cbow生成样本对def encode(x, vocabulary, tokenizertokenizer): return [vocabulary[s] for s in tokenizer(x)] X [] Y [] for i, x in zip(range(10000), train_dataset): for w1, w2 in to_cbow(encode(x[1], vocab), window_size5): X.append(w1) Y.append(w2) X torch.tensor(X) Y torch.tensor(Y)注意此处训练时使用的窗口大小是5与 TensorFlow 版的window_size1不同属于实现上的差异可以自行实验对比。随后用自定义的SimpleIterableDataset封装并打乱数据经DataLoader以 batch size 256 供训练使用。4. 训练循环def train_epoch(net, dataloader, lr0.01, optimizerNone, loss_fntorch.nn.CrossEntropyLoss(), epochsNone, report_freq1): optimizer optimizer or torch.optim.Adam(net.parameters(), lrlr) loss_fn loss_fn.to(device) net.train() for i in range(epochs): total_loss, j 0, 0 for labels, features in dataloader: optimizer.zero_grad() features, labels features.to(device), labels.to(device) out net(features) loss loss_fn(out, labels) loss.backward() optimizer.step() total_loss loss j 1 if i % report_freq 0: print(fEpoch: {i1}: loss{total_loss.item()/j}) return total_loss.item()/j train_epoch(netmodel, dataloaderdl, optimizertorch.optim.SGD(model.parameters(), lr0.1), loss_fntorch.nn.CrossEntropyLoss(), epochs10)损失函数使用CrossEntropyLoss同样只需要词的编号作为标签可选用SGDlr0.1或默认的Adamlr0.01优化器先训练 10 个 epoch 起步实际训练日志显示 loss 从第 1 轮约 5.66 稳步降至第 10 轮约 5.55。5. 提取向量与近义词查询vectors torch.stack([embedder(torch.tensor(vocab[s])) for s in vocab.itos], 0) paris_vec embedder(torch.tensor(vocab[paris])) print(paris_vec) def close_words(x, n5): vec embedder(torch.tensor(vocab[x])) top5 np.linalg.norm(vectors.detach().numpy() - vec.detach().numpy(), axis1).argsort()[:n] return [ vocab.itos[x] for x in top5 ]实际查询结果示例close_words(microsoft)→[microsoft, quoted, lp, rate, top]close_words(basketball)→[basketball, lot, sinai, states, healthdaynews]close_words(funds)→[funds, travel, sydney, japan, business]与 TensorFlow 版本相同PyTorch 版也观察到词向量在语义上出现了可解释的聚类现象。结论训练词嵌入并不复杂通过上述两个 Notebook 可以看到前一课中看似「魔法」般的词嵌入14-Embeddings 课程其训练过程实际上只需要「嵌入层 一个输出层」加上巧妙的 CBoW 样本构造即可完成。课程给出的结论是训练词嵌入并不是一项复杂的任务当面对领域特定文本时我们完全有能力训练自己的词向量。同时要记住一个关键局限传统预训练嵌入如 Word2Vec是静态的——一个词的所有含义被编码进同一个向量存在词义消歧word sense disambiguation问题。例如play在「I went to aplayat the theatre.」和「John wants toplaywith his friends.」中含义截然不同却被表示为同一向量。解决之道是基于语言模型的上下文嵌入contextual embeddings课程将在后续如 Transformer、大语言模型相关章节中深入探讨。 实战作业训练 Skip-Gram 模型语言建模这一课配套的实验室作业lab/README.md要求你将 CBoW 代码改造为Skip-Gram 模型任务训练一个带嵌入层的网络使其能在 $N$ 个 token 宽的 Skip-Gram 窗口中预测相邻词。可以直接使用本课的 CBoW-TF.ipynb 代码并稍作修改——核心改动就是把样本对的构造逻辑从「上下文词 → 中心词」反转为「中心词 → 上下文词」。数据集可以使用任何一本书。Project Gutenberg 提供大量免费文本例如 Lewis Carroll 的《爱丽丝漫游奇境记》也可以用如下代码获取莎士比亚戏剧全集path_to_file tf.keras.utils.get_file( shakespeare.txt, https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt) text open(path_to_file, rb).read().decode(encodingutf-8)进阶探索方向作业原文建议嵌入大小如何影响结果质量不同的文本风格如新闻 vs 文学作品如何影响结果选取若干差异明显的词及其同义词取它们的向量表示用 PCA 降至 2 维后绘制在二维平面上——观察是否存在可识别的模式运行环境与准备工作本课属于 NLP 专题lessons/5-NLP/README.md的一部分。若使用本地 Python 环境运行需按框架安装依赖# PyTorch pip install -r lessons/5-NLP/requirements-pytorch.txt # TensorFlow pip install -r lessons/5-NLP/requirements-tf.txt另外本节部分示例会训练较大的模型课程文档给出如下 GPU 注意事项建议在GPU 机器上运行 Notebook 以减少等待时间训练大模型时可能耗尽 GPU 显存可通过减小 minibatch size缓解旧版 TensorFlow 在同一 Python 内核中训练多个模型时可能无法正确释放 GPU 显存可配置按需增长的显存分配策略physical_devices tf.config.list_physical_devices(GPU) if len(physical_devices)0: tf.config.experimental.set_memory_growth(physical_devices[0], True)复习与自学课程文档推荐了以下自学习方向均为业界公认的官方学习资源可通过搜索引擎检索到PyTorch 官方的语言建模 / 词嵌入教程TensorFlow 官方的 Word2Vec 训练教程使用gensim框架——仅需几行代码即可训练最常用的词嵌入如 Word2Vec、FastText 等。结合本课源码建议的完整学习路径是先运行 CBoW-TF.ipynb 与 CBoW-PyTorch.ipynb 建立感性认识再完成 Skip-Gram 改造作业lab/README.md最后用自己训练的向量做 PCA 可视化验证「语义相近的词在向量空间中彼此靠近」这一核心性质——至此你就完整掌握了语言建模与词向量训练的从理论到实战的闭环。赞分享教程人工智能机器学习深度学习【免费下载链接】AI-For-Beginners12 Weeks, 24 Lessons, AI for All!项目地址https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners点击查看免费下载相关推荐AI for Beginners 语言建模实战无监督训练 CBoW 与 Skip-gram 词向量全解析AI for Beginners 语言建模实战无监督训练 CBoW 与 Skip gram 词向量全解析 本篇技术指南基于 AI for Beginners教程人工智能机器学习深度学习AI-For-Beginners 语言建模实战从零训练 Word2Vec 词向量N-Gram、CBoW 与 Skip-gramAI For Beginners 语言建模实战从零训练 Word2Vec 词向量N Gram、CBoW 与 Skip gram 本文是开源课程 AI Fo教程人工智能机器学习深度学习AI-For-Beginners语言建模CBOW与Skip-gram实战AI For Beginners语言建模CBOW与Skip gram实战 引言为什么需要词嵌入 在传统的自然语言处理中我们通常使用词袋模型Bag of教程人工智能机器学习深度学习上一篇SOFAJRaft嵌入式KV存储实现从RheaKV到分布式锁的完整应用指南下一篇Laf 云数据库 database-ql 指南MongoDB 文档模型、集合与数据类型全解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考