0
点赞
收藏
分享

微信扫一扫

【Tensorflow】TensorFlow深层神经网络学习笔记

TensorFlow深层神经网络学习笔记

1. 深度学习与深层神经网络

深度学习是一类通过多层非线性变换对高复杂性数据建模算法的合集。 深度学习有两个重要的特性——多层非线性。所以基本上可以认为深度学习是深层神经网络的代名词。

1.1 线性模型的局限性

在线性模型中,模型的输出为输入的加权和一个线性模型中通过输入得到输出的函数被称为一个线性变换。在之前的对于神经网络的介绍中,采用了前向传播算法,是一个线性模型线性模型的特点是任意线性模型的组合都是一个线性变换,这样即使神经网络有多层,他和单层神经网络也没有什么区别。这也正是线性模型的局限之处,也是为什么深度学习要强调非线性。

线性模型能解决线性可分的问题,而深度学习要解决的是复杂问题。所谓复杂问题,至少是无法通过直线(或者高维平面)划分的。

1.2 激活函数实现去线性化

如果将每一个神经元(也就是神经网络中的节点)的输出通过一个非线性函数,那么整个神经网络的模型也就不再是线性的了。这个非线性函数就是激活函数。
TensorFlow提供了7种不同的非线性激活函数,最常见的3种是:tf.nn.relutf.sigmoidtf.tanh。当然TensorFlow也支持使用自己定义的激活函数。

1.3 多层网络解决异或运算

多层可以直接解决异或问题,而单层无法完成。
感知机可以简单地理解为单层的神经网络,感知机会先将输入进行加权和,然后再通过激活函数最后得到输出。这个结构就是一个没有隐藏层的神经网络。
下面这张图是仅仅用感知机去划分的结果,分类不明显:
感知机
当加入隐藏层之后,抑或问题就可以得到很好的解决。下面这张是使用ReLU并循环141次的结果,分类效果明显:
ReLU分类

2. 损失函数定义

神经网络的优化目标通过损失函数(loss function)来定义损失函数依靠预测结果和期望结果之间差异来判定最终分类情况。实际情况中会根据实际应用选择分类情况。

2.1 经典损失函数

分类问题和回归问题是监督学习的两大种类,这一部分重点介绍分类问题和回归问题的经典损失函数。

分类问题希望解决的是将不同的样本分到事先定义好的类别中。通过神经网络解决多分类问题通常方法是设置个输出节点,为类别个数。对于每一个样例,神经网络得到一个n维数组作为输出结果,如果一个样本属于类别,那么对应输出节点的输出值应该为1,如识别数字1,结果应该越接近[0,1,0,0,0,0,0,0,0,0]越好。

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

交叉熵是一个信息论中的概念,原本用于计算平均编码长度的。给定两个概率分布 p p p q q q,通过 q q q来表示 p p p的交叉熵为: H ( p , q ) = 0 ∑ x p ( x ) log ⁡ q ( x ) H(p,q)=0\sum_{x}p(x)\log{q(x)} H(p,q)=0xp(x)logq(x)
注意交叉熵刻画的是两个概率分布之间的距离。然而神经网络输出却不一定是一个概率分布(即要求概率均大于0且概率之和必须是1)。所以经常使用Softmax回归是一个常用的方法,它将神经网络前向传播的结果变成概率分布
通过Softmax层将神经网络输出变成一个概率分布
概率论领域中,Softmax函数又称归一化函数。
假设原始的神经网络输出为 y 1 , y 2 , . . . , y n y_1,y_2,...,y_n y1,y2,...,yn,那么经过Softmax回归处理之后的输出为:
s o f t m a x ( y ) i = y i ′ = e y i ∑ j = 1 n e y j softmax(y)_i=y'_i=\frac{e^{y_i}}{\sum_{j=1}^n e^{y_j}} softmax(y)i=yi=j=1neyjeyi
原始神经网络的输出被用作置信度来生成新的输出,而新的输出满足概率分布的所有要求。这样就可以通过交叉熵来计算预测的概率分布和真实答案的概率分布之间的距离了,也就是说交叉熵越小,两个概率分布越接近

TensorFlow中实现交叉熵代码如下:

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

其中,y_代表正确结果,y代表预测结果。通过tf.clip_by_value函数可以将一个张量中的数值限制在一个范围之内,这样避免一些运算错误。tf.log函数完成了对张量中所有元素依次求对数的功能。矩阵乘法需要使用tf.matmul函数来完成。

因为交叉熵一般和Softmax回归一起使用,所以TensorFlow对这两个功能统一封装,提供了tf.nn.softmax_cross_entropy_with_logits()。例如使用softmax回归后的交叉熵损失函数:

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

y代表原始神经网络的输出结果,y_指的是正确答案。通过这样一个命令就可以得到使用了softmax回归之后的交叉熵。在只有一个正确答案的分类问题中,TensorFlow提供了tf.nn.sparse_softmax_cross_entropy_with_logits()函数来进一步加速计算过程(后面会用到)。

与分类问题不同,回归问题需要解决的是对具体数值的预测。这些问题需要预测的不是一个实现定义好的类别,而是一个任意实数。解决回归问题的神经网络一般只有一个输出节点,这个节点的输出值就是预测值。对于回归问题,最常用的损失函数是均方误差(MSE,mean squared error),定义如下:
M S E ( y , y ′ ) = ∑ i = 1 n ( y i − y i ′ ) 2 n MSE(y,y')=\frac{\sum_{i=1}^n (y_i-y'_i)^2}{n} MSE(y,y)=ni=1n(yiyi)2
其中 y i y_i yi为一个batch中第 i i i个数据的正确答案,而 y i ′ y'_i yi为神经网络给出的预测值。使用TensorFlow实现均方误差损失函数:

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

其中,y代表了神经网络的输出答案,y_代表了标准答案。

2.2 自定义损失函数

TensorFlow不仅支持经典的损失函数,还可以优化任意的自定义损失函数,使得神经网络优化的结果更接近实际需求。

例如,为了最大化预期利润,需要将损失函数和利润直接联系起来,损失函数定义的是损失。要将利润最大化,定义的损失函数应该刻画成本或者代价。 一个产品成本一元,利润10元,那么少预测一个就少赚10元,多预测一个少赚1元,如果神经网络模型是最小化的均方误差,就无法预测最大化收益,我们可以定义下列函数:
自定义损失函数
TensorFlow代码实现:

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

以上代码中tf.greater的输入是两个张量,此函数会比较这两个输入张量中每一个元素的大小,并返回比较结果。当张量维度不一样时,TensorFlow会进行类似NumPy广播(broadcasting)操作。第一个为选择条件依据,True时,tf.where函数会选择第二个参数中的值,False使用第三个参数中的值。 代码如下:

import tensorflow._api.v2.compat.v1 as tf
tf.disable_v2_behavior()

v1 = tf.constant([1.0, 2.0, 3.0, 4.0])
v2 = tf.constant([4.0, 3.0, 2.0, 1.0])

sees = tf.InteractiveSession()
print(tf.greater(v1, v2).eval())
# 输出[false flase true true]

print(tf.where(tf.greater(v1, v2), v1, v2).eval())
# 输出[4. 3. 3. 4.],类似?=运算符
sees.close()

在定义了损失函数之后,下面通过一个简单的神经网络程序来讲解损失函数对模型训练结果的影响。

import tensorflow._api.v2.compat.v1 as tf
from numpy.random import RandomState

tf.disable_v2_behavior()

batch_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)
# 设置回归的正确值为两个输入的和加上一个随机量
# 之所以要加上一个随机量,是为了加入不可预测的噪声,否则不同损失函数的意义就不大
# 因为不同损失函数都会在能完全预测正确的时候最低。一般来说噪声为一个均值为0的小量,所以这里设置噪声为-0.05~0.05的随机数
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 = 5000
    for i in range(STEPS):
        start = (i * batch_size) % dataset_size
        end = 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.0193471]
 [1.0428091]]

3. 神经网络优化算法

具体介绍通过反向传播算法(backpropagation)梯度下降算法(gradient decent)调整参数取值。梯度下降算法主要用于优化单个参数的取值,而反向传播算法给出了一个高效的方式在所有参数上使用梯度下降算法,使模型在训练数据上损失函数尽可能小

3.1 梯度下降法

梯度下降算法是最常用的神经网络优化方法。参数的梯度可以通过求偏导实现,对于参数 θ \theta θ,梯度为 ∂ J ( θ ) ∂ θ \frac{\partial J(\theta )}{\partial \theta} θJ(θ)。有了梯度,还需要定义一个学习率 η \eta η(learning rate)来定义每次参数更新的幅度。

从直观上理解,可以认为学习率定义的就是每次参数移动的幅度。通过参数的梯度和学习率,参数的新公式为 θ n + 1 = θ n − η ∂ J ( θ n ) ∂ θ n \theta_{n+1}=\theta_n-\eta \frac{\partial J(\theta_n)}{\partial \theta_n} θn+1=θnηθnJ(θn)

神经网络的优化过程可以分为两个阶段

  1. 通过前向传播算法得到预测值,并将预测值和真实值做对比得到两者之间的差距
  2. 通过反向传播算法计算损失函数对每一个参数的梯度再根据梯度和学习率使用梯度下降法更新每一个参数

注意:梯度下降算法并不能保证被优化的函数达到全局最优解。在训练神经网络时,参数的初始值会很大程度影响最后的结果。只有当损失函数是凸函数时,梯度下降算法才能达到全局最优解

另外,梯度下降算法还有一个问题是训练时间过长。为了加速训练过程,可以使用随机梯度下降算法(stochastic gradient descent)。这个算法不是在全部训练数据上的损失函数,而是在每一轮迭代中,随机优化某一条训练数据上的损失函数,这样速度就大大加快了。它的问题在于,某条数据上的损失函数更小并不代表全部数据上的损失函数最小,因此使用这种方法可能甚至达不到局部最优。

所以,一般采用这两种方法的折中——每次计算一小部分训练数据的损失函数,称之为batch。通过矩阵运算,每次使用一个batch可以大大减小收敛所需要的迭代次数,同时可以使收敛到的结果更加接近梯度下降的效果。
下面是TensorFlow中实现神经网络的训练过程:

import tensorflow._api.v2.compat.v1 as tf
tf.disable_v2_behavior()

batch_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:
    tf.global_variables_initializer().run() # 参数初始化
    # 迭代更新参数
    for i in range(Steps):
        # 准备batch_size个训练数据,一般将所有训练数据随机打乱后再选取可以得到
        # 更好的优化效果
        sess.run(train_step,feed_dict={x:X[start:end],y_:Y[start:end]})

4. 神经网络进一步优化

  • 通过指数衰减的方法设置梯度下降算法的学习率(调整的步长)。通过指数衰减的学习率既可以让模型在训练的前期快速接近较优解,又可以保证模型在训练后期不会有太大的波动。
  • 过拟合问题。过拟合是一个常见的问题。
  • 滑动平均模型。该模型会将每一轮迭代得到的模型综合起来,从而使得最终得到的模型更加健壮(robust)

4.1 学习率的设置

学习率决定了参数每次更新的幅度,幅度如果过大会导致参数在极优值两侧来回移动。相反,学习率过小会大大降低优化速度。因此学习率不能过大也不能太小。

为了解决学习率的问题,TensorFlow提供了一种更加灵活的学习率设置方法——指数衰减法tf.train.exponential_decay函数实现了指数衰减学习率。它可以先使用较大的学习率来快速获得一个比较优的解,然后随着迭代的继续逐步减小学习率,使得模型在训练后期更加稳定

tf.train.exponential_decay可以通过设置参数staircase选择不同的衰减方式。staircase默认值False,设置为True时,global_step/decay_steps会被转化成整数,使得学习率会成为一个阶梯函数(staircase function)。这样的设置下,decay_steps通常代表了完整的使用一遍训练数据所需要的迭代次数。这个迭代轮数也就是总训练样本数除以每一个batch中的训练样本数。这种设置的常用场景是每完整地过完一遍训练数据,学习率就减小一次。 这可以使得训练数据集中的所有数据对模型训练有相等的作用。
代码实现:

decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)

其中,decayed_learning_rate为每一轮优化时使用的学习率,learning_rate为事先设定的初始学习率,decay_rate为衰减系数,decay_steps为衰减速度。
下面给出一段代码演示如何在TensorFlow中使用tf.train.exponential_decay函数:

global_step=tf.Variable(0)
 
# 通过exponential_decay函数生成学习率
# learning_rate = tf.train.exponential_decay(初始学习率, global_step, 轮数, 学习率乘的倍数(0.96), staircase=True)
learning_rate = tf.train.exponential_decay(0.1,global_step,100,0.96,staircase=True)
 
# 使用指数衰减的学习率。在,minmize函数中传入global_step将自动更新global_step参数,使学习率额更新
learning_step = tf.train.GradientDecentOptimizer(learning_rate).minmize(...my loss..., global_step=global_step)

上面设置初始学习率为0.1,因为staircase=True,即学习率为阶梯函数,所以每训练100轮后学习率乘以0.96。一般,学习率的初始值、衰减系数和衰减速度都是根据经验设置。而且,损失函数下降速度和迭代结束后总损失没必然联系,不能通过前几轮损失函数下降的速度来比较不同神经网络的效果。

4.2 过拟合问题

在真实的应用中,希望通过训练出来的模型对未知的数据给出判断。过拟合指的是模型很好的记忆了每一个训练数据中随机噪声的部分而忘记了要去学习训练数据中通用的趋势
即:过度拟合训练数据中的随机噪声虽然可以得到非常小的损失函数,但是对于未知数据可能无法做出可靠的判断 。
过拟合问题
为了避免过拟合问题,一个常用方法是正则化(regularization)正则化的思想就是在损失函数中加入刻画模型复杂度的指标。假设用于刻画模型在训练数据上表现得损失函数为 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 θ表示一个神经网络中的所有参数,包括边上的权重 w w w和偏置项 b b b。一般来说模型复杂度只由权重 w w w决定,常用的刻画模型复杂度的函数 R ( w ) R(w) R(w)有两种:
(1)L1正则化 R ( w ) = ∣ ∣ w ∣ ∣ 1 = ∑ i ∣ w i ∣ R(w)=||w||_1=\sum_i|w_i| R(w)=w1=iwi
(2)L2正则化 R ( w ) = ∣ ∣ w ∣ ∣ 2 2 = ∑ i ∣ w i ∣ 2 R(w)=||w||_2^2=\sum_i |w_i|^2 R(w)=w22=iwi2
两种正则化方法都是希望通过限制权重的大小,使得模型不能任意拟合训练数据中的随机噪音。但这两种正则化的方法也有很大区别

  • 首先,L1正则化会让参数变得稀疏,而L2正则化不会。所谓参数变得稀疏是指会有更多的参数变为0,这样就可以达到类似特征选取的功能。之所以L2正则化不会,是因为当这个参数很小时,这个参数的平方基本上就可以忽略了,于是模型不会进一步将这个参数调整为0。
  • 其次,L1正则化的计算公式不可导,而L2正则化公式可导。因为在优化时需要计算损失函数的偏导数,所以对含有L2正则化损失函数的优化要更加简介。优化带L1正则化的损失函数要复杂一些。

实践中,常将两种正则化方法结合起来使用: R ( w ) = ∑ i α ∣ w i ∣ + ( 1 − α ) w i 2 R(w)=\sum_i \alpha |w_i|+(1-\alpha)w_i^2 R(w)=iαwi+(1α)wi2

下面代码是TensorFlow带L2正则化的损失函数定义:

w=tf.Variable(tf.random_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)

loss为定义的损失函数,有两个部分组成。第一个部分是均方误差损失函数;第二个部分是正则化,防止模型过度模拟训练数据中的随机噪声。 l a m b d a lambda lambda参数表示了正则化项的权重,也就是 λ \lambda λ w w w为需要计算正则化损失的参数。

TensorFlow提供了tf.contrib.layers.l2_regularizer函数,这个函数可以计算一个给定参数的L2正则化项的值。tf.contrib.layers.l1_regularizer函数可以计算L1正则化项的值。 示例如下:

import tensorflow._api.v2.compat.v1 as tf
tf.disable_v2_behavior()
 
weights=tf.constant([[1.0,2.0],[3.0,4.0]])
with tf.Session() as sess:
    # 输出为(|1|+|-2|+|-3|+|4|)×0.5=5。0.5为正则化项的权重。
    print(sess.run(tf.contrib.layers.l1_regularizer(.5)(weights))) # TensorFlow 2.x之后把contrib这个库取消了
    # 输出为(1^2+(-2)^2+(-3)^2+4^2)/2*0.5=7.5
    print(sess.run(tf.contrib.layers.l2_regularizer(.5)(weights)))

上述过程可以计算简单的神经网络中的正则化的损失函数。但是当神经网络中的参数增多之后,可能会导致损失函数loss的定义很长,可读性差而且容易出错。更糟糕的是,当网络结构变复杂后,定义网络结构的部分和计算损失函数的部分可能不在同一个函数中,这样通过变量这种方式计算损失函数就不方便了。 此时,可以通过TensorFlow提供的集合(collection)。集合可以在一个计算图(tf.Graph)中保存一组实体(比如张量)。
示例:通过集合计算一个5层神经网络带L2正则化的损失函数的计算方法。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @FileName  :test.py.py
# @Time      :2021/12/7 15:39
# @Author    :PangXZ

import tensorflow._api.v2.compat.v1 as tf
import tensorflow.keras.regularizers as regularizers

tf.disable_v2_behavior()


# 获取一层神经网络边上的权重,并将这个权重的L2正则化损失加入名称为'losses'的集合中
def get_weight(shape, lambda1):
    # 生成一个变量
    var = tf.Variable(tf.random_normal(shape), dtype=tf.float32)
    # add_to_collection 函数将这个新生成变量的L2正则化损失项加入集合
    # 这个函数第一个参数losses是集合的名字,第二个参数是要加入这个集合的内容
    tf.add_to_collection('losses', regularizers.l2(lambda1)(var)) # tensorflow2的L2正则化位置进行了调整
    return var


x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
batch_size = 8
# 定义了每一层网络中节点的个数
layer_dimension = [2, 10, 5, 3, 1]
# 神经网络的层数
n_layers = len(layer_dimension)

# 这个变量维护前向传播时最深层的节点,开始的时候就是输入层
cur_layer = x
# 当前层的节点个数
in_dimension = layer_dimension[0]

# 通过一个循环来生成5层全连接的神经网络结构
for i in range(1, n_layers):
    # layer_demension[i]为下一层的节点数
    out_dimension = layer_dimension[i]
    # 生成当前层中权重的变量,并将这个变量的L2正则化损失加入计算图上的集合
    weight = get_weight([in_dimension, out_dimension], 0.003)
    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_dimension[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'))


# 运行结果出错
WARNING:tensorflow:From D:\Installed\Anaconda3\lib\site-packages\tensorflow\python\compat\v2_compat.py:107: disable_resource_variables (from tensorflow.python.ops.variable_scope) is deprecated and will be removed in a future version.
Instructions for updating:
non-resource variables are not supported in the long term

4.3 滑动平均模型

在采用随机梯度下降算法训练神经网络时,使用滑动平均模型在很多应用中都可以提高性能
TensorFlow中提供了tf.train.ExponentialMovingAverage来实现滑动平均模型初始化这个函数时,需要提供一个衰减率。这个衰减率将用于控制模型更新的速率。ExponentialMovingAverage对每一个变量会维护一个影子变量(shadow variable),这个影子变量的初始值就是相应变量的初始值,而每次运行变量更新时,影子变量的值会更新为:
s h a d o w v a r i a b l e = d e c a y × s h a d o w v a r i a b l e + ( 1 − d e c a y ) × v a r i a b l e shadow_variable=decay\times shadow_variable + (1-decay)\times variable shadowvariable=decay×shadowvariable+(1decay)×variable
其中,shadow_variable为影子变量,variable为待更新的变量,decay为衰减率。衰减率决定了模型更新的速度,衰减率越大模型越稳定。一般设为0.999或者0.9999 (非常接近于1的数)。

为了使得模型在训练前期可以更新的更快,ExponentialMovingAverage还提供了num_updates参数来动态设置decay的大小。如果在ExponentialMovingAverage初始化时提供了num_updates参数,那么每次使用的衰减率将是:
m i n { d e c a y , 1 + n u m u p d a t e s 10 + n u m u p d a t e s } min\{decay, \frac{1+num_updates}{10+num_updates}\} min{decay,10+numupdates1+numupdates}

示例:ExponentialMovingAverage如何被使用。

import tensorflow as tf # 适用于tensorFlow v1版本中
 
# 定义一个变量用于计算滑动平均,这个变量的初始值为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:
    tf.global_variables_initializer().run()
    #通过ema.average(v1)获取滑动平均之后变量的取值。在初始化之后变量v1的值和v1的滑动平均都为0
    print(sess.run([v1,ema.average(v1)]))
 
    #更新变量v1的值为5
    sess.run(tf.assign(v1, 5))
    #更新v1的滑动平均值。衰减率为min(0.99,0.1)=0.1
    #v1的滑动平均会被更新为 0.1*0+0.9*5=4.5
    sess.run(maintain_averages_op)
    print(sess.run([v1,ema.average(v1)]))
    #[5.0,4.5]
 
    #更新step的值为10000
    sess.run(tf.assign(step, 10000))
    #更新v1的值为10
    sess.run(tf.assign(v1, 10))
    #更新v1的滑动平均值。衰减率为min(0.99,(1+10000)/(10+10000))=0.99
    #v1的滑动平均会被更新为0.99*4.5+0.01*10=4.555
    sess.run(maintain_averages_op)
    print(sess.run([v1,ema.average(v1)]))
    #[10.0, 4.5549998]
 
    #再次更新滑动平均值,得到的新滑动平均值为 0.99*4.555+0.01*10=4.60945
    sess.run(maintain_averages_op)
    print(sess.run([v1,ema.average(v1)]))
    #[10.0, 4.6094499]

注意:以上代码在TensorFlow v2版本中运行时会出现较多的问题

参考资料

  1. 《TensorFlow:实战Google深度学习框架(第2版)》
举报

相关推荐

0 条评论