TF入门04-TF实现Word2Vec

作者: 七八音 | 来源:发表于2020-05-28 23:29 被阅读0次

    Word2Vec是一组用来产生词嵌入的模型,包括两种主要的模型:skip-gram和CBOW。

    Skip-gram vs CBOW

    算法层面上, 两种模型很相似,CBOW模型是从两边预测中心词,skip-gram模型是中心词预测两边。比如,对于“The quick brown fox jumps”,CBOW模型从上下文"the, "quick", "fox", 和"jumps"预测中心词“brown”,skip-gram模型从“brown”预测上下文"the, "quick", "fox", 和"jumps"。

    统计层面上,CBOW模型通过将上下文看做一个样本组成,将分布信息做了平滑处理。大多数情况下,cbow模型适用于小型数据集;skip-gram模型将每个上下文-中心词对看做一个样本,用在大型数据集上表现更好。

    image

    在本文中,我们使用skip-gram模型来构建word2vec。为了得到词嵌入向量,我们需要构建一个单隐藏层的神经网络,然后用来执行特定任务来完成训练;但是训练得到的模型并不是我们需要的。我们只关注隐藏层的权重,这些权重就是词嵌入向量。

    上面的特定任务是指给定中心词预测上下文。对于句子中的某个词,在词的上下文中随机选择一个词;网络模型可以输出整个词典中每个词是中心词上下文的概率。

    Softmax、Negative Sampling & Noise Contrastive Estimation(NCE)

    为了得到某个词的所有可能上下文词的概率分布,我们使用softmax来实现。softmax函数根据输入x_i输出一个概率值p_i。在这里x_i表示中心词的一个可能的上下文:
    softmax(x_i) = \frac{exp(x_i)}{\sum_j exp(x_j)}
    但是,softmax用于归一化的分母的计算需要遍历整个词典,通常情况下词典长度在百万级别,而且指数的计算也比较耗时,这就导致了softmax的计算量进一步加大

    为了规避这个计算瓶颈,我们可以使用分层softmax(hierarchical softmax)和基于采样的softmax。论文Distributed Representations of Words and Phrases and their Compositionality 指出训练skip-gram模型,和分层softmax方法相比,使用negative sampling的方法训练速度更快,得到的词向量更好。

    Negative Sampling(负采样)是基于采样方法的一种。基于采样的方法也包括重要性采样(importance sampling)和目标采样(target sampling)。负采样方法是NCE的简化版:负采样对噪声样本(负样本)的采样数量k以及噪声数据服从的分布Q做了假定,kQ(w)=1

    负采样方法用于学习词嵌入表示,并不能保证其梯度值和softmax函数梯度值相近;而NCE方法随着负样本采样数的增加其提取值也愈来愈逼近于softmax的梯度值。Mnih and Teh(2012)表明使用25个噪声样本的计算结果与softmax的计算值差距不大,而且运算速度能加快45倍。因此,我们使用NCE来实现word2vec。

    基于采样的方法,无论是负采样还是NCE方法,只适用于训练阶段;在应用阶段还需要执行softmax来得到正则化的概率结果。

    数据介绍

    2006年3月3日的维基百科文本的100MB数据text8。

    100MB数据训练得到的词嵌入虽然表现不太好,但是从中也能看到一些有趣的现象。使用空格切分数据后,文本包括17005207个词。为了得到更好的词嵌入,需要使用更大的数据集。

    Overview

    使用TensorFlow实现模型,需要景观两个阶段:定义计算图以及图的运行。

    阶段一:图定义

    1. 导入数据(tf.data 、placeholders)
    2. 定义权重
    3. 定义模型
    4. 定义损失函数loss
    5. 定义优化器

    阶段二:执行运算图

    1. 变量初始化
    2. 初始化迭代器/向模型传送数据
    3. 执行前向计算
    4. 计算cost
    5. 梯度计算来调整模型参数
    image

    阶段一:图定义

    1. 创建dataset,生成样本

    skip-gram模型的输入为(中心词,上下文词)pair对。数据传送到模型之前,需要将字符串类型转换成indices表示,如果“computer”是词典中第100个单词,那么对象下标为99。

    每一个样本数据是一个标量,BATCH_SIZE个输入样本的构成tensor的shape 为[BATCH_SIZE],输出样本的shape为[BATCH_sIZE, 1].

    image

    2.定义权重

    在embedding矩阵中每一行表示一个词的向量表示。如果词向量长度为EMBED_SIZE,embedding矩阵的shape为[VOCAB_SIZE, EMBED_SIZE]

    image

    3. Inference

    为了从embed_matrix中得到对应输入的词向量表示,我们可以使用tf.nn.embedding_lookup来实现:

    image

    这个函数相当于一个查表操作,根据输入ids在params找到对应的向量。

    image

    如果输入是one_hot表示,向量乘以矩阵可以很快地找到one_hot非零值对应的向量(one_hot中非零值为第4个,相乘后结果就是矩阵的第4行);使用相乘方法,由于one_hot表示有很多0值进而会产生许多不必要的计算;使用tf.nn.lookup就可以节省这些不必要的计算。

    为了得到中心词的embedding表示,

    embed = tf.nn.embedding_lookup(embed_matrix, center_words, name='embed')
    

    4. 定义损失函数

    ​ TensorFlow已经为我们实现了NCE损失函数:

    tf.nn.nce_loss(
        weights,
        biases,
        labels,
        inputs,
        num_sampled,
        num_classes,
        num_true=1,
        sampled_values=None,
        remove_accidental_hits=False,
        partition_strategy='mod',
        name='nce_loss'
    )
    

    为了计算NCE loss,需要定义计算loss的隐藏层权重weights和偏置biases。在训练过程中这些参数会自动更新、优化。在采样之后,最终结果的计算过程如下:

    tf.matmul(embed, tf.transpose(nce_weight)) + nce_bias
    

    这项计算包含在tf.nn_nce_loss的计算过程中。

    nce_weight = tf.get_variable('nce_weight', shape=[VOCAB_SIZE, EMBED_SIZE], initializer=tf.truncated_normal_initializer(stddev=1.0 / (EMBED_SIZE ** 0.5)))
    nce_bias = tf.get_variable('nce_bias', initializer=tf.zeros([VOCAB_SIZE]))
    

    损失函数定义如下:

    loss = tf.reduce_mean(tf.nn.nce_loss(weights=nce_weight,
                                  biases=nce_bias,
                                  labels=target_words,
                                  inputs=embed,
                                  num_sampled=NUM_SAMPLED,
                                  num_classes=VOCAB_SIZE))
    

    5. 定义优化器

    optimizer = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(loss)
    

    阶段二:图的执行

    创建一个会话来执行优化op。

    with tf.Session() as sess:
        # 迭代器初始化
        sess.run(iterator.initializer)
        # 变量初始化
        sess.run(tf.global_variables_initializer())
    
        writer = tf.summary.FileWriter('graphs/word2vec_simple', sess.graph)
    
        for index in range(NUM_TRAIN_STEPS):
            try:
                # 执行优化,计算loss
                loss_batch, _ = sess.run([loss, optimizer])
            except tf.errors.OutOfRangeError:
                sess.run(iterator.initializer)
        writer.close()
    

    完整代码地址:ClickMe


    欢迎关注公众号,一起学习

    Reference

    Stanford CS 20: Tensorflow for Deep Learning Research

    相关文章

      网友评论

        本文标题:TF入门04-TF实现Word2Vec

        本文链接:https://www.haomeiwen.com/subject/tnhtzhtx.html