TensorFlow学习_(3)深层神经网络

目录:
1. 什么是深度学习
2. 损失函数
3. 梯度下降算法
4. 学习率
5. 滑动平均模型
6. 过拟合及解决方法

正文:

什么是深度学习(深层神经网络)

  1. 概念
    深度学习:一类通过多层非线性变换对高复杂性数据建模算法的合集。

  2. 线性模型的局限性

    • 线性模型为输出的加权和,线性模型的组合仍为线性;
    • 线性模型只可以解决线性可分问题,绝大多数实际问题是无法通过线性分割的。
    • 导致20世纪70年代神经网络研究的重大低潮原因:感知机无法解决异或问题(简单理解为什么感知机不能解决异或问题
  3. 解决线性局限性的方法:激活函数去线性化

    3.1. 如果将一个神经元的输出通过一个非线性函数,整个神经网络的模型就不再是线性的,这个非线性函数就是激活函数。下图是加入偏置项(bias)和激活函数的神经元示意图。

    加入偏置项和激活函数

    3.2. 常见的激活函数

    • ReLU函数: f(x) = max(x,0)
    • sigmoid函数:sigmoid
    • tanh函数:tanh
  4. 隐藏层
    每通过一次隐藏层会经过一次特征抽取,逐层抽取更高维度的特征。这可以很好的解决异或问题。下图摘自《TensorFlow实战Google深度学习框架》。

    隐藏层

    神经网络的层数概念:隐藏层 + 输出层,输入层为第0层,不计在内。

损失函数

  1. 经典损失函数

    1.1. 交叉熵
    交叉熵的理解请参考:如何通俗的解释交叉熵-CyberRep的回答

    #交叉熵TensorFlow实现
    cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0)))
    
    • y_ 代表正确结果,y 代表预测结果;
    • tf.clip_by_value() 函数将张量的数值限定在一个范围内,可以避免如 log0 的运算错误;
    • tf.log() 函数实现了对张量中所有元素依次求对数的功能;
    • 交叉熵刻画的是两个概率分布之间的距离,然而神经网络的输出不一定是一个概率分布。Softmax回归 可以解决这个问题。在TensorFlow中,他只是一个额外的处理层,假设原始输出为y1, y2, … , yn, 经过Softmax回归处理后输出为:
      softmax
      因为交叉熵一般会与softmax回归一起使用,TensorFlow对他们进行了封装:

      
      # TensorFlow实现
      
      cross_entropy = tf.nn.softmax_cross_entropy_with_logits(y, y_)
      
      # 只有一个正确答案的问题中,使用如下语句加速处理:
      
      cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(y, y_)

    1.2. 均方误差
    预测值和真值差值的平方和的平均数。

    #均方误差TensorFlow实现
    mse = tf.reduce_mean(tf.square(y - y_))
    
  2. 自定义损失函数

    举例:如某商品,成本1元,利润10元,少预测一个就少挣10元,多预测一个就少挣1元。为了最大化利润,定义损失函数如下(y为真实值,y’为预测值):
    CostFunc

    TensorFlow实现:

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

    例程说明损失函数对模型训练结果的影响:

    
    #coding:utf-8
    
    
    import tensorflow as tf
    from numpy.random import RandomState
    
    
    #1. 生成模拟数据集
    
    BATCH_SIZE = 8
    rdm = RandomState(1)
    X = rdm.rand(128, 2)
    
    #设置回归的正确值为两个输入的和加上一个随机量,随机量是为了加入不可预测的噪音。
    
    
    #否则不同损失函数的意义就不大了,因为不同损失函数都在能完全预测正确的时候最低。
    
    
    #一般来说噪音为一个均值为0的小量。
    
    Y = [[x1 + x2 + (rdm.rand() / 10.0 - 0.05)] for (x1, x2) in X]
    
    
    #2. 定义神经网络的相关参数和变量
    
    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)
    
    
    #3. 定义损失函数
    
    
    #定义损失函数使得预测少了的损失大,于是模型应该偏向多的方向预测。
    
    LOSS_LESS = 10
    LOSS_MORE = 1
    loss = tf.reduce_sum(tf.where(tf.greater(y, y_), LOSS_MORE * (y - y_), LOSS_LESS * (y_ - y)))
    train_step = tf.train.AdamOptimizer(0.001).minimize(loss)
    
    
    #4. 训练模型
    
    with tf.Session() as sess:
        init_op = tf.initialize_all_variables()
        sess.run(init_op)
        STEPS = 5000
        for i in range(STEPS):
            start = (i * BATCH_SIZE) % 128
            end = (i * BATCH_SIZE) % 128 + BATCH_SIZE
            sess.run(train_step, feed_dict = {x:X[start:end], y_:Y[start:end]})
    
            if i % 1000 == 0:
                print("After %d training step(s), w1 is" % (i))
                print(sess.run(w1), "\n")
    
        print("Final w1 is:\n", sess.run(w1))
    
    
    #输出结果
    
    """
    After 0 training step(s), w1 is: 
    [[-0.81031823]
     [ 1.4855988 ]] 
    
    After 1000 training step(s), w1 is: 
    [[ 0.01247112]
     [ 2.1385448 ]] 
    
    After 2000 training step(s), w1 is: 
    [[ 0.45567414]
     [ 2.17060661]] 
    
    After 3000 training step(s), w1 is: 
    [[ 0.69968724]
     [ 1.8465308 ]] 
    
    After 4000 training step(s), w1 is: 
    [[ 0.89886665]
     [ 1.29736018]] 
    
    Final w1 is: 
    [[ 1.01934695]
     [ 1.04280889]]
    """
    
    #5. 重新定义损失函数,使得预测多了的损失大,于是模型应该偏向少的方向预测。
    
    LOSS_LESS = 1
    LOSS_MORE = 10
    loss = tf.reduce_sum(tf.where(tf.greater(y, y_), LOSS_MORE * (y - y_), LOSS_LESS * (y_ - y)))
    train_step = tf.train.AdamOptimizer(0.001).minimize(loss)
    
    
    #输出结果
    
    """
    After 0 training step(s), w1 is: 
    [[-0.81231821]
     [ 1.48359871]] 
    
    After 1000 training step(s), w1 is: 
    [[ 0.18643527]
     [ 1.07393336]] 
    
    After 2000 training step(s), w1 is: 
    [[ 0.95444274]
     [ 0.98088616]] 
    
    After 3000 training step(s), w1 is: 
    [[ 0.95574027]
     [ 0.9806633 ]] 
    
    After 4000 training step(s), w1 is: 
    [[ 0.95466018]
     [ 0.98135227]] 
    
    Final w1 is: 
    [[ 0.95525807]
     [ 0.9813394 ]]
    """
    
    
    #6. 定义损失函数为MSE。
    
    loss = tf.reduce_mean(tf.square(y_ - y))
    train_step = tf.train.AdamOptimizer(0.001).minimize(loss)
    
    
    #输出结果
    
    """
    After 0 training step(s), w1 is: 
    [[-0.81031823]
     [ 1.4855988 ]] 
    
    After 1000 training step(s), w1 is: 
    [[-0.13337609]
     [ 1.81309223]] 
    
    After 2000 training step(s), w1 is: 
    [[ 0.32190299]
     [ 1.52463484]] 
    
    After 3000 training step(s), w1 is: 
    [[ 0.67850214]
     [ 1.25297272]] 
    
    After 4000 training step(s), w1 is: 
    [[ 0.89473999]
     [ 1.08598232]] 
    
    Final w1 is: 
    [[ 0.97437561]
     [ 1.0243336 ]]
    """

梯度下降算法

梯度下降算法主要用于优化单个参数的取值,参数更新的公式为:
梯度下降

yita为学习率,即每次参数更新的幅度;J(θ)为损失函数。

  • 需要注意的是,梯度下降算法并不能够保证被优化的函数达到全局最优解,如下图所示:
    非全局最优解
    由此可见,在训练神经网络时,参数的初值会很大程度影响最后得到的结果。只有当损失函数为凸函数时,梯度下降算法才能保证达到全局最优解。

  • 海量数据下,计算所有组训练数据非常耗时。
    随机梯度下降算法:在每一轮迭代中,随即优化某一条训练数据上的损失函数。
    实际应用中,采用梯度下降和随机梯度下降结合的方法:每次计算一个batch的训练数据的损失函数。

学习率的设置

学习率过小,会导致梯度衰减过慢;学习率过大,会导致参数在极优值两侧来回移动,不收敛。

未解决设定学习率问题,TensorFlow提供了指数衰减法,tf.train.exponential_decay 函数,会指数级减小学习率。通过此方法可以先使用较大的学习率快速得到较优的解,后期趋于稳定。
yita

# TensorFlow实现
learning_rate = tf.train.exponentid_decay(state_rate, global_step, decay_steps, decay_rate, staircase)

其中,state_rate为初始学习率,decay_rate为衰减系数,global_step为运行了几轮,decay_steps为每轮的学习步数。staircase为bool值,默认为false,取true时g/d会被转化为整数,学习率将以阶梯形式下降。

实例说明学习率设置对优化结果的影响:

#coding:utf-8
#假设我们要最小化函数  y=x^2, 选择初始点  x_0=5
#1. 学习率为1的时候,x在5和-5之间震荡。
import tensorflow as tf
TRAINING_STEPS = 10
LEARNING_RATE = 1.0

x = tf.Variable(tf.constant(5, dtype=tf.float32), name="x")
y = tf.square(x)

train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(y)

with tf.Session() as sess:
    sess.run(tf.initialize_all_variables())
    for i in range(TRAINING_STEPS):
        sess.run(train_step)
        x_value = sess.run(x)
        print("After %s iteration(s): x%s is %f."% (i+1, i+1, x_value))

# 打印结果
"""
After 1 iteration(s): x1 is -5.000000.
After 2 iteration(s): x2 is 5.000000.
After 3 iteration(s): x3 is -5.000000.
After 4 iteration(s): x4 is 5.000000.
After 5 iteration(s): x5 is -5.000000.
After 6 iteration(s): x6 is 5.000000.
After 7 iteration(s): x7 is -5.000000.
After 8 iteration(s): x8 is 5.000000.
After 9 iteration(s): x9 is -5.000000.
After 10 iteration(s): x10 is 5.000000.
"""
#coding:utf-8
#2. 学习率为0.001的时候,下降速度过慢,在901轮时才收敛到0.823355。
import tensorflow as tf
TRAINING_STEPS = 1000
LEARNING_RATE = 0.001
x = tf.Variable(tf.constant(5, dtype=tf.float32), name="x")
y = tf.square(x)

train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(y)

with tf.Session() as sess:
    sess.run(tf.initialize_all_variables())
    for i in range(TRAINING_STEPS):
        sess.run(train_step)
        if i % 100 == 0:
            x_value = sess.run(x)
            print("After %s iteration(s): x%s is %f."% (i+1, i+1, x_value))

# 打印结果
"""
After 1 iteration(s): x1 is 4.990000.
After 101 iteration(s): x101 is 4.084646.
After 201 iteration(s): x201 is 3.343555.
After 301 iteration(s): x301 is 2.736923.
After 401 iteration(s): x401 is 2.240355.
After 501 iteration(s): x501 is 1.833880.
After 601 iteration(s): x601 is 1.501153.
After 701 iteration(s): x701 is 1.228794.
After 801 iteration(s): x801 is 1.005850.
After 901 iteration(s): x901 is 0.823355.
"""
#coding:utf-8
#3. 使用指数衰减的学习率,在迭代初期得到较高的下降速度,可以在较小的训练轮数下取得不错的收敛程度。
import tensorflow as tf
TRAINING_STEPS = 100
global_step = tf.Variable(0)
LEARNING_RATE = tf.train.exponential_decay(0.1, global_step, 1, 0.96, staircase=True)

x = tf.Variable(tf.constant(5, dtype=tf.float32), name="x")
y = tf.square(x)
train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(y, global_step=global_step)

with tf.Session() as sess:
    sess.run(tf.initialize_all_variables())
    for i in range(TRAINING_STEPS):
        sess.run(train_step)
        if i % 10 == 0:
            LEARNING_RATE_value = sess.run(LEARNING_RATE)
            x_value = sess.run(x)
            print("After %s iteration(s): x%s is %f, learning rate is %f."% (i+1, i+1, x_value, LEARNING_RATE_value))

# 打印结果
"""
After 1 iteration(s): x1 is 4.000000, learning rate is 0.096000.
After 11 iteration(s): x11 is 0.690561, learning rate is 0.063824.
After 21 iteration(s): x21 is 0.222583, learning rate is 0.042432.
After 31 iteration(s): x31 is 0.106405, learning rate is 0.028210.
After 41 iteration(s): x41 is 0.065548, learning rate is 0.018755.
After 51 iteration(s): x51 is 0.047625, learning rate is 0.012469.
After 61 iteration(s): x61 is 0.038558, learning rate is 0.008290.
After 71 iteration(s): x71 is 0.033523, learning rate is 0.005511.
After 81 iteration(s): x81 is 0.030553, learning rate is 0.003664.
After 91 iteration(s): x91 is 0.028727, learning rate is 0.002436.
"""

滑动平均模型

滑动平均模型(ExponentialMovingAverage)可以使模型在测试数据上更加健壮(robust)。tensorflow 下的 tf.train.ExponentialMovingAverage 需要提供一个衰减率(decay),该衰减率用于控制模型更新的速度,ExponentialMovingAverage 对每一个(待更新训练学习的)变量(variable)都会维护一个影子变量(shadow variable)。影子变量的初始值就是这个变量的初始值,变量值会更新为:

    shadow_variable=decay×shadow_variable+(1−decay)×variable

由上述公式,decay决定了模型的更新速度,decay越大,模型越趋于稳定。ExponentialMovingAverage 还提供了 num_updates 参数来动态设置 decay 的大小:
decay
其中num_updates为神经网络中已经迭代的轮数。

下面以一个实例介绍滑动平均模型用法:

#coding:utf-8
import tensorflow as tf

#1. 定义变量及滑动平均类
# 定义一个32位浮点变量,初始值为0.0  这个代码就是不断更新v1参数,优化v1参数,滑动平均做了个v1的影子。
v1 = tf.Variable(0, dtype=tf.float32)
# 定义num_updates(模拟NN中迭代的迭代轮数),初始值为0,不可被优化(训练),这个参数不训练
steps = tf.Variable(0, trainable=False)
# 实例化滑动平均类(class),给删减率delay0.99,当前轮数steps
ema = tf.train.ExponentialMovingAverage(0.99, steps)
# 定义更新变量滑动平均的操作,这里需要给定一个列表,
# 每次运行刷新列表中的元素被更新,即每次sess.run(maintain_avaerages_op)更新v1。
ema_op = ema.apply([v1])

#2. 查看不同迭代中变量取值的变化。

with tf.Session() as sess:

    # 初始化
    init_op = tf.initialize_all_variables()
    sess.run(init_op)
    #ema.average(v1)获取滑动平均后的取值,初始值为0
    print(sess.run([v1, ema.average(v1)])) 
    # 输出: [0.0, 0.0]

    # 更新变量v1的值为5
    sess.run(tf.assign(v1, 5))
    sess.run(ema_op)
    print(sess.run([v1, ema.average(v1)]))
    # 输出: [5.0, 4.5]

    # 更新模拟步数step的值为10000,更新v1值为10
    sess.run(tf.assign(steps, 10000))
    sess.run(tf.assign(v1, 10))
    # 此时decay = min{0.99, (1 + step) / (10 + step) = 0.1} = 0.1
    sess.run(ema_op)
    print(sess.run([v1, ema.average(v1)]))
    # 输出: [10.0, 4.5549998]

    # 更新一次v1的滑动平均值
    sess.run(ema_op)
    print(sess.run([v1, ema.average(v1)]))
    # 输出: [10.0, 4.6094499]

    sess.run(ema_op)
    print(sess.run([v1, ema.average(v1)]))
    # 输出: [10.0, 4.6633554]

    sess.run(ema_op)
    print(sess.run([v1, ema.average(v1)]))
    # 输出: [10.0, 4.7167215]

过拟合

过拟合:当一个模型过为复杂后,他可以很好的“记忆”每个训练数据中随机噪音的部分而忘记去“学习”训练数据中的通用趋势。如下图:
过拟合

未解决此问题,常用正则化的方法:在损失函数中人为加入刻画模型复杂程度的指标。通过限制权重大小,使得模型不能任意拟合训练数据中的随机噪音。

J(θ) = J(θ) + λR(w)

其中R(w)刻画模型的复杂程度,λ表示模型复杂损失在总损失中的比例。

常用R(w)有两种:

  • L1正则化:
    L1正则化

    loss = tf.reduce_mean(tf.square(y_ - y)) + tf.contrib.layers.l1_regularizer(lamda)(w)
    
  • L2正则化:
    L2正则化

    loss = tf.reduce_mean(tf.square(y_ - y)) + tf.contrib.layers.l1_regularizer(lamda)(w)
    

两种正则化方式的区别:

  • L1正则化会让参数变得更稀疏,即会有更多的参数变为0,可以达到类似特征筛选的目的。L2不会有此功能,因为当参数很小时,如0.01,此参数的平方基本上可以忽略,于是模型不会进一步将此参数调为0.
  • L1正则化计算公式不可导,L2可导。因为在优化时需要计算损失函数的偏导数,所以L2优化更加便捷。

实例说明正则化解决过拟合问题:

# coding:utf-8
# 通过集合计算一个5层神经网络,解决非线性分类问题

#1. 生成模拟数据集。

import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np

data = []
label = []
np.random.seed(0)

# 以原点为圆心,半径为1的圆把散点划分成红蓝两部分,并加入随机噪音。
for i in range(150):
    x1 = np.random.uniform(-1,1)
    x2 = np.random.uniform(0,2)
    if x1**2 + x2**2 <= 1:
        data.append([np.random.normal(x1, 0.1),np.random.normal(x2,0.1)])
        label.append(0)
    else:
        data.append([np.random.normal(x1, 0.1), np.random.normal(x2, 0.1)])
        label.append(1)

# hstack()按列顺序将数组水平堆叠
# reshape()创建改变了尺寸的新数组
# scatter()画散点图
# 具体用法见下文链接
data = np.hstack(data).reshape(-1,2)
label = np.hstack(label)#.reshape(-1, 1)
plt.scatter(data[:,0], data[:,1], c=label,
            cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
plt.show()
nlabel = np.hstack(label).reshape(-1, 1)


#2. 定义一个获取权重,并自动加入正则项到损失的函数。

def get_weight(shape, lambda1):
    var = tf.Variable(tf.random_normal(shape), dtype=tf.float32)
    # tf.add_to_collection将新生成变量的L2正则化损失项加入集合
    tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(lambda1)(var))
    return var

#3. 定义神经网络。

x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
sample_size = len(data)

# 定义网络中每层节点的个数
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):
    # 下一层的节点个数
    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.elu(tf.matmul(cur_layer, weight) + bias)
    # 更新节点个数
    in_dimension = layer_dimension[i]
# 预测值
y= cur_layer

# 损失函数的定义
# 在定义神经网络前向传播的同时已经将所有的L2正则化损失加入了图上的集合
# 这里只需计算刻画模型在训练数据上表现的损失函数并加入集合。
mse_loss = tf.reduce_sum(tf.pow(y_ - y, 2)) / sample_size
tf.add_to_collection('losses', mse_loss)
loss = tf.add_n(tf.get_collection('losses'))

#4. 训练不带正则项的损失函数mse_loss

# 定义训练的目标函数mse_loss,训练次数及训练模型
train_op = tf.train.AdamOptimizer(0.001).minimize(mse_loss)
TRAINING_STEPS = 40000

with tf.Session() as sess:
    tf.initialize_all_variables().run()
    for i in range(TRAINING_STEPS):
        sess.run(train_op, feed_dict={x: data, y_: nlabel})
        if i % 2000 == 0:
            print("After %d steps, mse_loss: %f" % (i,sess.run(mse_loss, feed_dict={x: data, y_: nlabel})))

    # 画出训练后的分割曲线
    xx, yy = np.mgrid[-1.2:1.2:.01, -0.2:2.2:.01]
    grid = np.c_[xx.ravel(), yy.ravel()]
    probs = sess.run(y, feed_dict={x:grid})
    probs = probs.reshape(xx.shape)

    plt.scatter(data[:,0], data[:,1], c=label,
                cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
    plt.contour(xx, yy, probs, levels=[.5], cmap="Greys", vmin=0, vmax=.1)
    plt.show()

#5. 训练带正则项的损失函数loss

# 定义训练的目标函数loss,训练次数及训练模型
train_op = tf.train.AdamOptimizer(0.001).minimize(loss)
TRAINING_STEPS = 40000

with tf.Session() as sess:
    tf.initialize_all_variables().run()
    for i in range(TRAINING_STEPS):
        sess.run(train_op, feed_dict={x: data, y_: nlabel})
        if i % 2000 == 0:
            print("After %d steps, loss: %f" % (i, sess.run(loss, feed_dict={x: data, y_:nlabel})))

    # 画出训练后的分割曲线
    xx, yy = np.mgrid[-1:1:.01, 0:2:.01]
    grid = np.c_[xx.ravel(), yy.ravel()]
    probs = sess.run(y, feed_dict={x:grid})
    print(probs)
    probs = probs.reshape(xx.shape)

plt.scatter(data[:,0], data[:,1], c=label,
            cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
plt.contour(xx, yy, probs, levels=[.5], cmap="Greys", vmin=0, vmax=.1)
plt.show()

初始散点图:
scatter

过拟合分类图:
过拟合

正常分类图:
正常

hstack()函数参考Numpy中stack(),hstack(),vstack()函数详解

reshape()函数参考Python中reshape的用法

scatter 参考PYthon——plt.scatter各参数详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值