《TensorFlow自然语言处理》—2.2.4 定义TensorFlow操作
2.2.4 定义TensorFlow操作
如果看一看https://www.tensorf?low.org/api_docs/python/上的TensorFlow API,会看到TensorFlow有数量巨大的可用操作。
在这里,我们选择其中几个进行介绍。
2.2.4.1 比较操作
比较操作对于比较两个张量非常有用。以下代码示例包含一些有用的比较操作。你可以在https://www.tensorf?low.org/api_guides/python/control_f?low_ops的比较运算符部分中找到比较运算符的完整列表。此外,为了理解这些操作的工作原理,让我们考虑两个示例张量x和y:
2.2.4.2 数学运算
TensorFlow允许对从简单到复杂的张量执行数学运算,我们将讨论TensorFlow提供的几个数学运算,在https://www.tensorflow.org/api_guides/python/math_ops可以看到完整的列表。
2.2.4.3 分散和聚合操作
分散和聚合操作在矩阵操作任务中起着至关重要的作用,因为这两种操作的变体是在TensorFlow中索引张量的唯一方法(直到最近)。换句话说,你不能像在NumPy中那样访问TensorFlow中的张量元素(例如,x [1, 0],其中x是2D numpy.ndarray)。分散操作允许你将值分配给给定张量的特定索引,而聚合操作允许你提取给定张量的切片(即个体元素)。以下代码显示分散和聚合操作的几个变体:
2.2.4.4 神经网络相关操作
现在让我们看看几个有用的神经网络相关的操作,我们将在后面的章节中大量使用它们。在这里讨论的操作涵盖了从简单的逐元素变换(即激活),到计算一组参数相对于另一个值的偏导数,我们还会实现一个简单的神经网络作为练习。
(1)神经网络中使用的非线性激活
非线性激活使神经网络能够在许多任务中表现良好。通常,在神经网络中的每个层输出之后都会有非线性激活变换(即激活层)(除最后一层之外)。非线性变换有助于神经网络学习数据中存在的各种非线性模式。这对于现实中复杂的问题非常有用,与线性模式相比,数据通常具有更复杂的非线性模式。如果层之间没有非线性激活,深层神经网络将是一堆相互堆叠的线性变换层。而且,一组线性层基本上可以压缩成单个较大的线性层。总之,如果没有非线性激活,我们就无法创建具有多层的神经网络。
让我们通过一个例子来观察非线性激活的重要性。首先,回想一下我们在sigmoid示例中看到的神经网络的计算。如果我们忽视b,它将是这样的:
假设一个三层神经网络(每层的权重为W1、W2和W3),每个层都执行上面的计算,完整的计算如下所示:
但是,如果去掉非线性激活函数(就是sigmoid),就会是这样:
因此,在没有非线性激活的情况下,可以将三层减少成单个线性层。
现在,我们将列出神经网络中两种常用的非线性激活,以及它们如何在TensorFlow中实现:
(2)卷积操作
卷积运算是一种广泛使用的信号处理技术。对于图像,使用卷积可以产生图像的不同效果。使用卷积进行边缘检测的示例如图2.6所示,其实现方法是在图像顶部移动卷积滤波器,从而在每个位置产生不同的输出(参见本节后面的图2.7)。具体来说,在每个位置,对于与卷积滤波器重叠的图像块(与卷积滤波器大小相同),在卷积滤波器中对其元素进行逐元素相乘相加,并对结果求和:
以下是卷积操作的实现:
在这里,过多的方括号可能会让你认为去掉这些冗余括号可以很容易地理解这个例子,不幸的是,事实并非如此。对于tf.conv2d(…)操作,TensorFlow要求input、f?ilter和strides具有精确的格式。现在我们将更详细地介绍tf.conv2d(input, f?ilter, strides, padding)中的每个参数:
input:这通常是4D张量,其维度应按[batch_size,height,width,channels]排序。
batch_size:这是单批数据中的数据量(例如,如图像和单词的输入)。我们通常批量处理数据,因为进行学习的数据集很大。在给定的训练步骤,我们随机抽样一小批数据,这些数据近似代表完整的数据集。通过许多次执行此操作,我们可以很好地逼近完整的数据集。这个batch_size参数与我们在TensorFlow输入管道示例中讨论的参数相同。
height和width:这是输入的高度和宽度。
channels:这是输入的深度(例如,对于RGB图像其值为3,表示有3个通道)。
filter:这是一个4D张量,表示卷积运算的卷积窗口,其维度应为[height,width,in_channels,out_channels]:
height和width:这是卷积核的高度和宽度(通常小于输入的高度和宽度)
in_channels:这是该层的输入的通道数
out_channels:这是要在该层的输出中生成的通道数
strides:这是一个包含四个元素的列表,其中元素是[batch_stride,height_stride,width_stride,channels_stride]。strides参数表示卷积窗口在输入上单次滑动期间要跳过的元素数。如果你不完全了解步长是什么,则可以使用默认值1。
padding:这可以是['SAME','VALID']之一,它决定如何处理输入边界附近的卷积运算。VALID操作在没有填充的情况下执行卷积。如果我们用大小为h的卷积窗口卷积长度为n的输入,则输出大小为(n - h + 1 < n),输出大小的减小会严重限制神经网络的深度。SAME将用零填充边界,使输出具有与输入相同的高度和宽度。要更好地了解卷积核大小、步长和填充是什么,请参见图2.7。
(3)池化操作
池化操作的行为与卷积操作类似,但最终输出不同。池化操作取该位置的图像块的最大值,而不是输出卷积核和图像块的逐元素相乘的总和(参见图2.8)。
图2.7 卷积操作
(4)定义损失
我们知道,为了让神经网络学习有用的东西,需要定义一个损失。在TensorFlow中有几种可以自动计算损失的函数,其中两种函数如下面的代码所示。tf.nn.l2_loss函数是均方误差损失,而tf.nn.softmax_cross_entropy_with_logits_v2是另一种类型的损失,在分类任务中它有更好的性能。这里的logits指的是神经网络的没有归一化的输出(即神经网络最后一层的线性输出):
图2.8 最大值池化操作
(5)优化神经网络
在定义了神经网络的损失之后,我们的目标是尽量减少这种损失,优化就是用于此的过程。换句话说,优化器的目标是找到对于所有输入均给出最小损失的神经网络参数(即权重和偏差值)。同样,TensorFlow提供了几种不同的优化器,因此,我们不必从头开始实现它们。
图2.9展示一个简单的优化问题,以及优化是如何随时间进行的。曲线可以想象为损失曲线(对于高维,则是损失曲面),其中x可以被认为是神经网络的参数(在这里,是具有单个权重的神经网络),而y可以被认为是损失。起点设为x = 2,从这一点开始,我们使用优化器来达到在x = 0时获得的最小值y(即损失)。更具体地说,我们在给定点的与梯度相反的方向上移动一些小步长,并以这种方式继续走几个步长。然而,在实际问题中,损失曲面不会像图中那样好,它会更复杂:
图2.9 优化过程
在此示例中,我们使用GradientDescentOptimizer。learning_rate参数表示在最小化损失方向上的步长(两个点之间的距离):
每次使用session.run(minimize_op)执行最小化损失运算时,都会接近给出最小值tf_y的tf_x值。
(6)控制流操作
控制流操作,顾名思义,控制图中元素执行的顺序。例如,假设我们需要按顺序执行以下计算:
确切地说,如果x = 2,我们应该得到z = 14。让我们首先尝试以最简单的方式实现这一点:
理想情况下,我们希望x = 7和z = 14,但是,TensorFlow产生x = 2和z = 4。这不是你期待的答案。这是因为除非你明确指定,否则TensorFlow不关心事物的执行顺序。控制流操作就是使你能控制执行顺序的操作。要修复上述代码,我们执行以下操作:
现在,结果应该是x = 7和z = 14。tf.control_dependencies(…)操作确保在执行嵌套操作之前将执行作为参数传递给它的运算。
- 点赞
- 收藏
- 关注作者
评论(0)