Tensorflow学习之调优

机器学习 2017-09-02

深度神经网络中最重要的就是参数调整,tensorflow提供了一些列便捷的方法优化神经网络。

  1. 自定义损失函数 主要通过tf.reduce_sum()实现。
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))  #tf.greater用于比较输入值大小
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)

#生成模拟数据集
rdm = RandomState(1)
X = rdm.rand(128,2)
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) % 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(u"第 %d 次训练后, w1的值是: " % (i))
            print (sess.run(w1), "\n")
    print (u"最终w1的值是: \n", sess.run(w1))

输出是

第 0 次训练后, w1的值是: [[-0.81031823] [ 1.4855988 ]]

第 1000 次训练后, w1的值是: [[ 0.01247112] [ 2.1385448 ]]

第 2000 次训练后, w1的值是: [[ 0.45567414] [ 2.17060661]]

第 3000 次训练后, w1的值是: [[ 0.69968724] [ 1.8465308 ]]

第 4000 次训练后, w1的值是: [[ 0.89886665] [ 1.29736018]]

最终w1的值是: [[ 1.01934695] [ 1.04280889]]

修改损失函数部分,令 loss_less = 10 loss_more = 1 输出是

第 0 次训练后, w1的值是: [[-0.81231821] [ 1.48359871]]

第 1000 次训练后, w1的值是: [[ 0.18643527] [ 1.07393336]]

第 2000 次训练后, w1的值是: [[ 0.95444274] [ 0.98088616]]

第 3000 次训练后, w1的值是: [[ 0.95574027] [ 0.9806633 ]]

第 4000 次训练后, w1的值是: [[ 0.95466018] [ 0.98135227]]

最终w1的值是: [[ 0.95525807] [ 0.9813394 ]]

如果将损失函数设为MSE,将上述代码改为 loss = tf.losses.mean_squarederror(y, y) 输出是

第 0 次训练后, w1的值是: [[-0.81031823] [ 1.4855988 ]]

第 1000 次训练后, w1的值是: [[-0.13337609] [ 1.81309223]]

第 2000 次训练后, w1的值是: [[ 0.32190299] [ 1.52463484]]

第 3000 次训练后, w1的值是: [[ 0.67850214] [ 1.25297272]]

第 4000 次训练后, w1的值是: [[ 0.89473999] [ 1.08598232]]

最终w1的值是: [[ 0.97437561] [ 1.0243336 ]]

可见不同的损失函数对结果会产生影响。

  1. 自定义学习率 假设要最小化函数 y=x*x , 选择初始点x=5。

    import tensorflow as tf
    TRAINING_STEPS = 10
    LEARNING_RATE = 1 #学习率设定
    x = tf.Variable(tf.constant(5, dtype=tf.float32), name="x")
    y = tf.square(x)
    train_op = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(y)
    with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for i in range(TRAINING_STEPS):
        sess.run(train_op)
        x_value = sess.run(x)
        print "在第 %s 迭代后: x%s 是 %f."% (i+1, i+1, x_value) 

    如果将TRAINING_STEPS设为1000,LEARNING_RATE设为0.001,则需要更长的时间收敛。如果使用指数衰减的学习率,在迭代初期得到较高的下降速度,可以在较小的训练轮数下取得不错的收敛程度,这在数据量较大的训练中十分重要。

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_op = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(y, global_step=global_step)
  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)

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()

结果是

1.png

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

def get_weight(shape, lambda1):
    var = tf.Variable(tf.random_normal(shape), dtype=tf.float32)
    tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(lambda1)(var))
    return var

定义一个神经网络:

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]

# 循环生成网络结构
for i in range(1, n_layers):
    out_dimension = layer_dimension[i]
    weight = get_weight([in_dimension, out_dimension], 0.003)
    bias = tf.Variable(tf.constant(0.1, shape=[out_dimension]))
    cur_layer = tf.nn.elu(tf.matmul(cur_layer, weight) + bias)
    in_dimension = layer_dimension[i]

y= cur_layer

# 损失函数的定义。
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'))

如果是训练不带正则项的损失函数mse_loss

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

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

    # 画出训练后的分割曲线       
    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()

结果是

2.png

存在过拟合。

如果是训练带正则项的损失函数loss

# 定义训练的目标函数loss,训练次数及训练模型
train_op = tf.train.AdamOptimizer(0.001).minimize(loss)
TRAINING_STEPS = 40000
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    for i in range(TRAINING_STEPS):
        sess.run(train_op, feed_dict={x: data, y_: label})
        if i % 2000 == 0:
            print("After %d steps, loss: %f" % (i, sess.run(loss, feed_dict={x: data, y_: label})))

    # 画出训练后的分割曲线       
    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})
    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()

结果是

3.png

  1. 使用滑动平均模型

滑动平均模型可以提高模型在测试数据上的健壮性,主要方法是tf.train.ExponentialMovingAverage()

import tensorflow as tf

v1 = tf.Variable(0, dtype=tf.float32)
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)
    print (sess.run([v1, ema.average(v1)]))

    # 更新变量v1的取值
    sess.run(tf.assign(v1, 5))
    sess.run(maintain_averages_op)
    print (sess.run([v1, ema.average(v1)])) 

    # 更新step和v1的取值
    sess.run(tf.assign(step, 10000))  
    sess.run(tf.assign(v1, 10))
    sess.run(maintain_averages_op)
    print (sess.run([v1, ema.average(v1)]))       

    # 更新一次v1的滑动平均值
    sess.run(maintain_averages_op)
    print (sess.run([v1, ema.average(v1)]))

本文由 Tony 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

如果对您有用,您的支持将鼓励我继续创作!