深度学习实战:使用TensorFlow构建NLP模型

2017 年 8 月 2 日 AI前线 武维
作者|武维
编辑|陈思
传统的自然语言处理方法涉及到了很多语言学本身的知识,而深度学习,是表征学习(representation learning)的一种方法,在机器翻译、自动问答、文本分类、情感分析、信息抽取、序列标注、语法解析等领域都有广泛的应用。本文作者将主要结合 TensorFlow 平台,讲解 TensorFlow 词向量生成模型(Vector Representations of Words);使用 RNN、LSTM 模型进行语言预测;以及 TensorFlow 自动翻译模型。
前言

2013 年末谷歌发布的 word2vec 工具,将一个词表示为词向量,将文字数字化,有效地应用于文本分析。2016 年谷歌开源自动生成文本摘要模型及相关 TensorFlow 代码。2016/2017 年,谷歌发布 / 升级语言处理框架 SyntaxNet,识别率提高 25%,为 40 种语言带来文本分割和词态分析功能。2017 年谷歌官方开源 tf-seq2seq,一种通用编码器 / 解码器框架,实现自动翻译。

Word2Vec 数学原理简介

我们将自然语言交给机器学习来处理,但机器无法直接理解人类语言。那么首先要做的事情就是要将语言数学化,Hinton 于 1986 年提出 Distributed Representation 方法,通过训练将语言中的每一个词映射成一个固定长度的向量。所有这些向量构成词向量空间,每个向量可视为空间中的一个点,这样就可以根据词之间的距离来判断它们之间的相似性,并且可以把其应用扩展到句子、文档及中文分词。

Word2Vec 中用到两个模型,CBOW 模型 (Continuous Bag-of-Words model) 和 Skip-gram 模型(Continuous Skip-gram Model)。模型示例如下,是三层结构的神经网络模型,包括输入层,投影层和输出层。

CBOW 模型是在已知当前词 wt 的上下文

环境下,预测当前词;Skip-gram 模型是在已知当前词的前提下,预测其上下文

CBOW 模型对于小数据集比较有效;Skip-gram 模型对于大数据集比较有效。训练模型采用最大化似然概率作为目标函数,如下面的 softmax 函数所示:

其中 score($ w_t $, h),表示在 $ w_t $ 的上下文环境下,预测结果是的概率得分。上述目标函数,可以转换为极大化似然函数,如下所示:

求解上述概率模型的计算成本是非常高昂的,需要在神经网络的每一次训练过程中,计算每个词在他的上下文环境中出现的概率得分,如下所示:

然而在使用 word2vec 方法进行特性学习的时候,并不需要计算全概率模型。在 CBOW 模型和 skip-gram 模型中,使用了逻辑回归(logistic regression)二分类方法进行 $ w_t $ 的预测。如下图 CBOW 模型所示,为了提高模型的训练速度和改善词向量的质量,通常采用随机负采样(Negative Sampling)的方法,噪音样本

为选中的负采样。

TensorFlow 近义词模型

本章讲解使用 TensorFlow word2vec 模型寻找近义词,输入数据是一大段英文文章,输出是相应词的近义词。比如,通过学习文章可以得到和 five 意思相近的词有: four, three, seven, eight, six, two, zero, nine。通过对大段英文文章的训练,当神经网络训练到 10 万次迭代,网络 Loss 值减小到 4.6 左右的时候,学习得到的相关近似词,如下图所示:

下面为 TensorFlow word2vec API 使用说明:

构建词向量变量,vocabulary_size 为字典大小,embedding_size 为词向量大小
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 值
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))
定义使用随机梯度下降法执行优化操作,最小化 loss 值
optimizer = tf.train.
GradientDescentOptimizer(
learning_rate=1.0).minimize(loss)
通过 TensorFlow Session Run 的方法执行模型训练
for inputs, 
labels in generate_batch(...):  feed_dict = {train_inputs: inputs,
   train_labels: labels}  _, cur_loss = session.run(
   [optimizer, loss],
   feed_dict=feed_dict)
TensorFlow 语言预测模型

本章主要回顾 RNN、LSTM 技术原理,并基于 RNN/LSTM 技术训练语言模型。也就是给定一个单词序列,预测最有可能出现的下一个单词。例如,给定 [had, a, general] 3 个单词的 LSTM 输入序列,预测下一个单词是什么?如下图所示:

RNN 技术原理

循环神经网络(Recurrent Neural Network, RNN)是一类用于处理序列数据的神经网络。和卷积神经网络的区别在于,卷积网络是适用于处理网格化数据(如图像数据)的神经网络,而循环神经网络是适用于处理序列化数据的神经网络。例如,你要预测句子的下一个单词是什么,一般需要用到前面的单词,因为一个句子中前后单词并不是独立的。RNN 之所以称为循环神经网路,即一个序列当前的输出与前面的输出也有关。具体的表现形式为网络会对前面的信息进行记忆并应用于当前输出的计算中,即隐藏层之间的节点不再无连接而是有连接的,并且隐藏层的输入不仅包括输入层的输出还包括上一时刻隐藏层的输出。如下图所示:

其中 xt 表示第 t 个序列的输入,例如第 t 个单词的词向量;st 为隐藏层第 t 步的状态,它是网络的记忆单元。st 根据当前输入层的输出与上一步隐藏层的状态进行计算。可以认为隐藏层状态 st 是网络的记忆单元;st 包含了前面所有步的隐藏层状态。ot 是第 t 步的输出,输出层的输出 ot 只与当前步的 st 有关。在 RNN 中,每一层各自都共享参数 U,V,W,大大地降低了网络中需要学习的参数,提高训练速度。

LSTM 技术原理

RNN 有一问题,反向传播时,梯度也会呈指数倍数的衰减,导致经过许多阶段传播后的梯度倾向于消失,不能处理长期依赖的问题。虽然 RNN 理论上可以处理任意长度的序列,但实习应用中,RNN 很难处理长度超过 10 的序列。为了解决 RNN 梯度消失的问题,提出了 Long Short-Term Memory 模块,通过门的开关实现序列上的记忆功能,当误差从输出层反向传播回来时,可以使用模块的记忆元记下来。所以 LSTM 可以记住比较长时间内的信息。常见的 LSTM 模块如下图所示:

LSTM 使用记忆单元 Memory Cell 来保存状态信息,在时刻 t 的输入如上图的 gt 所示,除接受本时刻的输入 xt 外,还接受上一时刻的输出 ht−1,并且经过非线性激活函数σ,LSTM 并不接纳所有输入 gt,而是在网络中加入两个门,输入门(input gate)、输出门(output gate)。input gate 如上图的 it 所示,跟输入层一样,接受 xt 与 ht−1,经过σ函数后产生一个 0-1 向量,0 代表关闭、1 代表开启,这样来对输入进行控制。LSTM 中引入 forget gate,forget gate 与 input/output gate 一样,接受 xt 与 ht−1 作为输入。将 Memory Cell 中的内容与 forget gate 逐元素相乘,然后存放到 Memory Cell 里,这样 Memory Cell 里既有上一时刻 t−1 的状态,又添加了时刻 t 的状态。LSTM Memory Cell 的更新公式为:

output gate 类似于 input gate 同样会产生一个 0-1 向量来控制 Memory Cell 到输出层的输出,如下公式所示:

三个门协作使得  LSTM 存储块可以存取长期信息,比如说只要输入门保持关闭,记忆单元的信息就不会被后面时刻的输入所覆盖。

使用 TensorFlow 构建单词预测模型

首先下载 PTB 的模型数据 

http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz, 

该数据集大概包含 10000 个不同的单词,并对不常用的单词进行了标注。

首先需要对样本数据集进行预处理,把每个单词用整数标注,即构建词典索引,如下所示:

读取训练数据
data = _read_words(filename)

  ** 按照单词出现频率,进行排序 **

  counter = collections.Counter(data)

  count_pairs = sorted(counter.items(), 
   key=lambda x: (-x[1], 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]
生成训练数据的 data 和 label,其中 epoch_size 为该 epoch 的训练迭代次数,num_steps 为 LSTM 的序列长度
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,其中 size 为隐藏神经元的数量
lstm_cell = 
tf.contrib.rnn.BasicLSTMCell(
size, forget_bias=0.0,
state_is_tuple=True)
如果为训练模式,为保证训练鲁棒性,定义 dropout 操作
attn_cell = 
tf.contrib.rnn.DropoutWrapper(
lstm_cell,

output_keep_prob=config.keep_prob)
根据层数配置,定义多层 RNN 神经网络
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())
根据单词索引,查找词向量,如下图所示。从单词索引找到对应的 One-hot encoding,然后红色的 weight 就直接对应了输出节点的值,也就是对应的 embedding 向量。
inputs = tf.nn.embedding_lookup(
embedding, input_.input_data)

定义 RNN 网络,其中 state 为 LSTM Cell 的状态,cell_output 为 LSTM Cell 的输出
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)
定义训练的 loss 值就,如下公式所示。

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 值
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)
单词困惑度 $e^loss$
perplexity = np.exp(costs / iters)
TensorFlow 语言翻译模型

本节主要讲解使用 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 函数:

  • basic_rnn_seq2seq:输入和输出都是 embedding 的形式;encoder 和 decoder 用相同的 RNN cell,但不共享权值参数;

  • tied_rnn_seq2seq:同 basic_rnn_seq2seq,但 encoder 和 decoder 共享权值参数;

  • embedding_rnn_seq2seq:同 basic_rnn_seq2seq,但输入和输出改为 id 的形式,函数会在内部创建分别用于 encoder 和 decoder 的 embedding 矩阵;

  • embedding_tied_rnn_seq2seq:同 tied_rnn_seq2seq,但输入和输出改为 id 形式,函数会在内部创建分别用于 encoder 和 decoder 的 embedding 矩阵;

  • embedding_attention_seq2seq:同 embedding_rnn_seq2seq,但多了 attention 机制;

embedding_rnn_seq2seq 函数接口使用说明如下:

  • encoder_inputs:encoder 的输入

  • decoder_inputs:decoder 的输入

  • cell:RNN_Cell 的实例

  • num_encoder_symbols,num_decoder_symbols:分别是编码和解码的大小

  • embedding_size:词向量的维度

  • output_projection:decoder 的 output 向量投影到词表空间时,用到的投影矩阵和偏置项

  • feed_previous:若为 True, 只有第一个 decoder 的输入符号有用,所有的 decoder 输入都依赖于上一步的输出;

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 官方提供了英语到法语的翻译示例(https://github.com/tensorflow/models/tree/master/tutorials/rnn/translate),采用的是 http://www.statmt.org 网站提供的语料数据,主要包含:giga-fren.release2.fixed.en(英文语料,3.6G)和 giga-fren.release2.fixed.fr(法文语料,4.3G)。该示例的代码结构如下所示:

  • seq2seq_model.py:seq2seq 的 TensorFlow 模型采用了 embedding_attention_seq2seq 用于创建 seq2seq 模型。

  • data_utils.py:对语料数据进行数据预处理,根据语料数据生成词典库;并基于词典库把要翻译的语句转换成用用词 ID 表示的训练序列。如下图所示:

  • translate.py:主函数入口,执行翻译模型的训练

执行模型训练
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 及官方示例。

查看作者上一篇文章:

图像识别利器:TensorFlow 与卷积神经网络实例解析

参考文献
  1. http://www.tensorflow.org

  2. 深度学习利器: 分布式 TensorFlow 及实例分析

  3. 深度学习利器:TensorFlow 使用实战

  4. 深度学习利器:TensorFlow 系统架构与高性能程序设计

  5. 深度学习利器:TensorFlow 与深度卷积神经网络

作者简介

武维:IBM Spectrum Computing 研发工程师,博士,系统架构师,主要从事大数据,深度学习,云计算等领域的研发工作。


AI 前线微信社群
入群方法

关注 AI 前线公众账号(直接识别下图二维码),点击自动回复中的链接,按照提示进行就可以啦!还可以在公众号主页点击下方菜单“加入社群”获得入群方法~AI 前线,期待你的加入!



新功能推荐

你也许尝试过 InfoQ 网站的搜索方式,体验并不好。

你知道公众号上有很多优质内容,但除了在历史列表人肉检索,查询渠道并不多。

有没有一种搜索方式,能整合 InfoQ 中文站、微信公众号矩阵的全部资源?极客搜索,这款针对极客邦科技全站内容资源的轻量级搜索引擎,做到了!

链接: 

http://s.geekbang.org?utm_source=weixinbanner

扫描下方二维码,极客(即刻)试用!


登录查看更多
0

相关内容

连续词袋模型(CBOW),利用上下文或周围的单词来预测中心词。其输入为某一个特征词的上下文相关对应的词向量(单词的one-hot编码);输出为这特定的一个词的词向量(单词的one-hot编码)。
基于多头注意力胶囊网络的文本分类模型
专知会员服务
76+阅读 · 2020年5月24日
深度学习自然语言处理概述,216页ppt,Jindřich Helcl
专知会员服务
212+阅读 · 2020年4月26日
Sklearn 与 TensorFlow 机器学习实用指南,385页pdf
专知会员服务
129+阅读 · 2020年3月15日
Transformer文本分类代码
专知会员服务
116+阅读 · 2020年2月3日
一网打尽!100+深度学习模型TensorFlow与Pytorch代码实现集合
详解谷歌最强NLP模型BERT(理论+实战)
AI100
11+阅读 · 2019年1月18日
自然语言处理中的词表征(第二部分)
AI研习社
3+阅读 · 2018年12月31日
深度上下文词向量
微信AI
27+阅读 · 2018年9月13日
基于 Doc2vec 训练句子向量
AI研习社
6+阅读 · 2018年5月16日
实战 | 手把手教你用PyTorch实现图像描述(附完整代码)
基于TensorFlow的深度学习实战
七月在线实验室
9+阅读 · 2018年4月25日
Tensorflow 文本分类-Python深度学习
Python程序员
12+阅读 · 2017年11月22日
教程 | 在Python和TensorFlow上构建Word2Vec词嵌入模型
机器之心
6+阅读 · 2017年11月20日
深度学习 | 利用词嵌入对文本进行情感分析
沈浩老师
11+阅读 · 2017年10月19日
在深度学习TensorFlow 框架上使用 LSTM 进行情感分析
北京思腾合力科技有限公司
4+阅读 · 2017年8月9日
Arxiv
45+阅读 · 2019年12月20日
Tutorial on NLP-Inspired Network Embedding
Arxiv
7+阅读 · 2019年10月16日
Embedding Logical Queries on Knowledge Graphs
Arxiv
3+阅读 · 2019年2月19日
Arxiv
22+阅读 · 2018年8月30日
Arxiv
3+阅读 · 2018年8月27日
Arxiv
6+阅读 · 2018年6月20日
VIP会员
相关VIP内容
基于多头注意力胶囊网络的文本分类模型
专知会员服务
76+阅读 · 2020年5月24日
深度学习自然语言处理概述,216页ppt,Jindřich Helcl
专知会员服务
212+阅读 · 2020年4月26日
Sklearn 与 TensorFlow 机器学习实用指南,385页pdf
专知会员服务
129+阅读 · 2020年3月15日
Transformer文本分类代码
专知会员服务
116+阅读 · 2020年2月3日
一网打尽!100+深度学习模型TensorFlow与Pytorch代码实现集合
相关资讯
详解谷歌最强NLP模型BERT(理论+实战)
AI100
11+阅读 · 2019年1月18日
自然语言处理中的词表征(第二部分)
AI研习社
3+阅读 · 2018年12月31日
深度上下文词向量
微信AI
27+阅读 · 2018年9月13日
基于 Doc2vec 训练句子向量
AI研习社
6+阅读 · 2018年5月16日
实战 | 手把手教你用PyTorch实现图像描述(附完整代码)
基于TensorFlow的深度学习实战
七月在线实验室
9+阅读 · 2018年4月25日
Tensorflow 文本分类-Python深度学习
Python程序员
12+阅读 · 2017年11月22日
教程 | 在Python和TensorFlow上构建Word2Vec词嵌入模型
机器之心
6+阅读 · 2017年11月20日
深度学习 | 利用词嵌入对文本进行情感分析
沈浩老师
11+阅读 · 2017年10月19日
在深度学习TensorFlow 框架上使用 LSTM 进行情感分析
北京思腾合力科技有限公司
4+阅读 · 2017年8月9日
相关论文
Arxiv
45+阅读 · 2019年12月20日
Tutorial on NLP-Inspired Network Embedding
Arxiv
7+阅读 · 2019年10月16日
Embedding Logical Queries on Knowledge Graphs
Arxiv
3+阅读 · 2019年2月19日
Arxiv
22+阅读 · 2018年8月30日
Arxiv
3+阅读 · 2018年8月27日
Arxiv
6+阅读 · 2018年6月20日
Top
微信扫码咨询专知VIP会员