Word2Vec 中用到两个模型,CBOW 模型 (Continuous Bag-of-Words model) 和 Skip-gram 模型(Continuous Skip-gram Model)。模型示例如下,是三层结构的神经网络模型,包括输入层,投影层和输出层。
(点击放大图像)
(点击放大图像)
其中 score(wt, h),表示在的上下文环境下,预测结果是的概率得分。上述目标函数,可以转换为极大化似然函数,如下所示:
(点击放大图像)
求解上述概率模型的计算成本是非常高昂的,需要在神经网络的每一次训练过程中,计算每个词在他的上下文环境中出现的概率得分,如下所示:
(点击放大图像)
然而在使用 word2vec 方法进行特性学习的时候,并不需要计算全概率模型。在 CBOW 模型和 skip-gram 模型中,使用了逻辑回归(logistic regression)二分类方法进行的预测。如下图 CBOW 模型所示,为了提高模型的训练速度和改善词向量的质量,通常采用随机负采样(Negative Sampling)的方法,噪音样本 w1,w2,w3,wk… 为选中的负采样。
(点击放大图像)
本章讲解使用 TensorFlow word2vec 模型寻找近义词,输入数据是一大段英文文章,输出是相应词的近义词。比如,通过学习文章可以得到和 five 意思相近的词有: four, three, seven, eight, six, two, zero, nine。通过对大段英文文章的训练,当神经网络训练到 10 万次迭代,网络 Loss 值减小到 4.6 左右的时候,学习得到的相关近似词,如下图所示:
(点击放大图像)
下面为 TensorFlow word2vec API 使用说明:
- embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
- 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]))
- train_inputs = tf.placeholder(tf.int32, shape=[batch_size])
- train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])
- embed = tf.nn.embedding_lookup(embeddings, train_inputs)
- 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))
- optimizer = tf.train.GradientDescentOptimizer(learning_rate = 1.0).minimize(loss)
- for inputs, labels in generate_batch(...):
- feed_dict = {train_inputs: inputs, train_labels: labels}
- _, cur_loss = session.run([optimizer, loss], feed_dict=feed_dict)
本章主要回顾 RNN、LSTM 技术原理,并基于 RNN/LSTM 技术训练语言模型。也就是给定一个单词序列,预测最有可能出现的下一个单词。例如,给定 [had, a, general] 3 个单词的 LSTM 输入序列,预测下一个单词是什么?如下图所示:
(点击放大图像)
循环神经网络(Recurrent Neural Network, RNN)是一类用于处理序列数据的神经网络。和卷积神经网络的区别在于,卷积网络是适用于处理网格化数据(如图像数据)的神经网络,而循环神经网络是适用于处理序列化数据的神经网络。例如,你要预测句子的下一个单词是什么,一般需要用到前面的单词,因为一个句子中前后单词并不是独立的。RNN 之所以称为循环神经网路,即一个序列当前的输出与前面的输出也有关。具体的表现形式为网络会对前面的信息进行记忆并应用于当前输出的计算中,即隐藏层之间的节点不再无连接而是有连接的,并且隐藏层的输入不仅包括输入层的输出还包括上一时刻隐藏层的输出。如下图所示:
(点击放大图像)
RNN 有一问题,反向传播时,梯度也会呈指数倍数的衰减,导致经过许多阶段传播后的梯度倾向于消失,不能处理长期依赖的问题。虽然 RNN 理论上可以处理任意长度的序列,但实习应用中,RNN 很难处理长度超过 10 的序列。为了解决 RNN 梯度消失的问题,提出了 Long Short-Term Memory 模块,通过门的开关实现序列上的记忆功能,当误差从输出层反向传播回来时,可以使用模块的记忆元记下来。所以 LSTM 可以记住比较长时间内的信息。常见的 LSTM 模块如下图所示:
(点击放大图像)
(点击放大图像)
output gate 类似于 input gate 同样会产生一个 0-1 向量来控制 Memory Cell 到输出层的输出,如下公式所示:
(点击放大图像)
三个门协作使得 LSTM 存储块可以存取长期信息,比如说只要输入门保持关闭,记忆单元的信息就不会被后面时刻的输入所覆盖。
首先下载 PTB 的模型数据,该数据集大概包含 10000 个不同的单词,并对不常用的单词进行了标注。
首先需要对样本数据集进行预处理,把每个单词用整数标注,即构建词典索引,如下所示:
- data = _read_words(filename)
- #按照单词出现频率,进行排序
- counter = collections.Counter(data)
- count_pairs = sorted(counter.items(), key=lambda x: (-x1, x[0]))
- #构建词典及词典索引
- words, _ = list(zip(*count_pairs))
- word_to_id = dict(zip(words, range(len(words))))
接着读取训练数据文本,把单词序列转换为单词索引序列,生成训练数据,如下所示:
data = _read_words(filename) data = [word_to_id[word] for word in data if word in word_to_id]
- i = tf.train.range_input_producer(epoch_size, shuffle=False).dequeue()
- x = tf.strided_slice(data, [0, i * num_steps], [batch_size, (i + 1) * num_steps])
- x.set_shape([batch_size, num_steps])
- y = tf.strided_slice(data, [0, i * num_steps + 1], [batch_size, (i + 1) * num_steps + 1])
- y.set_shape([batch_size, num_steps])
- lstm_cell = tf.contrib.rnn.BasicLSTMCell(size,
- forget_bias=0.0, state_is_tuple=True)
- attn_cell = tf.contrib.rnn.DropoutWrapper(lstm_cell,
- output_keep_prob=config.keep_prob)
- cell = tf.contrib.rnn.MultiRNNCell([attn_cell
- for _ in range(config.num_layers)], state_is_tuple = True)
- embedding = tf.get_variable("embedding",
- [vocab_size, size], dtype=data_type())
- inputs = tf.nn.embedding_lookup(embedding, input_.input_data)
(点击放大图像)
- for time_step in range(num_steps):
- if time_step > 0: tf.get_variable_scope().reuse_variables()
- (cell_output, state) = cell(inputs[:, time_step, :], state)
- outputs.append(cell_output)
(点击放大图像)
- softmax_w = tf.get_variable("softmax_w", [size, vocab_size], dtype=data_type())
- softmax_b = tf.get_variable("softmax_b", [vocab_size], dtype=data_type())
- logits = tf.matmul(output, softmax_w) + softmax_b
- loss = tf.contrib.legacy_seq2seq.sequence_loss_by_example([logits],
- [tf.reshape(input_.targets, [-1])], [tf.ones([batch_size * num_steps], dtype=data_type())])
- cost = tf.reduce_sum(loss) / batch_size
- tvars = tf.trainable_variables()
- grads, _ = tf.clip_by_global_norm(tf.gradients(cost, tvars), config.max_grad_norm)
- optimizer = tf.train.GradientDescentOptimizer(self._lr)
- perplexity = np.exp(costs / iters)
本节主要讲解使用 TensorFlow 实现 RNN、LSTM 的语言翻译模型。基础的 sequence-to-sequence 模型主要包含两个 RNN 网络,一个 RNN 网络用于编码 Sequence 的输入,另一个 RNN 网络用于产生 Sequence 的输出。基础架构如下图所示
(点击放大图像)
上图中的每个方框表示 RNN 中的一个 Cell。在上图的模型中,每个输入会被编码成固定长度的状态向量,然后传递给解码器。2014 年,Bahdanau 在论文 "Neural Machine Translation by Jointly Learning to Align and Translate" 中引入了 Attention 机制。Attention 机制允许解码器在每一步输出时参与到原文的不同部分,让模型根据输入的句子以及已经产生的内容来影响翻译结果。一个加入 attention 机制的多层 LSTM sequence-to-sequence 网络结构如下图所示:
(点击放大图像)
针对上述 sequence-to-sequence 模型,TensorFlow 封装成了可以直接调用的函数 API,只需要几百行的代码就能实现一个初级的翻译模型。tf.nn.seq2seq 文件共实现了 5 个 seq2seq 函数:
embedding_rnn_seq2seq 函数接口使用说明如下:
- outputs, states = embedding_rnn_seq2seq(
- encoder_inputs, decoder_inputs, cell,
- num_encoder_symbols, num_decoder_symbols,
- embedding_size, output_projection=None,
- feed_previous=False)
TensorFlow 官方提供了英语到法语的翻译示例,采用的是 statmt 网站提供的语料数据,主要包含:giga-fren.release2.fixed.en(英文语料,3.6G)和 giga-fren.release2.fixed.fr(法文语料,4.3G)。该示例的代码结构如下所示:
(点击放大图像)
- python translate.py
- --data_dir [your_data_directory] --train_dir [checkpoints_directory]
- --en_vocab_size=40000 --fr_vocab_size=40000
随着 TensorFlow 新版本的不断发布以及新模型的不断增加,TensorFlow 已成为主流的深度学习平台。本文主要介绍了 TensorFlow 在自然语言处理领域的相关模型和应用。首先介绍了 Word2Vec 数学原理,以及如何使用 TensorFlow 学习词向量;接着回顾了 RNN、LSTM 的技术原理,讲解了 TensorFlow 的语言预测模型;最后实例分析了 TensorFlow sequence-to-sequence 的机器翻译 API 及官方示例。
来源: http://www.infoq.com/cn/articles/tensorflow-and-nlp-model