word2vec
背景
想要让机器理解自然语言,首先肯定要找到一种方法将自然语言(符号)数学化,在机器学习中,比较常见的词表示方法是one-hot方法。
one-hot 是将用向量的形式来表示词,向量的维度是词表的长度,其中绝大多数元素为 0,只有一个维度的值为 1,这个维度就代表了当前的词;one-hot这种稀疏表示方式,表示的两个词的词向量是孤立的,不能从两个词的向量中看出两个词之间的语义关系;且如果词表过大,容易造成维度灾难。
为了解决one_hot的这些缺点,发展出了Distributed representation词向量表示方法,以及现在最火最好用的Word2vec词向量表示方法。
Word2vec
根据语言模型的不同,又可分为“CBOW”和“Skip-gram”两种模型。而根据两种降低训练复杂度的方法又可分为“Hierarchical Softmax”
和“Negative Sampling”。
- CBOW 模型:拿一个词语的上下文作为输入,来预测这个词语本身
- Skip-gram 模型:用一个词语作为输入,来预测它周围的上下文
CBOW
输入是one-hot向量表示的词,之后Projection成N维的低维稠密向量并加权求和,然后与W’相乘,经过softmax求得预测单词的概率分布y,概率分布中概率最大的单词就是应该输出的单词。
Skip-gram
(SG模型还看不太懂,之后有时间再补,哭唧唧)
加速方法
为了提高word vector的质量,加快训练速度,Google提出了层次softmax和负采样的加速方法。
- 层次Softmax对低频词效果较好;
- 负采样对高频词效果较好,向量维度较低时效果更好。
层次softmax
本质: 把 N 分类问题变成 log(N)次二分类。
为了避免要计算所有词的softmax概率,word2vec采样了霍夫曼树来代替从隐藏层到输出softmax层的映射。
该方法不用为了获得最后的概率分布而评估神经网络中的W个输出结点,而只需要评估大约log2(W)个结点。层次Softmax使用一种二叉树结构来表示词典里的所有词,V个词都是二叉树的叶子结点,而这棵树一共有V−1个非叶子结点。一般采用二叉哈弗曼树,因为它会给频率高的词一个更短的编码。
负采样
**本质:**预测总体类别的一个子集。
让我们回顾一下训练的详细过程:每次用一个训练样本,就要更新所有的权重,这显然对于有大量参数和大量样本的模型来说,十分耗费计算量。但其实每次用一个训练样本时,我们并不需要更新全部的参数,我们只需要更新那部分与这个样本相关的参数即可。而负采样的思想就是每次用一个训练样本更新一小部分参数。负采样的意思是每次用的那个训练样本最后输出的值其实只有一个词的值是1,而其他不正确的词都是0,我们可以从那么是0的单词中采样一部分如5~20词来进行参数更新,而不使用全部的词。
参考:采样
实现
import gensim
import pickle
vector_size = 100
##辅助函数
def sentence2list(sentence):
return sentence.strip().split()
##准备数据
sentences_train = list(x_train.loc[:, 'word_seg'].apply(sentence2list))
sentences_test = list(x_test.loc[:, 'word_seg'].apply(sentence2list))
sentences = sentences_train + sentences_test
##训练
model = gensim.models.Word2Vec(sentences=sentences,size=vector_size, window=5, min_count=5, workers=8, sg=1, iter=5)
##保存结果
wv = model.wv
vocab_list = wv.index2word
word_idx_dict = {}
for idx, word in enumerate(vocab_list):
word_idx_dict[word] = idx
vectors_arr = wv.vectors
vectors_arr = np.concatenate((np.zeros(100)[np.newaxis, :], vectors_arr), axis=0) # 第0位置的vector为'unk'的vector
print(word_idx_dict)
print(vectors_arr)
f_wordidx = open('word_seg_word_idx_dict.pkl', 'wb')
f_vectors = open('word_seg_vectors_arr.pkl', 'wb')
pickle.dump(word_idx_dict, f_wordidx)
pickle.dump(vectors_arr, f_vectors)
f_wordidx.close()
f_vectors.close()