TensorFlow 入门(二)
四、神经网络简介
在本章中,我们将介绍以下主题:
- 什么是神经网络?
- 单层感知机
- 逻辑回归
- 多层感知机
- 多层感知机分类
- 多层感知机函数近似
什么是人工神经网络?
人工神经网络(ANN)是一种信息处理系统,其运行机制受生物神经电路的启发。 由于它们的特性,神经网络是机器学习系统(尤其是在人工智能环境中)真正革命的主角。 根据各种架构,人工神经网络拥有许多相互连接的简单处理单元。 如果我们看一下稍后报告的 ANN 的架构,可以看到隐藏单元在输入和输出中与外部层通信,而输入和输出单元仅与网络的隐藏层通信。
每个单元或节点都模拟神经元在生物神经网络中的作用。 每个节点,即人工神经元,都具有非常简单的操作:如果其接收到的信号总量超过其激活阈值(由所谓的激活函数定义),它将变为活动状态。 如果节点变为活动,它会发射信号,该信号沿着传输通道传输到与其连接的另一个单元。 每个连接点都充当过滤器,将消息转换为抑制性信号或兴奋性信号,根据其各自的特征来增加或减少强度。 连接点模拟生物突触,并具有通过将传输信号乘以权重(其值取决于连接本身)来权衡传输信号强度的基本功能。
ANN 原理图
神经网络架构
连接节点的方式,总层数,即输入和输出之间的节点级别,以及每层神经元的数量-所有这些都定义了神经网络的架构。 例如,在多层网络中(我们将在本章的第二部分中介绍这些网络),可以识别层的人工神经元,使得:
- 每个神经元都与下一层的所有神经元相连
- 属于同一层的神经元之间没有连接
- 层数和每层神经元的数量取决于要解决的问题
现在,我们开始探索神经网络模型,介绍最简单的神经网络模型:单层感知机或所谓的罗森布拉特感知机。
单层感知机
单层感知机是第一个神经网络模型,由 Frank Rosenblatt 于 1958 年提出。 在此模型中,神经元局部记忆的内容由权重向量W = (w1, w2,......, wn)组成。 该计算是在计算输入向量X =(x1, x2,......, xn)的总和之后执行的,每个输入向量均与权重向量的相应元素相乘; 那么输出中提供的值(即加权总和)将是激活函数的输入。 如果结果大于某个阈值,则此函数返回1,否则返回-1。 在下图中,激活函数是所谓的sign函数:
+1 x > 0
sign(x)=
−1 otherwise
可以使用其他激活函数,最好是非线性激活函数(例如sigmoid函数,我们将在下一部分中看到)。 网络的学习过程是迭代的:通过使用称为训练集的选定集,可以为每个学习周期(称为周期)稍微修改突触权重。 在每个循环中,必须修改权重以最小化成本函数,该成本函数特定于所考虑的问题。 最后,当感知机已在训练集上进行训练后,将在其他输入(测试集)上对其进行测试,以验证其概括能力。
Rosemblatt 的感知机架构
现在让我们看看如何使用 TensorFlow 对图像分类问题实现单层神经网络。
逻辑回归
该算法与我们在第 3 章“机器学习入门”开始看到的规范线性回归无关,但是它是允许我们解决监督分类问题的算法。 实际上,为了估计因变量,现在我们利用所谓的逻辑函数或 Sigmoid。 正是由于这个函数,我们将此算法称为逻辑回归。 Sigmoid 函数具有以下模式:
Sigmoid 函数
如我们所见,因变量的取值严格在0和1之间,这正是为我们服务的内容。 对于逻辑回归,我们希望我们的函数告诉我们属于我们的类别某个特定元素的概率是多少。 我们再次记得,通过神经网络对监督学习被配置为权重优化的迭代过程; 然后根据训练集的网络表现对它们进行修改。 实际上,其目标是使损失函数最小化,该函数表明网络行为偏离所需行为的程度。 然后,在测试集上验证网络的表现,该测试集由除受过训练的图像以外的其他图像组成。
我们将要实现的训练的基本步骤如下:
- 权重在训练开始时用随机值初始化。
- 对于训练集的每个元素,都会计算误差,即期望输出与实际输出之间的差。 此误差用于调整权重。
- 重复该过程,以随机顺序将训练集的所有示例重新提交给网络,直到整个训练集上的误差不小于某个阈值,或者直到达到最大迭代次数为止。
现在让我们详细了解如何使用 TensorFlow 实现逻辑回归。 我们要解决的问题是对来自 MNIST 数据集的图像进行分类,如第 3 章,“机器学习入门”中的手写数字的数据库。
TensorFlow 实现
要实现 TensorFlow,我们需要执行以下步骤:
-
首先,我们必须导入所有必需的库:
import input_data import tensorflow as tf import matplotlib.pyplot as plt -
我们使用第 3 章“机器学习入门”引入的 MNIST 数据集部分中的
input_data.read函数,将图像上传到我们的问题中:mnist = input_data.read_data_sets("/tmp/data/", one_hot=True) -
然后,我们设置训练阶段的总次数:
training_epochs = 25 -
我们还必须定义构建模型所需的其他参数:
learning_rate = 0.01 batch_size = 100 display_step = 1 -
现在我们转到模型的构建。
建立模型
将x定义为输入张量; 它表示大小为28 x 28 = 784像素的 MNIST 数据图像:
x = tf.placeholder("float", [None, 784])
我们回想起我们的问题是为每种可能的隶属度类别(从 0 到 9 的数字)分配一个概率值。 在计算的最后,我们将使用概率分布,该分布为我们提供了对我们的预测的置信值。
因此,我们要获得的输出将是一个具有10概率的输出张量,每个张量都对应一个数字(当然,概率之和必须为 1):
y = tf.placeholder("float", [None, 10])
为了给每个图像分配概率,我们将使用所谓的 softmax 激活函数。
softmax函数在两个主要步骤中指定:
- 计算证据,即某个图像属于特定类别
- 将证据转换为属于 10 个可能类别中的每个类别的概率
为了求值证据,我们首先将权重输入张量定义为W:
W = tf.Variable(tf.zeros([784, 10]))
对于给定的图像,我们可以通过简单地将张量W与输入张量x相乘来求值每个类别i的证据。 使用 TensorFlow,我们应该具有以下内容:
evidence = tf.matmul(x, W)
通常,模型包括代表偏差的额外参数,该参数表示一定程度的不确定性。 在我们的案例中,证据的最终公式如下:
evidence = tf.matmul(x, W) + b
这意味着,对于每个i(从 0 到 9),我们都有一个Wi矩阵元素784 (28 × 28),其中矩阵的每个元素j与输入图像的相应分量j相乘(784 部分),并添加相应的偏置元素bi。
因此,要定义证据,我们必须定义以下偏差张量:
b = tf.Variable(tf.zeros([10]))
第二步是最终使用softmax函数获得概率的输出向量,即activation:
activation = tf.nn.softmax(tf.matmul(x, W) + b)
TensorFlow 的tf.nn.softmax函数提供了来自输入证据张量的基于概率的输出。 一旦实现模型,我们就可以指定必要的代码,以通过迭代训练算法找到权重W和偏置b网络。 在每次迭代中,训练算法都会获取训练数据,应用神经网络,并将结果与预期结果进行比较。
注意
TensorFlow 提供了许多其他激活函数。 有关更好的参考,请参见这里。
为了训练我们的模型并知道何时有一个好的模型,我们必须定义如何定义模型的准确率。 我们的目标是尝试获取参数W和b的值,这些值会最小化指示模型有多糟糕的度量值。
不同的度量标准计算了期望输出和训练数据输出之间的误差。 常见的误差度量是均方误差或平方欧几里德距离。 但是,有一些研究发现建议对这种神经网络使用其他指标。
在此示例中,我们使用所谓的cross-entropy error函数。 它定义为:
cross_entropy = y*tf.lg(activation)
为了最小化cross_entropy,我们可以使用tf.reduce_mean和tf.reduce_sum的以下组合来构建成本函数:
cost = tf.reduce_mean\
(-tf.reduce_sum\
(cross_entropy, reduction_indices=1))
然后,我们必须使用梯度下降优化算法将其最小化:
optimizer = tf.train.GradientDescentOptimizer\
(learning_rate).minimize(cost)
只需几行代码即可构建神经网络模型!
启动会话
现在是构建会话并启动我们的神经网络模型的时候了。
我们修复了以下列表以可视化训练过程:
avg_set = []
epoch_set=[]
然后我们初始化 TensorFlow 变量:
init = tf.initialize_all_variables()
开始会话:
with tf.Session() as sess:
sess.run(init)
如前所述,每个周期都是一个训练周期:
for epoch in range(training_epochs):
avg_cost = 0.
total_batch = int(mnist.train.num_examples/batch_size)
然后我们遍历所有批次:
for i in range(total_batch):
batch_xs, batch_ys = \
mnist.train.next_batch(batch_size)
使用批量数据拟合训练:
sess.run(optimizer, feed_dict={x: batch_xs, y: batch_ys})
用给定的图像值(x)和实际输出(y_)计算运行train_step函数的平均损失:
avg_cost += sess.run\
(cost, feed_dict={x: batch_xs,\
y: batch_ys})/total_batch
在计算过程中,我们每个周期显示一个日志:
if epoch % display_step == 0:
print "Epoch:",\
'%04d' % (epoch+1),\
"cost=","{:.9f}".format(avg_cost)
print " Training phase finished"
让我们获得模式的准确率。 如果y值最高的索引与实数向量中的correct_prediction均值使我们具有准确率,则是正确的。 我们需要使用测试集(mnist.test)运行准确率函数。
我们使用x和y的关键图像和标签:
correct_prediction = tf.equal\
(tf.argmax(activation, 1),\
tf.argmax(y, 1))
accuracy = tf.reduce_mean\
(tf.cast(correct_prediction, "float"))
print "MODEL accuracy:", accuracy.eval({x: mnist.test.images,\
y: mnist.test.labels})
测试和评估
我们之前显示了训练阶段,并且对于每个周期,我们都打印了相对成本函数:
Python 2.7.10 (default, Oct 14 2015, 16:09:02) [GCC 5.2.1 20151010] on linux2 Type "copyright", "credits" or "license()" for more information. >>> ======================= RESTART ============================
>>>
Extracting /tmp/data/train-images-idx3-ubyte.gz
Extracting /tmp/data/train-labels-idx1-ubyte.gz
Extracting /tmp/data/t10k-images-idx3-ubyte.gz
Extracting /tmp/data/t10k-labels-idx1-ubyte.gz
Epoch: 0001 cost= 1.174406662
Epoch: 0002 cost= 0.661956009
Epoch: 0003 cost= 0.550468774
Epoch: 0004 cost= 0.496588717
Epoch: 0005 cost= 0.463674555
Epoch: 0006 cost= 0.440907706
Epoch: 0007 cost= 0.423837747
Epoch: 0008 cost= 0.410590841
Epoch: 0009 cost= 0.399881751
Epoch: 0010 cost= 0.390916621
Epoch: 0011 cost= 0.383320325
Epoch: 0012 cost= 0.376767031
Epoch: 0013 cost= 0.371007620
Epoch: 0014 cost= 0.365922904
Epoch: 0015 cost= 0.361327561
Epoch: 0016 cost= 0.357258660
Epoch: 0017 cost= 0.353508228
Epoch: 0018 cost= 0.350164634
Epoch: 0019 cost= 0.347015593
Epoch: 0020 cost= 0.344140861
Epoch: 0021 cost= 0.341420144
Epoch: 0022 cost= 0.338980592
Epoch: 0023 cost= 0.336655581
Epoch: 0024 cost= 0.334488012
Epoch: 0025 cost= 0.332488823
Training phase finished
如您所见,在训练阶段,成本函数被最小化。 在测试的最后,我们展示了实现模型的准确率:
Model Accuracy: 0.9475
>>>
最后,使用以下代码行,我们可以可视化网络的训练阶段:
plt.plot(epoch_set,avg_set, 'o',\
label='Logistic Regression Training phase')
plt.ylabel('cost')
plt.xlabel('epoch')
plt.legend()
plt.show()
逻辑回归训练阶段
源代码
# Import MINST data
import input_data
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)
import tensorflow as tf
import matplotlib.pyplot as plt
# Parameters
learning_rate = 0.01
training_epochs = 25
batch_size = 100
display_step = 1
# tf Graph Input
x = tf.placeholder("float", [None, 784])
y = tf.placeholder("float", [None, 10])
# Create model
# Set model weights
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
# Construct model
activation = tf.nn.softmax(tf.matmul(x, W) + b)
# Minimize error using cross entropy
cross_entropy = y*tf.log(activation)
cost = tf.reduce_mean\
(-tf.reduce_sum\
(cross_entropy,reduction_indices=1))
optimizer = tf.train.\
GradientDescentOptimizer(learning_rate).minimize(cost)
#Plot settings
avg_set = []
epoch_set=[]
# Initializing the variables
init = tf.initialize_all_variables()
# Launch the graph
with tf.Session() as sess:
sess.run(init)
# Training cycle
for epoch in range(training_epochs):
avg_cost = 0.
total_batch = int(mnist.train.num_examples/batch_size)
# Loop over all batches
for i in range(total_batch):
batch_xs, batch_ys = \
mnist.train.next_batch(batch_size)
# Fit training using batch data
sess.run(optimizer, \
feed_dict={x: batch_xs, y: batch_ys})
# Compute average loss
avg_cost += sess.run(cost,feed_dict=\
{x: batch_xs,\
y: batch_ys})/total_batch
# Display logs per epoch step
if epoch % display_step == 0:
print "Epoch:", '%04d' % (epoch+1),\
"cost=", "{:.9f}".format(avg_cost)
avg_set.append(avg_cost)
epoch_set.append(epoch+1)
print "Training phase finished"
plt.plot(epoch_set,avg_set, 'o',\
label='Logistic Regression Training phase')
plt.ylabel('cost')
plt.xlabel('epoch')
plt.legend()
plt.show()
# Test model
correct_prediction = tf.equal\
(tf.argmax(activation, 1),\
tf.argmax(y, 1))
# Calculate accuracy
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
print "Model accuracy:", accuracy.eval({x: mnist.test.images,\
y: mnist.test.labels})
多层感知机
更复杂和有效的架构是多层感知机(MLP)。 它基本上由多层感知机组成,因此至少存在隐藏的层,即未连接到网络的输入或输出:
MLP 架构
根据上一段中概述的原则,通常使用监督学习来训练这种类型的网络。 特别地,用于 MLP 网络的典型学习算法是所谓的反向传播算法。
注意
反向传播算法是一种用于神经网络的学习算法。 它将系统的输出值与所需值进行比较。 基于由此计算出的差异(即误差),该算法通过逐步收敛所需输出值的集合来修改神经网络的突触权重。
重要的是要注意,在 MLP 网络中,尽管您不知道网络隐藏层的神经元的期望输出,但是始终可以通过基于最小误差函数的最小化应用有监督的学习方法。 梯度下降技术的应用。
在以下示例中,我们显示了针对图像分类问题(MNIST)的 MLP 实现。
多层感知机分类
导入必要的库:
import input_data
import tensorflow as tf
import matplotlib.pyplot as plt
加载图像进行分类:
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)
修复 MLP 模型的一些参数:
网络学习率:
learning_rate = 0.001
周期:
training_epochs = 20
要分类的图像数量:
batch_size = 100
display_step = 1
第一层的神经元数量:
n_hidden_1 = 256
第二层的神经元数量:
n_hidden_2 = 256
输入的大小(每个图像都有784像素):
n_input = 784 # MNIST data input (img shape: 28*28)
输出类的大小:
n_classes = 10
因此,应该注意的是,尽管对于给定的应用,输入和输出大小是完美定义的,但是对于如何定义隐藏层的数量和每层神经元的数量,没有严格的标准。
每个选择都必须基于类似应用的经验,例如:
- 当增加隐藏层的数量时,我们还应该在学习阶段增加必要的训练集的大小,并增加要更新的连接数。 这导致训练时间增加。
- 另外,如果隐藏层中的神经元太多,不仅有更多的权重需要更新,而且网络还倾向于从训练示例集中学习太多,从而导致泛化能力很差。 但是,如果隐藏的神经元太少,即使使用训练集,网络也无法学习。
建立模型
输入层是x张量[1×784],它表示要分类的图像:
x = tf.placeholder("float", [None, n_input])
输出张量y等于类数:
y = tf.placeholder("float", [None, n_classes])
在中间,我们有两个隐藏层。 第一层由权重的h张量构成,其权重为[784 × 256],其中256是该层的节点总数:
h = tf.Variable(tf.random_normal([n_input, n_hidden_1]))
对于第 1 层,我们必须定义各自的偏置张量:
bias_layer_1 = tf.Variable(tf.random_normal([n_hidden_1]))
每个神经元接收要与hij权重连接相结合进行分类的输入图像像素,并添加到偏置张量的各个值中:
layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x,h),bias_layer_1))
它通过activation函数将其输出发送到下一层的神经元。 必须说,每个神经元的特征可能不同,但是在实践中,我们对所有神经元(通常为 Sigmoid)采用一个共同的特征。 有时输出神经元具有线性激活函数。 有趣的是,隐藏层中神经元的激活函数不能是线性的,因为在这种情况下,MLP 网络将等效于具有两层的网络,因此不再是 MLP 类型。 第二层必须执行与第一相同的步骤。
第二中间层由权重张量[256 × 256]的形状表示:
w = tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2]))
有偏置张量:
bias_layer_2 = tf.Variable(tf.random_normal([n_hidden_2]))
第二层中的每个神经元都接收来自第 1 层神经元的输入,并与权重Wij连接相结合,并添加到第 2 层的各个偏差中:
layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1,w),bias_layer_2))
它将其输出发送到下一层,即输出层:
output = tf.Variable(tf.random_normal([n_hidden_2, n_classes]))
bias_output = tf.Variable(tf.random_normal([n_classes]))
output_layer = tf.matmul(layer_2, output) + bias_output
输出层接收来自层 2 的 n 个刺激(256)作为输入,该 n 个刺激被转换为每个数的相应概率类别。
至于逻辑回归,我们然后定义cost函数:
cost = tf.reduce_mean\
(tf.nn.softmax_cross_entropy_with_logits\
(output_layer, y))
TensorFlow 函数tf .nn.softmax_cross_entropy_with_logits计算 softmax 层的成本。 仅在训练期间使用。 logits 是模型输出的未归一化对数概率(将 softmax 归一化之前输出的值)。
使cost函数最小化的相应优化器是:
optimizer = tf.train.AdamOptimizer\
(learning_rate=learning_rate).minimize(cost)
tf.train.AdamOptimizer使用 Kingma 和 Ba 的 Adam 算法控制学习率。 与简单的tf.train.GradientDescentOptimizer相比,Adam 具有多个优点。 实际上,它使用了较大的有效步长,并且该算法将收敛到该步长而无需微调。
一个简单的tf.train.GradientDescentOptimizer也可以在您的 MLP 中使用,但需要更多的超参数调整,才能使其快速收敛。
注意
TensorFlow 提供了优化器基类来计算损失的梯度并将梯度应用于变量。 此类定义用于添加操作以训练模型的 API。 您永远不会直接使用此类,而是实例化其子类之一。 请参阅这里以查看优化程序的实现。
启动会话
以下是启动会话的步骤:
-
定义集合:
avg_set = [] epoch_set=[] -
初始化变量:
init = tf.initialize_all_variables() -
启动图:
with tf.Session() as sess: sess.run(init) -
定义训练周期:
for epoch in range(training_epochs): avg_cost = 0. total_batch = int(mnist.train.num_examples/batch_size) -
循环所有批次(100):
for i in range(total_batch): batch_xs, batch_ys = mnist.train.next_batch(batch_size) -
使用批次数据进行拟合训练:
sess.run(optimizer, feed_dict={x: batch_xs, y: batch_ys}) -
计算平均损失:
avg_cost += sess.run(cost,feed_dict={x: batch_xs,\ y: batch_ys})/total_batch Display logs per epoch step if epoch % display_step == 0: print "Epoch:", '%04d' % (epoch+1),\ "cost=", "{:.9f}".format(avg_cost) avg_set.append(avg_cost) epoch_set.append(epoch+1) print "Training phase finished" -
使用以下代码行,我们绘制了训练阶段:
plt.plot(epoch_set,avg_set, 'o', label='MLP Training phase') plt.ylabel('cost') plt.xlabel('epoch') plt.legend() plt.show() -
最后,我们可以测试 MLP 模型:
correct_prediction = tf.equal(tf.argmax(output_layer, 1),\ tf.argmax(y, 1)) evaluating its accuracy accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) print "Model Accuracy:", accuracy.eval({x: mnist.test.images,\ y: mnist.test.labels}) -
这是 20 个周期后的输出结果:
```py
Python 2.7.10 (default, Oct 14 2015, 16:09:02) [GCC 5.2.1 20151010] on linux2 Type "copyright", "credits" or "license()" for more information.
>>> ========================== RESTART ==============================
>>>
Succesfully downloaded train-images-idx3-ubyte.gz 9912422 bytes.
Extracting /tmp/data/train-images-idx3-ubyte.gz
Succesfully downloaded train-labels-idx1-ubyte.gz 28881 bytes.
Extracting /tmp/data/train-labels-idx1-ubyte.gz
Succesfully downloaded t10k-images-idx3-ubyte.gz 1648877 bytes.
Extracting /tmp/data/t10k-images-idx3-ubyte.gz
Succesfully downloaded t10k-labels-idx1-ubyte.gz 4542 bytes.
Extracting /tmp/data/t10k-labels-idx1-ubyte.gz
Epoch: 0001 cost= 1.723947845
Epoch: 0002 cost= 0.539266024
Epoch: 0003 cost= 0.362600502
Epoch: 0004 cost= 0.266637279
Epoch: 0005 cost= 0.205345784
Epoch: 0006 cost= 0.159139332
Epoch: 0007 cost= 0.125232637
Epoch: 0008 cost= 0.098572041
Epoch: 0009 cost= 0.077509963
Epoch: 0010 cost= 0.061127526
Epoch: 0011 cost= 0.048033808
Epoch: 0012 cost= 0.037297983
Epoch: 0013 cost= 0.028884999
Epoch: 0014 cost= 0.022818390
Epoch: 0015 cost= 0.017447586
Epoch: 0016 cost= 0.013652348
Epoch: 0017 cost= 0.010417282
Epoch: 0018 cost= 0.008079228
Epoch: 0019 cost= 0.006203546
Epoch: 0020 cost= 0.004961207
Training phase finished
Model Accuracy: 0.9775
>>>
```
我们在下图中显示了训练阶段:
多层感知机的训练阶段
源代码
# Import MINST data
import input_data
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)
import tensorflow as tf
import matplotlib.pyplot as plt
# Parameters
learning_rate = 0.001
training_epochs = 20
batch_size = 100
display_step = 1
# Network Parameters
n_hidden_1 = 256 # 1st layer num features
n_hidden_2 = 256 # 2nd layer num features
n_input = 784 # MNIST data input (img shape: 28*28)
n_classes = 10 # MNIST total classes (0-9 digits)
# tf Graph input
x = tf.placeholder("float", [None, n_input])
y = tf.placeholder("float", [None, n_classes])
#weights layer 1
h = tf.Variable(tf.random_normal([n_input, n_hidden_1]))
#bias layer 1
bias_layer_1 = tf.Variable(tf.random_normal([n_hidden_1]))
#layer 1
layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x,h),bias_layer_1))
#weights layer 2
w = tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2]))
#bias layer 2
bias_layer_2 = tf.Variable(tf.random_normal([n_hidden_2]))
#layer 2
layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1,w),bias_layer_2))
#weights output layer
output = tf.Variable(tf.random_normal([n_hidden_2, n_classes]))
#biar output layer
bias_output = tf.Variable(tf.random_normal([n_classes]))
#output layer
output_layer = tf.matmul(layer_2, output) + bias_output
# cost function
cost = tf.reduce_mean\
(tf.nn.softmax_cross_entropy_with_logits(output_layer, y))
# optimizer
optimizer = tf.train.AdamOptimizer\
(learning_rate=learning_rate).minimize(cost)
#Plot settings
avg_set = []
epoch_set=[]
# Initializing the variables
init = tf.initialize_all_variables()
# Launch the graph
with tf.Session() as sess:
sess.run(init)
# Training cycle
for epoch in range(training_epochs):
avg_cost = 0.
total_batch = int(mnist.train.num_examples/batch_size)
# Loop over all batches
for i in range(total_batch):
batch_xs, batch_ys = mnist.train.next_batch(batch_size)
# Fit training using batch data
sess.run(optimizer, feed_dict={x: batch_xs, y: batch_ys})
# Compute average loss
avg_cost += sess.run(cost, \
feed_dict={x: batch_xs,\
y: batch_ys})/total_batch
# Display logs per epoch step
if epoch % display_step == 0:
print "Epoch:", '%04d' % (epoch+1),\
"cost=", "{:.9f}".format(avg_cost)
avg_set.append(avg_cost)
epoch_set.append(epoch+1)
print "Training phase finished"
plt.plot(epoch_set,avg_set, 'o', label='MLP Training phase')
plt.ylabel('cost')
plt.xlabel('epoch')
plt.legend()
plt.show()
# Test model
correct_prediction = tf.equal(tf.argmax(output_layer, 1),\
tf.argmax(y, 1))
# Calculate accuracy
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
print "Model Accuracy:", accuracy.eval({x: mnist.test.images,\ y: mnist.test.labels})
多层感知机函数近似
在以下示例中,我们实现了一个 MLP 网络,该网络将能够学习任意函数f (x)的趋势。 在训练阶段,网络将必须从一组已知点中学习x和f (x),而在测试阶段,网络将仅从x值中扣除f (x)的值。
这个非常简单的网络将由单个隐藏层构建。
导入必要的库:
import tensorflow as tf
import numpy as np
import math, random
import matplotlib.pyplot as plt
我们建立数据模型。 要学习的函数将遵循cosine函数的趋势,并针对1000点进行了评估,并向其中添加了很少的随机误差(噪声)以重现真实情况:
NUM_points = 1000
np.random.seed(NUM_points)
function_to_learn = lambda x: np.cos(x) + \
0.1*np.random.randn(*x.shape)
我们的 MLP 网络将由10神经元的隐藏层形成:
layer_1_neurons = 10
网络一次学习100点,总共学习1500个学习周期(周期):
batch_size = 100
NUM_EPOCHS = 1500
最后,我们构造训练集和测试集:
all_x contiene tutti i punti
all_x = np.float32(np.random.uniform\
(-2*math.pi, 2*math.pi,\
(1, NUM_points))).T
np.random.shuffle(all_x)
train_size = int(900)
前900点在训练集中:
x_training = all_x[:train_size]
y_training = function_to_learn(x_training)
最后一个100将在验证集中:
x_validation = all_x[train_size:]
y_validation = function_to_learn(x_validation)
使用 matplotlib,我们显示以下集合:
plt.figure(1)
plt.scatter(x_training, y_training, c='blue', label='train')
plt.scatter(x_validation, y_validation,c='red',label='validation')
plt.legend()
plt.show()
训练和验证集
建立模型
首先,我们为输入张量(X)和输出张量(Y)创建占位符:
X = tf.placeholder(tf.float32, [None, 1], name="X")
Y = tf.placeholder(tf.float32, [None, 1], name="Y")
然后,我们构建[1 x 10]尺寸的隐藏层:
w_h = tf.Variable(tf.random_uniform([1, layer_1_neurons],\
minval=-1, maxval=1, \
dtype=tf.float32))
b_h = tf.Variable(tf.zeros([1, layer_1_neurons], \
dtype=tf.float32))
它从X输入张量接收输入值,并与权重w_hij连接相结合,并加上第 1 层的各个偏置:
h = tf.nn.sigmoid(tf.matmul(X, w_h) + b_h)
输出层是一个[10 x 1]张量:
w_o = tf.Variable(tf.random_uniform([layer_1_neurons, 1],\
minval=-1, maxval=1,\
dtype=tf.float32))
b_o = tf.Variable(tf.zeros([1, 1], dtype=tf.float32))
第二层中的每个神经元都从层 1 的神经元接收输入,并与权重w_oij连接相结合,并与输出层的各个偏置相加:
model = tf.matmul(h, w_o) + b_o
然后,我们为新定义的模型定义优化器:
train_op = tf.train.AdamOptimizer().minimize\
(tf.nn.l2_loss(model - Y))
我们还注意到,在这种情况下,采用的成本函数如下:
tf.nn.l2_loss(model - Y)
tf.nn.l2_loss函数是一个 TensorFlow,它计算不具有sqrt的张量的 L2 范数的一半,也就是说,前一个函数的输出如下:
output = sum((model - Y) ** 2) / 2
对于我们的示例,tf.nn.l2_loss函数可以是可行的成本函数。
启动会话
让我们建立求值图:
sess = tf.Session()
sess.run(tf.initialize_all_variables())
现在我们可以启动学习过程:
errors = []
for i in range(NUM_EPOCHS):
for start, end in zip(range(0, len(x_training), batch_size),\
range(batch_size,\
len(x_training), batch_size)):
sess.run(train_op, feed_dict={X: x_training[start:end],\
Y: y_training[start:end]})
cost = sess.run(tf.nn.l2_loss(model - y_validation),\
feed_dict={X:x_validation})
errors.append(cost)
if i%100 == 0: print "epoch %d, cost = %g" % (i, cost)
将这个网络运行 1400 个周期,我们将看到误差逐渐减少并最终收敛:
Python 2.7.10 (default, Oct 14 2015, 16:09:02) [GCC 5.2.1 20151010] on linux2 Type "copyright", "credits" or "license()" for more information.
>>> ======================= RESTART ============================
>>>
epoch 0, cost = 55.9286
epoch 100, cost = 22.0084
epoch 200, cost = 18.033
epoch 300, cost = 14.0481
epoch 400, cost = 9.74721
epoch 500, cost = 5.83419
epoch 600, cost = 3.05434
epoch 700, cost = 1.53706
epoch 800, cost = 0.91719
epoch 900, cost = 0.726675
epoch 1000, cost = 0.668316
epoch 1100, cost = 0.633737
epoch 1200, cost = 0.608306
epoch 1300, cost = 0.590429
epoch 1400, cost = 0.574602
>>>
**以下代码行使我们能够显示成本在运行周期中的变化:
plt.plot(errors,label='MLP Function Approximation')
plt.xlabel('epochs')
plt.ylabel('cost')
plt.legend()
plt.show()
多层感知机的训练阶段
总结
在本章中,我们介绍了人工神经网络。 人工神经元是一种数学模型,在某种程度上模仿了活神经元的特性。 网络的每个神经元都有一个非常简单的操作,包括接收到的信号总量超过激活阈值时变为活动状态。 学习过程通常是监督的:神经网络使用训练集来推断输入和相应输出之间的关系,而学习算法会修改网络的权重以使成本函数最小化,它表示训练集有关的预测误差。 如果训练成功,则即使事先不知道输出结果,神经网络也将能够做出预测。 在本章中,我们使用 TensorFlow 实现了一些涉及神经网络的示例。 在使用 Rosemblatt 的感知机进行分类的问题中,我们已经看到神经网络作为逻辑回归算法来解决分类和回归问题。 在本章的最后,在实现图像分类器之前,我们介绍了多层感知机架构,然后在数学函数仿真器的实现中,我们已经看到了该架构的实际应用。
在下一章中,我们最后介绍深度学习模型; 我们将研究和实现更复杂的神经网络架构,例如卷积神经网络和循环神经网络。
五、深度学习
在本章中,我们将介绍以下主题:
- 深度学习技巧
- 卷积神经网络(CNN)
- CNN 架构
- CNN 的 TensorFlow 实现
- 循环神经网络(RNN)
- RNN 架构
- 使用 TensorFlow 进行自然语言处理
深度学习技巧
深度学习技术是机器学习研究人员近几十年来所迈出的关键一步,已提供了许多应用(如图像识别和语音识别)中前所未有的成功成果。
有多种原因导致开发深度学习并将其置于机器学习范围内的关注焦点。 这些原因之一是硬件的进步,以及新处理器的可用性,例如图形处理单元(GPU),它们大大减少了训练网络所需的时间,降低了 10/20 的时间。
另一个原因肯定是越来越容易找到,要在其上训练系统的数据集越多,这些数据集就需要训练一定深度和输入数据的高维结构。 深度学习包含一组方法,这些方法使系统可以在多个级别上获取数据的分层表示。 这是通过组合简单的单元(不是线性的)来实现的,每个简单的单元从输入级别开始将其自身级别的表示形式转换为更高级别的表示形式更抽象。 通过足够数量的这些转换,可以学习相当复杂的输入输出函数。
关于分类问题,例如最高级别的表示,突出显示与分类相关的输入数据方面,从而抑制那些对分类目的没有影响的方面。
图像分类系统中的分层特征提取
前面的方案描述了图像分类系统(人脸识别器)的特征:每个块逐渐提取输入图像的特征,处理从先前块中已经预处理过的数据,提取输入图像的日益复杂的特征, 从而构建表征基于深度学习的系统的分层数据表示。
层次结构特征的可能表示形式如下:
pixel --> edge --> texture --> motif --> part --> object
但是,在文本识别问题中,可以将层次表示形式构造如下:
character --> word --> word group --> clause --> sentence --> story
因此,深度学习架构是多层架构,它由简单的单元组成,所有单元都受训练,其中许多单元包含非线性变换。 每个单元都对其输入进行转换以改善其属性,以仅出于分类目的选择和放大相关方面,以及其不变性,即其倾向忽略无关的方面。
因此,通过多级非线性转换,深度大约在 5 到 20 级之间,深度学习系统可以学习并实现极其复杂和复杂的特征,同时对最小的相关细节非常敏感,以及对输入数据无关方面的大变化非常不敏感,在对象识别的情况下,这些变化可能是:图像的背景,亮度或所表示对象的位置。
以下部分将借助 TensorFlow 说明两种重要的深度神经网络类型:卷积神经网络(CNN),主要针对分类问题,然后针对自然语言处理(NLP)问题的循环神经网络(RNNs)问题。
卷积神经网络
卷积神经网络(CNN)是面向神经网络的一种特殊类型的深度学习,在许多实际应用中都取得了优异的成绩,尤其是图像中的对象识别。
实际上,CNN 被设计为处理以多个数组形式表示的数据,例如彩色图像,可通过包含像素颜色强度的三个二维数组表示。 CNN 与普通神经网络之间的实质区别在于,前者在图像上直接操作,而后者在从图像中提取的特征上。 因此,与普通神经网络不同,CNN 的输入将是二维,特征将是输入图像的像素。
CNN 是几乎所有识别问题的主要方法。 这种类型的网络所提供的出色表现实际上已经促使诸如 Google 和 Facebook 之类的最大的技术公司投资于此类网络的研发项目,并开发和分发基于 CNN 的产品图像识别。
CNN 架构
CNN 使用三个基本概念:局部感受野,卷积和合并。
在卷积网络中,我们认为输入类似于下图所示:
输入神经元
CNN 背后的概念之一是本地连接。 实际上,CNN 利用输入数据中可能存在的空间相关性。 第一后续层的每个神经元仅连接某些输入神经元。 该区域称为局部感受野。 在下图中,用会聚*到隐藏的神经元的黑色5x5正方形表示:
从输入到隐藏的神经元
隐藏的神经元当然只会处理其接受区域内的输入数据,而不会实现其外部的变化。 但是,很容易看到,根据深度的基本原理,通过叠加多层本地连接的层,升级后,与输入相比,您将拥有处理越来越多的全局数据的学习单元,以使表现达到不断增长的抽象水平。
注意
本地连接的原因在于以下事实:在数组形式的数据(例如图像)中,值通常高度相关,从而形成了易于识别的不同数据组。
每个连接都学习一个权重(因此它将得到5x5 = 25),而不是隐藏的神经元及其关联的连接会学习一个总偏差,然后我们将通过不时执行移位来将区域连接到单个神经元,例如在以下图中:
卷积运算
该操作称为卷积。这样,如果我们有一个28x28输入和5x5区域的图像,我们将在隐藏层中获得24x24神经元。 我们说过,每个神经元都有一个偏差和与该区域相关的5x5权重:我们将对所有24x24神经元使用这些权重和偏差。 这意味着第一隐藏层中的所有神经元将识别相同的特征,只是在输入图像中放置的位置不同。 因此,从输入层到隐藏特征图的连接图称为共享权重,偏置称为共享偏置,因为它们事实上是共享的。
显然,我们需要识别的不仅仅是一个特征图,因此,一个完整的卷积层是由多个特征图组成的。
多个特征图
在上图中,我们看到了三个特征图。 当然,实际上它的数量会增加,甚至可以使用具有 20 或 40 个特征图的卷积层。 权重和偏差共享的一个巨大优势是卷积网络中涉及的参数的显着降低。 考虑我们的示例,对于每个特征图,我们需要 25 个权重(5x5)和一个偏差(共享)。 总共有 26 个参数。 假设我们有 20 个特征图,我们将定义 520 个参数。 在具有 784 个输入神经元和例如 30 个隐藏层神经元的完全连接的网络中,我们需要 30 个以上的784x30偏差权重,以达到总共 23.550 个参数。
差异是显而易见的。 卷积网络还使用池化层,它们是紧接在卷积层之后的层。 这些简化了前一层的输出信息(卷积)。 它获取从卷积层出来的输入特征图,并准备一个压缩的特征图。 例如,我们可以说池化层可以以其所有单元汇总在上一层神经元的2x2区域中。
该技术称为池化,可以用以下方案概括:
池化操作有助于简化从一层到下一层的信息
显然,我们通常有更多的特征图,并且我们将最大池分别应用于每个特征图。
从输入层到第二个隐藏层
因此,我们假设第一个隐藏层具有三个尺寸为24x24的特征图,第二个隐藏层的尺寸将为 12x12,因为我们假设每个单元汇总一个2x2区域。
结合这三个思想,我们形成了一个完整的卷积网络。 其架构可以显示如下:
CNN 架构架构
让我们总结一下:有28x28个输入神经元,后跟一个卷积层,具有局部接收场5x5和 3 个特征图。 作为的结果,我们获得了3x24x24神经元的隐藏层。 然后在特征图的 3 个区域上将最大池应用于2x2,从而获得3x12x12的隐藏层。 最后一层是完全连接的:它将最大池化层的所有神经元连接到所有 10 个输出神经元,有助于识别相应的输出。
然后将通过梯度下降和反向传播算法训练该网络。
CNN 的 TensorFlow 实现
在以下示例中,我们将看到 CNN 在图像分类问题中的作用。 我们想展示构建 CNN 网络的过程:要执行哪些步骤以及需要执行哪些推理才能对整个网络进行适当的尺寸标注,当然还要如何使用 TensorFlow 进行实现。
初始化步骤
-
加载并准备 MNIST 数据:
import tensorflow as tf import input_data mnist = input_data.read_data_sets("/tmp/data/", one_hot=True) -
定义所有 CNN 参数:
learning_rate = 0.001 training_iters = 100000 batch_size = 128 display_step = 10 -
MNIST 数据输入(每个形状为
28x28数组像素):n_input = 784 -
MNIST 的总类别(0-9)
n_classes = 10 -
为了减少过拟合,我们应用了丢弃技术。 该术语是指在神经网络中删除单元(隐藏,输入和输出)。 确定要消除的神经元是随机的; 一种方法是应用概率,正如我们将在代码中看到的那样。 因此,我们定义以下参数(待调整):
dropout = 0.75 -
定义输入图的占位符。
x占位符包含 MNIST 数据输入(恰好 728 像素):x = tf.placeholder(tf.float32, [None, n_input]) -
然后,我们使用 TensorFlow
reshape运算符将 4D 输入图像的形式更改为张量:
_X = tf.reshape(x, shape=[-1, 28, 28, 1])
第二和第三个尺寸对应于图像的宽度和高度,而第二个尺寸是色彩通道的总数(在我们的情况下为 1)。
因此,我们可以将输入图像显示为尺寸为28x28的二维张量:
我们问题的输入张量
输出张量将包含每个数字的输出概率,以进行分类:
y = tf.placeholder(tf.float32, [None, n_classes]).
第一卷积层
隐藏层的每个神经元都连接到尺寸为5x5的输入张量的一小部分。 这意味着隐藏层的大小为24x24。 我们还定义和初始化共享权重和共享偏差的张量:
wc1 = tf.Variable(tf.random_normal([5, 5, 1, 32]))
bc1 = tf.Variable(tf.random_normal([32]))
回想一下,要识别图像,我们需要的不仅仅是特征图。 该数量仅是我们正在考虑的第一层特征图的数量。 在我们的例子中,卷积层由 32 个特征图组成。
下一步是第一个卷积层和conv1的构造:
conv1 = conv2d(_X,wc1,bc1)
在此,conv2d是以下函数:
def conv2d(img, w, b):
return tf.nn.relu(tf.nn.bias_add\
(tf.nn.conv2d(img, w,\
strides=[1, 1, 1, 1],\
padding='SAME'),b))
为此,我们使用了 TensorFlow tf.nn.conv2d函数。 它根据输入张量和共享权重计算 2D 卷积。 然后,该操作的结果将被添加到偏置bc1矩阵。 为此,我们使用函数tf.nn.conv2d从输入张量和共享权重的张量计算出二维卷积。 然后,该操作的结果将添加到偏置bc1矩阵中。 tf.nn.relu是 Relu 函数(整流线性单元),它是深层神经网络隐藏层中的常见激活函数。
我们将这个激活函数应用于卷积函数的返回值。 填充值为'SAME',它指示输出张量将具有与输入张量相同的大小。
表示卷积层的一种方法为conv1,如下所示:
第一隐藏层
卷积操作之后,我们强加了池化步骤,该步骤简化了先前创建的卷积层的输出信息。
在我们的示例中,让我们采用卷积层的2x2区域,然后我们将汇总池层中每个点的信息。
conv1 = max_pool(conv1, k=2)
在此,对于池操作,我们实现了以下函数:
def max_pool(img, k):
return tf.nn.max_pool(img, \
ksize=[1, k, k, 1],\
strides=[1, k, k, 1],\
padding='SAME')
tf.nn.max_pool函数对输入执行最大池化。 当然,我们将最大池化应用于每个卷积层,并且将有很多层池化和卷积。 在合并阶段结束时,我们将具有12x12x32卷积隐藏层。
下图显示了池化和卷积操作后的 CNN 层:
第一次卷积和合并操作后的 CNN
最后的操作是通过在卷积层上应用tf.nn.dropout TensorFlow 运算符来减少过拟合。 为此,我们为占位符(keep_prob)在删除期间保留神经元输出的概率创建一个占位符:
keep_prob = tf. placeholder(tf.float32)
conv1 = tf.nn.dropout(conv1,keep_prob)
第二卷积层
对于第二隐藏层,我们必须应用与第一层相同的操作,因此我们定义并初始化共享权重和共享偏置的张量:
wc2 = tf.Variable(tf.random_normal([5, 5, 32, 64]))
bc2 = tf.Variable(tf.random_normal([64]))
如您所注意到的,第二个隐藏层将具有5x5窗口的 64 个特征,而输入层的数量将从第一个卷积获得的层中给出。 接下来,我们将第二层应用于卷积conv1张量,但是这次我们将 64 组5x5过滤器分别应用于 32 个conv1层:
conv2 = conv2d(conv1,wc2,bc2)
它为我们提供了 64 个14x14数组,通过最大池化将其减少到 64 个7x7数组:
conv2 = max_pool(conv2, k=2)
最后,我们再次使用丢弃操作:
conv2 = tf.nn.dropout(conv2, keep_prob)
生成的层是7x7 x 64卷积张量,因为我们从输入张量12x12和5x5的滑动窗口开始,考虑到步幅为 1。
建立第二个隐藏层
密集连接层
在此步骤中,我们将建立一个紧密连接的层,用于处理整个图像。 权重和偏差张量如下:
wd1 = tf.Variable(tf.random_normal([7*7*64, 1024]))
bd1 = tf.Variable(tf.random_normal([1024]))
如您所注意到的,该层将由1024神经元形成。
然后我们将来自第二个卷积层的张量整形为一批向量:
dense1 = tf.reshape(conv2, [-1, wd1.get_shape().as_list()[0]])
将该张量乘以权重矩阵wd1,将张量偏差bd1相加,然后执行 RELU 操作:
dense1 = tf.nn.relu(tf.add(tf.matmul(dense1, wd1),bd1))
我们再次使用丢弃运算符完成这一层:
dense1 = tf.nn.dropout(dense1, keep_prob)
输出层
最后一层定义张量wout和bout:
wout = tf.Variable(tf.random_normal([1024, n_classes]))
bout = tf.Variable(tf.random_normal([n_classes]))
在应用softmax函数之前,我们必须计算该图像属于某个类别的证据:
pred = tf.add(tf.matmul(dense1, wout), bout)
测试和训练模型
必须将证据转换为 10 种可能类别中每一种的概率(该方法与我们在第 4 章“神经网络介绍”中看到的方法相同)。 因此,我们定义了成本函数,该函数通过应用softmax函数来评估模型的质量:
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y))
使用 TensorFlow AdamOptimizer函数对其函数进行优化:
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
以下张量将在模型的评估阶段使用:
correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
启动会话
初始化变量:
init = tf.initialize_all_variables()
建立求值图:
with tf.Session() as sess:
sess.run(init)
step = 1
让我们训练网络直到training_iters:
while step * batch_size < training_iters:
batch_xs, batch_ys = mnist.train.next_batch(batch_size)
使用batch数据进行健身训练:
sess.run(optimizer, feed_dict={x: batch_xs,\
y: batch_ys,\
keep_prob: dropout})
if step % display_step == 0:
计算accuracy:
acc = sess.run(accuracy, feed_dict={x: batch_xs,\
y: batch_ys,\
keep_prob: 1.})
计算loss:
loss = sess.run(cost, feed_dict={x: batch_xs,\
y: batch_ys,\
keep_prob: 1.})
print "Iter " + str(step*batch_size) +\
", Minibatch Loss= " + \
"{:.6f}".format(loss) + \
", Training Accuracy= " + \
"{:.5f}".format(acc)
step += 1
print "Optimization Finished!"
我们打印256 MNIST 测试图像的准确率:
print "Testing Accuracy:",\
sess.run(accuracy,\
feed_dict={x: mnist.test.images[:256], \
y: mnist.test.labels[:256],\
keep_prob: 1.})
运行代码,我们得到以下输出:
Extracting /tmp/data/train-images-idx3-ubyte.gz
Extracting /tmp/data/train-labels-idx1-ubyte.gz
Extracting /tmp/data/t10k-images-idx3-ubyte.gz
Extracting /tmp/data/t10k-labels-idx1-ubyte.gz
Iter 1280, Minibatch Loss= 27900.769531,
Training Accuracy= 0.17188
Iter 2560, Minibatch Loss= 17168.949219, Training Accuracy= 0.21094
Iter 3840, Minibatch Loss= 15000.724609, Training Accuracy= 0.41406
Iter 5120, Minibatch Loss= 8000.896484, Training Accuracy= 0.49219
Iter 6400, Minibatch Loss= 4587.275391, Training Accuracy= 0.61719
Iter 7680, Minibatch Loss= 5949.988281, Training Accuracy= 0.69531
Iter 8960, Minibatch Loss= 4932.690430, Training Accuracy= 0.70312
Iter 10240, Minibatch Loss= 5066.223633, Training Accuracy= 0.70312 . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
Iter 81920, Minibatch Loss= 442.895020, Training Accuracy= 0.93750
Iter 83200, Minibatch Loss= 273.936676, Training Accuracy= 0.93750
Iter 84480, Minibatch Loss= 1169.810303, Training Accuracy= 0.89062
Iter 85760, Minibatch Loss= 737.561157, Training Accuracy= 0.90625
Iter 87040, Minibatch Loss= 583.576965, Training Accuracy= 0.89844
Iter 88320, Minibatch Loss= 375.274475, Training Accuracy= 0.93750
Iter 89600, Minibatch Loss= 183.815613, Training Accuracy= 0.94531
Iter 90880, Minibatch Loss= 410.157867, Training Accuracy= 0.89844
Iter 92160, Minibatch Loss= 895.187683, Training Accuracy= 0.84375
Iter 93440, Minibatch Loss= 819.893555, Training Accuracy= 0.89062
Iter 94720, Minibatch Loss= 460.179779, Training Accuracy= 0.90625
Iter 96000, Minibatch Loss= 514.344482, Training Accuracy= 0.87500
Iter 97280, Minibatch Loss= 507.836975, Training Accuracy= 0.89844
Iter 98560, Minibatch Loss= 353.565735, Training Accuracy= 0.92188
Iter 99840, Minibatch Loss= 195.138626, Training Accuracy= 0.93750
Optimization Finished!
Testing Accuracy: 0.921875
它提供约 99.2% 的精度。 显然,它不代表最新技术,因为该示例的目的只是看如何构建 CNN。 该模型可以进一步完善以提供更好的结果。
源代码
# Import MINST data
import input_data
mnist = input_data.read_data_sets("/tmp/data/",one_hot=True)
import tensorflow as tf
# Parameters
learning_rate = 0.001
training_iters = 100000
batch_size = 128
display_step = 10
# Network Parameters
n_input = 784 # MNIST data input (img shape: 28*28)
n_classes = 10 # MNIST total classes (0-9 digits)
dropout = 0.75 # Dropout, probability to keep units
# tf Graph input
x = tf.placeholder(tf.float32, [None, n_input])
y = tf.placeholder(tf.float32, [None, n_classes])
#dropout (keep probability)
keep_prob = tf.placeholder(tf.float32)
# Create model
def conv2d(img, w, b):
return tf.nn.relu(tf.nn.bias_add\
(tf.nn.conv2d(img, w,\
strides=[1, 1, 1, 1],\
padding='SAME'),b))
def max_pool(img, k):
return tf.nn.max_pool(img, \
ksize=[1, k, k, 1],\
strides=[1, k, k, 1],\
padding='SAME')
# Store layers weight & bias
# 5x5 conv, 1 input, 32 outputs
wc1 = tf.Variable(tf.random_normal([5, 5, 1, 32]))
bc1 = tf.Variable(tf.random_normal([32]))
# 5x5 conv, 32 inputs, 64 outputs
wc2 = tf.Variable(tf.random_normal([5, 5, 32, 64]))
bc2 = tf.Variable(tf.random_normal([64]))
# fully connected, 7*7*64 inputs, 1024 outputs
wd1 = tf.Variable(tf.random_normal([7*7*64, 1024]))
# 1024 inputs, 10 outputs (class prediction)
wout = tf.Variable(tf.random_normal([1024, n_classes]))
bd1 = tf.Variable(tf.random_normal([1024]))
bout = tf.Variable(tf.random_normal([n_classes]))
# Construct model
_X = tf.reshape(x, shape=[-1, 28, 28, 1])
# Convolution Layer
conv1 = conv2d(_X,wc1,bc1)
# Max Pooling (down-sampling)
conv1 = max_pool(conv1, k=2)
# Apply Dropout
conv1 = tf.nn.dropout(conv1,keep_prob)
# Convolution Layer
conv2 = conv2d(conv1,wc2,bc2)
# Max Pooling (down-sampling)
conv2 = max_pool(conv2, k=2)
# Apply Dropout
conv2 = tf.nn.dropout(conv2, keep_prob)
# Fully connected layer
# Reshape conv2 output to fit dense layer input
dense1 = tf.reshape(conv2, [-1, wd1.get_shape().as_list()[0]])
# Relu activation
dense1 = tf.nn.relu(tf.add(tf.matmul(dense1, wd1),bd1))
# Apply Dropout
dense1 = tf.nn.dropout(dense1, keep_prob)
# Output, class prediction
pred = tf.add(tf.matmul(dense1, wout), bout)
# Define loss and optimizer
cost = tf.reduce_mean\
(tf.nn.softmax_cross_entropy_with_logits(pred, y))
optimizer =\
tf.train.AdamOptimizer\
(learning_rate=learning_rate).minimize(cost)
# Evaluate model
correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
# Initializing the variables
init = tf.initialize_all_variables()
# Launch the graph
with tf.Session() as sess:
sess.run(init)
step = 1
# Keep training until reach max iterations
while step * batch_size < training_iters:
batch_xs, batch_ys = mnist.train.next_batch(batch_size)
# Fit training using batch data
sess.run(optimizer, feed_dict={x: batch_xs,\
y: batch_ys,\
keep_prob: dropout})
if step % display_step == 0:
# Calculate batch accuracy
acc = sess.run(accuracy, feed_dict={x: batch_xs,\
y: batch_ys,\
keep_prob: 1.})
# Calculate batch loss
loss = sess.run(cost, feed_dict={x: batch_xs,\
y: batch_ys,\
keep_prob: 1.})
print "Iter " + str(step*batch_size) +\
", Minibatch Loss= " + \
"{:.6f}".format(loss) + \
", Training Accuracy= " + \
"{:.5f}".format(acc)
step += 1
print "Optimization Finished!"
# Calculate accuracy for 256 mnist test images
print "Testing Accuracy:",\
sess.run(accuracy,\
feed_dict={x: mnist.test.images[:256], \
y: mnist.test.labels[:256],\
keep_prob: 1.})
循环神经网络
另一个面向深度学习的架构是所谓的循环神经网络(RNN)的架构。 RNN 的基本思想是在输入中使用序列信息类型。 在神经网络中,我们通常假设每个输入和输出都独立于所有其他输入和输出。 但是,对于许多类型的问题,此假设并不肯定。 例如,如果您想预测一个短语的下一个单词,那么知道短语之前的单词肯定很重要。 这些神经网络称为循环,因为它们对输入序列的所有元素执行相同的计算,并且除当前输入外,每个元素的输出还取决于所有先前的计算。
RNN 架构
RNN 一次处理一个顺序输入项,维护一种更新状态向量,该向量包含有关序列的所有过去元素的信息。 通常,RNN 具有以下类型的形状:
RNN 架构架构
上图显示了 RNN 的方面,其展开版本,解释了在每个时间点的整个输入序列的网络结构。 很明显,与典型的多级神经网络不同,在每个级别中使用多个参数,RNN 始终使用相同的参数,命名为U,V和W(请参见上图)。 此外,在相同输入序列的多个时刻的每个,RNN 执行相同的计算。 共享相同的参数,可以大大减少网络在训练阶段必须学习的参数数量,从而也可以缩短训练时间。
显然,您也可以训练这种类型的网络,因为参数是在每个时间共享的,所以为每个输出计算的梯度不仅取决于当前计算,还取决于以前的。 例如,为了计算时间t = 4时的梯度,有必要使向后传播之前三个时间点的梯度,然后求和所得的梯度。 同样,整个输入序列通常被认为是训练集的单元素。
但是,这种类型的网络的训练会遇到所谓的梯度消失/爆炸问题; 计算得出的梯度和向后传播的梯度在每个时间点倾向于使增大或减小,然后在一定时间间隔后,趋于无穷大或收敛为零。
现在让我们检查 RNN 的工作方式。X[t]; 是时间t上的网络输入,例如可以是代表句子的单词,而S[t]的向量。 是网络的状态向量。 可以认为是系统的一种记忆,其中包含有关输入序列的所有先前元素的信息。 从当前输入(时间t)开始求值t时刻的状态向量,并通过U和W参数的先前时刻(时间t-1)求值状态:
S[t] = f([U] * X[t] + [W] * S[t-1])
函数f是非线性函数,例如整流线性单元(ReLu),而O[t]; 是使用参数V计算的t时刻的输出。
输出将取决于网络所使用的问题的类型。 例如,如果您要预测句子的下一个单词,则它可能是系统词汇表中每个单词的概率向量。
LSTM 网络
长短期记忆(LSTM)网络是 RNN 架构基本模型的扩展。 主要思想是改进网络,为其提供显式内存。 实际上,尽管 LSTM 网络与 RNN 并没有本质上的不同,但它们配备了特殊的隐藏单元,称为存储单元,其行为是长时间记住以前的输入。
LSTM 单元
LSTM 单元具有三个门和四个输入权重, x[t](从数据到输入和三个门),而h[t]是单元的输出。
LSTM 块包含确定输入是否足够重要以进行保存的门。 该块由四个单元组成:
- 输入门:允许在结构中输入值
- 遗忘门:删除结构中包含的值
- 输出门:确定设备何时输出陷在结构中的值
- 单元:启用或禁用存储单元
在下一个示例中,我们将在语言处理问题中看到 LSTM 网络的 TensorFlow 实现。
使用 TensorFlow 的 NLP
RNN 已被证明在诸如预测文本中的下一个字符或类似地预测句子中的下一个顺序词等问题上具有出色的表现。 但是,它们也用于更复杂的问题,例如机器翻译。 在这种情况下,网络将输入源语言中的单词序列,而您想要输出语言目标中的相应单词序列。 最后,RNN 被广泛使用的另一个非常重要的应用是语音识别。 在下文中,我们将开发一个计算模型,该模型可以根据前一个单词的顺序来预测文本中的下一个单词。 为了测量模型的精度,我们将使用 Penn 树库(PTB)数据集,该数据集是衡量这些模型精度的基准 。
本示例引用您在 TensorFlow 发行版的/rnn/ptb目录中找到的文件。 它包含以下两个文件:
ptb_word_lm.py:在 PTB 数据集上训练语言模型的队列reader.py:读取数据集的代码
与前面的示例不同,我们将仅介绍所实现过程的伪代码,以便理解模型构建背后的主要思想,而不会陷入不必要的实现细节中。 源代码很长,一行一行地解释代码太麻烦了。
注意
有关其他参考,请参见这里。
下载数据
您可以从网页下载数据,然后提取数据文件夹。 数据集经过预处理,包含 10000 个不同的单词,包括句子结尾标记和稀有单词的特殊符号(<unk>)。 我们将reader.py中的所有参数都转换为唯一的整数标识符,以使神经网络更易于处理。
要使用tar解压缩.tgz文件,您需要使用以下命令:
tar -xvzf /path/to/yourfile.tgz
建立模型
该模型使用 LSTM 实现 RNN 的架构。 实际上,它计划通过包括存储单元来增加 RNN 的架构,该存储单元允许保存有关长期时间依赖的信息。
TensorFlow 库允许您通过以下命令创建 LSTM:
lstm = rnn_cell.BasicLSTMCell(size)
这里size应该是 LSTM 要使用的单元数。 LSTM 内存初始化为零:
state = tf.zeros([batch_size, lstm.state_size])
在计算过程中,用输出值更新要检查状态值的每个单词之后,以下是实现的步骤的伪代码列表:
loss = 0.0
for current_batch_of_words in words_in_dataset:
output, state = lstm(current_batch_of_words, state)
然后,output用于对下一个单词的预测进行预测:
logits = tf.matmul(output, softmax_w) + softmax_b
probabilities = tf.nn.softmax(logits)
loss += loss_function(probabilities, target_words)
loss函数将目标单词的平均负对数概率最小化,它是 TensorFow 函数:
tf.nn.seq2seq.sequence_loss_by_example
它计算平均每个单词的困惑度,它的值衡量模型的准确率(值越小则表示最佳表现),并将在整个训练过程中进行监控。
运行代码
实现的模型支持三种类型的配置:small,medium和large。 它们之间的区别在于 LSTM 的大小和用于训练的一组超参数。 模型越大,应获得的结果越好。 small模型应该能够在测试集上达到低于 120 的困惑度,而large模型则能够达到低于 80 的困惑度,尽管可能需要花费几个小时来训练。
要执行模型,只需键入以下内容:
python ptb_word_lm --data_path=/tmp/simple-examples/data/ --model small
在/tmp/simple-examples/data/中,您必须已经从 PTB 数据集中下载了数据。
以下列表显示了训练 8 个小时后的运行情况(对于小型配置,为 13 个周期):
Epoch: 1 Learning rate: 1.000
0.004 perplexity: 5263.762 speed: 391 wps
0.104 perplexity: 837.607 speed: 429 wps
0.204 perplexity: 617.207 speed: 442 wps
0.304 perplexity: 498.160 speed: 438 wps
0.404 perplexity: 430.516 speed: 436 wps
0.504 perplexity: 386.339 speed: 427 wps
0.604 perplexity: 348.393 speed: 431 wps
0.703 perplexity: 322.351 speed: 432 wps
0.803 perplexity: 301.630 speed: 431 wps
0.903 perplexity: 282.417 speed: 434 wps
Epoch: 1 Train Perplexity: 268.124
Epoch: 1 Valid Perplexity: 180.210
Epoch: 2 Learning rate: 1.000
0.004 perplexity: 209.082 speed: 448 wps
0.104 perplexity: 150.589 speed: 437 wps
0.204 perplexity: 157.965 speed: 436 wps
0.304 perplexity: 152.896 speed: 453 wps
0.404 perplexity: 150.299 speed: 458 wps
0.504 perplexity: 147.984 speed: 462 wps
0.604 perplexity: 143.367 speed: 462 wps
0.703 perplexity: 141.246 speed: 446 wps
0.803 perplexity: 139.299 speed: 436 wps
0.903 perplexity: 135.632 speed: 435 wps
Epoch: 2 Train Perplexity: 133.576
Epoch: 2 Valid Perplexity: 143.072
............................................................
Epoch: 12 Learning rate: 0.008
0.004 perplexity: 57.011 speed: 347 wps
0.104 perplexity: 41.305 speed: 356 wps
0.204 perplexity: 45.136 speed: 356 wps
0.304 perplexity: 43.386 speed: 357 wps
0.404 perplexity: 42.624 speed: 358 wps
0.504 perplexity: 41.980 speed: 358 wps
0.604 perplexity: 40.549 speed: 357 wps
0.703 perplexity: 39.943 speed: 357 wps
0.803 perplexity: 39.287 speed: 358 wps
0.903 perplexity: 37.949 speed: 359 wps
Epoch: 12 Train Perplexity: 37.125
Epoch: 12 Valid Perplexity: 123.571
Epoch: 13 Learning rate: 0.004
0.004 perplexity: 56.576 speed: 365 wps
0.104 perplexity: 40.989 speed: 358 wps
0.204 perplexity: 44.809 speed: 358 wps
0.304 perplexity: 43.082 speed: 356 wps
0.404 perplexity: 42.332 speed: 356 wps
0.504 perplexity: 41.694 speed: 356 wps
0.604 perplexity: 40.275 speed: 357 wps
0.703 perplexity: 39.673 speed: 356 wps
0.803 perplexity: 39.021 speed: 356 wps
0.903 perplexity: 37.690 speed: 356 wps
Epoch: 13 Train Perplexity: 36.869
Epoch: 13 Valid Perplexity: 123.358
Test Perplexity: 117.171
如您所见,在每个周期之后,困惑变得更低了。
总结
在本章中,我们概述了深度学习技术,研究了使用中的两种深度学习架构,即 CNN 和 RNN。 通过 TensorFlow 库,我们开发了用于图像分类问题的卷积神经网络架构。 本章的最后一部分专门介绍 RNN,我们在其中描述了 TensorFlow 的 RNN 教程,其中建立了 LSTM 网络以预测英语句子中的下一个单词。
下一章介绍了用于 GPU 计算的 TensorFlow 功能,并介绍了 TensorFlow 服务,这是一种用于机器学习模型的高表现,开源服务系统,专为生产环境设计,并针对 TensorFlow 进行了优化。
六、TensorFlow GPU 编程和服务
在本章中,我们将介绍以下主题:
- GPU 编程
- TensorFlow 服务:
- 如何安装 TensorFlow 服务
- 如何使用 TensorFlow 服务
- 如何加载和导出 TensorFlow 模型
GPU 编程
在第 5 章,“深度学习”中,我们针对 NLP 应用训练了循环神经网络(RNN), 深度学习应用可能需要大量计算。 但是,您可以通过图形处理单元(GPU)使用并行编程技术来减少训练时间。 实际上,现代图形单元的计算资源使它们能够执行并行代码部分,从而确保了高性能。
GPU 编程模型是一种编程策略,包括将 CPU 替换为 GPU 以加速各种应用的执行。 该策略的应用范围非常广泛,并且每天都在增长。 目前,GPU 能够减少跨平台(从汽车到手机,从平板电脑到无人机和机器人)的应用执行时间。
下图显示了 GPU 编程模型如何工作。 在该应用中,有一些调用告诉 CPU 放弃代码 GPU 的特定部分,并使其运行以提高执行速度。 此类特定部分依赖两个 GPU 的原因取决于 GPU 架构提供的速度。 GPU 具有许多流式多处理器(SMP),每个处理器都具有许多计算核心。 这些内核借助单指令多线程(SIMT)调用能够执行 ALU 和其他操作,从而大大减少了执行时间。
在 GPU 编程模型中,有一些代码在 CPU 中顺序执行,而某些部分则由 GPU 并行执行
TensorFlow 具有可以利用此编程模型的功能(如果您具有 NVIDIA GPU),支持 GPU 的包版本需要 Cuda Toolkit 7.0 和 6.5 CUDNN V2。
注意
对于 Cuda 环境的安装,我们建议参考 Cuda 安装页面:
TensorFlow 通过以下方式引用这些设备:
/cpu:0:引用服务器 CPU/gpu:0:GPU 服务器(如果只有一个)/gpu:1:第二个 GPU 服务器,依此类推
要找出分配给我们的操作的设备,张紧器需要创建会话,并选择将实例化的log_device_placement设置为True。
考虑以下示例。
我们创建一个计算图; a和b将是两个矩阵:
a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
在c中,我们将这两个输入张量的矩阵相乘:
c = tf.matmul(a, b)
然后,我们将log_device_placement设置为True来建立会话:
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
最后,我们启动会话:
print sess.run(c)
您应该看到以下输出:
Device mapping:
/job:localhost/replica:0/task:0/gpu:0 -> device: 0, name: Tesla K40c, pci bus
id: 0000:05:00.0
b: /job:localhost/replica:0/task:0/gpu:0
a: /job:localhost/replica:0/task:0/gpu:0
MatMul: /job:localhost/replica:0/task:0/gpu:0
[[ 22\. 28.]
[ 49\. 64.]]
如果您希望某个特定的操作在您选择的设备上运行而不是自动为您选择的设备,则可以使用tf.device创建设备上下文,以便该上下文中的所有操作将具有相同的设备分配。
让我们使用tf.device指令创建相同的计算图:
with tf.device('/cpu:0'):
a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
c = tf.matmul(a, b)
同样,我们构建会话图并启动它:
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
print sess.run(c)
您会看到a和b已分配给cpu:0:
Device mapping:
/job:localhost/replica:0/task:0/gpu:0 -> device: 0, name: Tesla K40c, pci bus
id: 0000:05:00.0
b: /job:localhost/replica:0/task:0/cpu:0
a: /job:localhost/replica:0/task:0/cpu:0
MatMul: /job:localhost/replica:0/task:0/gpu:0
[[ 22\. 28.]
[ 49\. 64.]]
如果您拥有多个 GPU,则可以在创建会话时在配置选项中将allow_soft_placement设置为True来直接选择它。
TensorFlow 服务
服务是 TensorFlow 包,已开发该包将机器学习模型带入生产系统。 这意味着开发人员可以使用 TensorFlow 服务的 API 来构建服务器以服务于已实现的模型。
服务的模型每次都可以根据其客户提供的数据进行推断和预测,从而可以改进模型。
为了与服务系统进行通信,客户端使用 Google 开发的高性能开源远程过程调用(RPC)接口,称为 gRPC。
典型的管道(请参见下图)是将训练数据馈送到学习器,后者输出模型。 经过验证后,即可将其部署到 TensorFlow 服务系统。 随着新数据的可用或模型的改进,随着时间的推移启动和迭代我们的模型非常普遍。
TensorFlow 服务管道
如何安装 TensorFlow 服务
要编译和使用 TensorFlow 服务,您需要设置一些先决条件。
Bazel
TensorFlow 服务需要 Bazel 0.2.0 )或更高版本。 下载bazel-0.2.0-installer-linux-x86_64.sh。
注意
Bazel 是使软件构建和测试自动化的工具。 支持的构建任务包括运行编译器和链接器以生成可执行程序和库,以及组装可部署的包。
运行以下命令:
chmod +x bazel-0.2.0-installer-linux-x86_64.sh
./bazel-0.2.0-installer-linux-x86_64.sh -user
最后,设置您的环境。 将其导出到您的~/.bashrc目录中:
export PATH="$PATH:$HOME/bin"
gRPC
我们的教程使用 gRPC(0.13 或更高版本)作为我们的 RPC 框架。
注意
您可以在这个页面上找到其他参考。
TensorFlow 服务依赖项
要安装 TensorFlow 服务依赖项,请执行以下操作:
sudo apt-get update && sudo apt-get install -y \
build-essential \
curl \
git \
libfreetype6-dev \
libpng12-dev \
libzmq3-dev \
pkg-config \
python-dev \
python-numpy \
python-pip \
software-properties-common \
swig \
zip \
zlib1g-dev
然后通过运行以下命令来配置 TensorFlow:
cd tensorflow
./configure
cd ..
安装服务
使用 Git 克隆存储库:
git clone --recurse-submodules
https://github.com/tensorflow/serving
cd serving
需要--recurse-submodules选项来获取 TensorFlow,gRPC 和 TensorFlow 服务所依赖的其他库。 要构建 TensorFlow,您必须使用 Bazel:
bazel build tensorflow_serving/
二进制文件将放置在bazel-bin目录中,并且可以使用以下命令运行:
/bazel-bin/tensorflow_serving/example/mnist_inference
最后,您可以通过执行以下命令来测试安装:
bazel test tensorflow_serving/
如何使用 TensorFlow 服务
在本教程中,我们将展示如何导出训练有素的 TensorFlow 模型,以及如何构建服务器为导出的模型提供服务。 实现的模型是用于手写图像分类(MNIST 数据)的 Softmax 回归模型。
该代码将由两部分组成:
- 训练和导出模型的 Python 文件(
mnist_export.py) - 一个 C++ 文件(
mnist_inference.cc),该文件加载导出的模型并运行 gRPC 服务为其提供服务
在以下各节中,我们报告使用 TensorFlow 服务的基本步骤。 对于其他参考,您可以查看这里。
训练和导出 TensorFlow 模型
如您在mnist_export.py中看到的,训练的方法与 MNIST 中的方法相同。 对于初学者教程,请参考以下链接。
TensorFlow 图在 TensorFlow 会话sess中启动,输入张量(图像)为x,输出张量(Softmax 分数)为y。 然后我们使用 TensorFlow 服务导出器导出模型; 它构建了经过训练的模型的快照,以便以后可以加载以进行推断。 现在,让我们看一下用于导出训练后的模型的主要功能。
导入exporter以序列化模型:
from tensorflow_serving.session_bundle import exporter
然后,您必须使用 TensorFlow 函数tf.train.Saver定义saver。 它的sharded 参数等于True:
saver = tf.train.Saver(sharded=True)
saver用于将图的变量值序列化为模型导出,以便以后可以正确还原它们。
下一步是定义model_exporter:
model_exporter = exporter.Exporter(saver)
signature = exporter.classification_signature\
(input_tensor=x, scores_tensor=y)
model_exporter.init(sess.graph.as_graph_def(),
default_graph_signature=signature)
model_exporter采用以下两个参数:
sess.graph.as_graph_def()是该图的原型。 导出会将 protobuf 序列化为模型导出,以便稍后可以正确恢复 TensorFlow 图。default_graph_signature=signature指定模型导出签名。 签名指定要导出的模型类型,以及运行推理时绑定到的输入/输出张量。 在这种情况下,您可以使用exporter.classification_signature将该模型指定为分类模型。
最后,我们创建export:
model_exporter.export(export_path,tf.constant\
(FLAGS.export_version), sess)
model_exporter.export采用以下参数:
export_path是导出目录的路径。 如果目录不存在,导出将创建该目录。tf.constant(FLAGS.export_version)是一个张量,指定模型的版本。 导出同一模型的较新版本时,应指定一个较大的整数值。 每个版本将导出到给定路径下的不同子目录。sess是 TensorFlow 会话,其中包含您要导出的经过训练的模型。
执行会话
要导出模型,请首先清除导出目录:
$>rm -rf /tmp/mnist_model
然后,使用bazel构建mnist_export示例:
$>bazel build //tensorflow_serving/example:mnist_export
最后,您可以运行以下示例:
$>bazel-bin/tensorflow_serving/example/mnist_export /tmp/mnist_model
Training model...
Done training!
Exporting trained model to /tmp/mnist_model
Done exporting!
在导出目录中,我们应该有一个子目录,用于导出模型的每个版本:
$>ls /tmp/mnist_model
00000001
对应的子目录的默认值为1,因为我们先前将tf.constant(FLAGS.export_version)指定为模型版本,而FLAGS.export_version的默认值为1。
子目录的每个版本都包含以下文件:
export.meta是模型的序列化tensorflow::MetaGraphDef。 它包括模型的图定义,以及模型的元数据,例如签名。export-?????-of-?????是保存图的序列化变量的文件。
$>ls /tmp/mnist_model/00000001
checkpoint export-00000-of-00001 export.meta
加载和导出 TensorFlow 模型
用于加载导出的 TensorFlow 模型的 C++ 代码在mnist_inference.cc中的main()函数中。 在这里,我们报告摘录; 我们不考虑用于批量的参数。 如果要调整最大批量大小,超时阈值或用于批量推理的后台线程数,可以通过在BatchingParameters中设置更多值来进行调整:
int main(int argc, char** argv)
{
SessionBundleConfig session_bundle_config;
. . . Here batching parameters
std::unique_ptr<SessionBundleFactory> bundle_factory;
TF_QCHECK_OK(
SessionBundleFactory::Create(session_bundle_config,
&bundle_factory));
std::unique_ptr<SessionBundle> bundle(new SessionBundle);
TF_QCHECK_OK(bundle_factory->CreateSessionBundle(bundle_path,
&bundle));
......
RunServer(FLAGS_port, std::move(bundle));
return 0;
}
SessionBundle是 TensorFlow 服务的组件。 让我们考虑包含文件SessionBundle.h:
struct SessionBundle {
std::unique_ptr<tensorflow::Session> session;
tensorflow::MetaGraphDef meta_graph_def;
};
session参数是一个 TensorFlow 会话,具有原始图和正确还原了必要变量的图。
SessionBundleFactory::CreateSessionBundle()从bundle_path加载导出的 TensorFlow 模型,并创建一个SessionBundle对象以对该模型进行推理。
RunServer启动了一个 gRPC 服务器,该服务器导出单个Classify() API。
每个推理请求将按以下步骤处理:
- 验证输入。 对于每个推理请求,服务器都只需要一个 MNIST 格式的图像。
- 将输入转换为推断输入张量并创建输出张量占位符。
- 运行推断。
要运行推理,必须键入以下命令:
$>bazel build //tensorflow_serving/example:mnist_inference
$>bazel-bin/tensorflow_serving/example/mnist_inference --port=9000 /tmp/mnist_model/00000001
测试服务
要测试服务器,我们使用mnist_client.py工具。
该客户端下载 MNIST 测试数据,将其作为请求发送到服务器,并计算推断错误率。
要运行它,请键入以下命令:
$>bazel build //tensorflow_serving/example:mnist_client
$>bazel-bin/tensorflow_serving/example/mnist_client --num_tests=1000
--server=localhost:9000
Inference error rate: 10.5%
结果确认服务器成功加载并运行了经过训练的模型。 实际上,对于 1,000 张图像,推理错误率为 10.5% ,这为训练后的 Softmax 模型提供了 91% 的准确率。
总结
我们在本章中描述了 TensorFlow 的两个重要功能。 首先是使用称为 GPU 计算的编程模型的可能性,通过该模型可以加快代码的速度(例如,神经网络的训练阶段)。 本章的第二部分专门描述框架 TensorFlow 服务。 这是一个用于机器学习模型的高性能,开源服务系统,专为生产环境而设计,并针对 TensorFlow 进行了优化。 这个强大的框架可以运行多个模型,这些模型可以根据现实世界的数据随时间变化,从而可以更有效地利用 GPU 资源,并允许开发人员改善自己的机器学习模型。