tensorflow实战学习笔记(1)

tensorflow提供了三种不同的加速神经网路训练的并行计算模式

(一)数据并行:

(二)模型并行:

(三)流水线并行:

主流深度学习框架对比(2017):

第一章

Tensorflow实现Softmax Regression识别手写数字

 这是深度学习领域一个非常简单的hello world式的项目:

数据集:28x28像素的手写数字组成。

1.在导入mnist 数据集时,会碰到一系列的问题,在这里做一些简要的说明:

(1)导入数据集时报错如下:(图是别地找的,但报错信息就是这样)

碰到这种情况,直接在官网上下载数据集,不用解压,将存放数据集的路径添加到数据读取函数中,就可以解决,具体见后面的代码:

(2)由于我之前用的是cpu版本的tensorflow,在做mnist实验时没有遇到这种情况,后来更换为gpu版本的时,在读取数据时会报错, 具体信息是:SymbolDatabase has no attribute RegisterServiceDescriptor(图是别地找的,但报错信息就是这样)

百度了一下发现解决的方案很少,也没有提供有价值的信息, 最后在github上这里找到了一些信息。看大家的讨论大致意思是由于protobuf版本的问题,打开pycharm的interpreter:

有一个3.2.0版本的protobuf.

可是当我在anaconda prompt下输入pip  list时,发现这样的信息:

怎么会同时从在两个版本的protobuf,我好想知道为什么了尴尬

把这两个protobuf 都卸载掉, 重新pip install protobuf.  这次安装的时最新3.6版本


问题解决了。

继续我的mnist手写字体识别。。。。。。。。用一个没有隐含层的神经网络实现mnist手写字体识别。

重点:理解softmax regression

在处理多分类问题时,通常需要使用softmax regression模型,它的工作原理是:将可以判断为某类的特征相加,

然后将这写特征转换为判定是这一类的概率。具体的原理及数学推导可参考这篇博客。

2.理解交叉熵损失函数:

cross-entropy的定义如下:


    其中y是预测的概率分布,y'是真实的概率分布(即Label的one-hot编码),通常可以用它来描述模型对真实概率分布估计的准确程度。

现在有了损失函数定义,现在再定义一个优化算法,就可以对模型进行训练:

常见的优化算法有随机梯度下降。

完整代码:

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

mnist_data_path = r'E:\code\112\tensorflow_project\chapter5\data\tensorflow_data'
mnist = input_data.read_data_sets(mnist_data_path, one_hot=True)

input_nodes = 28 * 28
out_nodes = 10
x = tf.placeholder(dtype=tf.float32, shape=[None, input_nodes], name='x-input')
y_ = tf.placeholder(dtype=tf.float32, shape=[None, out_nodes], name='y-input')
# 定义权重矩阵
w = tf.Variable(initial_value=tf.random_normal(shape=[input_nodes, out_nodes], mean=0, stddev=0.1),
                dtype=tf.float32, trainable=True)
b = tf.Variable(initial_value=tf.zeros([10]), dtype=tf.float32, trainable=True)
output = tf.matmul(x, w) + b
y = tf.nn.softmax(output)
# 定义一个loss function来描述模型对分类问题的分类精度
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y)))
train_step = tf.train.GradientDescentOptimizer(learning_rate=0.5).minimize(cross_entropy)

correction_prediction = tf.equal(tf.argmax(y, axis=1), tf.argmax(y_, axis=1))
accuracy = tf.reduce_mean(tf.cast(correction_prediction, tf.float32))

with tf.Session() as sess:
    init_op = tf.initialize_all_variables()
    sess.run(init_op)
    for i in range(1000):
        # 每次训练都从数据集中随机抽取一百个数据,构成一个mini-batch,对神经网络进行训练
        # 使用一小部分样本进行训练称为随机梯度下降(SGD),如果每次训练都使用全部样本,计算量大,而且也不容易跳出局部最优。
        # 使用随机梯度下降可以得到更快的收敛速度
        batch_x, batch_y = mnist.train.next_batch(100)
        sess.run(train_step, feed_dict={x: batch_x, y_: batch_y})
        # print(i)
        if i % 10 == 0:
            accuracy_result = sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})
            print('After {} iterations the accuracy is {} .'.format(i, accuracy_result))

运行结果:

通过上面的例子,实现了一个非常简单的softmax regression算法

下面对之前的程序进行一些改进:对神经网络添加隐藏层, 隐藏层的节点数为500,同时对神经网络参数的更新采用滑动均值模型,采用指数衰减的学习率。

(1)关于滑动均值模型:在网络的训练中采用滑动均值模型,在测试中不使用。滑动均值模型与一阶滞后滤波具有相似性,一阶滞后滤波具体表达式为:

a的取值范围为[0, 1].

                                   本次滤波结果=(1-a)本次采样值+a上次滤波结果,

采用此算法的优点是:

1、降低周期性的干扰;

2、在波动频率较高的场合有很好的效果。

     在TensorFlow中提供了tf.train.ExponentialMovingAverage 来实现滑动平均模型,在采用随机梯度下降算法训练神经网络时,使用其可以提高模型在测试数据上的鲁棒性(robustness)。TensorFlow下的 tf.train.ExponentialMovingAverage 需要提供一个衰减率参数decay。该衰减率用于控制模型更新的速度。ExponentialMovingAverage 对每一个待更新的变量(variable)都会维护一个影子变量(shadow variable)。影子变量的初始值就是这个变量的初始值:

上述公式与之前介绍的一阶滞后滤波法的公式相比较,会发现有很多相似的地方,从名字上面也可以很好的理解这个算法的原理:平滑、滤波,即使数据平滑变化,通过调整参数来调整变化的稳定性。

在滑动平滑模型中, decay 决定了模型更新的速度,越大越趋于稳定。实际运用中,decay 一般会设置为十分接近 1 的常数(0.999或0.9999)。为了使得模型在训练的初始阶段更新得更快,ExponentialMovingAverage 还提供了 num_updates 参数来动态设置 decay 的大小:

关于滑动平均模型参考了这篇博客,作者表达的很清楚,引用一下

(2)在模型中添加正则化:通过正则化,能够将表征模型复杂度的指标加入到损失函数中。

regularizer_L2 = tf.contrib.layers.l2_regularizer(regularizer_rate)
loss_function = cross_entropy_mean + regularizer_L2(weight_1) + regularizer_L2(weight_2)

(3)指数衰减学习率:

在训练模型的时候,通常会遇到这种情况:我们平衡模型的训练速度和损失(loss)后选择了相对合适的学习率(learning rate),但是训练集的损失下降到一定的程度后就不在下降了,比如training loss一直在0.8和0.9之间来回震荡,不能进一步下降。如下图所示:

遇到这种情况通常可以通过适当降低学习率(learning rate)来实现。但是,降低学习率又会延长训练所需的时间。

学习率衰减(learning rate decay)就是一种可以平衡这两者之间矛盾的解决方案。学习率衰减的基本思想是:学习率随着训练的进行逐渐衰减。

  学习率衰减基本有两种实现方法:

  1. 线性衰减。例如:每过5个epochs学习率减半
  2. 指数衰减。例如:每过5个epochs将学习率乘以0.1

我采用的是指数衰减的方法:

采用指数衰减必须初始化一个初始的学习率,以及一个衰减的速率。

learning_rate_raw = 0.8    # 初始的学习率
learning_rate_decay = 0.99    # 学习率的衰减率

计算的公式为:

decayed_learning_rate = learning_rate * decay_rate^(global_step/decay_steps)

其中:

  • decayed_learning_rate:  优化后的每一轮的学习效率。
  • learning_rate:               最初设置的学习效率。
  • decay_rate:                  衰减系数。
  • decay_steps:                衰减速度。

同时将神经网络的前向传播过程结构化:通过inference函数来表示

# 计算神经网络的前向传播结果, 改用tf.variable_scope() 管理变量
def inference_update(input_tensor, reuse=False):
    with tf.variable_scope('layer1', reuse=reuse):   # 在第一次构建网络时需要创建变量
        weight_1 = tf.get_variable(name='weight_1', dtype=tf.float32, initializer=tf.random_normal(shape=[input_nodes, output_nodes], mean=0, stddev=1), trainable=True)
        bias_1 = tf.get_variable(name='bias_1', dtype=tf.float32, initializer=tf.constant(value=0.01, shape=[layer1_nodes]))
    layer_1 = tf.nn.relu(tf.matmul(input_tensor, weight_1) + bias_1)

    # 定义第二层的神经网络变量和参数
    with tf.variable_scope('layer2', reuse=reuse):
        weight_2 = tf.get_variable(name='weight_2', initializer=tf.random_normal(shape=[layer1_nodes, output_nodes], mean=0, stddev=1), dtype=tf.float32, trainable=True)
        bias_2 = tf.get_variable(name='bias_2', initializer=tf.constant(0.01, shape=[output_nodes]), dtype=tf.float32)
        layer_2 = tf.matmul(layer_1, weight_2) + bias_2
    return layer_2

再通过定义train()函数来训练神经网络:

def train(mnist):
    # 定义数据输入位置
    x = tf.placeholder(dtype=tf.float32, shape=[None, input_nodes], name='x-input')
    y_ = tf.placeholder(dtype=tf.float32, shape=[None, output_nodes], name='y-input')
    # 生成隐藏层参数
    weight_1 = tf.Variable(tf.random_normal(shape=[input_nodes, layer1_nodes], mean=0, stddev=0.1), dtype=tf.float32, name='weight1', trainable=True)
    weight_2 = tf.Variable(tf.random_normal(shape=[layer1_nodes, output_nodes], mean=0, stddev=0.1), dtype=tf.float32, name='weight2', trainable=True)
    bias_1 = tf.Variable(tf.constant(value=0.1, shape=[layer1_nodes]))
    bias_2 = tf.Variable(tf.constant(value=0.1, shape=[output_nodes]))

    # 计算在当前条件下神经网络的前向传播结果, 没有对参数使用滑动平均
    y = inference(input_tensor=x, avg_class=None, weight_1=weight_1, bias_1=bias_1, weight_2=weight_2, bias_2=bias_2)

    # 计算在当前条件下神经网络的前向传播结果, 对参数使用了滑动平均
    # 定义存储训练轮数的变量   trainable=False
    global_step = tf.Variable(initial_value=0, trainable=False)
    # 初始滑动平均类, 给定的参数为滑动平均衰减率和训练轮数
    """
    关于滑动平均模型:
    实际的原理是对参数的更新进行一阶之后滤波
    对每一个参数会维护一个影子变量shadow_variable
    shadow_variable = shadow_variable * decay + (1 - decay) * variable
    decay 决定了模型的更心速度
    num_updates参数来动态设置参数的大小
    decay = min{decay, (1+num_updates)/(10+num_up_dates)}
    
    """
    variable_average = tf.train.ExponentialMovingAverage(decay=moving_average_decay, num_updates=global_step)
    # 在所有神经网络参数使用滑动平均模型
    variable_averages_op = variable_average.apply(tf.trainable_variables())  # 对所有的train_able=True的参数使用滑动平均模型
    average_y = inference(input_tensor=x, avg_class=variable_average, weight_1=weight_1, bias_1=bias_1, weight_2=weight_2, bias_2=bias_2)

    # 用交叉熵作为损失函数  sparse_softmax_cross_entropy_with_logits()函数加速交叉熵的计算 当分类问题中只有一个正确结果,
    # 使用该函数可以加速计算
    # 参数: logits=y, labels=tf.arg_max(y_, 1)  用预测的结果去表达正确的标签
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.arg_max(y_, 1))  # 找到y_每一行的最大值,即正确的值
    cross_entropy_mean = tf.reduce_mean(cross_entropy)

    # 计算L2正则化损失函数   # 将模型复杂度加入到Loss_function中
    regularizer_L2 = tf.contrib.layers.l2_regularizer(regularizer_rate)
    loss_function = cross_entropy_mean + regularizer_L2(weight_1) + regularizer_L2(weight_2)

    # 设置指数衰减的学习率
    learning_rate_adjust = tf.train.exponential_decay(learning_rate=learning_rate, global_step=global_step, decay_steps=mnist.train.num_examples/batch_size, decay_rate=learning_rate_decay)
    """
    pass
    """

    train_step = tf.train.GradientDescentOptimizer(learning_rate=learning_rate_adjust).minimize(loss_function, global_step=global_step)
    # 在minize()函数中传入global_step,global_step 将自动更新
    train_op = tf.group(train_step, variable_averages_op)   # 每过一遍数据, 都要执行这两步, 所以用tf.group()将他们封装到一块

    correct_prediction = tf.equal(tf.arg_max(average_y, dimension=1), tf.arg_max(y_, 1))   # 判断两个张量是否相等
    accurancy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))    # cast将Bool型的数据转换为实数

    with tf.Session() as sess:
        sess.run(tf.initialize_all_variables())
        validate_feed = {x: mnist.validation.images, y_: mnist.validation.labels}
        test_feed = {x: mnist.test.images, y_: mnist.test.labels}

        # 训练模型
        # 迭代的训练神经网络
        for i_count in range(train_steps):
            xs, ys = mnist.train.next_batch(batch_size)
            sess.run(train_op, feed_dict={x: xs, y_: ys})  # 训练
            if i_count % 500 == 0:
                validate_acc = sess.run(accurancy, feed_dict=validate_feed)
                print("After {} step(s) training the model accurancy is {}".format(i_count, validate_acc))

        # 训练结束后测试数据上的精度
        test_acc = sess.run(accurancy, feed_dict=test_feed)
        print("After {} steps the model accuracy is {}".format(train_steps, test_acc
# encoding: utf-8
import numpy as np
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

batch_size = 100
input_nodes = 784   # 输入节点的个数
output_nodes = 10   # 输出节点的个数

layer1_nodes = 500   # 隐藏层的节点数

learning_rate = 0.1  # 学习率
learning_rate_decay = 0.99  # 学习率的衰减
regularizer_rate = 0.0001   # 正则化
train_steps = 30000         # 训练轮数
moving_average_decay = 0.99   # 滑动平均衰减率


# 计算神经网络的前向传播结果
def inference(input_tensor, avg_class, weight_1, bias_1, weight_2, bias_2):
    if avg_class is None:    # 没有滑动平均模型
        layer_1 = tf.nn.relu(tf.matmul(input_tensor, weight_1) + bias_1)
        # 计算输出层的前向传播结果
        return tf.matmul(layer_1, weight_2) + bias_2
    else:
        layer_1 = tf.nn.relu(tf.matmul(input_tensor, avg_class.average(weight_1)) + avg_class.average(bias_1))
        return tf.matmul(layer_1, avg_class.average(weight_2)) + avg_class.average(bias_2)


# 计算神经网络的前向传播结果, 改用tf.variable_scope() 管理变量
def inference_update(input_tensor, reuse=False):
    with tf.variable_scope('layer1', reuse=reuse):   # 在第一次构建网络时需要创建变量
        weight_1 = tf.get_variable(name='weight_1', dtype=tf.float32, initializer=tf.random_normal(shape=[input_nodes, output_nodes], mean=0, stddev=1), trainable=True)
        bias_1 = tf.get_variable(name='bias_1', dtype=tf.float32, initializer=tf.constant(value=0.01, shape=[layer1_nodes]))
    layer_1 = tf.nn.relu(tf.matmul(input_tensor, weight_1) + bias_1)

    # 定义第二层的神经网络变量和参数
    with tf.variable_scope('layer2', reuse=reuse):
        weight_2 = tf.get_variable(name='weight_2', initializer=tf.random_normal(shape=[layer1_nodes, output_nodes], mean=0, stddev=1), dtype=tf.float32, trainable=True)
        bias_2 = tf.get_variable(name='bias_2', initializer=tf.constant(0.01, shape=[output_nodes]), dtype=tf.float32)
        layer_2 = tf.matmul(layer_1, weight_2) + bias_2
    return layer_2


def train(mnist):
    # 定义数据输入位置
    x = tf.placeholder(dtype=tf.float32, shape=[None, input_nodes], name='x-input')
    y_ = tf.placeholder(dtype=tf.float32, shape=[None, output_nodes], name='y-input')
    # 生成隐藏层参数
    weight_1 = tf.Variable(tf.random_normal(shape=[input_nodes, layer1_nodes], mean=0, stddev=0.1),
                           dtype=tf.float32, name='weight1', trainable=True)
    weight_2 = tf.Variable(tf.random_normal(shape=[layer1_nodes, output_nodes], mean=0, stddev=0.1),
                           dtype=tf.float32, name='weight2', trainable=True)
    bias_1 = tf.Variable(tf.constant(value=0.1, shape=[layer1_nodes]), trainable=True)
    bias_2 = tf.Variable(tf.constant(value=0.1, shape=[output_nodes]), trainable=True)

    # 计算在当前条件下神经网络的前向传播结果, 没有对参数使用滑动平均
    y = inference(input_tensor=x, avg_class=None, weight_1=weight_1, bias_1=bias_1, weight_2=weight_2, bias_2=bias_2)

    # 计算在当前条件下神经网络的前向传播结果, 对参数使用了滑动平均
    # 定义存储训练轮数的变量   trainable=False
    global_step = tf.Variable(initial_value=0, trainable=False)
    # 初始滑动平均类, 给定的参数为滑动平均衰减率和训练轮数
    """
    关于滑动平均模型:
    实际的原理是对参数的更新进行一阶之后滤波
    对每一个参数会维护一个影子变量shadow_variable
    shadow_variable = shadow_variable * decay + (1 - decay) * variable
    decay 决定了模型的更心速度
    num_updates参数来动态设置参数的大小
    decay = min{decay, (1+num_updates)/(10+num_up_dates)}
    
    """
    variable_average = tf.train.ExponentialMovingAverage(decay=moving_average_decay, num_updates=global_step)
    # 在所有神经网络参数使用滑动平均模型
    variable_averages_op = variable_average.apply(tf.trainable_variables())  # 对所有的train_able=True的参数使用滑动平均模型
    average_y = inference(input_tensor=x, avg_class=variable_average, weight_1=weight_1, bias_1=bias_1, weight_2=weight_2, bias_2=bias_2)

    # 用交叉熵作为损失函数  sparse_softmax_cross_entropy_with_logits()函数加速交叉熵的计算 当分类问题中只有一个正确结果,
    # 使用该函数可以加速计算
    # 参数: logits=y, labels=tf.arg_max(y_, 1)  用预测的结果去表达正确的标签
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.arg_max(y_, 1))  # 找到y_每一行的最大值,即正确的值
    cross_entropy_mean = tf.reduce_mean(cross_entropy)

    # 计算L2正则化损失函数   # 将模型复杂度加入到Loss_function中
    regularizer_L2 = tf.contrib.layers.l2_regularizer(regularizer_rate)
    loss_function = cross_entropy_mean + regularizer_L2(weight_1) + regularizer_L2(weight_2)

    # 设置指数衰减的学习率
    learning_rate_adjust = tf.train.exponential_decay(learning_rate=learning_rate,
                                                      global_step=global_step,
                                                      decay_steps=mnist.train.num_examples/batch_size,
                                                      decay_rate=learning_rate_decay)
    """
    pass
    """

    train_step = tf.train.GradientDescentOptimizer(learning_rate=learning_rate_adjust).minimize(loss_function, global_step=global_step)
    # 在minize()函数中传入global_step,global_step 将自动更新
    train_op = tf.group(train_step, variable_averages_op)   # 每过一遍数据, 都要执行这两步, 所以用tf.group()将他们封装到一块

    correct_prediction = tf.equal(tf.arg_max(average_y, dimension=1), tf.arg_max(y_, 1))   # 判断两个张量是否相等
    accurancy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))    # cast将Bool型的数据转换为实数

    with tf.Session() as sess:
        sess.run(tf.initialize_all_variables())
        validate_feed = {x: mnist.validation.images, y_: mnist.validation.labels}
        test_feed = {x: mnist.test.images, y_: mnist.test.labels}

        # 训练模型
        # 迭代的训练神经网络
        for i_count in range(train_steps):
            xs, ys = mnist.train.next_batch(batch_size)
            sess.run(train_op, feed_dict={x: xs, y_: ys})  # 训练
            if i_count % 500 == 0:
                validate_acc = sess.run(accurancy, feed_dict=validate_feed)
                print("After {} step(s) training the model accurancy is {}".format(i_count, validate_acc))

        # 训练结束后测试数据上的精度
        test_acc = sess.run(accurancy, feed_dict=test_feed)
        print("After {} steps the model accuracy is {}".format(train_steps, test_acc))


# 定义主程序入口
def main(argv=None):
    data_path = r'E:\code\112\tensorflow_project\chapter5\data\tensorflow_data'
    mnist = input_data.read_data_sets(data_path, one_hot=True)
    train(mnist)


if __name__ == "__main__":
    tf.app.run()



程序的运行结果如下所示:


后面持续更行,会添加模型保存以及调用tensorboard的部分程序。。。

奋斗


猜你喜欢

转载自blog.csdn.net/zj1131190425/article/details/80743510