- 文档
- 教程
- 人工智能
- 深度学习
- NLP
- 计算机视觉
- 强化学习
【免费下载链接】d2l-en
Interactive deep learning book with multi-framework code, math, and discussions. Adopted at 500 universities from 70 countries including Stanford, MIT, Harvard, and Cambridge.
本文围绕《动手学深度学习》(d2l-en)仓库中的 相似性与类比章节 展开:以TokenEmbedding加载大规模语料预训练的 GloVe 与 fastText 词向量,用余弦相似度实现knn近邻检索,从而完成同义词查找与a:b::c:d词类比推理,并借助 PCA 将语义关系降维可视化。读完本文,你将掌握预训练词向量的加载、检索与复用的完整套路,可直接迁移到情感分析、机器翻译等下游 NLP 任务中。
1. 从训练 word2vec 到使用预训练词向量
在 word2vec 章节 中,我们曾在小型数据集(PTB 语料)上从零训练 word2vec 词嵌入模型,并用词向量的余弦相似度检索同义词。其核心依据是:one-hot 向量无法表达词间相似度,而低维稠密的分布式表示使语义相近的词在向量空间中距离更近。
然而在实际工程中,从零训练词向量既昂贵又难以获得高质量结果。规模语料上预训练好的词向量,往往可以直接迁移到下游 NLP 任务(如文本分类、序列标注、机器翻译)作为初始化或静态特征。本节演示如何直接使用这些预训练词向量完成同义词与类比的查找,后续章节(如 BERT 预训练、情感分析)还会继续复用这一套加载与检索机制。
2. 数据源与下载:GloVe 与 fastText 预训练向量
本文使用的预训练词向量有两类:
- GloVe:提供 50、100、300 维三档(
glove.6b.50d、glove.6b.100d、glove.42b.300d),分别在 60 亿 token 与 420 亿 token 的语料上训练得到; - fastText:支持多种语言,本文使用英文
wiki.en(300 维),训练语料为 Wikipedia。
仓库在 d2l/mxnet.py 中为这些数据资源注册了下载条目,每条记录由「下载 URL 前缀 + 文件名」和「SHA-1 校验值」两部分组成:
d2l.DATA_HUB['glove.6b.50d'] = (d2l.DATA_URL + 'glove.6B.50d.zip', '0b8703943ccdb6eb788e6f091b8946e82231bc4d') d2l.DATA_HUB['glove.6b.100d'] = (d2l.DATA_URL + 'glove.6B.100d.zip', 'cd43bfb07e44e6f27cbcc7bc9ae3d80284fdaf5a') d2l.DATA_HUB['glove.42b.300d'] = (d2l.DATA_URL + 'glove.42B.300d.zip', 'b5116e234e9eb9076672cfeabf5469f3eec904fa') d2l.DATA_HUB['wiki.en'] = (d2l.DATA_URL + 'wiki.en.zip', 'c1816da3821ae9f43899be655002f6c723e91b88')上述DATA_HUB与DATA_URL的定义位于 d2l/mxnet.py。底层下载与解压由d2l.download_extract(name)完成:它先按 SHA-1 校验值检查本地缓存,命中则直接复用,否则下载并解压 zip 包(实现见 d2l/mxnet.py)。这意味着首次实例化预训练词向量时会自动联网下载,之后再次使用则直接读取缓存,不会重复下载。
使用前提说明:GloVe 与 fastText 为第三方公开数据,下载地址指向其官方发布渠道;由于文件体积较大(300 维 GloVe 达数百 MB),请确保网络可达且磁盘空间充足。
3. TokenEmbedding:预训练词向量的统一加载接口
为统一加载上述预训练词向量,d2l-en 定义了TokenEmbedding类(源码位于 d2l/mxnet.py),其完整实现如下:
class TokenEmbedding: """Token Embedding.""" def __init__(self, embedding_name): self.idx_to_token, self.idx_to_vec = self._load_embedding( embedding_name) self.unknown_idx = 0 self.token_to_idx = {token: idx for idx, token in enumerate(self.idx_to_token)} def _load_embedding(self, embedding_name): idx_to_token, idx_to_vec = ['<unk>'], [] data_dir = d2l.download_extract(embedding_name) # GloVe website: https://nlp.stanford.edu/projects/glove/ # fastText website: https://fasttext.cc/ with open(os.path.join(data_dir, 'vec.txt'), 'r') as f: for line in f: elems = line.rstrip().split(' ') token, elems = elems[0], [float(elem) for elem in elems[1:]] # Skip header information, such as the top row in fastText if len(elems) > 1: idx_to_token.append(token) idx_to_vec.append(elems) idx_to_vec = [[0] * len(idx_to_vec[0])] + idx_to_vec return idx_to_token, d2l.tensor(idx_to_vec) def __getitem__(self, tokens): indices = [self.token_to_idx.get(token, self.unknown_idx) for token in tokens] vecs = self.idx_to_vec[d2l.tensor(indices)] return vecs def __len__(self): return len(self.idx_to_token)该类包含三个核心设计,理解它们对正确使用预训练词向量至关重要:
(1)未知词(<unk>)兜底。加载时先在idx_to_token头部插入<unk>,并在矩阵最前面补一行全零向量(idx_to_vec = [[0] * len(idx_to_vec[0])] + idx_to_vec)。token_to_idx构建为token -> 索引的字典,__getitem__用token_to_idx.get(token, self.unknown_idx)完成查表:任何未登录词都会被映射到unknown_idx = 0(即<unk>),返回全零向量,避免越界错误。
(2)跳过表头行。GloVe 与 fastText 的vec.txt文本格式为每行「token + 空格分隔的浮点数向量」,但 fastText 文件首行是「词数 维度」的头部信息。通过if len(elems) > 1判断即可跳过这类头部行,使同一份加载逻辑兼容两种格式。
(3)双索引结构。idx_to_token(索引→词)与token_to_idx(词→索引)互为逆映射,分别服务于「取向量」与「查词表」两类操作;__len__返回词典规模,包含词汇数与<unk>两个来源。
实例化 50 维 GloVe(基于 Wikipedia 子集预训练)并查看词典规模:
glove_6b50d = TokenEmbedding('glove.6b.50d') len(glove_6b50d) # 400001:400,000 个词 + 1 个特殊 unknown 标记词典约含 40 万词。词与索引的双向转换示例:
glove_6b50d.token_to_idx['beautiful'], glove_6b50d.idx_to_token[3367]4. 用 k 近邻(knn)与余弦相似度查找同义词
同义词查找的本质是:给定查询词,在全词典的向量空间中找出与其余弦相似度最高的 k 个词。余弦相似度公式为
$$\frac{\mathbf{x}^\top \mathbf{y}}{|\mathbf{x}| |\mathbf{y}|} \in [-1, 1],$$
其取值与向量模长无关,只反映方向一致性,因此比欧氏距离更适合衡量语义相似性(该思想也用于 word2vec 训练,见 word2vec.md)。
为使后续类比查找也能复用近邻检索逻辑,原文档将这部分独立封装为knn函数:
def knn(W, x, k): # The added 1e-9 is for numerical stability cos = np.dot(W, x.reshape(-1,)) / ( np.sqrt(np.sum(W * W, axis=1) + 1e-9) * np.sqrt((x * x).sum())) topk = npx.topk(cos, k=k, ret_typ='indices') return topk, [cos[int(i)] for i in topk]实现要点:
- 查询向量
x先reshape(-1,)为 1 维;W的每一行是一个词的词向量,np.dot(W, x)批量算出词典全部词的分子; - 分母为各词向量范数与查询向量范数的乘积,
+1e-9防止零向量(如<unk>的全零向量)导致除零,保证数值稳定性; npx.topk(cos, k=k, ret_typ='indices')一次性返回相似度最高的 k 个索引。
在此基础上定义同义词检索函数(与 word2vec 训练章节 中在训练后模型上检索的思路一致,区别在于这里作用于预训练词向量表embed.idx_to_vec):
def get_similar_tokens(query_token, k, embed): topk, cos = knn(embed.idx_to_vec, embed[[query_token]], k + 1) for i, c in zip(topk[1:], cos[1:]): # Remove input words print(f'cosine sim={float(c):.3f}: {embed.idx_to_token[int(i)]}')注意两个细节:
- 检索
k + 1个近邻后从topk[1:]开始输出,剔除查询词自身(查询词与自己的余弦相似度必然最高); <unk>词向量为全零,其相似度计算由knn中的1e-9兜底,不会引发数值错误。
在glove_6b50d上分别查找 "chip"、"baby"、"beautiful" 的 3 个近义词:
get_similar_tokens('chip', 3, glove_6b50d) get_similar_tokens('baby', 3, glove_6b50d) get_similar_tokens('beautiful', 3, glove_6b50d)可观察到:输出词与查询词的余弦相似度数值接近、语义相关(如 "beautiful" 会返回 "gorgeous"、"lovely" 一类形容词),这正体现了预训练词向量捕捉语义相似性的能力。
5. 词类比推理:a
:c:d 的向量算术
类比查找是预训练词向量最经典的「涌现能力」之一。形如man : woman :: son : daughter的类比关系,可形式化定义为:给定关系a : b :: c : d中的前三个词a、b、c,求d。
其背后的向量假设是:类比关系在向量空间中对应一个恒定偏移向量,即vec(b) - vec(a) ≈ vec(d) - vec(c)。因此d应取与结果向量vec(c) + vec(b) - vec(a)余弦相似度最高的那个词(不含<unk>与输入词)。
def get_analogy(token_a, token_b, token_c, embed): vecs = embed[[token_a, token_b, token_c]] x = vecs[1] - vecs[0] + vecs[2] topk, cos = knn(embed.idx_to_vec, x, 1) return embed.idx_to_token[int(topk[0])] # Remove unknown words逐行解读:
embed[[token_a, token_b, token_c]]批量取出三个词的向量,形状为(3, 词向量维度);x = vecs[1] - vecs[0] + vecs[2]对应vec(b) - vec(a) + vec(c);- 调用
knn取相似度最高的 1 个索引,映射回词表得到答案。
用 GloVe 6B.50d 验证四类经典类比:
get_analogy('man', 'woman', 'son', glove_6b50d) # 期望 daughter(男-女) get_analogy('beijing', 'china', 'tokyo', glove_6b50d) # 期望 japan(首都-国家) get_analogy('bad', 'worst', 'big', glove_6b50d) # 期望 biggest(形容词-最高级) get_analogy('do', 'did', 'go', glove_6b50d) # 期望 went(一般现在-过去时)- 男-女关系:
vec(man) - vec(woman) ≈ vec(son) - vec(daughter); - 首都-国家关系:
vec(beijing) - vec(china) ≈ vec(tokyo) - vec(japan); - 形容词-最高级与动词时态同理,验证预训练词向量编码了词法与句法层面的规则性(regularity)。
值得说明的是:类比结果的正确性取决于预训练词向量的质量与语料覆盖度,并非所有查询都能得到理想答案;本节展示的是该方法在主流预训练模型上的典型表现。
6. PCA 降维可视化语义关系
向量空间中词与词的位置关系可以直接用二维散点图呈现。由于词向量通常为 50~300 维,先用主成分分析(PCA)降到 2 维,再把词对以连线方式画出,即可直观检验「同类别词对的相对位移是否一致」——这正是类比偏移向量假设的可视化验证。
def visualization(token_pairs, embed): plt.figure(figsize=(7, 5)) vecs = np.concatenate([embed[pair] for pair in token_pairs]) vecs_pca = PCA(n_components=2).fit_transform(numpy.array(vecs)) for i, pair in enumerate(token_pairs): x1, y1 = vecs_pca[2 * i] x2, y2 = vecs_pca[2 * i + 1] plt.scatter(x1, y1) plt.scatter(x2, y2) plt.annotate(pair[0], xy=(x1, y1)) plt.annotate(pair[1], xy=(x2, y2)) plt.plot([x1, x2], [y1, y2]) plt.show()embed[pair]利用TokenEmbedding.__getitem__批量查表,np.concatenate将所有词对的向量拼接成一个矩阵;- 用
sklearn.decomposition.PCA(n_components=2)降维,便于在二维平面标注与连线; - 每个词对的两端点以散点标出并标注词形,词对间以线段相连。
绘制六组「男-女」类词对的二维投影:
token_pairs = [['man', 'woman'], ['son', 'daughter'], ['king', 'queen'], ['uncle', 'aunt'], ['sir', 'madam'], ['sister', 'brother']] visualization(token_pairs, glove_6b50d)从图中可以看出:同类别词对的连线方向大致平行,说明男-女这一语义关系在词向量空间中对应一致的偏移方向,从视觉上印证了第 5 节类比向量算术的合理性。
7. 小结与进阶练习
小结:
- 大规模语料预训练的词向量通常可直接迁移到下游 NLP 任务,无需从零训练;
- 借助余弦相似度与 k 近邻检索,可以基于预训练词向量查找同义词;借助
vec(b) - vec(a) + vec(c)的向量算术,可以完成词类比推理; TokenEmbedding统一了 GloVe 与 fastText 的加载、查表与未知词兜底逻辑,可直接复用于后续各 NLP 章节。
进阶练习(来自原文档):
- 使用
TokenEmbedding('wiki.en')加载 300 维 fastText 词向量,重复上述同义词与类比实验,对比与 GloVe 结果(词典规模、检索质量)的差异; - 若词典极大(如数亿词),逐词计算余弦相似度再排序的
knn为全表扫描,思考如何加速同义词与类比的查找——可参考的方向包括:向量量化(如npx.topk只优化了排序阶段而非距离计算)、近似最近邻(ANN)索引(如基于图的 HNSW)、或子采样/哈希分桶等工程手段。
相关延伸阅读:GloVe 模型的从零训练见 contrib/glove-pretraining.md,fastText 的子词建模见 contrib/fasttext-pretraining.md,word2vec 从零训练与近邻检索见 word2vec.md 与 word2vec-pretraining.md。
- 文档
- 教程
- 人工智能
- 深度学习
- NLP
- 计算机视觉
- 强化学习
【免费下载链接】d2l-en
Interactive deep learning book with multi-framework code, math, and discussions. Adopted at 500 universities from 70 countries including Stanford, MIT, Harvard, and Cambridge.
相关推荐
利用预训练词向量完成词相似度与词类比任务:基于 d2l-en 的 TokenEmbedding 实战指南
利用预训练词向量完成词相似度与词类比任务:基于 d2l en 的 TokenEmbedding 实战指南 在 词向量预训练章节 https://link.git
文档教程人工智能深度学习NLP计算机视觉强化学习《动手学深度学习》词向量实战:基于预训练 GloVe 与 fastText 的词相似性与类比推理
《动手学深度学习》词向量实战:基于预训练 GloVe 与 fastText 的词相似性与类比推理 本篇实战指南以《动手学深度学习》(d2l zh)中"词的相似性
人工智能深度学习机器学习教程预训练词向量实战:用 GloVe 求近义词与类比词(d2l-zh 完整代码与源码解析)
预训练词向量实战:用 GloVe 求近义词与类比词(d2l zh 完整代码与源码解析) 导读 本文基于《动手学深度学习》d2l zh 仓库中的“求近义词和类比词
人工智能深度学习机器学习教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考