赞
踩
作业要求:
手写体数字识别
数据集:MNIST
数据集下载网址:http://yann.lecun.com/exdb/mnist/
参考网址: https://zhuanlan.zhihu.com/p/101262336
https://blog.csdn.net/panrenlong/article/details/81736754
深度神经网络方法(任选):CNN、RNN、GNN、LSTM、MM、GCN……
典型结构(任选):AlexNet、VGG、GoogleNet……
目标:只有一个,让网络跑起来
数据理解与获取:
MNIST数据集是由60000个训练样本和10000个测试样本组成,每个样本都是一张28 * 28像素的灰度手写数字图片。
获取方法一:
官方网站下载:http://yann.lecun.com/exdb/mnist/
一共4个文件,训练集、训练集标签、测试集、测试集标签
获取方法二:
使用TensorFlow中input_data.py脚本来读取数据及标签,使用这种方式时,可以不用事先下载好数据集,会自动下载并存放到指定的位置。
实现思路
使用tensorflow编写卷积神经网络(CNN)代码进行MNIST数据集的手写数字识别,整体采用Alexnet网络架构搭建神经网络,由于MNIST影像大小为28 x 28,所以网络结构做了相应调整,从而适应数据。MNIST训练集数据用于模型训练,计算训练集准确率,训练好模型将其保存在model文件夹,使用MNIST测试集进行模型测试,计算测试集准确率,总体测试准确率在96%以上。
网络架构介绍——AlexNet
Alexnet架构本质上与Lenet架构相同,Alexnet将CNN卷积神经网络的基本原理应用到更深,更宽的网络中。
对比Lenet架构:
① 更大的池化窗口,使用最大池化层
② 更大的卷积核窗口和步长
③ 新加了三层卷积层,更多的输出通道
Alexnet架构主要使用到的新技术如下:
(1) 使用ReLu作为CNN的激活函数,标准的CNN模型采用tanh或sigmoid激活函数,但是在进行梯度下降时,神经元梯度会趋于饱和,参数更新速度慢,在进行梯度下降计算时,Relu函数的训练速度更快,错误率更低。
(2) 训练时使用Dropout随机忽略一部分神经元,以避免模型过拟合,以0.5的概率对每个隐层神经元的输出设为0,那些“失活的”的神经元不再进行前向传播并且不参与反向传播。dropout在前两个全连接层中使用,非常有效避免了过拟合。
(3) 在CNN中采用重叠的最大池化,此前CNN中普遍使用平均池化,Alexnet网络架构全部采用最大池化,避免平均池化的模糊化效果。并且Alexnet中提出让步长比池化核的尺寸小,这样池化层的输出之间会有重叠和覆盖,提升了特征的丰富性。
(4) 提出了LRU层,对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。
AlexNet网络架构示意图:
AlexNet网络架构各层处理细节示意图:
Alexnet网络架构层级处理流程图:
运行结果截图
测试集测试结果,输出测试准确率,整体在96%以上:
核心代码和说明
(1) 定义的超参数
(2) 模型结构
input-输入数据:MNIST
数据集被分为两部分:60000行的训练数据集(mnist.train)和10000行的测试数据集(mnist.test),每张图片是28281,输入数字是1784的数据,经过reshape后,维度格式为[28,28,1]
第1层卷积,卷积大小变化为2828—>2828
池化大小变化为2828—>1414
归一化大小变化为 1414—>1414
第2层卷积,卷积大小变化为1414—>1414
池化大小变化为1414—>77
归一化大小变化为 77—>77
第3层卷积,卷积大小变化为 77—> 77
第4层卷积,卷积大小变化为 77—> 77
第5层卷积,卷积大小变化为 77—> 77
池化大小变为 77—>44
归一化大小为77—>77
第1层全连接层 ‘wd1’: shape为44*256
dropout丢弃层
第2层全连接层
dropout丢弃层
output输出层
# 数据获取与各参数定义与设置 # 获取mnist数据集,进行one-hot编码,其为一位有效编码 mnist = input_data.read_data_sets("mnist_sets", one_hot=True) print("数据下载完成!") # 设置网络超参数 # 学习率 learning_rate = 0.0001 # 迭代次数,每个样本迭代20次 epochs = 20 # 每进行一次迭代选择128个样本 batch_size = 128 # 模型训练的步数 display_step = 10 # 设置网络参数,图片大小为28*28像素,即784,输入维度 n_input = 784 # 类别有0-9十种,输出维度 n_classes = 10 # 神经元丢弃概率,以0.5的概率对每个隐层神经元的输出设为0 dropout = 0.5 # 占位符,用placeholder先占地方,样本个数不确定为None x = tf.placeholder(tf.float32, [None, n_input]) y = tf.placeholder(tf.float32, [None, n_classes]) weights = { # wc1 卷积核11*11*1*96,输入为28*28*1,所以in_channel=1,96代表卷积核个数,表示有96个11*11*1的卷积核,96即产生特征图的个数 'wc1': weight_var('wc1', [11, 11, 1, 96]), # wc2 卷积核5*5*96*256,输入为14*14*96,所以in_channel=96,256代表卷积核个数,表示有256个14*14*96的卷积核,256即产生特征图的个数 'wc2': weight_var('wc2', [5, 5, 96, 256]), # wc3 卷积核3*3*256*384, 输入为7*7*256,所以in_channels=256,384代表卷积核个数,表示有384个7*7*256的卷积核,384即产生特征图的个数 'wc3': weight_var('wc3', [3, 3, 256, 384]), # wc4 卷积核3*3*384*384, 输入为4*4*384,所以in_channels=384,384代表卷积核个数,表示有384个3*3*384的卷积核,384即产生特征图的个数 'wc4': weight_var('wc4', [3, 3, 384, 384]), # wc5 卷积核3*3*384*256, 输入为4*4*384,所以in_channels=384,256代表卷积核个数,表示有256个3*3*384的卷积核,256即产生特征图的个数 'wc5': weight_var('wc5', [3, 3, 384, 256]), # wd1 2*2*256*4096, 输入为2*2*256,所以将其展平则为1*(2*2*256),4096表示全连接层神经元的个数 'wd1': weight_var('wd1', [4 * 4 * 256, 4096]), # wd2 4096*4096, 输入为[2*2*256,4096],4096表示有4096个神经元 'wd2': weight_var('wd2', [4096, 4096]), # out 4096,10, 输入为[4096,4096],10表示有10类—> 0-9 'out_w': weight_var('out_w', [4096, 10]) } # 设置偏移量 biases = { # 卷积层 'bc1': bias_var('bc1', [96]), 'bc2': bias_var('bc2', [256]), 'bc3': bias_var('bc3', [384]), 'bc4': bias_var('bc4', [384]), 'bc5': bias_var('bc5', [256]), # 全连接层 'bd1': bias_var('bd1', [4096]), 'bd2': bias_var('bd2', [4096]), # 输出层 'out_b': bias_var('out_b', [n_classes]) } # 定义卷积,池化,归一化,参数设置操作 # 定义卷积过程,参数为:名字,输入,卷积核,偏移量,步长 # padding=‘SAME’表示在做卷积前需要对输入图像进行0填充使卷积后的图像与输入图像有相同的维度 # x为一个四维的张量,四个维度分别表示一批训练的图片数量,图片高度和宽度及图片的通道数 def conv2d(name, x, W, b, strides=1, padding='SAME'): # strides = [1, strides, strides, 1]表示每个维度做卷积的步幅 x = tf.nn.conv2d(x, W, strides=[1, strides, strides, 1], padding=padding) x = tf.nn.bias_add(x, b) # Alexnet架构使用relu作为激活函数 return tf.nn.relu(x, name=name) # 定义池化过程 3*3 def maxpool2d(name, x, k=3, s=2, padding='SAME'): return tf.nn.max_pool(x, ksize=[1, k, k, 1], strides=[1, s, s, 1], padding=padding, name=name) # 局部响应归一化LRU,使得其中响应比较大的值相对更大,并抑制其他反馈较小的神经单元,提高模型泛化能力 def norm(name, l_input, lsize=5): return tf.nn.lrn(l_input, lsize, bias=1.0, alpha=0.0001, beta=0.75, name=name) # 设置网络参数,卷积核参数与偏移量 def weight_var(name, shape): return tf.get_variable(name=name, shape=shape, initializer=tf.contrib.layers.xavier_initializer()) def bias_var(name, shape): return tf.get_variable(name=name, shape=shape, initializer=tf.constant_initializer(0)) # 构建神经网络结构,定义Alexnet网络结构 def alexnet(x, weights, biases, dropout): # 输入数字是1*784的数据,将其reshape成28*28的影像 x = tf.reshape(x, shape=[-1, 28, 28, 1]) # 1 conv 第1层卷积,卷积大小变化为28*28->28*28 conv1 = conv2d('conv1', x, weights['wc1'], biases['bc1'], padding='SAME') # 池化大小变化为28*28->14*14 pool1 = maxpool2d('pool1', conv1, k=3, s=2, padding='SAME') # 归一化大小变化为 14*14->14*14 norm1 = norm('norm1', pool1, lsize=5) # 2 conv 第2层卷积,卷积大小变化为14*14->14*14 conv2 = conv2d('conv2', norm1, weights['wc2'], biases['bc2'], padding='SAME') # 池化大小变化为14*14->7*7 pool2 = maxpool2d('pool2', conv2, k=3, s=2, padding='SAME') # 归一化大小变化为 7*7->7*7 norm2 = norm('norm2', pool2, lsize=5) # 3 conv 第3层卷积,卷积大小变化为 7*7—> 7*7 conv3 = conv2d('conv3', norm2, weights['wc3'], biases['bc3'], padding='SAME') # 4 conv 第4层卷积,卷积大小变化为 7*7—> 7*7 conv4 = conv2d('conv4', conv3, weights['wc4'], biases['bc4'], padding='SAME') # 5 conv 第5层卷积,卷积大小变化为 7*7—> 7*7 conv5 = conv2d('conv5', conv4, weights['wc5'], biases['bc5'], padding='SAME') # 池化大小变为 7*7->4*4 pool5 = maxpool2d('pool5', conv5, k=3, s=2, padding='SAME') # 归一化大小为7*7->7*7 norm5 = norm('norm5', pool5, lsize=5) # 1 fc 第1层全连接层 'wd1': 形状大小为4*4*256 fc1 = tf.reshape(norm5, [-1, weights['wd1'].get_shape().as_list()[0]]) fc1 = tf.add(tf.matmul(fc1, weights['wd1']), biases['bd1']) fc1 = tf.nn.relu(fc1) # dropout丢弃层 fc1 = tf.nn.dropout(fc1, dropout) # 2 fc 第2全连接层 # fc2=tf.reshape(fc1,[-1,weights['wd2'].get_shape().as_list()[0]]) fc2 = tf.add(tf.matmul(fc1, weights['wd2']), biases['bd2']) fc2 = tf.nn.relu(fc2) # dropout丢弃层 fc2 = tf.nn.dropout(fc2, dropout) # output输出层 out = tf.add(tf.matmul(fc2, weights['out_w']), biases['out_b']) return out # 定义模型,损失函数和优化器 # 前向传播的预测值 pred = alexnet(x, weights, biases, dropout) # 交叉熵损失函数,参数分别为预测值pred和实际label值y,reduce_mean为求平均loss cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y)) # 梯度下降优化器 optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # tf.equal()对比预测值的索引和实际label的索引是否一样,一样返回True,不一样返回False correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1)) # 将pred即True或False转换为1或0,并对所有的判断结果求均值 accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) # 训练和评估模型,获得训练集的训练结果与测试集的测试准确率 # 初始化变量 init = tf.global_variables_initializer() # 定义一个Session with tf.Session() as sess: # 在sess里run一下初始化操作 sess.run(init) step = 1 # epoch 逐代循环 for epoch in range(epochs + 1): # 迭代 for _ in range(mnist.train.num_examples // batch_size): step += 1 # get x,y 逐个batch的去取数据 batch_x, batch_y = mnist.train.next_batch(batch_size) # 获取批数据 sess.run(optimizer, feed_dict={x: batch_x, y: batch_y}) # 展示损失和精确度 每display_step显示一次 if step % display_step == 0: # 计算损失值和精确度,并输出 loss, acc = sess.run([cost, accuracy], feed_dict={x: batch_x, y: batch_y}) print("Epoch " + str(epoch) + ", Minibatch Loss=" + \ "{:.6f}".format(loss) + ", Training Accuracy= " + \ "{:.5f}".format(acc)) print("Optimizer Finished!") # 存储训练好的模型 saver.save(sess, model + "/model.ckpt", global_step=epoch) # 测试准确率并对应输出 for _ in range(mnist.test.num_examples // batch_size): batch_x, batch_y = mnist.test.next_batch(batch_size) print("Testing Accuracy:", sess.run(accuracy, feed_dict={x: batch_x, y: batch_y}))
# 导入tensorflow包 import tensorflow as tf import os # 导入数据 from tensorflow.examples.tutorials.mnist import input_data # 获取mnist数据集,进行one-hot编码,其为一位有效编码 mnist = input_data.read_data_sets("mnist_sets", one_hot=True) print("数据下载完成!") # 设置网络超参数 # 学习率 learning_rate = 0.0001 # 迭代次数,每个样本迭代20次 epochs = 20 # 每进行一次迭代选择128个样本 batch_size = 128 # 模型训练的步数 display_step = 10 # 设置网络参数,图片大小为28*28像素,即784,输入维度 n_input = 784 # 类别有0-9十种,输出维度 n_classes = 10 # 神经元丢弃概率,以0.5的概率对每个隐层神经元的输出设为0 dropout = 0.5 # 占位符,用placeholder先占地方,样本个数不确定为None x = tf.placeholder(tf.float32, [None, n_input]) y = tf.placeholder(tf.float32, [None, n_classes]) # 定义卷积,池化,归一化,参数设置操作 # 定义卷积过程,参数为:名字,输入,卷积核,偏移量,步长 # padding=‘SAME’表示在做卷积前需要对输入图像进行0填充使卷积后的图像与输入图像有相同的维度 # x为一个四维的张量,四个维度分别表示一批训练的图片数量,图片高度和宽度及图片的通道数 def conv2d(name, x, W, b, strides=1, padding='SAME'): # strides = [1, strides, strides, 1]表示每个维度做卷积的步幅 x = tf.nn.conv2d(x, W, strides=[1, strides, strides, 1], padding=padding) x = tf.nn.bias_add(x, b) # Alexnet架构使用relu作为激活函数 return tf.nn.relu(x, name=name) # 定义池化过程 3*3 def maxpool2d(name, x, k=3, s=2, padding='SAME'): return tf.nn.max_pool(x, ksize=[1, k, k, 1], strides=[1, s, s, 1], padding=padding, name=name) # 局部响应归一化LRU,使得其中响应比较大的值相对更大,并抑制其他反馈较小的神经单元,提高模型泛化能力 def norm(name, l_input, lsize=5): return tf.nn.lrn(l_input, lsize, bias=1.0, alpha=0.0001, beta=0.75, name=name) # 设置网络参数,卷积核参数与偏移量 def weight_var(name, shape): return tf.get_variable(name=name, shape=shape, initializer=tf.contrib.layers.xavier_initializer()) def bias_var(name, shape): return tf.get_variable(name=name, shape=shape, initializer=tf.constant_initializer(0)) weights = { # wc1 卷积核11*11*1*96,输入为28*28*1,所以in_channel=1,96代表卷积核个数,表示有96个11*11*1的卷积核,96即产生特征图的个数 'wc1': weight_var('wc1', [11, 11, 1, 96]), # wc2 卷积核5*5*96*256,输入为14*14*96,所以in_channel=96,256代表卷积核个数,表示有256个14*14*96的卷积核,256即产生特征图的个数 'wc2': weight_var('wc2', [5, 5, 96, 256]), # wc3 卷积核3*3*256*384, 输入为7*7*256,所以in_channels=256,384代表卷积核个数,表示有384个7*7*256的卷积核,384即产生特征图的个数 'wc3': weight_var('wc3', [3, 3, 256, 384]), # wc4 卷积核3*3*384*384, 输入为4*4*384,所以in_channels=384,384代表卷积核个数,表示有384个3*3*384的卷积核,384即产生特征图的个数 'wc4': weight_var('wc4', [3, 3, 384, 384]), # wc5 卷积核3*3*384*256, 输入为4*4*384,所以in_channels=384,256代表卷积核个数,表示有256个3*3*384的卷积核,256即产生特征图的个数 'wc5': weight_var('wc5', [3, 3, 384, 256]), # wd1 2*2*256*4096, 输入为2*2*256,所以将其展平则为1*(2*2*256),4096表示全连接层神经元的个数 'wd1': weight_var('wd1', [4 * 4 * 256, 4096]), # wd2 4096*4096, 输入为[2*2*256,4096],4096表示有4096个神经元 'wd2': weight_var('wd2', [4096, 4096]), # out 4096,10, 输入为[4096,4096],10表示有10类————> 0-9 'out_w': weight_var('out_w', [4096, 10]) } # 设置偏移量 biases = { # 卷积层 'bc1': bias_var('bc1', [96]), 'bc2': bias_var('bc2', [256]), 'bc3': bias_var('bc3', [384]), 'bc4': bias_var('bc4', [384]), 'bc5': bias_var('bc5', [256]), # 全连接层 'bd1': bias_var('bd1', [4096]), 'bd2': bias_var('bd2', [4096]), # 输出层 'out_b': bias_var('out_b', [n_classes]) } # 构建神经网络结构,定义Alexnet网络结构 def alexnet(x, weights, biases, dropout): # 输入数字是1*784的数据,将其reshape成28*28的影像 x = tf.reshape(x, shape=[-1, 28, 28, 1]) # 1 conv 第1层卷积 # 卷积大小变化为28*28->28*28 conv1 = conv2d('conv1', x, weights['wc1'], biases['bc1'], padding='SAME') # 池化大小变化为28*28->14*14 pool1 = maxpool2d('pool1', conv1, k=3, s=2, padding='SAME') # 归一化大小变化为 14*14->14*14 norm1 = norm('norm1', pool1, lsize=5) # 2 conv 第2层卷积 # 卷积大小变化为14*14->14*14 conv2 = conv2d('conv2', norm1, weights['wc2'], biases['bc2'], padding='SAME') # 池化大小变化为14*14->7*7 pool2 = maxpool2d('pool2', conv2, k=3, s=2, padding='SAME') # 归一化大小变化为 7*7->7*7 norm2 = norm('norm2', pool2, lsize=5) # 3 conv 第3层卷积 # 卷积大小变化为 7*7—> 7*7 conv3 = conv2d('conv3', norm2, weights['wc3'], biases['bc3'], padding='SAME') # 4 conv 第4层卷积 # 卷积大小变化为 7*7—> 7*7 conv4 = conv2d('conv4', conv3, weights['wc4'], biases['bc4'], padding='SAME') # 5 conv 第5层卷积 # 卷积大小变化为 7*7—> 7*7 conv5 = conv2d('conv5', conv4, weights['wc5'], biases['bc5'], padding='SAME') # 池化大小变为 7*7->4*4 pool5 = maxpool2d('pool5', conv5, k=3, s=2, padding='SAME') # 归一化大小为7*7->7*7 norm5 = norm('norm5', pool5, lsize=5) # 1 fc 第1层全连接层 'wd1': shape为4*4*256 fc1 = tf.reshape(norm5, [-1, weights['wd1'].get_shape().as_list()[0]]) fc1 = tf.add(tf.matmul(fc1, weights['wd1']), biases['bd1']) fc1 = tf.nn.relu(fc1) # dropout丢弃层 fc1 = tf.nn.dropout(fc1, dropout) # 2 fc 第2全连接层 # fc2=tf.reshape(fc1,[-1,weights['wd2'].get_shape().as_list()[0]]) fc2 = tf.add(tf.matmul(fc1, weights['wd2']), biases['bd2']) fc2 = tf.nn.relu(fc2) # dropout丢弃层 fc2 = tf.nn.dropout(fc2, dropout) # output输出层 out = tf.add(tf.matmul(fc2, weights['out_w']), biases['out_b']) return out # 定义模型,损失函数和优化器 # 前向传播的预测值 pred = alexnet(x, weights, biases, dropout) # # 交叉熵损失函数,参数分别为预测值pred和实际label值y,reduce_mean为求平均loss cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y)) # 梯度下降优化器 optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # tf.equal()对比预测值的索引和实际label的索引是否一样,一样返回True,不一样返回False correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1)) # 将pred即True或False转换为1或0,并对所有的判断结果求均值 accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) # 定义一个saver,用于存储训练好的模型 saver = tf.train.Saver() # 定义存储路径 model = "./model" if not os.path.exists(model): os.makedirs(model) # 训练和评估模型,获得训练集的训练结果与测试集的测试准确率 # 初始化变量 init = tf.global_variables_initializer() # 定义一个Session with tf.Session() as sess: # 在sess里run一下初始化操作 sess.run(init) step = 1 # epoch 逐代循环 for epoch in range(epochs + 1): # 迭代 for _ in range(mnist.train.num_examples // batch_size): step += 1 # get x,y 逐个batch的去取数据 batch_x, batch_y = mnist.train.next_batch(batch_size) # 获取批数据 sess.run(optimizer, feed_dict={x: batch_x, y: batch_y}) # 展示损失和精确度 每display_step显示一次 if step % display_step == 0: # 计算损失值和精确度,并输出 loss, acc = sess.run([cost, accuracy], feed_dict={x: batch_x, y: batch_y}) print("Epoch " + str(epoch) + ", Minibatch Loss=" + \ "{:.6f}".format(loss) + ", Training Accuracy= " + \ "{:.5f}".format(acc)) print("Optimizer Finished!") # 存储训练好的模型 saver.save(sess, model + "/model.ckpt", global_step=epoch) # 测试准确率 for _ in range(mnist.test.num_examples // batch_size): batch_x, batch_y = mnist.test.next_batch(batch_size) print("Testing Accuracy:", sess.run(accuracy, feed_dict={x: batch_x, y: batch_y}))
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。