3WordEmbedding(上)
前面提到深度学习和NLP的“根”时候,提到所有的工作都是为了对语言提取特征来让语言表达起来更像“人话”。这个“人话”一定是包含很多因素如上下文环境,词义等等。
Word embedding含义:
这里要提到一个mapping的概念,这么多词(one-hot稀疏多维矩阵)如何mapping成一个维度更低,分布更为稠密,训练更复杂,包含多更多上下文环境,词义,句法,句义的矩阵。方法包含word2vec, elmo, BERT等。
Word embedding传统做法:
One-hot编码:比如句子”她的老公是李现”,向量:“她”是1,其他词是0.向量:“的”是1,其他词是0,依次类推。再比如第二句“她的老公是肖战”,也一样和第一句话做一个向量处理。这时候看“李现”和“肖战”两个向量的cos值距离来判断这两个词的相似度。这种方法比较简单粗暴,而且稀疏性高,分布平均,也看不出来哪个词更重要。
TF-IDF:TF(t)=词在文档中出现次数/文档总词数,就是词频
IDF(t)=log(文档总数/(包含该词的文档数+1)),就是逆文本频率
正常理解下,文章里某些词出现的频率越高,说明这些词越重要。但是有很多词如“的”,“是”,“在”就不对了,所以需要IDF来区分。IDF公式也很好理解,一个词越常见,分母越大,log取对数值越小并接近0,IDF就越小。TF*IDF后发现这个词在文档中出现的次数很高(TF),在其他文档中也出现次数高,IDF数值就会降下来,这样一相乘TF-IDF值就降下来了。所以通过TF-IDF能很好用于区别一个词的重要性。
Word embedding进入深度学习时代:
N-gram:通过马尔科夫假设,假设在一个句子中,后面一个词与前一个词有关:
P(w1,w2,w3,……wn)=P(w1)P(w2|w1)P(w3|w2)...P(wn|wn-1)
当然也可以假设后面的词与前面N个词有关,这就是常说中的N-gram.
NNLM:比较老,现在看不到了,构造也比较简单。比方说“她的老公是李现”
P(wn=”李现”|w1,w2,....w(n-1)) ,输入n-1个单词来预测“李现”。
过程三步走:1. one-hot输入 2. w相乘获得向量,拼接后接上隐层tanh[-1,1]做一次处理 2. softmax[0,1]判断后面哪个词概率大。
imageWord2Vec: word2Vec和NNLM结构基本类似,很有亲兄弟的感觉。不同是NNLM根据上文输出下文,word2Vec咱们字面意思也可以理解: word to vector, 最终输出的是vector向量(word embedding)。方法分CBOW和skip-gram,优化有hierarchical softmax 和negative sampling。
image imageCBOW是通过上下文推中间词,skip-gram是用一个词推上下文。结构和NNLM类似,中间一层隐层,就不详述了。咱们可以想象一下,一堆0.几向量组成的矩阵与预测值做完Loss后(交叉熵),每一层一层层链式求导反向传播梯度下降更新w和w’值,这样的计算开销是非常大的。这里重点讨论一下两个优化方法。
Hierachical softmax: huffman tree提供一个很好的算法,把高频词或者权重大的词放在前面一点。所以用huffman在遍历时,高频词走的步骤要少一些,如果词非常稀有罕见,树会特别深,走的路要更多。hierachical softmax主要是解决softmax归一化运算量比较大的问题,然后在结构上去解决softmax的运算。
imageNegativa sampling:在训练时,加上一些不相关的,比如她的老公是李现(neg: kun)训练词“李现”同时,也加上“kun”一起训练,这样就会同时更新”李现,kun”两个单词w,遵循概率最大化原则,把”李现”正样本的概率最大化,同时最小化负样本”kun”的概率。这样减少了整体的运算量,加快了整个运算量。
Word2vec代码实战:
imageWord2Vec中参数:sentence是句子,size是词向量的维度,window是向前5个词向后5个词,min_count是如果词出现的次数小于5就不管了,workers是调用几个线程。
用TensorFlow来构建word2vec
import tensorflow as tf
# 假设vocab_size = 1000
VOCAB_SIZE = 1000# 假设embedding_size = 128
EMBEDDINGS_SIZE = 128
# 输入单词x是一个[1,vocab_size]大小的矩阵。当然实际上我们一般会用一批单词作为输入,那么就是[N, vocab_size]的矩阵了
x = tf.placeholder(tf.float32, shape=(1,VOCAB_SIZE))# W1是一个[vocab_size, embedding_size]大小的矩阵
W1 = tf.Variable(tf.random_normal([VOCAB_SIZE, EMBEDDING_SIZE]))# b1是一个[1,embedding_size]大小的矩阵
b1 = tf.Variable(tf.random_normal([EMBEDDING_SIZE]))# 简单的矩阵乘法和加法
hidden = tf.add(tf.mutmul(x,W1),b1)
W2 = tf.Variable(tf.random_normal([EMBEDDING_SIZE,VOCAB_SIZE]))
b2 = tf.Variable(tf.random_normal([VOCAB_SIZE]))# 输出是一个vocab_size大小的矩阵,每个值都是一个词的概率值
prediction = tf.nn.softmax(tf.add(tf.mutmul(hidden,w2),b2))
再用cross entropy来训练损失函数
# 损失函数
cross_entropy_loss = tf.reduce_mean(-tf.reduce_sum(y_label * tf.log(prediction), reduction_indices=[1]))# 训练操作
train_op = tf.train.GradientDescentOptimizer(0.1).minimize(cross_entropy_loss)
NNLM和word2vec 由于模型结构简单(一层隐层)并不能提取出很多特征。同时在上下文及词语长度上也没法long-term dependency,所以需要长时间带记忆并且更深的网络来满足NLP的“根”。