NLP介绍与词向量初步 - Word2Vec基础 | 自在学
NLP介绍与词向量初步
自然语言处理(Natural Language Processing, NLP)是计算机科学、人工智能和语言学的交叉领域,致力于使计算机能够理解、解释和生成人类语言。这听起来简单,实则是人工智能中最具挑战性的研究方向之一。
为什么困难?因为自然语言与计算机熟悉的形式化语言(如编程语言)有着本质区别。编程语言的语法严格、语义明确——if x > 0就是if x > 0,没有第二种理解。
但自然语言充满歧义、灵活多变、高度依赖上下文。“银行”可以指金融机构,也可以指河岸;“我看见了那个拿望远镜的人”可以理解为我用望远镜看,也可以理解为那个人拿着望远镜。
更深层的挑战在于,人类交流时大量依赖隐含的常识和世界知识。当你听到“我打破了杯子,所以我需要打扫”,你立刻理解杯子破碎了、玻璃碎片散落了、需要清理以免受伤——但这些信息都没有明说。计算机却需要显式地学习或编码这些知识。
NLP的任务全景
NLP涵盖了从基础到高级的广泛任务。在理解类任务 中,文本分类是最常见的——判断一条评论是正面还是负面(情感分析),识别一封邮件是否是垃圾(垃圾邮件检测),或者将新闻归入政治、体育、科技等类别(主题分类)。信息抽取则更进一步,不仅理解文本,还要从中提取结构化信息——识别出文本中提到的人名、地名、机构名(命名实体识别),或者抽取"A公司收购了B公司"这样的关系。问答系统更加复杂,它需要阅读文章并回答针对文章的问题,或者从知识库中检索答案。
生成类任务 则要求机器产生新的文本。机器翻译将一种语言的文本翻译成另一种语言,这不仅需要理解源语言,还要流畅地生成目标语言。文本摘要要求提炼长文档的核心内容,生成简洁的概要。对话系统需要与用户进行多轮交互,理解意图、维护上下文、生成恰当的回复——Siri、Alexa、ChatGPT都属于这个范畴。
还有一类结构分析任务 ,它们揭示语言的内部结构。句法分析识别句子的语法结构——"我爱自然语言处理"中,"我"是主语、"爱"是谓语、"自然语言处理"是宾语。语义分析则更深入,试图理解句子的含义表示,将自然语言映射到形式化的语义表示。
这些任务相互关联、互为基础。机器翻译需要先理解源语言的句法和语义,才能生成正确的目标语言。问答系统需要信息抽取来定位关键信息。对话系统需要文本分类来识别用户意图。可以说,NLP是一个层次化的技术体系,每一层都建立在下层的基础之上。
词义表示:NLP的基石问题
在构建任何NLP系统之前,我们面临一个根本性问题:如何在计算机中表示词语的含义? 这个问题看似简单,实则深刻影响着后续所有任务的性能。
形式化地说,给定词汇表
V = { w 1 , w 2 , … , w ∣ V ∣ } V = \{w_1, w_2, \ldots, w_{|V|}\} V = { w 1 , w 2 , … , w ∣ V ∣ }
我们需要找到一个映射函数
f : V → R d f: V \rightarrow \mathbb{R}^d f : V → R d
将每个词映射到一个d d d 维实数向量空间中,使得语义相近的词在向量空间中也相近。这就像给每个词在一个多维空间中分配一个坐标,相似的词应该住在彼此附近。
传统方法的两难困境
最直观的方法是one-hot编码 :将每个词表示为一个∣ V ∣ |V| ∣ V ∣ 维向量,该词对应位置为1,其余为0。假设词汇表为
{ 国王 , 女王 , 男人 , 女人 } \{\text{国王}, \text{女王}, \text{男人}, \text{女人}\} { 国王 , 女王 , 男人 , 女人 }
那么“国王”就是[ 1 , 0 , 0 , 0 ] T [1,0,0,0]^T [ 1 , 0 , 0 , 0 ] T ,“女王”是[ 0 , 1 , 0 , 0 ] T [0,1,0,0]^T [ 0 , 1 , 0 , 0 ] T 。
这种表示简单直接,但有致命缺陷。任意两个不同词的向量都是正交的——国王 ⋅ 女王 = 0 \text{国王} \cdot \text{女王} = 0 国王 ⋅ 女王 = 0 ,国王 ⋅ 苹果 = 0 \text{国王} \cdot \text{苹果} = 0 国王 ⋅ 苹果 = 0 。尽管“国王”和“女王”在语义上高度相关,但在向量空间中它们与所有其他词的距离都完全相同。这种表示无法捕获任何语义关系。
而且,现实中的词汇表通常包含几万到几百万词。one-hot向量极其高维且稀疏(绝大部分元素是0),导致存储和计算效率极低。一个包含10万词的词汇表,每个词需要10万维向量——这在实际应用中是不可接受的。
另一种传统方法是使用人工构建的词汇资源,如WordNet 。WordNet是一个大型词汇数据库,包含了词语之间的语义关系——同义词、反义词、上下位关系(如"狗"是"动物"的下位词)等。这些关系由语言学家手工标注,质量很高。
但WordNet也有严重局限。首先,同义词集合无法捕获词语含义的细微差别——“聪明”、“智慧”、“机智”都是相似的,但在具体语境中不能随意替换。
其次,语言是不断演化的,新词、新义层出不穷(如“selfie”、“tweet”的新含义),WordNet需要持续人工维护,成本巨大。
最致命的是,WordNet依赖于构建者的主观判断,不同人可能有不同的语义划分标准,而且它缺乏量化相似度的有效方法——我们知道“国王”和“女王”相关,但相关程度是多少?如何比较“国王-女王”与“男人-女人”的相关性?
分布式语义假设:现代NLP的基石
现代NLP建立在一个优雅的语言学洞察之上——分布式语义假设(Distributional Hypothesis) 。这一思想最早由语言学家Zellig Harris在1954年提出,后被John Firth在1957年总结为一句著名的格言:
“You shall know a word by the company it keeps.”
(通过一个词的伴随词,你就能了解这个词的含义。)
核心思想是:词语的含义由其在大规模语料中出现的上下文决定 。如果两个词经常出现在相似的上下文中,那么它们的含义可能相近。这不是凭空想象,而是语言使用的客观规律。
让我们用“银行”这个多义词来理解。观察其在语料中的上下文:
上下文1:“我去银行 存钱” → 周围常出现“存款”、“贷款”、“ATM”、“账户”
上下文2:“河边的银行 很陡” → 周围常出现“河流”、“侵蚀”、“泥土”、“河岸”
第一个上下文的词语分布与金融相关,第二个与地理地貌相关。通过统计“银行”在不同上下文中的分布模式,我们可以区分其不同含义,甚至可以说第一个“银行”与“金融机构”相近,第二个与“河岸”相近。
这个假设的威力在于:我们不需要人工标注语义关系,只需要大量文本数据,就能通过统计共现模式自动学习词语含义 。这为现代NLP开辟了一条全新的道路——数据驱动的语义学习。
词向量
基于分布式语义假设,我们的目标是为每个词学习一个密集的、低维的实数向量(称为词向量 或词嵌入 ),使得语义相近的词在向量空间中也相近。
想象一个多维空间,每个词是这个空间中的一个点。我们希望:“国王”与“女王”的距离小于“国王”与“苹果”的距离;“男人”与“女人”的关系类似于“国王”与“女王”的关系。更理想的情况是,这种关系可以通过向量运算体现:
v ⃗ 国王 − v ⃗ 男人 ≈ v ⃗ 女王 − v ⃗ 女人 \vec{v}_{\text{国王}} - \vec{v}_{\text{男人}} \approx \vec{v}_{\text{女王}} - \vec{v}_{\text{女人}} v 国王 − v
也就是说,从“男人”到“国王”的向量变换(可以理解为“赋予王权”),应该类似于从“女人”到“女王”的变换。如果这个关系成立,那么我们可以通过向量运算进行类比推理:
v ⃗ 国王 − v ⃗ 男人 + v ⃗ 女人 ≈ v ⃗ 女王 \vec{v}_{\text{国王}} - \vec{v}_{\text{男人}} + \vec{v}_{\text{女人}} \approx \vec{v}_{\text{女王}} v 国王 − v
这就是著名的“国王 - 男人 + 女人 = 女王”向量运算。虽然听起来像魔法,但这正是Word2Vec等词向量模型实现的——通过学习,词向量能够在一定程度上捕获这种语义关系。
词向量通常使用50到300维——远小于词汇表大小(可能数万到数十万),但足以捕获丰富的语义信息。这种低维稠密表示既节省存储,又便于计算,更重要的是,它将语义关系转化为几何关系,让机器能够“理解”词义。
Word2Vec
Word2Vec是由Google的Tomas Mikolov等人在2013年提出的,是现代词向量方法的里程碑。其核心思想简单而强大:通过预测上下文来学习词向量 。
具体来说,Word2Vec让模型做这样一件事:给定一个中心词,预测其周围会出现哪些词。例如,在句子“我爱学习自然语言处理”中,如果中心词是“自然”,模型需要预测周围可能出现“学习”、“语言”、“处理”等词。
为什么这个任务能学到好的词向量?因为能预测相似上下文的词,往往有相似的含义。“自然语言处理”和“机器学习”都可能出现在“我爱学习___”、“研究___”、“应用___”这样的上下文中,所以模型会学到它们有相似的向量表示。
这是一个自监督学习(Self-supervised Learning)任务——我们不需要人工标注,只需要原始文本。文本本身就提供了监督信号:每个词的上下文就是它的“标签”。这使得Word2Vec可以在海量无标注文本上训练,这是它能够捕获丰富语义的关键。
Skip-gram模型
Word2Vec有两种架构:Skip-gram(给定中心词预测上下文)和CBOW(给定上下文预测中心词)。我们重点介绍Skip-gram,因为它在实践中通常表现更好。
给定一个长度为T T T 的文本序列
w 1 , w 2 , … , w T w_1, w_2, \ldots, w_T w 1 , w 2 , … , w T
Skip-gram的目标是最大化对数似然:
L = ∑ t = 1 T ∑ − m ≤ j ≤ m , j ≠ 0 log P ( w t + j ∣ w t ) \mathcal{L} = \sum_{t=1}^{T} \sum_{-m \leq j \leq m, j \neq 0} \log P(w_{t+j} | w_t) L = t = 1 ∑ T − m ≤ j ≤ m , j
这个公式说的是:对于文本中每个位置t t t 的中心词w t w_t w t ,我们希望最大化其上下文窗口中所有词w t + j w_{t+j} w t + j 出现的概率。窗口大小m m m 通常取5-10,意味着我们考虑中心词前后各5-10个词作为上下文。
关键问题是如何计算P ( w t + j ∣ w t ) P(w_{t+j} | w_t) P ( w t + j ∣ w t ) ?Word2Vec使用一个简单但有效的方法:每个词有两个向量表示 ——作为中心词时的向量v ⃗ w \vec{v}_w v ,和作为上下文词时的向量 。上下文词 在给定中心词 时出现的概率通过softmax计算:
P ( o ∣ c ) = exp ( u ⃗ o T v ⃗ c ) ∑ w ∈ V exp ( u ⃗ w T v ⃗ c ) P(o | c) = \frac{\exp(\vec{u}_o^T \vec{v}_c)}{\sum_{w \in V} \exp(\vec{u}_w^T \vec{v}_c)} P ( o ∣ c ) = ∑ w ∈ V exp (
这个公式背后的直觉是:如果u ⃗ o \vec{u}_o u o 和v ⃗ c \vec{v}_c v 的点积大(即两个向量方向相似),说明 作为 的上下文词的"合理性"高,概率就高。softmax归一化确保所有词的概率和为1。
让我们用一个具体例子理解。在句子"我爱学习自然语言处理"中,当中心词是"自然"(c = 自然 c = \text{自然} c = 自然 ),窗口m = 2 m=2 m = 2 时,我们要预测:
P ( 学习 ∣ 自然 ) P(\text{学习} | \text{自然}) P ( 学习 ∣ 自然 ) 应该高(因为"学习"在窗口内)
P ( 语言 ∣ 自然 ) P(\text{语言} | \text{自然}) P ( 语言 ∣ 自然 ) 应该高
P ( 处理 ∣ 自然 ) P(\text{处理} | \text{自然}) P ( 处理 ∣ 自然 ) 应该高
P ( 苹果 ∣ 自然 ) P(\text{苹果} | \text{自然}) P 应该低(因为"苹果"不在窗口内)
模型通过调整向量v ⃗ 自然 \vec{v}_{\text{自然}} v 自然 和u ⃗ 学习 \vec{u}_{\text{学习}} u 、 等,使得窗口内词的概率增大,窗口外词的概率减小。
Negative Sampling
朴素的Skip-gram有一个严重的计算瓶颈:softmax的分母需要对整个词汇表求和。对于一个10万词的词汇表,每次计算概率都要计算10万次指数和点积,这在实际中是不可接受的。
Negative Sampling 巧妙地解决了这个问题。核心思想是:我们不需要对所有词计算概率,只需要区分真实的上下文词(positive samples)和随机采样的非上下文词(negative samples)。
具体来说,对于一个真实的词对( c , o ) (c, o) ( c , o ) (中心词c c c 和其上下文词o o o ),我们随机采样k k k 个词(通常k = 5 k=5 k = 5 或k = 10 k=10 k = )作为"负样本",然后最大化目标:
log σ ( u ⃗ o T v ⃗ c ) + ∑ i = 1 k E w i ∼ P n ( w ) [ log σ ( − u ⃗ w i T v ⃗ c ) ] \log \sigma(\vec{u}_o^T \vec{v}_c) + \sum_{i=1}^{k} \mathbb{E}_{w_i \sim P_n(w)} [\log \sigma(-\vec{u}_{w_i}^T \vec{v}_c)] log σ ( u
第一项鼓励真实上下文词o o o 的概率高(σ ( u ⃗ o T v ⃗ c ) \sigma(\vec{u}_o^T \vec{v}_c) σ ( u o T 接近1),第二项鼓励负样本词 的概率低( 接近1,等价于 接近0)。
这里σ ( x ) = 1 1 + e − x \sigma(x) = \frac{1}{1+e^{-x}} σ ( x ) = 1 + e − x 1 是sigmoid函数,将点积映射到( 0 , 1 ) (0,1) ( 0 , 1 ) 区间,可以解释为概率。负样本从一个噪声分布 中采样,通常使用词频的 次方:
P n ( w ) = f ( w ) 3 / 4 ∑ w ′ f ( w ′ ) 3 / 4 P_n(w) = \frac{f(w)^{3/4}}{\sum_{w'} f(w')^{3/4}} P n ( w ) = ∑ w
为什么是3 / 4 3/4 3/4 次方而非直接使用词频?因为这样可以减少高频词被过度采样的问题。"的"、"了"等功能词频率极高,如果按原始频率采样,负样本几乎都是这些词,模型学不到有用信息。3 / 4 3/4 3/4 次方平滑了分布,让中低频词也有合理的采样概率。
通过Negative Sampling,每次更新只需要计算k + 1 k+1 k + 1 个词的梯度(1个正样本+k k k 个负样本),而不是整个词汇表,训练速度提升了数千倍。
PyTorch实现
让我们实现一个简化的Skip-gram模型。完整的Word2Vec实现涉及许多工程优化,但核心思想可以通过简洁的代码展现:
import torch
import torch.nn as nn
import torch.optim as optim
from collections import Counter
import numpy as np
class SkipGramModel ( nn . Module ):
"""简化的Skip-gram模型"""
def __init__ (self, vocab_size, embedding_dim):
"""
Args:
vocab_size: 词汇表大小
embedding_dim: 词向量维度
"""
这个实现虽然简化,但包含了Word2Vec的核心组件:Skip-gram架构、Negative Sampling、动态窗口、词频平滑等。在真实应用中,还需要考虑下采样高频词、分层softmax(另一种加速方法)、多线程训练等优化。
词向量的评估与应用
内在评估
训练好词向量后,如何评估其质量?一种方法是**词类比(Word Analogy)**任务,就像给词向量做智商测验。最著名的是Google Analogy Dataset,包含19,544个类比问题,分为语义类比和句法类比两类。
语义类比 例子:
“北京 : 中国 = 巴黎 : ?"(答案:法国)
“男人 : 女人 = 国王 : ?"(答案:女王)
句法类比 例子:
“walk : walking = swim : ?"(答案:swimming)
“good : better = bad : ?"(答案:worse)
评估方法是用向量运算:给定a : b = c : ? a : b = c : ? a : b = c : ? ,计算v ⃗ b − v ⃗ a + v ⃗ c \vec{v}_b - \vec{v}_a + \vec{v}_c v ,然后在词汇表中找最接近的词(余弦相似度最大)。如果找到的词是正确答案,则算对。
好的词向量在这个任务上能达到60-80%的准确率,这是相当惊人的——仅仅通过预测上下文,模型学会了抽象的语义和句法关系!
外在评估
更重要的评估是外在评估(Extrinsic Evaluation) :将词向量用于实际NLP任务,看是否提升性能。例如:
文本分类 :用词向量初始化输入层,然后训练分类器
命名实体识别 :每个词用词向量表示,输入序列标注模型
情感分析 :句子表示为词向量的平均或加权和,然后分类
在2013-2017年间,使用预训练词向量(Word2Vec、GloVe)成为标准做法,通常能带来2-5%的性能提升。虽然现在已经被BERT等预训练模型超越,但词向量的思想——从大规模无标注数据中学习通用表示,然后迁移到下游任务——奠定了现代NLP的基础。
练习与思考
解释为什么one-hot编码无法捕获词语的语义相似性。如果我们有100万词的词汇表,one-hot编码会有什么实际问题?
基于上面的代码,在一个中等规模的中文语料(如维基百科文章)上训练Word2Vec。可视化一些词的词向量(使用t-SNE降维到2维),观察语义相近的词是否聚集在一起。
Word2Vec学到的词向量能表示所有的语义信息吗?它有什么局限性?提示:思考“银行”这个多义词,Word2Vec为它学到的是一个向量,还是多个向量?
假设你要构建一个餐厅评论的情感分类系统,如何使用Word2Vec?你会在什么样的语料上训练词向量?是用通用语料(如维基百科)还是领域语料(如美食评论)?为什么?
男人
≈
男人
+
=
0
∑
log
P
(
w t + j
∣
w t
)
w
u
w T
)
c
(
苹果
∣
自然
)
学习
u ⃗ 语言 \vec{u}_{\text{语言}} u 语言 10
o
T
)
+
i = 1 ∑ k E w i ∼ P n ( w ) [ log σ ( − u w i T v c )] v
c
)
σ ( − u ⃗ w i T v ⃗ c ) \sigma(-\vec{u}_{w_i}^T \vec{v}_c) σ ( − u w i T v c ) σ ( u ⃗ w i T v ⃗ c ) \sigma(\vec{u}_{w_i}^T \vec{v}_c) σ ( u w i T v c )
′
f
(
w ′
) 3/4
f ( w ) 3/4
super
(SkipGramModel,
self
).
__init__
()
# 中心词嵌入矩阵
self .center_embeddings = nn.Embedding(vocab_size, embedding_dim)
# 上下文词嵌入矩阵
self .context_embeddings = nn.Embedding(vocab_size, embedding_dim)
# 初始化:使用均匀分布,范围[-0.5/dim, 0.5/dim]
# 这样初始化能让词向量不会太大也不会太小
self .center_embeddings.weight.data.uniform_( - 0.5 / embedding_dim, 0.5 / embedding_dim)
self .context_embeddings.weight.data.uniform_( 0 , 0 ) # 上下文词初始化为0
def forward (self, center_words, context_words, negative_words):
"""
前向传播
Args:
center_words: (batch_size,) 中心词ID
context_words: (batch_size,) 正样本上下文词ID
negative_words: (batch_size, num_negatives) 负样本词ID
Returns:
loss: 标量,负采样损失
"""
# 获取中心词向量 (batch_size, embedding_dim)
center_embeds = self .center_embeddings(center_words)
# 获取正样本上下文词向量 (batch_size, embedding_dim)
context_embeds = self .context_embeddings(context_words)
# 获取负样本词向量 (batch_size, num_negatives, embedding_dim)
negative_embeds = self .context_embeddings(negative_words)
# 计算正样本得分: (batch_size, embedding_dim) * (batch_size, embedding_dim) -> (batch_size,)
positive_score = torch.sum(center_embeds * context_embeds, dim = 1 )
positive_loss = - torch.log(torch.sigmoid(positive_score)).mean()
# 计算负样本得分: (batch_size, 1, embedding_dim) @ (batch_size, embedding_dim, num_negatives)
# -> (batch_size, num_negatives)
negative_score = torch.bmm(
center_embeds.unsqueeze( 1 ), # (batch_size, 1, embedding_dim)
negative_embeds.transpose( 1 , 2 ) # (batch_size, embedding_dim, num_negatives)
).squeeze( 1 ) # (batch_size, num_negatives)
negative_loss = - torch.log(torch.sigmoid( - negative_score)).mean()
return positive_loss + negative_loss
def get_embedding (self, word_id):
"""获取词向量(使用中心词嵌入)"""
return self .center_embeddings(torch.LongTensor([word_id])).detach().numpy()[ 0 ]
class Word2VecDataset :
"""Word2Vec数据集"""
def __init__ (self, sentences, window_size = 5 , min_count = 5 ):
"""
Args:
sentences: List[List[str]] 分词后的句子列表
window_size: 上下文窗口大小
min_count: 最小词频阈值
"""
self .window_size = window_size
# 构建词汇表
word_counts = Counter()
for sentence in sentences:
word_counts.update(sentence)
# 过滤低频词
self .vocab = {word: i for i, (word, count) in enumerate (word_counts.items())
if count >= min_count}
self .vocab[ '<UNK>' ] = len ( self .vocab)
self .id2word = {i: word for word, i in self .vocab.items()}
# 计算词频分布(用于负采样)
self .word_freq = np.array([word_counts[ self .id2word[i]] for i in range ( len ( self .vocab))])
self .word_freq = self .word_freq ** 0.75 # 3/4次方平滑
self .word_freq = self .word_freq / self .word_freq.sum()
# 生成训练样本
self .training_pairs = []
for sentence in sentences:
# 转换为ID
word_ids = [ self .vocab.get(word, self .vocab[ '<UNK>' ]) for word in sentence]
# 生成(中心词, 上下文词)对
for i, center_word in enumerate (word_ids):
# 动态窗口:随机选择1到window_size之间的窗口大小
window = np.random.randint( 1 , self .window_size + 1 )
# 获取上下文词
start = max ( 0 , i - window)
end = min ( len (word_ids), i + window + 1 )
for j in range (start, end):
if j != i:
self .training_pairs.append((center_word, word_ids[j]))
def __len__ (self):
return len ( self .training_pairs)
def __getitem__ (self, idx):
return self .training_pairs[idx]
def sample_negatives (self, batch_size, num_negatives):
"""采样负样本"""
return np.random.choice( len ( self .vocab), size = (batch_size, num_negatives),
p = self .word_freq)
def train_word2vec (sentences, embedding_dim = 100 , epochs = 5 , batch_size = 256 ,
num_negatives = 5 , learning_rate = 0.025 ):
"""
训练Word2Vec模型
Args:
sentences: List[List[str]] 分词后的句子列表
embedding_dim: 词向量维度
epochs: 训练轮数
batch_size: 批次大小
num_negatives: 负样本数量
learning_rate: 学习率
Returns:
model: 训练好的模型
dataset: 数据集(包含词汇表)
"""
# 准备数据
dataset = Word2VecDataset(sentences)
vocab_size = len (dataset.vocab)
print ( f "词汇表大小: { vocab_size } " )
print ( f "训练样本数: {len (dataset) } " )
# 创建模型
model = SkipGramModel(vocab_size, embedding_dim)
optimizer = optim.SGD(model.parameters(), lr = learning_rate)
# 训练循环
for epoch in range (epochs):
total_loss = 0
num_batches = 0
# 随机打乱数据
indices = np.random.permutation( len (dataset))
for i in range ( 0 , len (dataset), batch_size):
# 获取批次数据
batch_indices = indices[i:i + batch_size]
batch_pairs = [dataset[idx] for idx in batch_indices]
center_words = torch.LongTensor([pair[ 0 ] for pair in batch_pairs])
context_words = torch.LongTensor([pair[ 1 ] for pair in batch_pairs])
# 采样负样本
negative_words = torch.LongTensor(
dataset.sample_negatives( len (batch_pairs), num_negatives)
)
# 前向传播和反向传播
optimizer.zero_grad()
loss = model(center_words, context_words, negative_words)
loss.backward()
optimizer.step()
total_loss += loss.item()
num_batches += 1
avg_loss = total_loss / num_batches
print ( f "Epoch { epoch + 1} / { epochs } , Loss: { avg_loss :.4f } " )
return model, dataset
# 使用示例
if __name__ == "__main__" :
# 示例语料(实际应用中需要大规模语料)
sentences = [
[ "我" , "爱" , "自然语言处理" ],
[ "我" , "喜欢" , "机器学习" ],
[ "深度学习" , "很" , "有趣" ],
[ "神经网络" , "是" , "深度学习" , "的" , "基础" ],
[ "词向量" , "是" , "自然语言处理" , "的" , "基础" ]
]
# 训练模型
model, dataset = train_word2vec(sentences, embedding_dim = 50 , epochs = 100 )
# 获取词向量
word = "自然语言处理"
if word in dataset.vocab:
word_id = dataset.vocab[word]
embedding = model.get_embedding(word_id)
print ( f " \n ' { word } ' 的词向量(前10维): { embedding[: 10 ] } " )
b
−