教程|自然语言处理技术之TensorFlow学习框架详解

Posted 自然语言处理技术

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了教程|自然语言处理技术之TensorFlow学习框架详解相关的知识,希望对你有一定的参考价值。

    深度学习算法的成功使人工智能的研究和应用取得了突破性的进展,并极大地改变了人们的生活。越来越多的开发人员都在学习深度学习方面的开发技术。现在主流的深度学习框架包括TensorFlow, Caffe, Theano, Torch等。Google推出的TensorFlow是目前最为流行的开源深度学习框架,在图形分类、音频处理、推荐系统和自然语言处理等场景下都有丰富的应用。尽管功能强大,该框架学习门槛并不高,只要掌握Python安装和使用,并对机器学习和神经网络方面的知识有所了解就可以上手。

    本文详细介绍了TensorFlow的安装,编程的基础(包括计算图概念,会话,张量,变量,占位符,常用数学计算操作),并且最后利用TensorFlow对MNIST手写数字识别问题的编程进行详细的介绍,以便于大家对TensorFlow有个直观和全面的了解,当然更深入的学习大家可以查阅TensorFlow的官方文档,该文档已经有中文版本,后面会附相关的链接。


1TensorFlow安装

    我们先介绍TensorFlow的安装,TensorFlow对环境不挑剔,在Python2.7和Python3下面均可运行,操作系统Linux、MAC、Windows均可(只要是64位)。安装TensorFlow主要不同之处是TensorFlow安装包支持GPU和不支持GPU两种版本,名称分别为tensorflow-gpu和tensorflow。实际生产环境最好安装GPU的版本,以利于GPU强大的计算能力,不过这需要先安装相应的CUDA ToolKit和CuDNN,相比之下,安装不支持GPU的TensorFlow包容易一些,顺利的话执行pip install tensorflow就OK,如果再安装过程中遇到问题,可以根据错误提示在网上搜索解决办法,本文推荐通过Anaconda去安装TensorFlow,这里anaconda的安装就不介绍了。假设大家已经安装了Anaconda,那么可以通过如下步骤安装TensorFlow:

1:建立一个conda计算环境,名为tensorflow:

conda create -n tensorflow python=xx (xx为python版本,一般为2.7或者3.4)

2:激活TensorFlow环境,然后使用其中的pip安装TensorFlow,当使用easy_install时,使用--ignore-installed标记防止错误的产生,

source activate tensorflow

在Mac环境中而且是Python2.X的情况下使用如下命令安装:

pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/mac/tensorflow-0.8.0rc0-py2-none-any.whl

Mac环境中而且是Python3.X的情况下使用如下命令安装

pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/mac/tensorflow-0.8.0rc0-py3-none-any.whl

当不再使用TensorFlow的时候可以关闭该环境:

source deactivate

再次使用的时候再激活

source activate tensorflow

安装后可以在命令行下启动Python或者打开Jupyter Notebook,执行下面的语句验证是否安装成功:

import tensorflow as tf

用tf引用TensorFlow包已经成为一种约定。


2TensorFlow相关的基本概念

  1. 计算图概念  

    Tensor和Flow是TensorFlow最重要的两个概念,其中Tensor表示张量,即数据模型,而Flow表示流,直观表达为:张量之间通过计算相互转化的过程,它是TensorFlow的计算模型。TensorFlow是一个编程系统,使用图来表示计算任务,图中的每个节点被称为操作(operation)或者运算。每一条边代表计算之间的依赖关系。TensorFlow Python库中有一个默认图,操作构造器可以为其增加节点,这个默认图对许多程序已经足够使用。

    TensorFlow的计算一般分为两个阶段,第一阶段定义计算图中所有的计算,第二阶段为执行计算。以下代码给出了计算定义阶段:

import tensorflow as tf
# 使用tf.constant()函数创建两个常量操作,并产生1x2的矩阵,每个操作被称为一个节点,会被添加到默认的图中
a = tf.constant([1.0, 2.0], name='a')
b = tf.constant([2.0, 3.0], name='b')
results = a + b

程序会自动维护默认的计算图,通过

tf.get_default_graph()

函数可以获取当前默认的计算图。  

print(a.graph is tf.get_default_graph()) # 输出为True

除了默认的计算图,TensorFlow支持通过tf.Graph函数来生成新的计算图。不同计算图上的张量和运算不会共享。计算图不仅可以隔离张量和计算,还提供了管理张量和计算的机制,计算图可以通过tf.Graph.device函数来指定运行计算的设备。


2. TensorFlow数据模型之张量(Tensor)

    TensorFlow所有的数据都为Tensor,即TensorFlow图计算过程中,节点之间的数据流都是采用Tensor的形式进行,而且也只能采用Tensor形式进行。Tensor可以是一个变量也可以是一个数组,也可以是多维数组等。Tensor有几个重要的属性:

(1) Data types, 标志该Tensor存储的数据类型,如tf.float32, tf.String等

(2) Rank,即tensor是几维的数组,scalar rank为0,vector rank为1,matrix rank为3, N-demensional tensor rank 为N。

(3) Shape, tensor的形状,scalar rank为[], vector shap 为[D0] ,matrix shape为[D0, D1];

    Tensor在TensorFlow中实现不是直接采用数组的形式,它只是对TensorFlow中运算结果的引用。张量中并没有真正保存数字,它保存的是如何得到这些数字的计算过程,如:

import tensorflow as tf
# 使用tf.constant()函数创建两个常量操作,并产生1x2的矩阵,每个操作被称为一个节点,会被添加到默认的图中
a = tf.constant([1.0, 2.0], name='a')
b = tf.constant([2.0, 3.0], name='b')
results = a + b

print results

# 输出:Tensor("add:0", shape= (2,0), dtype=float32)

    TensorFlow中的张量和Numpy中的数组不同,TensorFlow的计算结果不是一个具体的数字,而是一个张量的结构。

    张量的使用可以分为两大类,第一类是对中间计算结果的引用,这样可以提高代码的可读性。如:

# 使用张量记录中间结果
a = tf.constant([1.0, 2.0], name='a')
b = tf.constant([2.0, 3.0], name='b')
results = a + b


# 直接计算向量的和,可读性较差
results = tf.constant([1.0, 2.0], name='a') + tf.constant([2.0, 3.0], name='b')

第二类是当计算图构造完成后,张量可以用来获得计算结果,得到真实的数字,下面介绍的会话可以得到这些具体的数字。


3. 会话 (Session)

会话的模式有两种,第一种需要明确调用会话生成函数和关闭会话函数,如下例所示。

sess = tf.Session() #创建会话
sess.run(...) # 运行会话
sess.close() # 关闭会话使得本次运行中使用得到的资源可以被释放

使用这种模式,需要明确调用Session.close()函数关闭会话,并释放资源。但当程序因为异常而退出时,关闭会话的函数可能就不会被执行,从而导致资源泄露。为了解决异常退出时资源释放的问题,有一种新的运行模式:上下文管理器,来使用会话

# 创建会话,通过Python中的上下文管理器来管理这个会话
with tf.Session() as sess:
   sess.run(...) # 运行会话
# 不需要调用函数关闭会话

   这种方式既解决了因为异常退出时资源释放的问题,又解决了忘记调用Session.close函数而产生的资源泄露 。另外TensorFlow也可以通过设定默认会话计算张量的取值:

a = tf.constant([1.0, 2.0], name='a')
b = tf.constant([2.0, 3.0], name='b')
results = a + b
sess = tf.Session()
with sess.as_default():
   print(results.eval) #执行会话并输出,相当于sess.run()

对于Session中run的方法中涉及几个重要的参数,特别是fetch和feed两个参数。

run(fetches,feed_dict=None,options=None,run_metadata=None)

fetches的作用就是执行一次Session时,其中待执行的内容放在fetches中,这里的fetches是一个有用的概念,在其它地方也用得到。fetches可以是一个图中的元素,或者是list,tuple,dict等,总之是一般结构都可以,而这个元素可以是一下几种类型。

(1) 操作:返回的为None

(2)Tensor:返回计算得到的Tensor中包含的值

(3)SparseTensor:返回计算得到的Tensor中包含的值

(4)string:这时string为操作和Tensor的名字,返回内容为计算得到的Tensor中包含的值

    feed_dict参数允许重新图中tensor的值,它是一个字典,键是tensor的名字,值是重写的tensor的内容,通常用在占位符(Placeholder)的实现。

    Session还有一种模式为交互式,主要用于Ipython这种交互场景中,它和标准的Session的唯一区别是它初始化自己是default的Session。

tf.InteractiveSession()

与sess.run(x)等效的语句为x.eval(session=sess)。


4. 变量(variable)

    变量,顾名思义就是可变的量,通常保存一些在计算中需要更新的量,如神经网络的参数等,官方描述为当训练模型时,用变量来存储和更新参数。变量包含张量(Tensor)存放于内存的缓存区。建模时它们需要被明确地初始化,模型训练后它们必须被存储到磁盘中,这些变量可在之后模型训练和分析时被加载。变量创建的实现方式为:

x = tf.Variable(a) #创建名为x的变量

其中a是一个numpy中类型中的ndarray,这里的a相当于对x进行了初始化。初始化的方法通常有两种,一种是所有变量一起初始化,一种是分别初始化。首先所有变量一起初始化使用:

tf.initialize_all_variables() 

还有一种是分别初始化:

tf.initialize_variables(var_list, name='init')

其中var_list是待初始化的变量列表,全部初始化等价于:

tf.initialize_variables(all_variables) 

这里注意,这两个函数的返回值都是操作,需要在Session中run()之后才能初始化。

实际上初始化还有几种情况,如:对于变量而言还有一个方法是variable.initialized_value()可以返回初始化的值,在用其它变量的值初始化一个新的变量时,使用其它变量的initialize_value()属性。

#创建一个随机分布的变量,服从正态分布,且标准方差为0.35
weights = tf.Vairable(tf.random_normal([784, 200], stdev=0.35), name="weights")
# 创建另外一个变量,和weights具有相同的值
w2 = tf.Variable(weights.initialized_value(), name="weight2")

4. 占位符(placeholder)

这个最能体现TensorFlow的优势,TensorFlow首先是搭建一个图然后描述一下操作,而占位符就是先站住一个位置,因为不知道输入的数量等一些具体的信息,所以可以先描述一下这一信息

x = tf.placeholder(tf.float32, [None, 784])

因为不知道x的具体数据,所以使用None代替,但是我们知道它的维度为784,float是x的精度。


5. TensorFlow中基础数学计算

这里介绍集中常见的基础数学计算,加减乘除,并直接使用代码介绍

# 加法
x = tf.add(5, 2)  # 7

# 减法
x = tf.substract(10, 4) # 6

# 乘法
x = tf.multiply(2, 5) # 10

# 除法
x = tf.div(10, 5) # 2

# 当尝试计算两个不同类型的值的时候会出现报错误,这时需要类型转换tf.cast函数
tf.substract(tf.cast(tf.constant(2.0, tf.int32), tf.constant(1))) #1

3TensorFlow机器学习入门

1. 数据导入

MNIST是一个非常有名的手写体数字识别数据集,常常用作机器学习的入门例子。TensorFlow的封装让使用MNIST更加方便,现在就以MNIST数字识别问题为例探讨如何使用TensorFlow进行机器学习相关的编程。

MNIST是一个图片集,包含70000张手写数字图片,如下所示

它也包含每一张图片对应的标签,告诉我们这个数字是几。如上面四张图片的标签分别是5,0,4,1。可以通过如下面的代码下载并保存在该数据集。

from tensorflow.examples.tutorials.mnist import input data
# MNIST_data为随意指定存储数据的临时目录
mnist = input_data.read_data_sets("MNIST_data/". one_hot=True)

下载下来的数据集被分成3部分:55000张训练数据(mnist.train); 5000张验证数据(mnist.validation);10000张测试数据(mnist.test)。切分的目的是确保模型设计的时有一个单独的测试数据集不用于训练而是用来评估这个模型的性能,从而更加容易把设计的模型推广到其它数据集上,当然也可以自己使用一些方法进行数据集的分配。

每一张图片包含28像素x28像素的像素点,可以使用数字数组来表示这张图片,如下图所示:

教程|自然语言处理技术之TensorFlow学习框架详解

  我们把这个数组展开成一个向量,长度是28x28 = 784。如何展开这个数组(数字间的顺序)不重要,只要保持各个图片采用相同的方式展开。从该角度来看,MNIST数据集的图片就是在784维向量空间里面的点。并且拥有比较复杂的结构。因此训练数据集是一个形状为[60000,784]的张量,在此张量里的每一个元素,都表示某张图片里的某个像素的灰度,其值介于0和1之间。

MNIST数据集的标签是长度为10的one-hot向量(因为前文代码中加载数据时指定了one_hot为True),一个one-hot向量除了某一位的数字是1外,其余各维度数字都是0。比如,标签3将表示成([0, 0, 0, 1, 0, 0, 0, 0, 0, 0])。因此,mnist.train.labels (标签)是一个[55000, 10]的数字矩阵。

教程|自然语言处理技术之TensorFlow学习框架详解

2. 设计模型

我们使用softmax的机器学习模型来通过训练预测图片里的数字。回顾一下,分类和回归是最基本的机器学习问题。线性回归是针对回归问题最基本的机器学习模型,其基本上思想是为各个影响因素分配合适的权重,预测的结果是各影响因素的加权和。逻辑(Logistic)回归则常用来处理分类问题,它在线性回归的基础上,通过logist函数把低于和高于参照值的结果分别转换为接近0和1的数值。不过逻辑回归只能处理二分问题。Softmax回归则是逻辑回归在多分类问题上的推广,其形式为:

教程|自然语言处理技术之TensorFlow学习框架详解

对softmax回归模型可以用下面的图解释,对于输入xi加权求和,再分别加上一个偏置量,最后再输入到softmax函数中:

教程|自然语言处理技术之TensorFlow学习框架详解

其中Wi代表权重,bi代表i类的偏置量,yi为该类型的概率。如果把它写成一个等式,可以得到:

教程|自然语言处理技术之TensorFlow学习框架详解

我们也可以使用向量表示这个计算过程:用矩阵乘法和向量相加,有助于提高计算效率:

教程|自然语言处理技术之TensorFlow学习框架详解

更进一步,可以写成更紧凑的方式:

教程|自然语言处理技术之TensorFlow学习框架详解

根据上面的模型,可以使用TensorFlow写出实现该模型公式的代码,需要注意的是,这里的x声明为2维的张量,其中第1维为任意长度,这样我们可以批量输入图片进行处理,另外简单起见,我们用0填充W和b:

x = tf.placeholder(tf.float32, [None, 784]) # 定义输入变量
W = tf.Variable(tf.zeros([784, 10])) # 定义权重矩阵
b = tf.Variabel(tf.zeros([10])) # 定义偏度矩阵
y = tf.nn.softmax(tf.matmul(x,W)+b) #调用tensorflow中神经网络相关的函数

除了模型外,我们还需要定义一个指标来指示如何优化模型中的参数,我们通常定义指标来表示一个模型不尽人意的程度,然后尽量最小化这个指标,这个指标称为成本函数(cost function),成本函数与模型是密切相关的,回归问题一般用均方差作成本函数,而对于分类问题,常使用交叉熵(cross-entroy),定义为:

其中y是我们通过模型预测的概率分布,y'是实际的分布(我们输入的one-hot vector)对其的理解涉及信息论方面的知识,这里可以将其看做反映预测不匹配的指标,或者说该指标反映实际情况出乎预料的程度,更详细的关于交叉熵可以参看其它相关的文献。在TensorFlow中使用如下代码对其进行计算:

cross_entropy = -tf.reduce_sum(y_*tf.log(y))

其中tf.log计算y的每个元素的对数。接下来使用y_的每个元素和tf.log(y_)的对应元素相乘。最后使用tf.reduce_sum函数计算张量的所有元素的总和。

    因为交叉熵一般会与Softmax回归一起使用,所以TensorFlow对这两个功能进行了统一的封装,并提供了tf.nn.softmax_cross_entropy_with_logits函数,可以直接通过如下的代码来实现使用了Softmax回归后的交叉熵的函数。注意与公式中y的不同,代码中的y是Softmax函数调用前的值,最后调用tf.reduce_mean函数取平均值,因为图片是批量传入的,针对每张图片会计算出一个交叉熵。

y = tf.matmul(x,W) + b
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logist(labels=y_,logits=y)

3.3 设计优化算法

    对于神经网络中的优化算法,前文()详细对各种优化算法进行了介绍,这里使用梯度下降算法以0.01的学习速率最小化交叉熵进行优化。

train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

梯度下降算法是一个简单的学习过程,TensorFlow只需将每个变量一点点往使得cost function不断降低的方向移动。

3.4 训练模型

    我们已经设置好了模型,在运行计算之前,需要添加一个操作来初始化我们所创建的所有的变量:

init = tf.initialize_all_variables()

    现在可以在一个Session里面启动我们的模型,并且初始化参量

sess = tf.Session()
sess.run(init)

现在可以开始模型的训练了,迭代1000次,注意会话对象执行的不是W,b也不是y而是train_step:

for i in range (1000)
   # 每次随机选取训练数据中100个批处理数据点,
   batch_xs, batch_ys = mnist.train.next_batch(100)
   # 对选取的数据点进行训练
   sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

使用一小部分的随机数据来进行训练被称为随机训练,在这里更确切的说是随机梯度下降训练,在理想情况下,我们希望用我们所有的数据来进行每一步的训练,因为这能给我们更好的训练结果,但显然这需要很大的计算开销,所以每一次的训练可以使用不同的数据子集,这样可以既减少计算开销,又可以最大化地学习到数据集的总体特性。

3.5 模型评估

到验证模型是否有效的时候了,我们可以基于训练好的W和b,用测试图片计算y,并取预测的数字和测试图片的实际标签进行对比。在Numpy中有一个非常有用的函数argmax,它能给出数组中最大元素所在的索引值。由于标签向量是由0,1组成,因此最大值1所在的索引位置就是类别标签。对于y而言,最大权重的索引位置就是预测的数字。因为softmax函数是单调递增的,下面的代码比较各个测试图片的预测与实际是否匹配,并通过均值函数计算正确率。

import numpy as np
ouput = sess.run(y, feed_dict={x: mnist.test.images})
print np.mean(np.argmax(output,1)==np.argmax(mnist.test.labels,1))

我们也可以让TensorFlow来执行比较,这在很多时候更为方便和高效,它里面也有类似的argmax函数:

# 判断是否一致
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_1,1))
# 计算正确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction,"float"))
# 使用会话进行运算
print sess.run(accuracy, feed_dict = {x: mnist.test.images, y_: mnist.test.labels})

使用该算法,最终的结果为91%左右,完整代码可以从https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/tutorials/mnist/mnist.py网站上进行查看


参考文献:

[1] TensorFlow 中文社区:http://www.tensorfly.cn/tfdoc/tutorials/mnist_beginners.html

[2] 博客:https://baijiahao.baidu.com/s?id=1574555070364620&wfr=spider&for=pc

自然语言处理技术

自然语言处理技术为您推送精品阅读

每天一个知识点,健康生活每一天


以上是关于教程|自然语言处理技术之TensorFlow学习框架详解的主要内容,如果未能解决你的问题,请参考以下文章

✪​ 教程 | 如何用TensorFlow生成周杰伦歌词?

下载最新TensorFlow深度学习教程指引《Learning TensorFlow,构建深度学习系统指引》

Tensorflow之MNIST解析

教程用于自然语言处理的深度学习方法,92页ppt

univariate_data 函数在 python tensorflow 教程(熊猫数据框)中不起作用

Tensorflow 写给初学者的深度学习教程之 MNIST 数字识别