NLP Embedding,Word2vec
Word2vec:CBOW介绍
1. 目的,流程梳理
NLP任务中需要将词以向量方式表示(嵌入数学空间中,word embedding),最原始的是one-hot编码,但是会导致词向量的维度太大,所以通常会使用分布式表示。本文主要围绕向量化方法word2vec中的CBOW展开。
总体而言,Word2Vec是借助预测词的过程,根据中间词与上下文之间的关系来训练Embedding矩阵,从而达到将One-Hot编码降维的效果,避免词向量过于冗长,降低复杂度。注意,word2vec的最终目的是那个embedding矩阵。
下面会介绍word2vec的具体过程,负采样的原理,以及一个demo实战。
2. word2vec 具体过程
Word2Vec是一个根据词的临近关系预测周围词的过程,会设置一个滑动窗口,在不断滑动的同时收集样本。
这里主要介绍CBOW。在CBOW目的是根据前后词预测中间词,就会收集如下的样本:
today | a |
---|---|
is | a |
good | a |
day | a |
而后构建一个三层的神经网络模型,通过这个模型来计算 p r e d i c t i o n prediction prediction向量。
输入层:上下文单词的One-Hot。
中间层:输入的 m m m个单词的One-Hot分别乘以共享的输入权重矩阵 W v × n W_{v\times n} Wv×n( v v v为词库大小, n n n为自己设定的降维目的, W W W为初始化权重矩阵 ),得到一个 m × n m\times n m×n的矩阵,进行简单加权平均得到一个 1 × n 1\times n 1×n大小的向量 s u m sum sum。
输出层: s u m sum sum向量和整个 W v × n W_{v\times n} Wv×n相乘,即 s u m sum sum向量和所有词向量求余弦值。**然后经过softmax得到 p r e d i c t i o n prediction prediction向量。 p r e d i c t i o n prediction prediction向量中每个维度的值代表每个词的概率。将 p r e d i c t i o n prediction prediction向量与正确词汇的one-hot编码之间的损失(一般是交叉熵)作为优化参数的依据。
说得更清晰一点:
假设词库单词50000个( v v v),需要化成300( n n n)维,窗口大小为5( m + 1 m+1 m+1,因为窗口包括前后词和本词)。
输入:4×50000
中间层:4×300 -> 300
输出层:50000
(softmax和交叉熵的具体内容放在笔记末尾。)
3. 负采样
负采样:
请关注word2vec具体过程中的加粗字体部分,这是负采样优化的部分。
可以看到,每次预测都需要将 s u m sum sum向量与词库中所有词向量求余弦,如果按照上文中的50000维降成300维的做法,需要求余弦50000次。这一步非常浪费,实际上我们想要的是 W v × n W_{v\times n} Wv×n矩阵,这个预测工作只是工具。
因此,可以选择多个负样本( v ′ − 1 v^{'}-1 v′−1不正确的 n n n维向量)和一个正样本(正确词的 n n n维向量)组成 W v ′ × n ′ W_{v'\times n}^{'} Wv′×n′。
比如,选择40个负样本,一个正样本,就只需要求余弦41次,大大减少工作量(当然,负采样每次让一个训练样本仅仅更新一小部分的权重)。
可以看出,改变的是prediction向量。prediction向量的计算流程图的改变如下:
4. demo
以下是我曾经做过的一个用于豆瓣电影评论预测评分的BiLSTM,在此单拎出它的word2vec部分进行展示。word2vec部分使用的是THUCNews数据库,由于实际训练数据集比较大(约需训练8h左右),不便放在Jupyter中展示,此处仅以 THUCNews/体育 部分为例展示训练过程。
(完整数据集在http://thuctc.thunlp.org/#%E8%8E%B7%E5%8F%96%E9%93%BE%E6%8E%A5,如想复现word2vec demo可点击链接选择数据集下的体育部分。)
from typing import TextIO
from MySentences import MySentences
import os
# jieba分词库
import jieba
import jieba.analyse
# gensim词向量训练库
from gensim.test.utils import common_texts, get_tmpfile
from gensim.models import Word2Vec
from gensim.models import word2vec
import numpy as np
分词:以体育为例
train_tokens = []
sum = 0
set = {''}
# File = './temp'
File = './THUCNews_files'
Files1 = os.listdir(File) # 得到该文件夹下的所有文件名(包含后缀),输出为列表形式
for file1 in Files1:
file11 = File+'/'+file1
files= os.listdir(file11)
for file in files:
with open(file11 + '/' + file, encoding='utf-8') as f1:
# 对每个文档的内容进行分词
document = f1.read()
document_cut = jieba.cut(document, cut_all=False)
# 分词之后用空格隔开并去掉标点符号
result = ' '.join(document_cut).replace(',', '').replace('。',
'').replace('?', '').replace('!', '') \
.replace('“', '').replace('”', '').replace(':', '').replace(';',
'').replace('…', '').replace('(',
'').replace(
')', '') \
.replace('—', '').replace('《', '').replace('》', '').replace('、',
'').replace('‘', '') \
.replace('’', '')
train_tokens.append(list(result.split()))
for word in result.split():
set.add(word)
# 处理好的语料文档的路径
datapath = './THUCNews_files_tokens/'+file1+'/a.txt'
with open(datapath, 'a', encoding="utf-8") as f2:
f2.write(result)
print(len(set))
#分好token后的句子中,以体育为例,展示训练过程
num_tokens = [ len(tokens) for tokens in train_tokens ]
num_tokens = np.array(num_tokens)
print(num_tokens)
print(np.sum(num_tokens))
116720
[386 834 857 … 200 308 703]
8130954
np.mean(num_tokens)
np.log(num_tokens)
np.max(num_tokens)
7851
import matplotlib.pyplot as plt
plt.hist(np.log(num_tokens), bins=100)
plt.xlim((0,10))
plt.ylabel('number of tokens')
plt.xlabel('length of tokens')
plt.title('Distribution of tokens length')
plt.show()
语料库中“体育”主题部分共8130954个词,最长评论有7851个词,经取对数处理后,评论长度基本呈正
态分布。由于整体语料库为体育部分十倍以上,为了迎合整体大语料,提高训练速度,在使用
Word2Vec训练时,使用CBOW模型进行训练。
from gensim.test.utils import common_texts, get_tmpfile
from gensim.models import Word2Vec
sentences=[]
f1=open('./THUCNews_files_tokens/体育/a.txt','r',encoding='utf8').readlines()
for line in f1:
sentences.append(line.split())
model = word2vec.Word2Vec(sentences,sg=0, vector_size=100,window=5, min_count=1,
workers=4)
model.save('ipynbtry')
import gensim.models
model = gensim.models.Word2Vec.load("ipynbtry")
#查看足球的向量
#查看与体育相关的词语的与之相似词
print(model.wv['足球'])
for word in ['足球','篮球','乒乓球','体育馆','体育','羽毛球','运动员','教练','跑步']:
print(word+":")
for key in model.wv.similar_by_word(word, topn=5):
print(key)
足球:
(‘足坛’, 0.7249346971511841)
(‘中国篮球’, 0.7049357891082764)
(‘足球运动’, 0.6868180632591248)
(‘篮球’, 0.6663395166397095)
(‘篮球运动’, 0.657491147518158)
篮球:
(‘篮球运动’, 0.7422261238098145)
(‘文化’, 0.6905624866485596)
(‘运动员’, 0.6898443698883057)
(‘足球运动’, 0.6897315979003906)
(‘中国篮球’, 0.6821549534797668)
…
可以看到,在该语料范围内,训练效果还是很出色的,模型很大程度捕捉到了语义相关性,
应该认为推广到全部训练集上也能保持较好的效果
5.一些补充
5.1 Softmax and Cross Entropy
Softmax:
通过Softmax函数就可以将多分类的输出值转换为范围在[0, 1]和为1的概率分布。
计算公式如下:
S
i
=
e
i
∑
j
e
j
S_{i}=\frac{e^{i}}{\sum_{j}{e^{j}}}
Si=∑jejei
Cross Entropy:
是一种损失函数(loss function),计算公式如下:
H
(
p
,
q
)
=
−
∑
p
(
x
i
)
l
o
g
(
q
(
x
i
)
)
H(p,q)=-\sum{p(x_{i})log(q(x_{i}))}
H(p,q)=−∑p(xi)log(q(xi))
交叉熵与KL散度的关系:
KL散度公式:
D
K
L
(
p
∣
∣
q
)
=
∑
p
(
x
i
)
l
o
g
(
p
(
x
i
)
q
(
x
i
)
)
D_{KL}(p||q)=\sum p(x_{i})log(\frac{p(x_{i})}{q_(x_i)})
DKL(p∣∣q)=∑p(xi)log(q(xi)p(xi))
KL散度的值越小表示两个分布越接近。
将其变形得到:
D
K
L
(
p
∣
∣
q
)
=
∑
p
(
x
i
)
l
o
g
(
p
(
x
i
)
)
−
∑
p
(
x
i
)
l
o
g
(
q
(
x
i
)
)
=
−
H
(
p
(
x
)
)
+
[
−
∑
p
(
x
i
)
l
o
g
(
q
(
x
i
)
)
]
D_{KL}(p||q)=\sum p(x_{i})log(p(x_{i}))-\sum p(x_{i})log(q(x_{i}))\\ =-H(p(x))+[-\sum p(x_{i})log(q(x_{i}))]
DKL(p∣∣q)=∑p(xi)log(p(xi))−∑p(xi)log(q(xi))=−H(p(x))+[−∑p(xi)log(q(xi))]
可以看到,其中后半部分即为我们的交叉熵。KL散度来评估predict和label之间的差别,因为前半部分是常量,所以常用交叉熵来代替KL散度,实际效果不变。
小白一枚,如果有错误,希望大家多多帮助指正,一起学习!
参考文章:
-
一条水里的鱼:白话Word2vec原理以及层softmax、负采样的实现。https://blog.csdn.net/qq_40859560/article/details/10921172
-
小虎AI实验室:轻松理解CBOW模型。https://blog.csdn.net/u010665216/article/details/78724856
-
意念回复:机器学习算法(十三):word2vec。https://blog.csdn.net/weixin_39910711/article/details/103696103
-
https://zhuanlan.zhihu.com/p/61944055