TensorFlow训练词向量

参考: https://www.tensorflow.org/tutorials/word2vec

官网的这个教程主要讲word2vec的skip-gram模型,没有讲CBOW,并且训练用的负采样,没有用层次softmax。

动机

动机其实就是分布式假说:

在相同上下文中的词有相似语义(words that appear in the same contexts share semantic meaning)

根据分布式假说表示词的方法分为两类:

  • count-based methods (e.g. Latent Semantic Analysis)
  • predictive methods (e.g. neural probabilistic language models)

预测方法的代表就是Word2Vec模型,有两种:

  • Continuous Bag-of-Words model (CBOW)
  • Skip-Gram model

CBOW 从上下文预测目标词,skip-gram正好相反,从目标词预测上下文中的词。

CBOW在许多分布式信息上进行平滑(将整个上下文作为一种情况),大多数情况下这个模型在小一点的数据集上更有效。可是,skip-gram将每一对context-target词作为一种新的情况,在大一点的数据集上的会有更好效果。

噪声对比训练

神经概率语言模型通常用最大似然估计来训练,即最大化给定输入h(历史信息,前n-1个词),输出下一个词wt的概率,使用softmax函数,取log后就是我们想要的准则函数,叫作log-likelihood。这个值的计算代价非常高,因为softmax的分母要计算整个词表的得分

训练数据的构造方法则为:对于每一个ngram片段,前n-1个词为构成输入数据(词向量拼接),第n个词构成输出类标。由于输出是一个softmax层,则对应词表大小个输出,如果模型训练ok的话,那么这里的第n个词对应的位置输出概率应该最大。

在word2vec中,不再需要计算整个词表每个词的得分。CBOW和skip-gram模型的核心是训练一个二元分类器,将目标词从k个构造的noise words区分出来。CBOW的模型图如下,skip-gram类似只是反过来。

此时目标函数就变为在当前上下文h下,目标词为1的概率log值,加上k个噪声词为0的概率log值的期望。在实践中,我们从噪声分布中采样k个词来近似计算期望。

这个目标可以看做是计算一个最优模型,这个模型赋予真实词高概率,赋予噪声词低概率。学术上,这个叫做负采样。这个方法使得训练变得非常有效,因为现在计算损失函数只需要考虑k个噪声词,而不是整个词表。在TensorFlow中,有一个非常相似的损失函数tf.nn.nce_loss()。

Skip-gram模型

举个例子说明训练的过程。

例子为:

the quick brown fox jumped over the lazy dog

上下文可以是语法词法等,这里定义为左边的词和右边的词,窗口大小设置为1,则可以得到context-target训练对如下:

([the, brown], quick), ([quick, fox], brown), ([brown, jumped], fox), ...

即通过quick预测the和brown, 从brown预测quick和fox,这样数据集变为:

(quick, the), (quick, brown), (brown, quick), (brown, fox), ...

目标函数是定义在整个数据集上的,但是实际训练以minibatch为单位计算,batch_size一般为16 <= batch_size <= 512。

想象一下训练过程,假设当前观察到上面第一个pair,即(quick, the),通过quick预测the,假定num_noise=1,并且通过噪声分布(一般就是词的先验分布)采样选出了噪声词sheep,当前目标变为:

优化的模型参数是词向量(embedding vector),求损失函数的梯度,沿梯度方向更新这个参数。当这个过程在整个数据集上不断重复的时候,每个词的词向量就会不断的“变来变去”,直到模型能够成功的从噪声词中区分真实词。

我们可以通过投影到二维空间来可视化学习到的词向量,用到了t-SNE降维技术。

实战

有了前面的理论基础,实现代码就比较容易了。

基础实现:tensorflow/examples/tutorials/word2vec/word2vec_basic.py

训练数据中,输入是batch_size个target word id构成的行向量,类标是batch_size个context word id构成的列向量:

# Input data.
train_inputs = tf.placeholder(tf.int32, shape=[batch_size])
train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])

词向量通过均匀分布初始化,shape为词表大小*词向量维数,通过tf.nn.embedding_lookup()查表将输入转为词向量形式。

# Look up embeddings for inputs.
embeddings = tf.Variable(
    tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
embed = tf.nn.embedding_lookup(embeddings, train_inputs)

噪声对比估计的损失按照逻辑回归模型定义,所以对于每一个词,都要有对应的权向量和偏置,通过截尾正态分布初始化(只保留两个标准差以内的值)。

# Construct the variables for the NCE loss
nce_weights = tf.Variable(
    tf.truncated_normal([vocabulary_size, embedding_size],
                        stddev=1.0 / math.sqrt(embedding_size)))
nce_biases = tf.Variable(tf.zeros([vocabulary_size]))

计算每个batch上的平均NCE损失

# Compute the average NCE loss for the batch.
# tf.nce_loss automatically draws a new sample of the negative labels each
# time we evaluate the loss.
loss = tf.reduce_mean(
  tf.nn.nce_loss(weights=nce_weights,
                 biases=nce_biases,
                 labels=train_labels,
                 inputs=embed,
                 num_sampled=num_sampled,
                 num_classes=vocabulary_size))

使用随机梯度下降训练

# Construct the SGD optimizer using a learning rate of 1.0.
optimizer = tf.train.GradientDescentOptimizer(1.0).minimize(loss)

最后就是创建session进行训练,详细可看完整源代码。

优化

基础代码只是实现了skip-gram的负采样模型,训练目标为tf.nn.nce_loss(),还可以试试tf.nn.sampled_softmax_loss(),也可以自己定义。

另外,读取数据也不是那么有效(单线程),可以试试New Data Formats中的方法自己定义数据reader,参考代码:
word2vec.py.

如果还想进一步提升效率,可以增加新的算子,参考代码:word2vec_optimized.py

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • 1.NLP当前热点方向 词法/句法分析 词嵌入(word embedding) 命名实体识别(Name Entit...
    __Aragorn阅读 5,990评论 1 9
  • 前面的文章主要从理论的角度介绍了自然语言人机对话系统所可能涉及到的多个领域的经典模型和基础知识。这篇文章,甚至之后...
    我偏笑_NSNirvana阅读 13,862评论 2 64
  • 在各种大举深度学习大旗的公司中,Google公司无疑是旗举得最高的,口号喊得最响亮的那一个。2013年末,Goog...
    chaaffff阅读 16,659评论 0 29
  • 最近读英文绘本比较少。 昨天中午我要求午休,但是呢,陈小冠突然发现这本书,上午我看来着放在床头了。先是自己翻看,然...
    木木sani阅读 339评论 0 0
  • 吵架,焦虑,焦虑,吵架,一直翻来覆去,这么多年,我的状态一直不好,所以一直没有比较理想的生活确实和我的状态有关。遇...
    kikin小鑫阅读 158评论 0 0