前一阵在网上找了一个基于CNN的mnist数据集的原码来修改,结合了大牛们的博客,并且添加了一些自己的详细备注。当遇到问题时,会继续补充,也希望大家讨论,指出解释或者代码中不合理的地方。话不多说,代码见:
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
from PIL import Image
import random
import numpy as np
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
"""
MNIST是深度学习的经典入门demo,他是由6万张训练图片和1万张测试图片构成的,
每张图片都是28*28大小,而且都是黑白色构成(这里的黑色是一个0-1的浮点数,
黑色越深表示数值越靠近1)
batch_xs, batch_ys = mnist.train.next_batch(2)
for one_pic_vic in batch_xs:
one_pic_arr = np.reshape(one_pic_vic,(28,28))
plt.imshow(one_pic_arr)
pylab.show()
"""
# 加载图片
def get_picture(img_path):
img = Image.open("test.jpg")# 加载RGB图片或灰度图片
img = img.resize((28, 28), Image.ANTIALIAS).convert('L')# 将图片重置为28*28*1
img = np.array(img.getdata()).reshape((1, 784))# 将图片的像素矩阵转化为一个向量
img = [10*(255-x)*1.0/255.0 for x in img]# 将图片的像素值缩小到0-1之间
return img[0]# 转化为向量
# 识别准确度
def compute_accuracy(v_xs, v_ys):
global prediction
y_pre = sess.run(prediction, feed_dict={xs: v_xs, keep_prob: 1})# 根据测试集中的图片预测数值
correct_prediction = tf.equal(tf.argmax(y_pre, 1), tf.argmax(v_ys, 1))# 预测值与测试集中的正确数值相比较,相同为True,不同为False
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))# 将布尔类型转化为单精度浮点类型
result = sess.run(accuracy, feed_dict={xs: v_xs, ys: v_ys, keep_prob: 1})# 运行accuracy,得到识别准确度
return result# 返回识别准确度
# 定义权重
def weight_variable(shape):
initial = tf.truncated_normal( shape, mean=0,stddev=0.1 ) # 生成随机数
"""shape表示生成张量的维度,mean是均值,stddev是标准差"""
return tf.Variable( initial )
# 定义偏值
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape) # biases最好定义为正数
return tf.Variable(initial)
# 定义卷积层
def conv2d(x, W): # 其中的x即为输入数据(例如RGB图片,W即为上面定义的权重)
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
# tensorflow中有定义好的卷积层,直接调用即可
# 其中,strides([1,x_movements,y_movements,1])为卷积核滑动步长,padding为补零方法
# 定义池化层
def max_pool_2x2(x): # 其中的x为convd2中运算完毕的x
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# 此处调用的是tensorflow中定义好的最大池化层
# 其中,ksize为池化核的大小,strides([1,x_movements,y_movements,1])为卷积核滑动步长,padding为补零方法
# 定义神经网络的placeholder placeholder用于传回
xs = tf.placeholder(tf.float32, [None, 43200]) # 因为输入数据是28*28的矩阵(包括了所有的输入图片) 表示列是784,行不定
ys = tf.placeholder(tf.float32, [None, 2]) # 其中的10是因为有0—9十个数字 表示列是10,行不定
keep_prob = tf.placeholder(tf.float32) #一维的数组
"""输入尺寸"""
x_image = tf.reshape(xs, [-1, 28, 28, 3]) # 其中的-1代表着一共输入了多少数据,两个28即为输入数据矩阵的大小,1为输入图片的通道数(因为是灰色照片)
"""重新定义图片"""
# 定义conv1 layer
W_conv1 = weight_variable([3, 3, 1, 32]) # 其中的两个3代表卷积核的大小,1代表输入的一张图片(包含许多特征)的通道数,
#32代表经过这一神经层处理后生成的通道数(特征数) 也是卷积核的深度 depth
"""注意: 随机生成初始化filter"""
b_conv1 = bias_variable([32]) # 因为生成了32个通道
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # 经过卷积运算和激活函数运算得到的数据,尺寸:28*28*32
"""卷积+激活"""
h_pool1 = max_pool_2x2(h_conv1) # 经过最大池化运算得到的数据,尺寸:14*14*32(池化不改变通道数只改变大小)
# 定义conv2 layer
W_conv2 = weight_variable([3, 3, 32, 64]) # 其中的两个3代表卷积核的大小,32代表输入图片(包含许多特征)经过conv1 layer处理后的通道数,
#64代表经过这一神经层处理后生成的通道数(特征数)
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) # 经过卷积运算和激励函数运算得到的数据,尺寸:14*14*64
h_pool2 = max_pool_2x2(h_conv2) # 经过最大池化运算得到的数据,尺寸:7*7*64(池化不改变通道数只改变大小)
# 定义conv3 layer
W_conv3 = weight_variable([3, 3, 64, 128]) # 其中的两个3代表卷积核的大小,32代表输入图片(包含许多特征)经过conv2 layer处理后的通道数,
#128代表经过这一神经层处理后生成的通道数(特征数)
b_conv3 = bias_variable([128])
h_conv3 = tf.nn.relu(conv2d(h_pool2, W_conv3) + b_conv3) # 经过卷积运算和激励函数运算得到的数据,尺寸:7*7*128(池化不改变通道数只改变大小)
# 定义func1 layer
W_fc1 = weight_variable([7 * 7 * 128, 1024])
"""注意: 随机生成初始化FC的权重"""
b_fc1 = bias_variable([1024])
h_pool2_falt = tf.reshape(h_conv3, [-1, 7 * 7 * 128]) # 将h_pool2的形状改为[-1,7*7*128]
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_falt, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
"""防止过拟合Dropout就是在不同的训练过程中随机扔掉一部分神经元。也就是让某个神经元的激活值以一定的概率p,
让其停止工作,这次训练过程中不更新权值,也不参加神经网络的计算。但是它的权重得保留下来(只是暂时不更新而已),因为下次样本输入时它可能又得工作了"""
# 定义func2 layer
W_fc2 = weight_variable([1024, 10])
"""输出10个分类,0~9"""
b_fc2 = bias_variable([10])
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2) # 进行分类,计算概率 使用softmax算法计算概率 两个矩阵中对应元素各自相乘
# 定义交叉熵损失函数(分类问题常用交叉熵损失函数)
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), reduction_indices=[1]))
"""
tf.reduce_mean函数的作用是求平均值 --- reduce_sum应该理解为压缩求和,用于降维 reduction_indices=[1] 表示将数据降到一维
求平均的原因是因为采用的小批量梯度下降
"""
# 定义训练函数
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) #使用Adam 算法 梯度下降优化器
with tf.Session() as sess: #上下文管理器
sess.run(tf.initialize_all_variables()) # 激活神经网络 初始化所有的参数
counter = 10
# 训练过程
for step in range(100):
batch_xs, batch_ys = mnist.train.next_batch(100) # 从训练集中一次提取100张图片进行训练
sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys, keep_prob: 0.5}) #keep_prob: dropout 随机率为0.5
if step % 1 == 0:
print('the remaining times trained is ', step, '.')
print()
# 保存神经网络中的参数
saver = tf.train.Saver()
save_path = saver.save(sess, 'my_Mnist_parameter1/save_parameter.ckpt')
# 提示神经网络已经完成训练
print('the current accuracy is ', compute_accuracy(mnist.test.images, mnist.test.labels), '.')
print('You have chosen to load image, please input the path of the image and the model will recognize it.')
# 加载图片并输出预测值
number_picture = get_picture("A")# 加载图片
print( number_picture )
prediction_num = sess.run(prediction, feed_dict={xs: np.array([number_picture]), keep_prob: 1})
prediction_num = np.argmax(prediction_num, axis=1)# 得到预测值
print('The predictive number is ', prediction_num[0], '.')# 输出预测值
# 判断是否继续输入图片
print('Continuing to input the image or not? Y/N')
choice = input()
# 确保输入的choice的值是Y或N
while (choice != 'Y' and choice != 'N'):
print('Warning! Please input Y or N rather than any other words!')
choice = input()
print('The training has been end!')
TensorFlow学习记录
1、在TensorFlow的世界里,变量的定义和初始化是分开的,所有关于图变量的赋值和计算都要通过tf.Session的run来进行。想要将所有图变量进行集体初始化时应该使用tf.global_variables_initializer。
2、with 语句是使用上下文管理器:上下文管理器就是实现了上下文协议的类,而上下文协议就是一个类要实现__enter__()和__exit__()两个方法。一个类只要实现了__enter__()和__exit__(),我们就称之为上下文管理器下面我们具体说下这两个方法。
3、交叉熵:在最理想的情况下,如果一个样本属于k,那么这个类别所对应的的输出节点的输出值应该为1,而其他节点的输出都为0,即[0,0,1,0,….0,0],这个数组也就是样本的Label,是神经网络最期望的输出结果,交叉熵就是用来判定实际的输出与期望的输出的接近程度! 详细推荐:https://blog.csdn.net/red_stone1/article/details/80735068
4、placeholder是TensorFlow中的占位符,用于将外部的数据传到TensorFlow当中,形成一个TensorFlow的数据。
5、tf.nn.conv2d是TensorFlow里面实现卷积的函数,参考文档对它的介绍并不是很详细,实际上这是搭建卷积神经网络比较核心的一个方法,非常重要 tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)
6、TensorFlow使用一个线程源源不断的将硬盘中的图片数据读入到一个内存队列中,另一个线程负责计算任务,所需数据直接从内存队列中获取。
7、
tf.train.batch(
tensors,
batch_size,
num_threads=1,
capacity=32,
enqueue_many=False,
shapes=None,
dynamic_pad=False,
allow_smaller_final_batch=False,
shared_name=None,
name=None)tensors:一个列表或字典的tensor用来进行入队
batch_size:设置每次从队列中获取出队数据的数量
num_threads:用来控制入队tensors线程的数量,如果num_threads大于1,则batch操作将是非确定性的,输出的batch可能会乱序
capacity:一个整数,用来设置队列中元素的最大数量
enqueue_many:在tensors中的tensor是否是单个样本
shapes:可选,每个样本的shape,默认是tensors的shape
dynamic_pad:Boolean值.允许输入变量的shape,出队后会自动填补维度,来保持与batch内的shapes相同
allow_samller_final_batch:可选,Boolean值,如果为True队列中的样本数量小于batch_size时,出队的数量会以最终遗留下来的样本进行出队,如果为Flalse,小于batch_size的样本不会做出队处理
shared_name:可选,通过设置该参数,可以对多个会话共享队列
name:可选,操作的名字
8、 batch size(批量尺寸)理解: 批量梯度下降:为了同时保证训练过程比较快,和最终训练参数的准确率。问题:小批量梯度下降,是否是求平均值,然后在更新一次参数,否则,如何才能达到其效果。
9、softmax算法:解决了两个问题:1)每个输出信号值在0至1之间,2)所有输出信号的和为1。
10、tf.matmul() 两个矩阵中对应元素各自相乘 ,做矩阵相乘的运算
11、tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None,name=None) """防止过拟合Dropout就是在不同的训练过程中随机扔掉一部分神经元。也就是让某个神经元的激活值以一定的概率p,让其停止工作,这次训练过程中不更新权值,也不参加神经网络的计算。但是它的权重得保留下来(只是暂时不更新而已),因为下次样本输入时它可能又得工作了,keep_prob: 设置神经元被选中的概率"""
12、tf.Variable 通过Variable()构造函数后,此variable的类型和形状固定不能修改了
13、tf.argmax(input , axis) axis=0时比较每一列的元素,将每一列最大元素所在的索引记录下来,axis=1的时候,将每一行最大元素所在的索引记录下来
14、os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表。这个列表以字母顺序。 它不包括 '.' 和'..' 即使它在文件夹中。
只支持在 Unix, Windows 下使用。
15、np.hstack:按水平方向(列顺序)堆叠数组构成一个新的数组