引言

TensorFlow 版本1.15pip3 install tensorflow==1.15.0
这是《TensorFlow实战Google深度学习框架(第2版)》的学习笔记,所有代码在TensorFlow 1.15版本中运行正常

深度学习与深层神经网络

维基百科对深度学习的精确定义为“一类通过多层非线性变换对高度复杂数据建模算法的合集”。

线性模型的局限性

在线性模型中,模型的输出yyy为输入xxx的加权和,满足以下关系的模型就是线性模型:

y=∑iwixi+by = \sum_i w_i x_i + b y=i∑​wi​xi​+b

其中wi,b∈Rw_i,b \in Rwi​,b∈R为模型的参数。被称之为线性模型是因为当模型的输入只有一个的时候,xxx和yyy形成了二维坐标系上的一条直线。类似地,当模型有nnn个输入时,xxx和yyy形成了n+1n+1n+1维空间中的一个平面。

而一个线性模型中通过输入得到输出的函数被称之为一个线性变换,上面的公式就是一个线性变换。线性模型的最大特点是任意线性模型的组合仍然还是线性模型。

然后线性模型能解决的问题是有限的,这是线性模型最大的局限性,也是为什么深度学习要强调非线性。

激活函数实现去线性化

在上篇文章中介绍的神经元结构的输出为所有输入的加权和,这导致整个神经网络是一个线性模型。如果每个神经元的输出通过一个非线性函数,那么整个神经网络的模型就不再是线性的了。这个非线性函数就是激活函数。

(图片转自博客常用激活函数图像)

从上图可以看出,这些激活函数的函数图像都不是一条直线。所以通过这些激活函数,每一个节点不再是线性变换,于是整个神经网络模型也就不再是线性的了。

目前TensorFlow提供了7中不同的非线性激活函数,tf.nn.relutf.sigmoidtf.tanh是其中比较常用的几个。TensorFlow也支持使用自己定义的激活函数。

a = tf.nn.relu(tf.matmul(x,w1) + b1)
y = tf.nn.relu(tf.matmul(a,w2) + b2)

上面代码展示了如何通过TenforFlow实现神经网络的带激活函数的前向传播算法。

多层网络解决异或运算

上面的两个小节讲解了线性变换的问题。在这一节中,将通过一个实际问题来讲解深度学习的另外一个重要性质——多层变换。

在神经网络的发展史上,一个很重要的问题就是异或问题。Frank Rosentblatt提出了感知机模型,从数学上完成了对神经网络的精确建模。感知机可以理解为单层的神经网络。

但是,有人证明感知机模型无法模拟异或运算。

损失函数定义

神经网络模型的优化目标是通过损失函数来定义的。

经典损失函数

通过神经网络解决多分类问题最常用的方法是设置n个输出节点,其中n为类别的个数。对于每一个样例,神经网络都可以得到一个n维数组作为输出结果。
数组中的每一个维度对应一个类别。在理想的情况下,如果一个样本属于类别k,那么这个类别所对应的输出节点的输出值应该为1,而其他节点的输出值都为0。

那么如何判断一个输出向量和期望的向量有多接近呢?交叉熵是常用的评判方法之一。交叉熵刻画了两个概率分布之间的距离,它是分类问题中使用比较广的一种损失函数。

给定两个概率分布ppp和qqq,它们的交叉熵为:
H(p,q)=−∑xp(x)log⁡q(x)H(p,q) = - \sum_x p(x) \log q(x) H(p,q)=−x∑​p(x)logq(x)

如果将分类问题中一个样例属于某一个类别看成一个概率事件,那么训练数据的正确答案就符合一个概率分布。如何将神经网络前向传播得到的结果也变成概率分布呢? Softmax回归就是一个非常常用的方法。

Softmax回归本身可以作为一个学习算法来优化分类结果,但在TensorFlow中,Softmax回归的参数被去掉了,它只是一层额外的处理层,将神经网络的输出变成一个概率分布。

假设原始的神经网络输出为y1,y2,⋯,yny_1,y_2,\cdots,y_ny1​,y2​,⋯,yn​,那么经过Softmax回归处理后的输出为:
softmax(yi)=yi′=eyi∑j=1neyjsoftmax(y_i) = y_i^{\prime} = \frac{e^{y_i}}{\sum_{j=1}^n e^{y_j}} softmax(yi​)=yi′​=∑j=1n​eyj​eyi​​

从交叉熵的公式中可以看到交叉熵函数不是对称的(H(p,q)≠H(q,p)H(p,q) \neq H(q,p)H(p,q)​=H(q,p)),它刻画的是通过概率分布qqq来表达概率分布ppp的困难程度。
因为正确答案是希望得到的结果,所以当交叉熵为神经网络的损失函数时,ppp代表正确答案,qqq代表的是预测值。

交叉熵越小,两个概率分布越接近。在上篇文章中,我们已经通过TensorFlow实现过交叉熵,其代码如下:

cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0)))

其中y_代表正确结果,yyy代表预测结果。

通过tf.clip_by_value函数将一个张量中的数值限定在一个范围之内,这样可以避免无效的log0log 0log0运算。下面给出该函数的简单样例:

import tensorflow as tfv = tf.constant([[1.0, 2.0, 3.0],[4.0,5.0,6.0]])
with tf.Session() as sess:print(tf.clip_by_value(v, 2.5 , 4.5).eval(session=sess))
#输出:
# [[2.5 2.5 3. ]
#  [4.  4.5 4.5]]

可以看到,小于2.5的数都被替换为2.5,大于4.5的数都被替换为4.5。这样可以保证进行log运算时,不会出现log0log 0log0这样的错误或大于1的概率。

其中第二个运算是tf.log函数,这个函数完成了对张量中所有元素依次求对数的功能:

import tensorflow as tfv = tf.constant([1.0, 2.0, 3.0])
with tf.Session() as sess:print(tf.log(v).eval(session=sess))
#输出:
# [0.        0.6931472 1.0986123]

第三个运算是乘法,在实现交叉熵的代码中直接将两个矩阵通过*操作相乘。这样操作不是矩阵乘法,而是元素之间直接相乘。矩阵乘法需要使用tf.matmul函数来完成。下面给出两个操作的区别:

import tensorflow as tfv1 = tf.constant([[1.0, 2.0],[3.0,4.0]])
v2 = tf.constant([[5.0, 6.0],[7.0,8.0]])sess = tf.Session()
with sess.as_default():print((v1 * v2) .eval())
#输出:
#[[ 5. 12.]
# [21. 32.]]with sess.as_default():print((tf.matmul(v1,v2) .eval()))
#输出:
#[[19. 22.]
# [43. 50.]]

通过上面这三个运算完成了对于每一个样例的每个类别交叉熵p(x)logq(x)p(x) log q(x)p(x)logq(x)的计算。这三步计算得到的结果是一个n×mn \times mn×m的二维矩阵,其中nnn是一个batch中样例的数量,mmm为类别数量。根据交叉熵的公式,应该将每行中的mmm个结果相加得到所有样例的交叉熵,然后再对这nnn行取平均得到一个batch的平均交叉熵。

以下代码简单展示了tf.reduce_mean函数(先求和再求均值)的使用方法:

import tensorflow as tfv = tf.constant([[1.0, 2.0, 3.0],[4.0,5.0, 6.0]])sess = tf.Session()
with sess.as_default():print(tf.reduce_mean(v) .eval()) #21 / 6 = 3.5

因为交叉熵一般会与softmax回归一起使用,所以TensorFlow对这两个功能进行了统一封装,提供了tf.nn.softmax_cross_entropy_with_logits函数。
比如可以直接通过以下代码来实现使用了softmax回归之后的交叉熵损失函数:

cross_entroy = tf.nn.softmax_cross_entropy_with_logits(labels=y_,logits=y)

其中y代表了预测结果,y_代表了真实结果。这是多分类问题,当处理二分类问题时,TensorFlow提供了tf.nn.sparse_softmax_cross_entropy_with_logits函数来加速计算过程。

与分类问题不同,回归问题解决的是对具体数值的预测。比如房价预测、销量预测等都是回归问题。这些问题预测的是一个任意实数。因此,解决回归问题的神经网络一般只有一个输出节点,这个节点的输出值就是预测值。对于回归问题,最常用的损失函数是均方误差(MSE,mean squared error)。定义如下:

MSE(y,y′)=∑i=1n(yi−yi′)2nMSE(y,y^{\prime}) = \frac{\sum_{i=1}^n (y_i - y^{\prime}_i)^2}{n} MSE(y,y′)=n∑i=1n​(yi​−yi′​)2​

其中yiy_iyi​为一个batch中第iii个数据的正确答案,而yi′y^{\prime}_iyi′​为神经网络给出的预测结果。以下代码展示了如何通过TensorFlow实现均方误差损失函数:

mse = tf.reduce_mean(tf.square(y_ - y))

其中y代表预测值,y_代表真实值。

自定义损失函数

本节介绍如何通过自定义损失函数的方法,使得神经网络优化的结果更加接近实际问题的需求。
以预测商品销量问题为例。

在预测商品销量时,如果预测多了(预测值比真实销量大),商家损失的是生产商品的成本;而如果预测少了,损失的是商品的利润。
因为一般商品的成本和商品的利润不会严格相等,所以使用上面介绍的均方误差损失函数就不能很好地最大化销售利润。
比如如果一个商品的成本是1元,但是利润是10元,那么少预测一个就少挣10元;而多预测一个才少挣1元。
为了最大化预期利润,需要将损失函数和利润直接联系起来。损失函数定义的是损失,所以要将利润最大化,定义的损失函数应该刻画成本或代价。
以下公式给出了一个当预测多于真实值和预测少于真实值时有不同损失系数的损失函数:

Loss(y,y′)=∑i=1nf(yi,yi′),f(x,y)={a(x−y)x>yb(y−x)x≤yLoss(y, y^{\prime}) = \sum_{i=1}^n f(y_i ,y^{\prime}_i), \,\,\,\, f(x,y) = \left\{ \begin{array}{lr} a(x-y) & x > y \\ b(y-x) & x \leq y \end{array} \right. Loss(y,y′)=i=1∑n​f(yi​,yi′​),f(x,y)={a(x−y)b(y−x)​x>yx≤y​

其中yiy_iyi​为一个batch中第iii个数据的正确答案,而yi′y^{\prime}_iyi′​为神经网络给出的预测结果。aaa和bbb是常量。在上面介绍的销量预测问题中,aaa就等于101010(正确答案多于预测答案的代价,而bbb等于111(正确答案少于预测答案的代价)。

通过对这个自定义损失函数的优化,模型提供的预测值更有可能最大化收益。下面给出代码实现:

loss = tf.reduce_sum(tf.where(tf.greater(v1,v2), (v1 - v2) * a , (v2 - v1) * b))

tf.greater的输入是两个张量,此函数会比较这两个输入张量中每一个元素的大小,并返回比较结果。当tf.greater的输入张量维度不一样时,TensorFlow会进行类似Numpy广播操作的处理。

tf.where函数有三个参数。第一个为选择条件依据,当选择条件为True时,tf.where函数会选择第二个参数中的值,否则使用第三个参数中的值。以下代码展示了tf.where函数和tf.greater函数的用法。

import tensorflow as tfv1 = tf.constant([1.0, 2.0, 3.0, 4.0])
v2 = tf.constant([4.0, 3.0, 2.0, 1.0])sess = tf.InteractiveSession()
print(tf.greater(v1,v2).eval()) # [False False  True  True]print(tf.where(tf.greater(v1,v2), v1, v2).eval()) # [4. 3. 3. 4.]sess.close()

tf.where函数判断和选择都是在元素级别进行。
在定义了损失函数后,下面通过一个简单的神经网络来阐述损失函数对模型训练结果的影响。在下面这个程序中,实现了一个拥有两个输入节点、一个输出节点,没有隐藏层的神经网络。

import tensorflow as tf
from numpy.random import RandomStatebatch_size = 8
# 两个输入节点
x = tf.placeholder(tf.float32, shape=(None, 2), name='x-input')
# 一个输出节点
y_ = tf.placeholder(tf.float32, shape=(None, 1), name='y-input')# 定义了单层神经网络前向传播的过程,这里就是简单加权和
w1 = tf.Variable(tf.random_normal([2,1], stddev=1, seed=1))
y = tf.matmul(x, w1)# 定义预测多了和预测少了的成本
loss_less = 10
loss_more = 1
loss = tf.reduce_sum(tf.where(tf.greater(y, y_), (y - y_) * loss_more, (y_ -y) * loss_less))train_step = tf.train.AdamOptimizer(0.001).minimize(loss)# 通过随机数生成一个模拟数据集
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size, 2)# y= x1 + x2
# 加上噪音
Y = [[x1 + x2 + rdm.rand() / 10.0 - 0.05] for (x1, x2) in X]# 训练神经网络
with tf.Session() as sess:init_op = tf.global_variables_initializer()sess.run(init_op)STEPS = 5000for i in range(STEPS):start = (i * batch_size) % dataset_sizeend = min(start + batch_size, dataset_size)sess.run(train_step,feed_dict={x: X[start:end], y_:Y[start:end]})print(sess.run(w1))

输出:

[[1.019347 ][1.0428089]]

从输出可以看到,预测函数是1.01x1+1.04x21.01x_1 + 1.04x_21.01x1​+1.04x2​,这比实际的x1+x2x_1 + x_2x1​+x2​要大。
因为在损失函数中指定预测少了的损失更大。这样模型会偏向于预测少一点。

而如果使用均方误差作为损失函数,那么w1w_1w1​会是[0.974,10.24]。使用均方误差损失函数会尽量让预测值离标准答案更近。
通过这个样例可以感受到,对于相同的神经网络,不同的损失函数会对训练得到的模型产生重要影响。

神经网络优化算法

梯度下降算法不一定能达到全局最优,只有当损失函数为凸函数时,梯度下降算法才能保证达到全局最优。
梯度下降算法的另一个问题是计算时间太长,因为要在全部训练数据上最小化损失,在海量训练数据下,这是非常耗时的。
为了加速训练过程,可以使用随机梯度下降算法。这个算法不是全部训练数据的损失函数,而是在每一轮迭代中,随机优化某一个训练数据上的损失函数。
这样参数更新速度就大大加快了,但是问题也非常明显:在某个数据上损失函数更小并不代表在全部数据上损失函数更小。

实际应用中一般采用这两算法的折中——每个计算一小部分训练数据的损失函数。这一小部分数据被称为一个批次(batch)。通过矩阵运算,每次在一个batch上优化神经网络的参数并不会比单个数据慢太多。另一方面,每次使用一个batch可以大大减小收敛所需要的迭代次数,同时可以使收敛到的结果更加接近梯度下降的效果。

下面给出TensorFlow中如何实现神经网络的训练过程。

import tensorflow as tf
from numpy.random import RandomStatebatch_size = n
x = tf.placeholder(tf.float32, shape=(batch_size, 2), name='x-input')
y_ = tf.placeholder(tf.float32, shape=(batch_size, 1), name='y-input')loss = ...
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)# 训练神经网络
with tf.Session() as sess:init_op = tf.global_variables_initializer()sess.run(init_op)...# 迭代地更新参数for i in range(STEPS):# 准备batch_size个训练数据。current_X, current_Y = ...sess.run(train_step,feed_dict={x: current_X, y_:current_Y})

神经网络进一步优化

上面介绍了优化神经网络的基本算法,本节将继续介绍神经网络优化过程中可能遇到的一些问题,以及解决这些问题的常用方法。

学习率的设置


(图片转自Setting the learning rate of your neural network)

学习率不能过大,也不能过小。如果过大,可能导致参数在极优值两侧来回移动;如果过小,优化速度又太慢。

TensorFlow提供了一种很灵活的学习率设置方法——指数衰减法。
tf.train.exponential_decay函数实现了指数衰减学习率。通过这个函数,可以先使用较大的学习率来快速得到一个比较优的解,然后随着迭代的继续逐步减少学习率,使得模型在训练后期更加稳定。
exponential_decay函数会指数级地减小学习率,它实现了以下代码的功能:

decayed_learing_rate = learning_rate *decay_rate ** (global_step / decay_steps)

decayed_learing_rate为每轮优化时使用的学习率,learning_rate为事先设定的初始学习率,decay_rate是衰减系数,即每一次学习都衰减为原来的decay_rate倍,decay_steps是衰减速度,如果staircase=True,那么每decay_steps更新一次decayed_learing_rate,如果是False那么每一步都更新一次decayed_learing_rate

tf.train.exponential_decay函数可以通过设置参数staircase选择不同的衰减方式,默认为False

这时学习率随迭代轮数变化的趋势如上图红线所示;当staircase被设成True时,global_step / decay_steps会被转化成整数。这使得学习率成为一个阶梯函数,如上图蓝线所示。]

在这样的设置下, decay_steps 通常代表了完整的使用一遍训练数据所需要的迭代轮数。
这个迭代轮数也就是总训练样本数除以每一个batch 中的训练样本数。
这种设置的常用场景是每完整地过完一遍训练数据,学习率就减小一次。这可以使得训练数据集中的所有数据对模型训练有相等的作用。当使用连续的指数衰减学习率时,不同的训练数据有不同的学习率,而当学习率减小时,对应的训练数据对模型训练结果的影响也就小了。

下面给出了在TensorFlow中如何使用tf.train.exponential_decay函数:

global_step = tf.Variable(0)# 通过exponential_decay 函数生成学习率
learning_rate = 0.1  # 初始学习速率
decay_rate = 0.96  # 衰减速率
global_steps = 1000  # 总学习次数
decay_steps = 100  learning_rate= tf.train.exponential_decay(learning_rate, global_step , decay_steps, decay_rate, staircase=True)
learning_step = tf.train.GradientDescentOptimizer(learning_rate.minimize(...my loss..,global_step=global_step)

过拟合问题

所谓过拟合,就是模型针对训练数据拟合的太好了,学习了很多训练数据的特征、误差,以至于在测试数据上表现的不好。
为了避免过拟合问题,一个常用的方法是正则化。就是在损失函数中假如刻画模型复杂度的指标。
假设用于刻画模型在训练数据上表现的损失函数为J(θ)J(\theta)J(θ),那么在优化时不是直接优化J(θ)J(\theta)J(θ),而是优化J(θ)+λR(w)J(\theta) + \lambda R(w)J(θ)+λR(w)。其中R(w)R(w)R(w)刻画的是模型的复杂程度,而λ\lambdaλ是模型复杂损失在总损失中的比例。θ\thetaθ是模型中所有的参数。

常用的刻画模型复杂度的函数由两种,一种是L1L1L1正则化,计算公式为:
R(w)=∣∣w∣∣1=∑i∣wi∣R(w) = ||w||_1 = \sum _ i |w_i| R(w)=∣∣w∣∣1​=i∑​∣wi​∣
另一种是L2L2L2正则化,公式为:
R(w)=∣∣w∣∣22=∑i∣wi2∣R(w) = ||w||_2^ 2 = \sum_i |w_i^ 2| R(w)=∣∣w∣∣22​=i∑​∣wi2​∣

无论是哪一种正则化方式,基本的思想都是希望通过限制权重的大小,使得模型不能任意拟合训练数据中的随机噪音。但这两种正则化的方法也有很大的区别。首先,L1L1L1正则化会让参数变得更稀疏,而L2L2L2 正则化不会。
所谓参数变得更稀疏是指会有更多的参数变为0 ,这样可以达到类似特征选取的功能。L2L2L2 正则化不会让参数变得稀疏的原因是当参数很小时,比如0.001 ,这个参数的平方基本上就可以忽略了,于是模型不会进一步将这个参数调整为0 。
其次, L1L1L1正则化的计算公式不可导,而L2L2L2 正则化公式可导。因为在优化时需要计算损失函数的偏导数,所以对含有L2L2L2 正则化损失函数的优化要更加简洁。优化带L1L1L1正则化的损失函数要更加复杂,而且优化方法也有很多种。

在实践中,也可以将L1L1L1正则化和L2L2L2 正则化同时使用:
R(w)=∑iα∣wi∣+(1−α)wi2R(w) = \sum _i \alpha |w_i| + (1-\alpha) w_i^2 R(w)=i∑​α∣wi​∣+(1−α)wi2​

下面给出一个简单的带L2L2L2正则化的损失函数定义:

w = tf.Variable(tf.ramdom_normal([2,1], stddev=1,seed=1))
y = tf.matmul(x, w)loss = tf.reduce_mean(tf.square(y_ - y)) + tf.contrib_layers.l2_regularizer(lambda_)(w)

lambda_就是公式J(θ)+λR(w)J(\theta) + \lambda R(w)J(θ)+λR(w)。tf.contrib_layers.l2_regularizer函数可以计算一个给定参数的L2L2L2正则化项的值。类似的,tf.contrib_layers.l1_regularizer计算的是L1L1L1正则化项的值。
下面给出使用这两个函数的样例:

weights = tf.constant([[1.0, -2.0], [-3.0, 4.0]]))
with tf.Session() as sess:# 输出为 (|1| + |-2| + |-3| + |4|) x 0.5 = 5 。其中0.5为正则化的权重print(sess.run(tf.contrib.layers.l1_regularizer(0.5)(weights))# 输出为(1²+(-2)²+(-3)²+4² ) / 2 x 0.5 = 7.5print(sess.run(tf.contrib.layers.l2_regularizer(0.5)(weights))

在简单的神经网络中,这样的方式就可以很好地计算带正则化的损失函数了。但当神经网络的参数增多之后,这样的方式首先可能导致损失函数loss的定义很长,可读性差且容易出错。更重要的是,当网络结构复杂之后定义网络结构的部分和计算损失函数的部分可能不在同一个函数中,这样通过变量这种方式计算损失函数就不方便了。

为了解决这个问题,可以使用TensorFlow中提供的集合。它可以在一个计算图中保存一组实体(比如张量)。以下代码给出了通过集合计算一个5层神经网络带L2L2L2正则化的损失函数的计算方法:

import tensorflow as tf# 获取一层神经网络边上的权重,并将这个权重的L2正则化损失加入名为losses的集合汇总
def get_weight(shape,lambda_):# 生成一个变量var = tf.Variable(tf.random_normal(shape), dtype=tf.float32)# add_to_collection函数将这个新生成变量的L2正则化损失项加入集合# 第一个参数losses是集合的名字,第二个参数是要加入这个集合的内容tf.add_to_collection('losses',tf.contrib.layers.l2_regularizer(lambda_)(var))# 返回生成的变量return varx = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
batch_size = 8
# 定义了每一层网络中的节点个数
layer_dimensions = [2, 10, 10 ,10 ,1]
# 神经网络的层数
n_layers = len(layer_dimensions)# 这个变量维护前向传播时最深层的节点,开始的时候就是输入层。
cur_layer = x
# 当前层的节点个数
in_dimension = layer_dimensions[0]
# 通过一个循环来生成5层全连接的神经网络结构
for i in range(1, n_layers):# layer_dimensions[i]为下一层节点的个数out_dimension = layer_dimensions[i]# 生成当前层中权重的变量,并将这个变量的L2正则化损失加入计算图上的集合weight = get_weight([in_dimension, out_dimension],0.001)bias = tf.Variable(tf.constant(0.1,shape=[out_dimension]))# 使用ReLU激活函数cur_layer = tf.nn.relu(tf.matmul(cur_layer, weight) + bias)# 进入下一层之前将下一层的节点个数更新为当前层节点个数in_dimension = layer_dimensions[i]# 在定义神经网络前向传播的同时已经将所有的L2正则化损失加入了图上的集合,
# 这里只需要计算刻画模型在训练数据上表现的损失函数
mse_loss = tf.reduce_mean(tf.square(y_ - cur_layer))# 将均方误差损失函数加入损失集合
tf.add_to_collection('losses', mse_loss)# get_collection返回一个列表,这个列表是所有这个集合中的元素。
# 在这个样例中,这些元素就是损失函数的不同部分,将它们加起来就可以得到最终的损失函数。
loss = tf.add_n(tf.get_collection('losses'))

从以上代码可以看出通过使用集合的方法在网络结构比较复杂的情况下可以使代码的可读性更高。

滑动平均模型

本节将介绍另外一个可以使模型在测试数据上更健壮的方法——滑动(移动)平均模型。
在TensorFlow中提供了tf.train.ExponentialMovingAverage来实现滑动平均模型。在初始化ExponentialMovingAverage时,需要提供一个衰减率(decy)。这个衰减率将用于控制模型更新的速度。
ExponentialMovingAverage对每一个变量会维护一个影子变量(shadow variable),这个影子变量的初始值就是相应变量的初始值,而每次运行变量更新时,影子变量的值会更新为:

shadow_variable = decay *  shadow_variable + (1 - decay) * variable

shadow_variable是影子变量,variable为待更新的变量,decay为衰减率。
从公式可以看到,decay决定了模型更新的速度,decay越大模型越趋于稳定。
在实际应用中,decay一般会设成非常接近1的数。为了使得模型在训练前期可以更新得更快,ExponentialMovingAverage还提供了num_updates参数来动态设置decay的大小。
如果在ExponentialMovingAverage初始化时提供了num_updates参数,那么每次使用的衰减率将是:
min{decay,1+num_updates10+num_updates}min \left\{ decay,\frac{1 + num\_updates}{10 + num\_updates} \right\} min{decay,10+num_updates1+num_updates​}

下面通过一段代码来解释ExponentialMovingAverage是如何使用的:

import tensorflow as tf
# 定义一个变量用于计算滑动平均,初始值为0
v1 = tf.Variable(0,dtype=tf.float32)
# step变量模拟神经网络中迭代的轮数,可以用于动态控制住衰减率
step = tf.Variable(0, trainable=False)# 定义一个滑动平均的类,初始化给定了衰减率0.99和控制衰减率的变量step。
ema = tf.train.ExponentialMovingAverage(0.99,step)
# 定义一个更新变量滑动平均的操作,这里需要给定一个类别,每次执行这个操作时
# 这个列表中的变量都会被更新
maintain_averages_op = ema.apply([v1])with tf.Session() as sess:# 初始化所有变量init_op = tf.global_variables_initializer()sess.run(init_op)# 通过ema.average(v1)获取滑动平均之后变量的取值。在初始化之后变量v1的值和# v1的滑动平均都为0print(sess.run([v1, ema.average(v1)])) #[0.0, 0.0]# 更新变量v1的值到5sess.run(tf.assign(v1, 5))# 更新v1的滑动平均值。衰减率为min{0.99,(1+step)/(10+step) = 0.1} = 0.1# 所以v1的滑动平均会被更新为0.1*0 + 0.9*5 = 4.5sess.run(maintain_averages_op)print(sess.run([v1, ema.average(v1)])) # [5.0, 4.5]# 更新step的值为10000sess.run(tf.assign(step, 10000))# 更新v1的值为10sess.run(tf.assign(v1, 10))# 更新v1的滑动平均值。衰减率为min{0.99,(1+step)/(10+step) ≈ 0.999} = 0.99# 所以v1的滑动平均被更新为0.99*4.5 + 0.01*10 = 4.555sess.run(maintain_averages_op)print(sess.run([v1, ema.average(v1)])) # [10.0, 4.555]# 再次更新滑动平均值,得到的新滑动平均值为0.99*4.555+0.01*10=4.60945sess.run(maintain_averages_op)print(sess.run([v1, ema.average(v1)])) #[10.0, 4.60945]

TensorFlow学习笔记——深层神经网络相关推荐

  1. 1.4)深度学习笔记------深层神经网络

    目录 1)Deep L-layer neural network 2)Forward Propagation in a Deep Network(重点) 3)Getting your matrix d ...

  2. Tensorflow学习笔记——搭建神经网络

    目录 1.搭建神经网络6步法 2.函数用法和介绍 (1)tf.keras.models.Sequential() (2)Model.compile() (3)model.fit() (4)model. ...

  3. tensorflow学习笔记二——建立一个简单的神经网络拟合二次函数

    tensorflow学习笔记二--建立一个简单的神经网络 2016-09-23 16:04 2973人阅读 评论(2) 收藏 举报  分类: tensorflow(4)  目录(?)[+] 本笔记目的 ...

  4. Win10:tensorflow学习笔记(4)

    前言 学以致用,以学促用.输出检验,完整闭环. 经过前段时间的努力,已经在电脑上搭好了深度学习系统,接下来就要开始跑程序了,将AI落地了. 安装win10下tensforlow 可以参照之前的例子:w ...

  5. tensorflow学习笔记——使用TensorFlow操作MNIST数据(1)

    续集请点击我:tensorflow学习笔记--使用TensorFlow操作MNIST数据(2) 本节开始学习使用tensorflow教程,当然从最简单的MNIST开始.这怎么说呢,就好比编程入门有He ...

  6. TensorFlow学习笔记(二):快速理解Tutorial第一个例子-MNIST机器学习入门 标签: 机器学习SoftmaxTensorFlow教程 2016-08-02 22:12 3729人阅

    TensorFlow学习笔记(二):快速理解Tutorial第一个例子-MNIST机器学习入门 标签: 机器学习SoftmaxTensorFlow教程 2016-08-02 22:12 3729人阅读 ...

  7. 01.神经网络和深度学习 W4.深层神经网络(作业:建立你的深度神经网络+图片猫预测)

    文章目录 作业1. 建立你的深度神经网络 1. 导入包 2. 算法主要流程 3. 初始化 3.1 两层神经网络 3.2 多层神经网络 4. 前向传播 4.1 线性模块 4.2 线性激活模块 4.3 多 ...

  8. tensorflow学习笔记1

    tensorflow学习笔记1 本文主要记录我在慕课上观看北大曹建老师的<人工智能实践:Tensorflow笔记>,链接:https://www.icourse163.org/course ...

  9. TensorFlow学习笔记--第三节张量(tensor)及其定义方法

    目录 在TensorFlow中,所有的数据通过张量的形式来表示 1张量及属性: 1.1维数(阶) 1.2 形状 1.3数据类型 TensorFlow 支持以下三种类型的张量: **1.常量** **2 ...

最新文章

  1. ASP.NET HTTP 运行时
  2. 坚果3“凉了”,罗永浩只提了一次人工智能
  3. qt toutf8函数_qt中的toUtf8, toLatin1, Local8bit, toUcs4(转)
  4. 数据结构经典书籍--数据结构与算法分析
  5. javascript中函数和方法的区别
  6. Bootstrap table后端分页(ssm版)
  7. 通过网络地址进行真机调试
  8. vim中的jk为什么是上下_JK轮胎的完整形式是什么?
  9. 江西省计算机应用基础对口高考复习,江西省对口高考计算机应用基础复习题(各章及综合练习)...
  10. 简单的多线程实例下载(供初学者下载学习)
  11. “一个人会不会一直穷下去”“先看看他关注的公众号”
  12. 动软分享社区系统 v1.6 震撼发布!
  13. 台式电脑备用电源_台式电源哪家稳?华硕 TUF GAMING六年质保,坚如磐石_电脑电源...
  14. 找轮转后的有序数组中第K小的数
  15. Latex希腊字母对照表
  16. flask peewee教程
  17. 第4章【思考与练习2】数据文件high-speed rail.csv存放着世界各国高速铁路的情况。对世界各国高铁的数据进行绘图分析。使用Basemap绘制地图及使用Pyecharts绘制地图。
  18. Ego的Spring框架笔记
  19. 高速信号的完整性分析
  20. 小程序电商对比传统电商的优势在哪?

热门文章

  1. python web编程之django post请求
  2. 一枚研发狗的自我认知历程
  3. df命令能够实现linux系统磁盘管理中,Linux_linux磁盘命令中df命令的作用,linux磁盘命令中df命令的使用, - phpStudy...
  4. jieba和wordcloud红楼梦人物出现次数生成词云
  5. 目前最值得收藏的100个各类资源站
  6. GGS ERROR 160 Bad column index
  7. 图片路径不存在,替换问题图片
  8. 大商创x支持mysql版本_大商创x全面升级2.0,匠心打造b2b2c多用户商城系统
  9. 计算机信息与科学学院青协,武汉东湖学院计算机科学学院青年志愿者协会
  10. linux上远程文件传输工具 scp sz rz