[TensorFlow] Python 从基础到项目学习笔记No.2

TensorFlow搭建深层神经网络

鉴于之前彻底的讲过神经网络的具体内容,本笔记就不过多赘述神经网络的构造了,只针对TensorFlow内部算法与函数介绍。

TensorFlow内置激活函数

下图为添加了激活函数的两层神经网络:

下面是上图的计算公式与结果:


目前 TensorFlow 提供了7种不同的非线性激活函数, tf.nn.relu 、tf.sigmoid 和 tf.tanh 是
其中比较常用的几个 ,下面是激活函数的使用代码展示:

a = tf.nn.relu(tf.matmul(x, w1) + biases1)
y = tf.nn.relu(tf.matmul(a, w2) + biases2)

TensorFlow损失函数

经典损失函数

分类问题

交叉熵(cross entropy)是常用的评判方法之一,交叉熵刻画了两个概率分布之间的距离,它是分类问题中使用比较广的一种损失函数。比如说真实概率分布p与预测概率分布q之间的交叉熵为:

两个概率分布由于需要取范围为0—1之间,所以一般分类器最后需要Softmax层来归一化全部的输出预测概率,Softmax层的使用方法与输出为:


这样就可以计算出预测的数值与真实数值之间的交叉熵了,代码展示:

import tensorflow as tf

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

'''
下面是上面交叉熵计算公式内部函数意义:
'''

'''1. tf.clip_by_value:将一个张量中的数值限制在一个范围之内'''
v = tf.constant([[1.0, 2.0, 3.0],[4.0, 5.0, 6.0]])
sess = tf.Session()
print(tf.clip_by_value(v, 2.5, 4.5).eval(session = sess))
'''
输出为:
[[2.5 2.5 3. ]
 [4.  4.5 4.5]]
'''

'''2. tf.log:对张量中所有元素依次求对数'''
v = tf.constant([1.0, 2.0, 3.0])
sess = tf.Session()
print(tf.log(v).eval(session = sess))
'''
输出为:
[0.        0.6931472 1.0986123]
'''

'''3. *:直接将两个矩阵通过“*”操作相乘。这个操作不是矩阵乘法,而是元素之间直接相乘。矩阵乘法需要使用 tf.matmul 函数来完成'''
v1 = tf.constant([[1.0, 2.0], [3.0, 4.0]])
v2 = tf.constant([[5.0, 6.0], [7.0, 8.0]])
sess = tf.Session()
print((v1 * v2).eval(session = sess))

print(tf.matmul(v1, v2).eval(session = sess))
'''
输出为:
*:	    [[ 5. 12.]
         [21. 32.]]
matmul: [[19. 22.]
         [43. 50.]]
'''

'''4. tf.reduce_mean:对矩阵整体求平均值'''
v = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
sess = tf.Session()
print(tf.reduce_mean(v).eval(session = sess))
'''
输出为:
3.5
'''

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

cross_entropy=tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y)
回归问题

对于回归问题,最常用的损失函数是均方误差(MSE,mean squared error)。它的定义如下:

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

自定义损失函数

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

所以实现上面损失函数的代码为:

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

'''
下面是上面损失函数计算公式内部函数意义:
'''

'''1. tf.greater:输入是两个张量,此函数会比较这两个输入张量中每一个元素的大小,并返回比较结果'''
v1 = 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]
'''

'''2. tf.where:当选择条件为 True 时,tf.where 函数会选择第二个参数中的值,否则使用第三个参数中的值'''
print(tf.where(tf.greater(v1, v2), v1, v2).eval())
'''
输出为:
[4. 3. 3. 4.]
'''

定义了损失函数之后,下面将通过一个简单的神经网络程序来讲解损失函数对模型训练结果的影响。在下面这个程序中,实现了一个拥有两个输入节点、 一个输出节点,没有隐藏层的神经网络,使用自定义损失函数:

import tensorflow as tf
from numpy.random import RandomState

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.019347 ]
 [1.0428089]]
'''

运行以上代码得到的结果,也就是说得到的预测函数是 1.02 x 1 + 1.04 x 2 1.02x_1+1.04x_2 1.02x1+1.04x2, 这要比 x 1 + x 2 x_1+x_2 x1+x2 大,因为在损失函数中指定预测少了的损失更大(loss_less> loss_more)。通过这个样例可以感受到,对于相同的神经网络,不同的损失函数会对训练得到的模型产生重要影响。

TensorFlow神经网络优化算法

以下代码给出了在 TensorFlow 中如何实现神经网络的训练过程,主要是将数据分成很多个batch,每次在一个batch上进行收敛,更加接近梯度下降的结果。

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:
# 参数初始化。
    ...
    # 迭代的更新参数
    for i in range(STEPS):
        # 准备 batch size 个训练数据。 一般将所有训练数据随机打乱之后再选取可以得到更好的优化效果。
        current_X, current_Y = ...
        sess.run(train_step, feed_dict={x : current_X , y_ : current_Y})

学习率的设置

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

decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_ steps)

其中 decayed_learning_rate 为每一轮优化时使用的学习率,learning_rate 为事先设定的初始学习率,decay_rate 为衰减系数,decay_steps 为衰减速度 。下图随着迭代轮数的增加,学习率逐步降低的过程。 tf.train.exponential_decay 函数可以通过设置参数 staircase选择不同的衰减方式。staircase 的默认值为 False,这时学习率随法代轮数变化的趋势如下图中灰色曲线所示。当 staircase 的值被设置为 True 时, global_step / decay_steps 会被转化成整数。这使得学习率成为一个阶梯函数( staircase function )。下图中黑色曲线显示了阶梯状的学习率。

下面简单写出学习率衰减的代码表示:

global_step= tf.Variable(O)
# 通过 exponential_decay 函数生成学习率。
learning_rate= tf.train.exponential_decay(0.1, global_step, 100, 0.96, staircase=True)
# 使用指数农减的学习惑。在 minimize 函数中传入 g lobal step 将自动更新
# global_step 参数,从而使得学习率也得到相应更新。
learning_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(...my_loss..., global_step=global_step)

防止过拟合

过拟合就是测试准确率低于训练准确率,究其原因就是他训练的太好了,以至于不能对新增加的数据,类似于噪声的数据进行很好的分类,下面是几种训练的情况:

既然不能将权重训练的过拟合,那么就需要正则化项来削弱过拟合的权重项。

本节主要讲L1正则化与L2正则化,在TensorFlow中,这两个正则化项很易于使用,下面是L2正则化的表示方法(lambda为权重):

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)

下面给出两个正则化项的计算方式:

weights = tf.constant([[1.0, -2.0], [-3.0, 4.0]])
with tf.Session() as sess:
    print(sess.run(tf.contrib.layers.l1_regularizer(.5)(weights)))
    print(sess.run(tf.contrib.layers.l2_regularizer(.5)(weights)))
'''
输出为:
5.0
7.5 # TensorFlow 会将L2的正则化损失值除以 2 使得求导得到的结果更加简洁。
'''

但当神经网络的参数增多之后,这样的方式首先可能导致损失函数 loss 的定义很长, 可读性差且容易出错。但更主要的是,当网络结构复杂之后定义网络结构的部分和计算损失函数的部分可能不在同一个函数中,这样通过变量这种方式计算损失函数就不方便了。为了解决这个问题,可以使用 TensorFlow 中提供的集合(collection )。以下代码给出了通过集合计算一个 5 层神经网络带 L2 正则化的损失函数的计算方法:

import tensorflow as tf

# 获取-层神经网络边上的权重,并将这个权重的 L2 正则化损失加入名称为’ losses ’的集合中
def get_weight(shape, lamda):
    # 生成一个变量。
    var = tf.Variable(tf.random_normal(shape), dtype = tf.float32)
    # add to collect 工。n 函数将这个新生成变量的 L2 正则化损失项加入集合。这个函数的第一个参数 ’ losses ’ 是集合的名字,第二个参数是要加入这个集合的内容。
    tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(lamda)(var))
    # 返回生成的变量。
    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, 10, 10, 1]
# 神经网络的层数。
n_layers = len(layer_dimension)

# 这个变量维护前向传播时最深层的节点,开始的时候就是输入层。
cur_layer = x
# 当前层的节点个数。
in_dimension = layer_dimension[0]
# 通过一个循环来生成 5 层全连接的冲经网络结构。
for i in range(1, n_layers):
    # layer dimension[i]为下一层的节点个数。
    out_dimension = layer_dimension[i]
    # 生成当前层中权重的变量,并将这个变量的 L2 正则化损失J1n入计算图上的 :f.1.1合。
    weight = get_weight((in_dimension, out_dimension) , 0.001)
    bias = tf.Variable(tf.constant(0.1, shape=[out_dimension]))
    # 使用 Re LU 激活函数。
    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_collectio口返回一个列表,这个列表是所有这个集合中的元素。在这个样例中,这些元素就是损失函数的不同部分,将它们加起来就可以得到最终的损失函数。
loss = tf.add_n(tf.get_collection('losses'))

小结

  1. 激活函数部分以relu激活函数为主,记住激活函数的用法。
  2. 损失函数方面,针对不同的问题使用不同的损失函数,例如分类问题的交叉熵损失函数可以使用 tf.nn.softmax_cross_entropy_with_logits 直接得到。
  3. 自定义学习率衰减函数tf.train.exponential_decay可以根据训练的epoch自动更新学习速率,是个很好的选择。
  4. 一般要在最终的loss上增添正则化项,以达到防止过拟合的效果,一般选择L2正则化为主,也就是tf.contrib.layers.l2_regularizer。
  5. 因为不能一直在末尾增添每个权重项的正则化损失,那么就将他们的loss加入集合内,也就是TensorFlow的tf.add_to_collection语句,将所有权重的正则化项与最终的损失函数在集合内相加,达到代码的最优可读性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值