Tensorflow网络优化

本文介绍了TensorFlow中网络优化的关键技术,包括损失函数——均方误差和交叉熵,自定义损失函数的实现,学习率的设置与指数衰减策略,滑动平均增强模型泛化性,以及正则化防止过拟合。通过实例深入解析了这些概念,为神经网络的训练提供指导。
摘要由CSDN通过智能技术生成

本文来自于北京大学软件与微电子学院曹健老师的课程:人工智能实践(Tensorflow笔记)整理而成,加上少量私货。
本文约定:全大写加下划线拼接的变量为模型中的超参数,需要在代码起始处人为定义(遵循Python常量定义规则)

1. 损失函数(loss)

损失函数(loss):预测值(y)与已知答案(y_)的差距。
神经网络(Neural Network, NN)优化的目标通常是loss最小。常用的有三种损失:均方误差(Mean Squared Error, MSE),交叉熵(Cross Entropy, CE)和自定义损失函数。

1.1 均方误差

M S E ( y _ , y ) = ∑ i = 1 n ( y − y _ ) 2 n MSE\left( y\_,y\right) =\dfrac {\sum ^{n}_{i=1}\left( y-y\_\right) ^{2}}{n} MSE(y_,y)=ni=1n(yy_)2

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

栗子
现在需要预测酸奶的日销量y。变量x1和x2是影响日销量的因素。
建模前,已搜集到训练数据有:每天的x1,x2和真实销量y_。显然最佳情况即产量=销量)。
拟造数据集X,Y_:y_=x1+x2 噪声:-0.05 ~ +0.05,拟合可以预测销量的函数。

#coding:utf-8
import tensorflow as tf
import numpy as np

BATCH_SIZE = 8
SEED = 1234

rdm = np.random.RandomState(SEED)
X = rdm.rand(32, 2)
# rdm.rand()生成0-1之间的随机数
Y_ = [[x1+x2+(rdm.rand()/10.0-0.05)] for (x1, x2) in X]

# 搭建神经网络
x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
w1 = tf.Variable(tf.random_normal([2, 1], stddev=1, seed=1))
y = tf.matmul(x, w1)

# 损失函数
loss_mse = tf.reduce_mean(tf.square(y_-y))
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(loss_mse)

# 生成会话, 开始训练
with tf.Session() as sess:
    init_op = tf.global_variables_initializer()
    sess.run(init_op)
    STEPS = 20000
    for i in range(STEPS):
        start = (i * BATCH_SIZE) % 32
        end = start + BATCH_SIZE
        sess.run(train_step, feed_dict={x: X[start: end], y_: Y_[start: end]})
        if i % 500 == 0:
            print("After %d training steps, w1 is: " % i)
            print(sess.run(w1), '\n')
    print("Final w1 is: \n", sess.run(w1))

你也可以试试另外两个优化器

train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)
train_step = tf.train.MomentumOptimizer(learning_rate, momentum).minimize(loss)
train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss)

1.2 交叉熵

交叉熵(Cross Entropy, CE):表示两个概率分布之间的(某种)距离。
H ( y _ , y ) = − ∑ y _ ∗ log ⁡ y H\left( y\_,y\right) =-\sum y\_*\log y H(y_,y)=y_logy

栗子
二分类情况下,已知答案y_=(1,0),现有两个预测结果y1=(0.6,0.4) y2=(0.8,0.2),哪一个更接近正确答案?
H1((1,0),(0.6,0.4))=…=0.222
H2((1,0),(0.8,0.2))=…=0.097
根据交叉熵的结果,认为y2预测地更准确。

需要用到

# 当y小于1e-12时都为1e-12, 大于1时均为1, 类似一个relu函数
ce = -tf.reduce_mean(y_*tf.log(tf.clip_by_value(y, 1e-12, 1.0)))

实际情况中,为了让输出层为n分类的n个输出(y1,y2,…,yn)结果满足概率分布的充要条件,即
( 1 ) ∀ x , P ( x ) ∈ [ 0 , 1 ] ( 2 ) ∑ x P ( x = x ) = 1 \begin{aligned}\left( 1\right) \forall x,P\left( x\right) \in \left[ 0,1\right] \\ \left( 2\right) \sum _{x}P\left( x=x\right) =1\end{aligned} (1)x,P(x)[0,1](2)xP(x=x)=1
引入了softmax函数
s o f t m a x ( y i ) = e y i ∑ j = 1 n e y i softmax \left( y_{i}\right) =\dfrac {e^{y_{i}}}{\sum ^{n}_{j=1}e^{y_{i}}} softmax(yi)=j=1neyieyi

# tf.argmax(input,axis)根据axis取值的不同返回每行或者每列最大值的索引, 0-行, 1-列
# 可以用这两行代替上面的一行
ce = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
cem = tf.reduce_mean(ce)

1.3 自定义损失函数

还是酸奶的栗子
如果预测商品销量,预测多了,会损失成本;预测少了,损失利润,如果利润≠成本,则MSE产生的loss无法保证利益最大化。
这里假设酸奶成本(COST)为1元,利润(PROFIT)为9元。
预测少了则损失利润9元,预测多了则损失成本1元。显然,预测少了的情况下损失更大,希望生成的预测往多了预测。

自定义损失函数:
l o s s ( y _ , y ) = ∑ n f ( y _ , y ) loss\left( y\_,y\right) =\sum _{n}f\left(y\_,y\right) loss(y_,y)=nf(y_,y)
f ( y _ , y ) = { P R O F I T ∗ ( y _ − y ) , y < y _ C O S T ∗ ( y − y _ ) , y ≥ y _ f\left( y\_,y\right) =\begin{cases}PROFIT\ast \left( y\_-y\right) ,y <y\_\\ COST\ast \left( y-y\_\right) ,y\geq y\_\end{cases} f(y_,y)={PROFIT(y_y),y<y_COST(yy_),yy_
需要用到

loss = tf.reduce_sum(tf.where(tf.greater(y, y_), COST(y-y_), PROFIT(y_-y)))

API说明如下
类似excel的if函数

2. 学习率(learning_rate):参数每次更新的幅度

w n + 1 = w n − l e a r n i n g _ r a t e ∗ ∇ w_{n+1}=w_{n}-learning\_rate*\nabla wn+1=wnlearning_rate

栗子
损失函数 l o s s = ( w + 1 ) 2 loss=(w+1)^2 loss=(w+1)2,梯度 ∇ = 2 w + 2 \nabla=2w+2 =2w+2
可以自己演算下 w w w初始化为5,学习率为0.2时参数迭代的变化。

这里不加证明直接引用结论:
学习率过大会引起参数震荡,不收敛
学习率过小收敛速度慢
由此引入了指数衰减学习率
l e a r n i n g _ r a t e = l e a r n i n g _ r a t e _ b a s e ∗ l e a r n i n g _ r a t e _ d e c a y g l o b a l _ s t e p l e a r n i n g _ r a t e _ s t e p learning\_rate=learning\_rate\_base * learning\_rate\_decay^{\frac{global\_step}{learning\_rate\_step}} learning_rate=learning_rate_baselearning_rate_decaylearning_rate_stepglobal_step
l e a r n i n g _ r a t e learning\_rate learning_rate:学习率基数,也成为学习率初始值
l e a r n i n g _ r a t e _ d e c a y learning\_rate\_decay learning_rate_decay:学习率衰减率,在0到1之间
g l o b a l _ s t e p global\_step global_step:运行了几轮BATCH_SIZE
l e a r n i n g _ r a t e _ s t e p learning\_rate\_step learning_rate_step:多少轮更新一次学习率,等于总样本数 / BATCH_SIZE

# staircase为true时LEARNING_RATE_STEP取整数, 学习率阶梯型递减, 否则连续递减
learning_rate = tf.train.exponential_decay(LEARNING_RATE_BASE,
                                           global_step, 
                                           LEARNING_RATE_STEP,
                                           LEARNING_RATE_DECAY,
                                           staircase=True)

3. 滑动平均(影子值)

概念:记录了每个参数一段时间内过往值的平均,增加了模型的泛化性。
针对所有的参数,即权重 w w w b b b(像是给参数增加了影子,当参数变化则影子带着之前的信息滞后更新)。

影子 = 衰减率 * 影子 + (1 - 衰减率)* 参数

影子初值 = 参数初值
衰减率   = m i n { M O V I N G _ A V E R A G E _ D E C A Y , 1 + 轮 数 10 + 轮 数 } \ = min\{MOVING\_AVERAGE\_DECAY,\frac{1+轮数}{10+轮数}\}  =min{MOVING_AVERAGE_DECAY,10+1+}

下面是一个栗子。可以看出,当轮数较大的时候,权重是偏向滑动平均的(即历史值)。在这里插入图片描述

# MOVING_AVERAGE_DECAY: 衰减率, global_step: 当前轮数
ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
# 对变量这个list应用ema方法
ema_op = ema.apply(tf.trainable_variables())
# ema.average(a): 查看变量a的滑动平均值
with tf.control_dependencies([train_step, ema_op]):
    train_op = tf.no_op(name='train')

4. 正则化

WHY:缓解训练出的参数过拟合现象。
WHAT:在损失函数中引入模型复杂度。
HOW:给权重 w w w增加权值,弱化训练数据的噪声(一般不正则化偏置 b b b)。

l o s s = l o s s ( y , y _ ) + R E G U L A R I Z E R ∗ l o s s ( w ) loss=loss(y,y\_) + REGULARIZER * loss(w) loss=loss(y,y_)+REGULARIZERloss(w)

l o s s ( y , y _ ) loss(y,y\_) loss(y,y_):模型中所有参数的损失函数,如交叉熵,均方误差等
R E G U L A R I Z E R REGULARIZER REGULARIZER:正则化系数,为一超参数
l o s s ( w ) loss(w) loss(w):需要正则化的参数

一般地,会用到L1正则和L2正则。

l o s s L 1 ( w ) = ∑ i ∣ w i ∣ loss_{L1}(w)=\sum _{i}\left| w_{i}\right| lossL1(w)=iwi
l o s s L 1 ( w ) = ∑ i ∣ w i 2 ∣ loss_{L1}(w)=\sum _{i}\left| w_{i}^2\right| lossL1(w)=iwi2

# tf.add_to_collection: 把value加到名为name的变量中去
tf.add_to_collection(name='losses', value=tf.contrib.layers.l2_regularizer(REGULARIZER)(w))
loss = cem + tf.add_n(tf.get_collection('losses'))

关于正则化的知识可以详细看下面的文章(建议从是什么,为什么,有哪些性质去把握)
正则化的理解
机器学习中常常提到的正则化到底是什么意思?
(待补充)

5. 总结

搭建神经网络固定套路(推荐使用模块化的代码结构,方便后期维护):

  1. 生成数据集
  2. 前向传播:设计结构,搭建网络
package forward

# 定义前向传播过程
def forward(x, regularizer):
    w =
    b =
    y =
    return y

# 获取正则化的w
def get_weight(shape, regularizer):
    w = tf.Variable(  )
    tf.add_to_collection(name='losses', tf.contrib.layers.l2_regularizer(REGULARIZER)(w))
    return w

# 获取b
def get_bias(shape):
    b = tf.Variable(  )
    return b
  1. 反向传播:训练网络,优化参数
package backward

def forward(x, regularizer):
    x = tf.placeholder(  )
    y_ = tf.placeholder(  )
    y = forward.forward(x, REGULARIZER)
    global_step = tf.Variable(0, trainable=False)

    # 定义损失函数以及正则化
    loss = # 根据需要自己定义损失函数, 再+tf.add_n(tf.get_collection('losses'))

    # 指数衰减学习率
    learning_rate = tf.train.exponential_decay(
        LEARNING_RATE_BASE,
        global_step,
        数据集总样本数 / BATCH_SIZE,
        LEARNING_RATE_DECAY,
        staircase=True
    )

    # 滑动平均
    ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
    ema_op = ema.apply(tf.trainable_variables())
    with tf.control_dependencies([train_step, ema_op]):
        train_op = tf.no_op(name='train')
        
    with tf.Session() as sess:
        init_op = tf.global_variables_initializer()
        sess.run(init_op)
        
        for i in range(STEPS):
            sess.run(train_step, feed_dict={x:  , y_:  })
            if i % 轮数 == 0:
                print(...)
                

if __name__ == '__main__':
    backward()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值