TensorFlow进阶(二) 搭建CNN识别MNIST手写数据集

1.分类问题

1.1 准备MNIST数据集
  • MNIST数据集:是一个入门级的计算机视觉数据集,它包含各种手写数字图片。
    MNIST数据
    它也包含每一张图片对应的标签,告诉我们这个是数字几。比如,上面这四张图片的标签分别是5,0,4,1。
  • MNIST数据集下载:官网地址
  • 导入方式:
import input_data
mnist = input_data.read_data_sets("文件路径",one_hot=true) 
  • one_hot编码:一个one-hot向量除了某一位的数字是1以外其余各维度数字都是0。比如,标签0将表示成([1,0,0,0,0,0,0,0,0,0,0])。
    ONE-HOT编码
1.2 预测MNIST手写数据集
import tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)

def simple_layer(inputs, input_size, output_size):
    """
    封装一层神经网络
    :param inputs: 数据
    :param input_size: 行数
    :param output_size: 列数
    :return: 无
    """
    w = tf.Variable(tf.truncated_normal([input_size,output_size]))
    # w = tf.Variable(tf.random_normal([input_size,output_size]))
    b = tf.Variable(tf.zeros([output_size])+0.1)
    return tf.matmul(inputs,w) + b

def draw_graph(data_array,x_label,y_label,name,order):
    """
    绘制loss图像
    :param data_array: 数据
    :param x_label: x轴名称
    :param y_label: y轴名称
    :param name: 图表名称
    :return: 无
    """
    plt.figure(order)
    plt.plot(data_array)
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    plt.title("lr={0} tr={1} bs={2}".format(learning_rate,train_epochs,batch_size))
    plt.savefig(name,dpi=200)

tf.reset_default_graph()    #重置图

#参数设置
learning_rate = 0.01   #学习率
train_epochs = 500  #训练轮数
batch_size = 100   #一批数据的大小为100
display_step = 50   #展示的步长
loss_record = []   #储存loss
accuracy_record = []   #存储accuracy

x = tf.placeholder(tf.float32, [None, 784])   #mnist 维度28*28 = 784
y = tf.placeholder(tf.float32, [None, 10])    #表示0-9个数字的

#预测层
pred = tf.nn.softmax(simple_layer(x,784,10))

#反向传播,将生成的pred与y进行一次交叉熵运算最小化误差cost
loss = tf.reduce_mean(-tf.reduce_sum(y * tf.log(pred), reduction_indices=1))   #reduce_sum是按列求和

#梯度下降优化器
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(loss)
correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
init = tf.global_variables_initializer()

saver = tf.train.Saver()   #获取保存对象
model_path = "mn/model.cpkt"     #设置保存模型的位置

#启动session,训练模型
with tf.Session() as sess:
    sess.run(init)    #初始化全部的值

    #启动循环开始训练
    for epoch in range(train_epochs):
        train_loss_epoch = 0
        train_accuracy_epoch = 0

        #将图片分组
        total_batch = int(mnist.train.num_examples/batch_size)

        #遍历全部数据集
        for i in range(total_batch):
            #获取一个批次的数据
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)

            #启动optimizer优化器
            sess.run(optimizer,feed_dict={x:batch_xs,y:batch_ys})
            # pred_s = sess.run(pred,feed_dict={x:batch_xs,y:batch_ys})
            # print(pred_s)
            train_loss = sess.run(loss, feed_dict={x:batch_xs, y:batch_ys})
            train_correct = sess.run(accuracy, feed_dict={x:batch_xs, y:batch_ys})

            #计算一次epochs的误差与准确率
            train_loss_epoch += train_loss / total_batch
            train_accuracy_epoch += train_correct / total_batch

        #显示训练中的详细信息,50步展示一次
        if (epoch+1) % display_step == 0:
            print("Epoch:", '%04d'%(epoch+1), " lost=", '{:.9f}'.format(train_loss_epoch)," accuracy=", "{:.4f}".format(train_accuracy_epoch))
        loss_record.append(train_loss_epoch)
        accuracy_record.append(train_accuracy_epoch)

    print("Training finished!")

    #测试model
    correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
    #计算准确率
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    print("测试集准确率:", accuracy.eval({x:mnist.test.images, y:mnist.test.labels}))

    #存储模型
    save_path = saver.save(sess, model_path)
    print("Model saved in file:%s"%save_path)
    #绘制图表
    draw_graph(loss_record,"train_epochs","loss","loss_graph.jpg",1)
    draw_graph(accuracy_record,"train_epochs","accuracy","accuracy_graph.jpg",2)
    print("Graph saved")

    
    
Output:
Epoch: 0050  lost= 0.599896277  accuracy= 0.8571
Epoch: 0100  lost= 0.477679570  accuracy= 0.8802
Epoch: 0150  lost= 0.421992446  accuracy= 0.8907
Epoch: 0200  lost= 0.388164593  accuracy= 0.8975
Epoch: 0250  lost= 0.364874119  accuracy= 0.9023
Epoch: 0300  lost= 0.347681733  accuracy= 0.9061
Epoch: 0350  lost= 0.334310911  accuracy= 0.9089
Epoch: 0400  lost= 0.323556781  accuracy= 0.9119
Epoch: 0450  lost= 0.314724997  accuracy= 0.9140
Epoch: 0500  lost= 0.307318334  accuracy= 0.9155
Training finished!
测试集准确率: 0.9117
Model saved in file:mn/model.cpkt
Graph saved

loss
loss

accuracy
accuracy

2.欠拟合与过拟合

  • 图示
    欠拟合与过拟合
2.1 什么是欠拟合?
  • 概念:如图一Underfitted,是模型简单或者说语料集偏少、特征太多,在训练集上的准确率不高,同时在测试集上的准确率也不高,这样如何训练都无法训练出有意义的参数,模型也得不到较好的效果。
2.2 如何避免欠拟合?
  • 1,添加其他特征项,模型出现欠拟合的时候是因为特征项不够导致的,可以添加其他特征项来很好地解决。
    例如,“组合”、“泛化”、“相关性”三类特征是特征添加的重要手段, 无论在什么场景,都可以照葫芦画瓢,总会得到意想不到的效果。 除上面的特征之外,“上下文特征”、“平台特征”等等,都可以作为特征添加的首选项。

  • 2,添加多项式特征,例如将线性模型通过添加二次项或者三次项使模型泛化能力更强。

  • 3,减少正则化参数,正则化的目的是用来防止过拟合的,当模型出现了欠拟合,则需要减少正则化参数。

2.3 什么是过拟合?
  • 概念:如图三Overfitted,具体表现在:深度学习的模型在提供的训练集上表现的效果非常好,但是在未经过训练集观察的测试集上,模型的效果很差,即输出的泛化能力很弱。
2.4 如何避免过拟合?
  • 1,获取和使用更多的数据集
    • 对于解决过拟合的办法就是给与足够多的数据集,让模型在更多的数据上进行“观察”和拟合,从而不断修正自己。然而不可能收集无限多的数据集,因此一个常用的方法就是调整已有数据,添加大量的噪音,或者对图形进行锐化,旋转,明暗度调整等优化。
  • 2,采用合适的模型
    • 目前来说,基于不同的情况和分类要求,对使用的模型也不同。过于简单或者过于复杂都会导致过拟合问题。
  • 3,使用dropout
    • dropout是一个非常有用和常用的方法。dropout层指的是模型在训练过程中每次按50%的几率关闭或忽略某些层的节点。使得模型在使用同样的数据进行训练时相当于从不同的模型中随机选择一个进行训练。
  • 4,权重衰减
    • 权重衰减(Weight-decay)又被称为正则化。具体的做法是将权重加入到损失函数中。
    • Loss = loss + reg(wi)
    • 在实际中使用L1正则和L2正则:
      • L1 = λ/n * Σ|wi| 所有的权重w值求和,乘以λ/n
      • L2 = λ/2n * Σ(wi)^2 所有的权重w的平方求和,乘以λ/2n
  • 5,Early Stopping
    • Early stoppin是参数微调的一种,即在每个循环结束一次以后(这里的循环可能是full data batch,也可能是mini batch size),计算模型的准确率(accuracy)。当准确率不在增加时就停止训练。
  • 6,使用Batch Normalization。参见7.Batch Normalization

3.CNN(卷积神经网络)

3.1 什么是CNN?
  • 概念:卷积神经网络(Convolutional Neural Networks, CNN)是一类包含卷积计算且具有深度结构的前馈神经网络(Feedforward Neural Networks),是深度学习(deep learning)的代表算法之一。
  • 卷积函数原型:tf.nn.conv2d(inputs,filter,strides,padding,use_cudnn_on_gpu=None,name=None)
    • inputs:需要做卷积处理的输入图像,要求是一个张量(tensor),具有[batch,in_height,in_width,in_channels],具体含义[训练时一批图片的数量,图像高度,图像宽度,图像通道数]。
    • filter:是CNN中的卷积核,要求是一个tensor,具有[filter_height,filter_width,in_channels,out_channels],具体含义[卷积核的高度,卷积核的宽度,输入图像的通道数,输出图像的通道数]。
    • strides:卷积时在每一维的步长,[1,strides1,strides2,1]第一维和第四维默认为1,第三维和第四维分别是平行和竖直滑行的步进长度。
    • padding:string类型的量,只能是"SAME",“VALID”,SAME会在卷积计算时,在外围会补齐0,而VALID不会补齐0。
    • use_cudnn_on_gpu:bool类型是否使用cudnn加速,默认为true。
  • 卷积计算
    • padding="SAME"时,卷积后图像为out_height=in_height/strides1,out_width=out_width/strides2。
    • padding="VALID"时,卷积后的图像为out_height=(in_height-filter_hieght+1)/strides1,out_width=(in_width-filter_width+1)/strides2。
  • 卷积图解
    • 计算的移动
      caculate conv
3.2 搭建一个CNN
  • 以MNIST数据集为例,添加卷积层
  • 示例代码
import tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)

def weights(shape):
    return tf.Variable(tf.truncated_normal(shape))

def simple_layer(inputs, input_size, output_size):
    """
    封装一层神经网络
    :param inputs: 数据
    :param input_size: 行数
    :param output_size: 列数
    :return: 无
    """
    w = tf.Variable(tf.truncated_normal([input_size,output_size]))
    # w = tf.Variable(tf.random_normal([input_size,output_size]))
    b = tf.Variable(tf.zeros([output_size])+0.1)
    return tf.matmul(inputs,w) + b

def conv2d_layer(inputs,filter,strides,padding,activation=None):
    output = tf.nn.conv2d(inputs,filter,strides,padding)
    return activation(output)

def max_pooling(inputs,kernel_size,strides,padding):
    return tf.nn.max_pool(inputs,kernel_size,strides,padding)

def draw_graph(data_array,x_label,y_label,name,order):
    """
    绘制loss图像
    :param data_array: 数据
    :param x_label: x轴名称
    :param y_label: y轴名称
    :param name: 图表名称
    :return: 无
    """
    plt.figure(order)
    plt.plot(data_array)
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    plt.title("lr={0} tr={1} bs={2}".format(learning_rate,train_epochs,batch_size))
    plt.savefig(name,dpi=200)
    
#参数设置
learning_rate = 0.001   #学习率
train_epochs = 500  #训练轮数
batch_size = 50   #一批数据的大小为100
display_step = 1   #展示的步长
loss_record = []   #储存loss
accuracy_record = []   #存储accuracy

x = tf.placeholder(tf.float32, [None, 784])   #mnist 维度28*28 = 784
y = tf.placeholder(tf.float32, [None, 10])    #表示0-9个数字的

x_image = tf.reshape(x,[-1,28,28,1])   #将[None,784]转换为[None,28,28,1]的形状
#卷积层
conv2d_layer1 = conv2d_layer(x_image,weights([5,5,1,16]),[1,1,1,1],"SAME",tf.nn.relu)
conv2d_pooling1 = max_pooling(conv2d_layer1,[1,2,2,1],[1,2,2,1],"SAME")    #[14,14,16]
#卷积层
conv2d_layer2 = conv2d_layer(conv2d_pooling1,weights([5,5,16,32]),[1,1,1,1],"SAME",tf.nn.relu)
conv2d_pooling2 = max_pooling(conv2d_layer2,[1,2,2,1],[1,2,2,1],"SAME")    #[7,7,32]

reshape = tf.reshape(conv2d_pooling2,[-1,7*7*32])   #展平[7*7*32]
#全连接层
pred = tf.nn.softmax(simple_layer(reshape,7*7*32,10))

#反向传播,将生成的pred与y进行一次交叉熵运算最小化误差cost
# loss = tf.reduce_mean(-tf.reduce_sum(y * tf.log(pred), reduction_indices=1))   #reduce_sum是按列求和
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y,logits=pred))
#梯度下降优化器
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss)
correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
init = tf.global_variables_initializer()

saver = tf.train.Saver()   #获取保存对象
model_path = "CNN/model.cpkt"     #设置保存模型的位置

#启动session,训练模型
with tf.Session() as sess:
    sess.run(init)    #初始化全部的值

    #启动循环开始训练
    for epoch in range(train_epochs):
        train_loss_epoch = 0
        train_accuracy_epoch = 0

        #将图片分组
        total_batch = int(mnist.train.num_examples/batch_size)

        #遍历全部数据集
        for i in range(total_batch):
            #获取一个批次的数据
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)

            #启动optimizer优化器
            sess.run(optimizer,feed_dict={x:batch_xs,y:batch_ys})
            # pred_s = sess.run(pred,feed_dict={x:batch_xs,y:batch_ys})
            # print(pred_s)
            train_loss = sess.run(loss, feed_dict={x:batch_xs, y:batch_ys})
            train_correct = sess.run(accuracy, feed_dict={x:batch_xs, y:batch_ys})

            #计算一次epochs的误差与准确率
            train_loss_epoch += train_loss / total_batch
            train_accuracy_epoch += train_correct / total_batch

        #显示训练中的详细信息,50步展示一次
        if (epoch+1) % display_step == 0:
            print("Epoch:", '%04d'%(epoch+1), " lost=", '{:.6f}'.format(train_loss_epoch)," accuracy=", "{:.6f}".format(train_accuracy_epoch))
        loss_record.append(train_loss_epoch)
        accuracy_record.append(train_accuracy_epoch)

    print("Training finished!")

    #存储模型
    save_path = saver.save(sess, model_path)
    print("Model saved in file:%s"%save_path)
    #绘制图表
    draw_graph(loss_record,"train_epochs","loss","CNN_loss_graph.jpg",1)
    draw_graph(accuracy_record,"train_epochs","accuracy","CNN_accuracy_graph.jpg",2)
    print("Graph saved")

4.saver 模型的存取

4.1 模型的存储
  • 示例代码
saver = tf.train.Saver()
save_path = "mn/model.cpkt"   #保存位置
saver.save(sess,save_path)  #保存现在的sess
4.2 模型的读取
  • 示例代码
saver = tf.train.Saver()
#读取训练好的模型到当前会话sess
saver.restore(sess,tf.train.latest_checkpoint(".//mn"))  #保存的文件夹

5.Batch Normalization

5.1 理解Batch Normalization
  • 一般来说,当模型设计完毕后,更多的是需要对输入的数据进行处理,不同的数据类型以及图片属性都会对模型的训练产生很大的影响。因此需要一种专门的方法去解决因图片不同而产生的差异影响。
  • 对于深度学习来说,数据在模型中训练是一个复杂的过程。即使训练模型网络的前面几层发生非常微小的变化,随着梯度下降算法的计算,这个微小的变化在后面几层也会被累积放大下去。
  • 当数据输入的属性分布发生改变时,即使是很微小的变化,在传递这个变化的过程时,网络的后端也会产生非常大的变化,从而需要整个模型,整个网络去重新适应和学习这个新的数据分布。如果训练的数据分布一直在发生变化,那么训练模型对最后的预测结果也是在一个比较大的错误率之间浮动。
  • Batch_Normalization是一种新近的对数据差异性进行处理的手段,归一化就是将数据的输入值减去其均值然后除以数据的标准差。通过对在“一个范围内”的数据进行规范化处理,使得输出结果的均值为0,方差为1。
  • 好处:对数据进行了一定程度上预处理,使得无论是训练数据集还是测试集都在一定范围内进行分布和波动,对数据点中包含的误差进行掩盖化处理,增强模型的泛化能力。
5.2 Batch Normalization的运用
  • TensorFlow提供了专门的函数来完成数据的Batch_Normalization的计算。
    • batch_normalization(x,mean,variance,offset,scale,variance_epsilon,name)
      • x:输入的数据文件。
      • mean:批量数据方差。
      • variance:批量数据方差。
      • offset:待训练的参数。
      • scale:待训练的参数。
      • variance_eplilon:方差编译系数。
      • name:名称。
    • 这里主要使用了Batch中的均值以及方差。offset和scale是在模型中需要训练的数据。variance_epsilon是需要设定的一个系数,一般情况下将其设置为0.0001。
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
利用tensorflow实现的卷积神经网络来进行MNIST手写数字图像的分类。 #导入numpy模块 import numpy as np #导入tensorflow模块,程序使用tensorflow来实现卷积神经网络 import tensorflow as tf #下载mnist数据集,并从mnist_data目录中读取数据 from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets('mnist_data',one_hot=True) #(1)这里的“mnist_data” 是和当前文件相同目录下的一个文件夹。自己先手工建立这个文件夹,然后从https://yann.lecun.com/exdb/mnist/ 下载所需的4个文件(即该网址中第三段“Four files are available on this site:”后面的四个文件),并放到目录MNIST_data下即可。 #(2)MNIST数据集手写数字字符的数据集。每个样本都是一张28*28像素的灰度手写数字图片。 #(3)one_hot表示独热编码,其值被设为true。在分类问题的数据集标注时,如何不采用独热编码的方式, 类别通常就是一个符号而已,比如说是9。但如果采用独热编码的方式,则每个类表示为一个列表list,共计有10个数值,但只有一个为1,其余均为0。例如,“9”的独热编码可以为[00000 00001]. #定义输入数据x和输出y的形状。函数tf.placeholder的目的是定义输入,可以理解为采用占位符进行占位。 #None这个位置的参数在这里被用于表示样本的个数,而由于样本个数此时具体是多少还无法确定,所以这设为None。而每个输入样本的特征数目是确定的,即为28*28。 input_x = tf.placeholder(tf.float32,[None,28*28])/255 #因为每个像素的取值范围是 0~255 output_y = tf.placeholder(tf.int32,[None,10]) #10表示10个类别 #输入层的输入数据input_x被reshape成四维数据,其中第一维的数据代表了图片数量 input_x_images = tf.reshape(input_x,[-1,28,28,1]) test_x = mnist.test.images[:3000] #读取测试集图片的特征,读取3000个图片 test_y = mnist.test.labels[:3000] #读取测试集图片的标签。就是这3000个图片所对应的标签

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值