欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

tensorflow实战学习笔记(1)

程序员文章站 2022-06-12 15:39:35
...

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

(一)数据并行:

(二)模型并行:

(三)流水线并行:

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

tensorflow实战学习笔记(1)

第一章

Tensorflow实现Softmax Regression识别手写数字

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

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

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

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

tensorflow实战学习笔记(1)

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

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

tensorflow实战学习笔记(1)

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

tensorflow实战学习笔记(1)

有一个3.2.0版本的protobuf.

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

tensorflow实战学习笔记(1)

怎么会同时从在两个版本的protobuf,我好想知道为什么了tensorflow实战学习笔记(1)

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

tensorflow实战学习笔记(1)

问题解决了。

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

重点:理解softmax regression

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

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

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

cross-entropy的定义如下:

tensorflow实战学习笔记(1)

    其中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))

运行结果:

tensorflow实战学习笔记(1)

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

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

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

tensorflow实战学习笔记(1)

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

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

采用此算法的优点是:

1、降低周期性的干扰;

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

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

tensorflow实战学习笔记(1)

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

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

tensorflow实战学习笔记(1)

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

(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之间来回震荡,不能进一步下降。如下图所示:

tensorflow实战学习笔记(1)

遇到这种情况通常可以通过适当降低学习率(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()



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

tensorflow实战学习笔记(1)

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

tensorflow实战学习笔记(1)