- 教程
- 人工智能
- 机器学习
- 深度学习
【免费下载链接】AI-For-Beginners
12 Weeks, 24 Lessons, AI for All!
本文是 AI-For-Beginners 课程「5-NLP / 14-Embeddings」配套作业(assignment.md)的完整实战指南。该作业要求学习者使用本课提供的 Notebook(PyTorch 或 TensorFlow 任一版本),换成自己的数据集(例如 Kaggle 上的 Beatles 歌词文本)重新运行,改写 Notebook 突出自己的观察,并记录实验结论。读完本文后,你将掌握:如何加载自定义文本数据集并构建词表、如何用Embedding/EmbeddingBag层完成从 bag-of-words 到 embedding bag 的迁移、如何接入 Word2Vec/GloVe 预训练向量,以及如何系统地记录和呈现自己的实验发现。
作业任务解读
作业原文(英文版)要求如下三件事,这也是本文组织内容的主线:
- 换数据重跑:使用本课配套 Notebook——EmbeddingsPyTorch.ipynb 或 EmbeddingsTF.ipynb——以你自己的数据集重新执行全部流程;数据可来自 Kaggle 等公开来源,使用时须保留合适的引用/归属声明。
- 改写 Notebook:在 Notebook 中突出你自己的发现,而不是简单照抄原有步骤;例如替换数据加载与词表构建环节,调整超参数并记录差异。
- 尝试不同类型的数据并记录结果:建议尝试与课程示例(新闻文本 AG News)不同类型的语料,比如歌词文本(作业示例中提及 Beatles 歌词),并用文字记录你的观察。
作业的完整体现在「记录」而非「跑通」:换数据集后,词表规模、未登录词比例、训练损失与准确率曲线、向量语义(如近义词、类比运算)都会发生变化,这些正是需要你在改写后的 Notebook 中呈现的发现。
预备知识:本课要重跑的到底是什么
要完成作业,必须先理解课程 README.md 与两个 Notebook 中反复出现的核心概念:
- Embedding(嵌入):把词表示成低维稠密向量。此前课程中基于 BoW / TF-IDF 的分类器使用的是长度等于
vocab_size的高维稀疏 one-hot 向量,既浪费内存,又无法表达词与词之间的语义相似性。嵌入层输入一个词编号,输出一个长度为embedding_size的稠密向量,从结构上看非常接近Linear/Dense层,区别在于它可以直接接收词编号,免去构造庞大的 one-hot 向量。 - Embedding bag(嵌入袋):把嵌入层作为网络第一层后,文本中的每个词先被映射为对应嵌入,再对全部嵌入做某种聚合(
sum、average或max),得到整条文本的数值表示,最后接线性分类器。两个 Notebook 中的分类器架构都是「Embedding → 聚合 → 线性分类器」三段式。 - 变长序列处理:BoW 下所有样本向量长度固定为
vocab_size;改用词嵌入后,每条样本的词数不同,需要处理变长序列——PyTorch 版给出「padding」与「offset」两种方案,TensorFlow 版则由TextVectorization自动填充 PAD token。 - 语义嵌入与预训练向量:普通嵌入层学到的向量不一定有语义,为了让近义词在向量空间(如欧氏距离)中彼此靠近,需要在大型语料上预训练,典型方法即 Word2Vec(含 CBoW 与 skip-gram 两种架构)、GloVe、FastText。预训练词表与语料词表通常不一致,这构成了换数据集后最值得观察的现象。
先跑通官方 Notebook:以 AG News 为基线
在换数据集之前,先分别跑通两个官方 Notebook,把基线结果记下来,作为后续对比的参照。
PyTorch 版流程
PyTorch 版依赖课程提供的辅助模块 torchnlp.py,其中封装了数据集加载、词表构建、编码、padding/offset 化与训练循环。它首先用torchtext.datasets.AG_NEWS加载新闻语料,用torchtext.data.utils.get_tokenizer('basic_english')做分词,并通过torchtext.vocab.vocab(counter, min_freq=min_freq)构建词表(支持ngrams与min_freq参数),最后返回train_dataset, test_dataset, classes, vocab四个对象。
第一步:构建 Embedding 分类器。这是课程中最基础的嵌入分类器实现:
class EmbedClassifier(torch.nn.Module): def __init__(self, vocab_size, embed_dim, num_class): super().__init__() self.embedding = torch.nn.Embedding(vocab_size, embed_dim) self.fc = torch.nn.Linear(embed_dim, num_class) def forward(self, x): x = self.embedding(x) x = torch.mean(x, dim=1) return self.fc(x)前向过程即「嵌入 → 对所有词向量取均值 → 全连接分类」,是 embedding bag 思想的最简实现。
第二步:用 padding 处理变长序列。由于每条样本词数不同,需要给DataLoader提供collate_fn:
def padify(b): # b 是长度为 batch_size 的元组列表:每项形如 (label, text_sequence) v = [encode(x[1]) for x in b] # 逐条把文本向量化为词编号序列 l = max(map(len, v)) # 取小批量内最长序列的长度 return ( # 返回 (标签张量, 特征张量) torch.LongTensor([t[0]-1 for t in b]), torch.stack([torch.nn.functional.pad(torch.tensor(t), (0, l-len(t)), mode='constant', value=0) for t in v]) ) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=padify, shuffle=True)注意torch.nn.functional.pad的(0, l-len(t))表示只在序列尾部填充,value=0对应 PAD 词编号;t[0]-1把 AG News 的 1~4 标签转成 0~3 索引。官方 Notebook 中设置embed_dim=32、lr=1、epoch_size=25000(为了省时只训练不足一个 epoch),训练约 2.25 万条记录后准确率约 0.76;继续训练并微调学习率可以逼近约 90% 的准确率。
第三步:改用 EmbeddingBag 与 offset 表示。padding 会把所有序列拉齐到批内最长长度,并非最高效的表示。另一种方案是把批内所有序列拼成一个长向量,再用 offset 向量记录每条序列的起点。课程配图 offset-sequence-representation.png 直观展示了该表示(图中以字符序列为例,本课处理的是词序列,原理相同)。对应实现:
class EmbedClassifier(torch.nn.Module): def __init__(self, vocab_size, embed_dim, num_class): super().__init__() self.embedding = torch.nn.EmbeddingBag(vocab_size, embed_dim) # 内置聚合层 self.fc = torch.nn.Linear(embed_dim, num_class) def forward(self, text, off): x = self.embedding(text, off) return self.fc(x) def offsetify(b): x = [torch.tensor(encode(t[1])) for t in b] o = [0] + [len(t) for t in x] # 累加长度得到各序列起点 o = torch.tensor(o[:-1]).cumsum(dim=0) return (torch.LongTensor([t[0]-1 for t in b]), torch.cat(x), o) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=offsetify, shuffle=True)torch.nn.EmbeddingBag的输入是「拼接后的内容向量 + offset 向量」,其内置聚合方式可选mean、sum或max。注意此时 dataloader 每批返回三个值(标签、文本、offset),官方 Notebook 为此专门定义了train_epoch_emb(torchnlp.py 中亦有等价封装),训练时以lr=4、epoch_size=25000得到约 0.755 的准确率。
第四步:接入预训练语义嵌入。使用 gensim 加载在 Google News 上预训练的 Word2Vec(api.load('word2vec-google-news-300'),首次下载耗时较长),可以观察语义特性:
for w, p in w2v.most_similar('neural'): print(f"{w} -> {p}") # neuronal -> 0.780, neurons -> 0.733, neural_circuits -> 0.725 ...以及著名的向量类比:king - man + woman ≈ queen(w2v.most_similar(positive=['king','woman'], negative=['man'])[0]返回('queen', 0.7118))。
把 Word2Vec 权重灌入嵌入层时,预训练词表与语料词表大概率不一致,因此要对缺失词初始化随机权重。官方实现用net.embedding.weight[i].data = torch.normal(0.0, 1.0, (embed_size,))兜底;在 AG News 上,vocab_size=95812的词表中找到约 4.1 万词,缺 5.4 万词。官方还演示了第二种思路:直接用torchtext.vocab.GloVe(name='6B', dim=50)作为加载语料的词表,使词表与预训练向量完全对齐,再通过net.embedding.weight.data = vocab.vectors一次性灌入全部权重。换数据集后,「预训练词表覆盖率」正是你最值得量化记录的指标。
TensorFlow 版流程
TensorFlow 版(EmbeddingsTF.ipynb)用tfds.load('ag_news_subset')加载数据,模型由四层堆叠而成:
TextVectorization(max_tokens=vocab_size,input_shape=(1,)):字符串 → 词编号张量,忽略低频词;Embedding(vocab_size, 100):每个词编号 → 100 维稠密向量,n×1张量变成n×100;Lambda(lambda x: tf.reduce_mean(x, axis=1)):沿第一个轴取平均,把整个序列压成 100 维;Dense(4, activation='softmax'):线性分类器。
model.summary()显示该模型约 300 万参数,其中嵌入层占 3000000(vocab_size=30000× 100)。词表通过vectorizer.adapt(ds_train.take(500).map(extract_text))在训练数据子集上自适应构建——官方注明这是为了加速,可能漏掉部分 token 使准确率略降,但现实中小样本子集往往已能给出不错的词表估计。基线训练得到val_acc约 0.864。
变长序列方面,vectorizer('Hello, world!')得到 2 个 token,vectorizer('I am glad to meet you!')得到 6 个 token;批量向量化时自动以 PAD token(编号 0)右填充为矩形张量,例如两条样本统一变成shape=(2, 6)。官方建议:为了减少 padding 浪费,可按序列长度递增排序后再分批,让每个 batch 内长度相近。
接入 Word2Vec 同样面临词表对齐问题,官方给出两条路线:
- 用 tokenizer 词表:遍历
vectorizer.get_vocabulary(),能查到的词用w2v.get_vector(w)填充权重矩阵W,查不到的词留零或生成随机向量;然后用keras.layers.Embedding(vocab_size, embed_size, weights=[W], trainable=False)构造冻结的预训练嵌入层。官方在 30000 词表上命中约 4551 词、缺 784 词(注:trainable=False会略降准确率但显著加快训练)。 - 用预训练词表:
vocab = list(w2v.vocab.keys()),用vectorizer.set_vocabulary(vocab)重建向量化层,再用 gensim 提供的便捷函数w2v.get_keras_embedding(train_embeddings=False)直接生成 Keras 嵌入层,从而彻底绕开两套词表不一致的问题。
两种实现最终都指向同一个结论:语料中的词在预训练词表中缺失越多,直接冻结预训练嵌入带来的收益越有限——这恰好是作业希望你用新数据集去验证的点。
换数据集重跑:以歌词类文本为例
官方 Notebook 的基线数据是新闻文本(AG News,4 类)。作业明确建议尝试不同类型的语料,比如歌词(Beatles 歌词即为作业给出的示例方向)。换数据集后的改造点集中在以下环节,可对照 Notebook 逐段替换:
1. 数据加载与标注。歌词数据集通常没有现成的四分类标签,常见的可行方案包括:按乐队/歌手分组(如把多支乐队的歌词分为几类)、按年代/专辑分组,或把任务改为二分类(如「是否为某乐队的歌词」)。替换torchtext.datasets.AG_NEWS或tfds.load('ag_news_subset')的加载代码即可,注意公开数据集需保留出处与归属声明。
2. 词表规模与参数。歌词词汇量远小于新闻语料。以官方 AG News 为例,PyTorch 版词表达 95812 词;换成歌词语料后词表通常缩至数千到数万量级。相应地可以:调低vocab_size(TensorFlow 版默认 30000)、调整min_freq(torchtext.vocab.vocab的最低词频过滤)、缩小embed_dim(官方示例用 32,预训练向量则固定为 300 或 50)。这些参数的每一次调整都应在改写后的 Notebook 中记录。
3. 训练循环与超参数。官方train_epoch/train_epoch_emb(见 torchnlp.py 与 torchnlp.py)支持lr、optimizer、loss_fn、epoch_size、report_freq等参数。歌词语料通常更小,建议跑满多个 epoch 并尝试不同学习率;report_freq控制打印进度的频率(官方为 200 条一个 batch 时每 200 批打印一次)。TensorFlow 版则直接通过model.fit(..., epochs=n)控制训练轮数。
4. 预训练向量的覆盖观测。这是最能体现「自己的发现」的环节:加载 Word2Vec/GloVe 后,统计你的词表中有多少词命中预训练词表、多少缺失,并观察不同数据类型(新闻 vs 歌词)命中率的差异。歌词中的俚语、缩略词、口语化表达通常覆盖率偏低,这可以直接解释「为何预训练嵌入带来的准确率提升有限」。
5. 语义操作演示。把most_similar与类比运算换成你的语料词:在歌词语料中考察近义词(如 'love'、'heart' 的邻近词)、验证king - man + woman类类比在你的词表上是否成立,并在 Notebook 中贴出向量距离的数值结果。
如何记录与呈现你的发现(作业评分关键)
作业的「重跑 + 改写 + 记录」要求最终产物是一份有观察、有对比、有结论的 Notebook。建议按以下框架组织:
- 基线对照表:先记录官方 AG News 基线的词表大小、参数、准确率,再记录自定义数据集上的对应数值,逐项对比。
- 观察一:词表结构差异。新闻 vs 歌词的词表规模、
min_freq过滤效果、常见词分布。 - 观察二:padding 与 offset 两种表示的影响。批内最长序列长度、padding 占比、训练速度与最终准确率是否有差异(PyTorch 版两条路径可直接对比)。
- 观察三:预训练嵌入的价值。命中率、冻结 vs 微调(
trainable=True/False)、embed_dim=300(Word2Vec)或 50(GloVe 6B)对训练时间与过拟合的影响。 - 观察四:语义验证。在你的语料中运行
most_similar与类比运算,展示输出并解释结果是否符合语义直觉。 - 限制与结论:基于上述数据,用文字总结「这类数据集在何种方案下表现更好,为什么」。
常见问题与排查要点
EmbeddingBag与Embedding的输入差异:EmbeddingBag接收拼接后的内容向量与 offset 两个输入,DataLoader返回三元组,必须使用适配的训练循环(PyTorch 版为train_epoch_emb),否则会因参数个数不匹配报错。- 词表不一致导致预训练向量无效:优先用「以预训练词表加载语料」的方案(PyTorch 用 GloVe 词表编码、TensorFlow 用
set_vocabulary+get_keras_embedding),并如实记录命中率。 - 过拟合:换用预训练嵌入后嵌入层参数量剧增(如 300 维),官方明确提示需要更多训练样本避免过拟合;歌词类小语料上建议减少
embed_dim或对嵌入层加冻结/正则。 - 下载耗时:Word2Vec Google News 300 维向量体积较大,首次
api.load或torchtext.vocab.GloVe下载可能需要较长时间,属正常现象,可在离线前先缓存。 - 数据归属:使用 Kaggle 等来源的数据集时,须在 Notebook 中保留数据集的引用与授权说明,这是作业的显式要求。
小结
本作业的价值不在于把官方 Notebook 原样再跑一遍,而在于「换一个数据域之后,词嵌入方案的每个环节会发生什么、为什么」。以本课两个官方 Notebook(PyTorch / TensorFlow)为起点,配合 torchnlp.py 辅助模块,替换数据加载、调整词表与嵌入参数、接入预训练向量,最后把词表覆盖率、训练曲线与语义类比结果整理成可复现的观察记录,你就完成了从「跑通示例」到「有自己结论的实践」的跨越。
- 教程
- 人工智能
- 机器学习
- 深度学习
【免费下载链接】AI-For-Beginners
12 Weeks, 24 Lessons, AI for All!
相关推荐
AI for Beginners:用自定义数据集重跑嵌入(Embeddings)实验——PyTorch 与 TensorFlow 双框架实战指南
AI for Beginners:用自定义数据集重跑嵌入(Embeddings)实验——PyTorch 与 TensorFlow 双框架实战指南 在 AI Fo
教程人工智能机器学习深度学习AI-For-Beginners 文本表示实战:用 PyTorch/TensorFlow 笔记本复跑自定义数据集,完成 UFO 目击文本分类
AI For Beginners 文本表示实战:用 PyTorch/TensorFlow 笔记本复跑自定义数据集,完成 UFO 目击文本分类 本文对应课程 13
教程人工智能机器学习深度学习词嵌入(Embeddings)实战:从 Embedding 层到 Word2Vec 语义向量——AI-For-Beginners NLP 课程第十四课
词嵌入(Embeddings)实战:从 Embedding 层到 Word2Vec 语义向量——AI For Beginners NLP 课程第十四课 导读 本
教程人工智能机器学习深度学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考