【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

深度学习利器:TensorFlow 与 NLP 模型

  • 2017-08-13
  • 本文字数:5586 字

    阅读完需:约 18 分钟

前言

自然语言处理(简称 NLP),是研究计算机处理人类语言的一门技术,NLP 技术让计算机可以基于一组技术和理论,分析、理解人类的沟通内容。传统的自然语言处理方法涉及到了很多语言学本身的知识,而深度学习,是表征学习(representation learning)的一种方法,在机器翻译、自动问答、文本分类、情感分析、信息抽取、序列标注、语法解析等领域都有广泛的应用。

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

Word2Vec 数学原理简介

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

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 近义词模型

本章讲解使用 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 之所以称为循环神经网路,即一个序列当前的输出与前面的输出也有关。具体的表现形式为网络会对前面的信息进行记忆并应用于当前输出的计算中,即隐藏层之间的节点不再无连接而是有连接的,并且隐藏层的输入不仅包括输入层的输出还包括上一时刻隐藏层的输出。如下图所示:

(点击放大图像)

LSTM 技术原理

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

(点击放大图像)

(点击放大图像)

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

(点击放大图像)

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

使用 TensorFlow 构建单词预测模型

首先下载 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]

生成训练数据的 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)

单词困惑度 eloss

复制代码
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 官方提供了英语到法语的翻译示例,采用的是 statmt 网站提供的语料数据,主要包含: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 及官方示例。

参考文献

  1. http://www.tensorflow.org
  2. 深度学习利器: 分布式 TensorFlow 及实例分析
  3. 深度学习利器:TensorFlow 使用实战
  4. 深度学习利器:TensorFlow 系统架构与高性能程序设计
  5. 深度学习利器:TensorFlow 与深度卷积神经网络

作者简介

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

公众号推荐:

跳进 AI 的奇妙世界,一起探索未来工作的新风貌!想要深入了解 AI 如何成为产业创新的新引擎?好奇哪些城市正成为 AI 人才的新磁场?《中国生成式 AI 开发者洞察 2024》由 InfoQ 研究中心精心打造,为你深度解锁生成式 AI 领域的最新开发者动态。无论你是资深研发者,还是对生成式 AI 充满好奇的新手,这份报告都是你不可错过的知识宝典。欢迎大家扫码关注「AI前线」公众号,回复「开发者洞察」领取。

2017-08-13 17:0712183

评论

发布
暂无评论
发现更多内容

烧录OpenHarmony 3.2(尝鲜版)步骤

离北况归

OpenHarmony OpenHarmony3.2

微擎同步粉丝不显示头像和昵称?

智伍应用

微擎 php开源

热烈庆祝“海泰密码技术融合创新中心&数据中心重启安全工程”双中心智能重启用

电子信息发烧客

网络安全 科技 科技企业

我们为什么选择使用分布式持续交付新星 Zadig ?

Zadig

DevOps 云原生 CI/CD 软件交付

什么是显卡?GPU服务器到底有什么作用?

Finovy Cloud

gpu GPU服务器

做了5年开源项目,我总结了以下提PR经验!

OpenHarmony开发者

OpenHarmony 开源生态

小程序和App同时拥有?两者兼得的一种技术方案

Speedoooo

微信小程序 APP开发 小程序容器 小程序转app

【刷题第16天】数组中出现次数超过一半的数字

白日梦

5月月更

10分钟,将微信小程序转换成App

Speedoooo

微信小程序 移动开发 小程序容器 小程序转app

满满干货!手把手教你实现基于eTS的HarmonyOS分布式计算器

HarmonyOS开发者

HarmonyOS ETS

SaaS应用:企业数字化转型性价比最高的方式

小炮

“双碳”大局中再看业务合同电子化

鲸品堂

节能 提效降本 双碳

人人皆为开发者?不可错过的低代码发展新趋势

云智慧AIOps社区

大前端 低代码 数据可视化

Apache SeaTunnel(Incubating)与计算引擎的解耦之道,重构API我们做了些什么

Apache SeaTunnel

Apache 大数据 开源 DolphinScheduler workflow

谈谈技术能力

阿里巴巴中间件

阿里云 程序员 中间件 技术思考

Apache ShenYu 网关正式支持 Dubbo3 服务代理

阿里巴巴中间件

阿里云 开源 微服务 云原生 dubbo

IET 试水SiFL中文项目 为中国工程师“走出去”创造宝贵机遇

E科讯

使用postMessage对iframe进行跨域数据传输

空城机

iframe postMessage 5月月更

Hoo研究院|区块链简报 20220523期

区块链前沿News

#区块链# Hoo 热点

netty系列之:epoll传输协议详解

程序那些事

Java Netty 程序那些事 5月月更

多款顶级好用的 Vue 表单设计器测评推荐,可拖拽生成表单

蒋川

Vue Element 组件 表单设计 Ant Design

使用 Provider 实现 Flutter 不相关页面状态数据共享

岛上码农

flutter ios 安卓开发 跨平台开发 5月月更

前端食堂技术周刊第 38 期:Remix v1.5.0、Babel v7.18.0、前端部署十五章、Tree Shaking 问题排查指南、一文搞懂前端技术发展

童欧巴

前端 Remix 前端部署

火山引擎A/B测试私有化实践

字节跳动数据平台

实验 火山引擎 私有化部署 ab测试

直播预告丨Hello HarmonyOS进阶课程第四课——ArkUI动画开发

HarmonyOS开发者

HarmonyOS arkui

为什么校招面试中“线程与进程的区别”老是被问到?我该如何回答?

宇宙之一粟

线程 进程 5月月更

英伟达周锡健:设计到数字营销的最后一公里

阿里云弹性计算

vr XR 视觉计算

Markdown语法简介

工程师日月

markdown语法 5月月更

《阿里云代码安全白皮书》5个维度应对3类代码安全问题

阿里云云效

云计算 阿里云 代码管理 代码托管 代码安全

成本节省 50%,10 人团队使用函数计算开发 wolai 在线文档应用

阿里巴巴中间件

阿里云 中间件 函数计算

干货复盘 | 易观分析“出海非洲战略”专题分享

易观分析

非洲战略

深度学习利器:TensorFlow与NLP模型_语言 & 开发_武维_InfoQ精选文章