[深度学习网络从入门到入土] 词向量模型word2vec
·
[深度学习网络从入门到入土] 词向量模型word2vec
📢个人导航
知乎:https://www.zhihu.com/people/byzh_rc
CSDN:https://blog.csdn.net/qq_54636039
注:本文仅对所述内容做了框架性引导,具体细节可查询其余相关资料or源码
参考文章:各方资料
文章目录
📖参考资料
Efficient Estimation of Word Representations in Vector Space.
🌱背景
在自然语言处理中,最基础的问题之一就是如何让计算机理解文字
对于人类而言,“猫”和“狗”具有一定语义相似性,但对于计算机来说,文字本质上只是符号
one-hot编码的问题:
- 维度灾难(词表如果有 10 万词,就需要 10 万维向量)
- 无法表达语义关系(cat 与 dog 的距离, cat 与 apple 的距离)
- 不同词之间彼此独立,没有共享信息
-> 能不能把单词映射到低维稠密向量空间,并且让“语义相近”的词距离更近 -> 词向量Word Embedding
word2vec 则是其中最经典、最具有里程碑意义的方法之一
⚙️架构(公式)

word2vec 本质上是一个“通过上下文预测单词”的浅层神经网络
训练完成后, 真正需要的不是预测模型, 而是其中学到的词向量矩阵
word2vec 主要有两种结构:
- 连续词袋模型: CBOW: Continuous Bag of Words
- 跳字模型: Skip-Gram
1. CBOW
用“上下文单词的集合”预测“当前单词”
不考虑上下文单词的顺序 -> “词袋”的意思
- 输入层:上下文的k个单词(比如左右各2个,共4个),每个单词用one-hot编码表示,维度为==[V, 1]==
- 隐藏层:将k个输入的one-hot向量分别与权重矩阵W(维度[V, N],N是词向量维度,通常取50-300)相乘,得到k个==[N, 1]==的向量,再对这k个向量取平均值,得到隐藏层输出h(维度[N, 1])
- 输出层:隐藏层输出h与权重矩阵W’(维度[N, V])相乘,得到维度[V, 1]的向量,再通过softmax函数归一化,得到每个单词作为“当前单词”的概率,目标是让真实当前单词的概率最大
h = 1 k ∑ i = 1 k W T x i h = \frac{1}{k} \sum_{i=1}^{k} W^T x_i h=k1i=1∑kWTxi
2. Skip-Gram
和CBOW相反,用“当前单词”预测“上下文单词”
同样不考虑上下文顺序,但更关注单个单词与周边单词的关联
- 输入层:当前单词的one-hot向量(维度==[V, 1]==)
- 隐藏层:输入向量与权重矩阵W(维度[V, N])相乘,得到隐藏层输出h(维度==[N, 1]==)
- 输出层:隐藏层输出h与权重矩阵W’(维度[N, V])相乘,得到维度[V, 1]的向量,通过softmax得到每个单词作为“上下文单词”的概率,目标是让真实上下文单词的概率之和最大
h = W T x h = W^T x h=WTx
👍优点/创新点
word2vec精准解决了one-hot编码的痛点:
- 解决维度灾难: 将one-hot的V维稀疏向量,转化为N维(50-300维)稠密向量
- 捕捉语义关联: “猫”和“狗”的向量余弦距离近,“猫”和“苹果”的距离远
- 结构简单,训练高效
👎缺点
- 忽略单词顺序和上下文依赖
(比如“我吃苹果”和“苹果吃我”,上下文单词完全一样,但语义完全不同) - 无法处理多义词
(word2vec只能给每个单词分配一个固定的词向量,无法体现多义性) - 依赖局部上下文,忽略全局信息
(word2vec只关注“当前单词的周边上下文”,无法利用整个语料库的全局信息) - 对稀有词、生僻词效果差
(无法学习到有效的词向量,通常会用随机向量或平均值替代) - 缺乏语法信息
💻代码实现
import torch
import torch.nn as nn
import torch.nn.functional as F
class Word2Vec(nn.Module):
"""
Word2Vec
支持:
1. CBOW
2. Skip-Gram
"""
def __init__(
self,
vocab_size,
embedding_dim,
model_type="skipgram",
):
super().__init__()
self.vocab_size = vocab_size
self.embedding_dim = embedding_dim
self.model_type = model_type.lower()
# W: [V, N]
# 输入词向量矩阵
self.embedding = nn.Embedding( # nn.Embedding 等价于 `one-hot` & `embedding matrix`
num_embeddings=vocab_size,
embedding_dim=embedding_dim,
)
# W': [N, V]
# 输出投影矩阵
self.output = nn.Linear(
embedding_dim,
vocab_size,
bias=False,
)
def forward(self, x):
"""
Skip-Gram:
x shape: [batch_size]
CBOW:
x shape: [batch_size, context_size]
"""
# =====================================================
# Skip-Gram
# =====================================================
if self.model_type == "skipgram":
# [B] -> [B, N]
h = self.embedding(x)
# [B, N] -> [B, V]
logits = self.output(h)
return logits
# =====================================================
# CBOW
# =====================================================
elif self.model_type == "cbow":
# [B, C] -> [B, C, N]
emb = self.embedding(x)
# 对上下文词向量取平均
# [B, C, N] -> [B, N]
h = emb.mean(dim=1)
# [B, N] -> [B, V]
logits = self.output(h)
return logits
else:
raise ValueError("model_type must be 'skipgram' or 'cbow'")
if __name__ == "__main__":
batch = 32
vocab_size = 10000 # 词表大小
embedding_dim = 128 # 词向量维度
# =========================================================
# Skip-Gram
# =========================================================
skipgram_model = Word2Vec(
vocab_size=vocab_size,
embedding_dim=embedding_dim,
model_type="skipgram",
)
# 当前单词
print("Skip-Gram shape:")
inputs = torch.randint(0, vocab_size, (batch,))
logits = skipgram_model(inputs)
print("- logits:", logits.shape) # 映射回词表的预测概率: [32, vocab_size]
x = torch.randint(0, vocab_size, (batch,))
print("- x_embedding:", skipgram_model.embedding(x).shape) # 真正要的word2vec的词向量
# =========================================================
# CBOW
# =========================================================
cbow_model = Word2Vec(
vocab_size=vocab_size,
embedding_dim=embedding_dim,
model_type="cbow",
)
# 上下文单词
print("CBOW shape:")
inputs = torch.randint(0, vocab_size, (batch, 4)) # 4代表: 4个上下文单词
logits = cbow_model(inputs)
print("- logits:", logits.shape) # 映射回词表的预测概率: [32, vocab_size]
x = torch.randint(0, vocab_size, (batch,))
print("- x_embedding:", cbow_model.embedding(x).shape) # 真正要的word2vec的词向量
更多推荐


所有评论(0)