从MNIST入门tensorflow———超详细分析MNIST代码

写在前面,tensorflow2.x已经出炉好久了,然而当前大部分资料仍是对应1.x版本,推荐新手们还是将1.x版本当作入门练习(1.14系列,1.15系列,1.16系列均可)

下面为MNIST相关代码详解,十分感谢所参考的各位前辈博客,笔芯

import tensorflow as tf

#导入mnist数据
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)


#CNN权重
# 参考https://blog.csdn.net/justgg/article/details/94362621
# 即tf.truncated_normal( 截断正态分布
# 	shape,               shape为常量(向量空间)的形状,如shape=[2,3,4]为X=2,Y=3,Z=4的矩阵
# 	mean=None,           正态分布均值(默认为0)
# 	stddev=None,         正态分布标准差(默认为1) 在tf.truncated_normal中如果x的取值在区间(μ-2σ,μ+2σ)之外则重新进行选择。这样保证了生成的值都在均值附近。
# 	dtype=None,          dtype为输出的数据类型,如tf.float32等
# 	seed=None,           随机数种子,为整型,设置后每次生成的随机数都一样。
# 	name=None            该函数操作的名字
# )
# tf.random_normal与tf.truncated_normal在于tf.random_normal会在整个正态分布空间内进行取值,而不是(μ-2σ,μ+2σ)内
# 关于seed,可用以下语句增加理解:
# i=0
# while(i<3):
#     tn1 = tf.truncated_normal([6],stddev=1,seed=1)
#     sess = tf.Session()
#     print(sess.run(tn1))
#     i+=1

# j=0
# while(j<3):
#     tn2 = tf.truncated_normal([6],stddev=1)
#     sess = tf.Session()
#     print(sess.run(tn2))
#     j+=1

def weight_variable(shape):  #用对称破坏的小噪声初始化权重
    initial =  tf.random.truncated_normal(shape,stddev=0.1)
    return tf.Variable(initial)

#偏置参数
#参考https://blog.csdn.net/csdn_jiayu/article/details/82155224
# tf.constant(               创建常量
#     value,                 value可为数字和数组,
#     dtype=None,            dtype为常量的数据类型,如tf.float32等,
#     shape=None,            shape为常量(向量空间)的形状,如shape=[2,3,4]为X=2,Y=3,Z=4的矩阵
#     name='Const',          name为该常量的名字,string类型
#     verify_shape=False     verify_shape默认为False,如果修改为True的话表示检查value的形状与shape是否相符,如果不符会报错。
# )

def bias_variable(shape):  #将偏置参数初始化为小的正数,以避免死神经元
    initial = tf.constant(0.1,shape = shape)
    return tf.Variable(initial)

#卷积
# 参考https://blog.csdn.net/mao_xiao_feng/article/details/78004522
# 参考https://blog.csdn.net/zuolixiangfisher/article/details/80528989
# 参考https://blog.csdn.net/qq_30934313/article/details/86626050
# tf.nn.conv2d (				卷积层
# 	input, 						输入,即卷积的图片,要求为一个张量,shape为 [ batch, in_height, in_weight, in_channel ],其中batch为图片的数量,in_height 为图片高度,in_weight 为图片宽度,in_channel 为图片的通道数,灰度图该值为1,彩色图为3。
# 	filter, 					卷积核参数,要求为一个张量,shape为 [ filter_height, filter_weight, in_channel, out_channels ],其中 filter_height 为卷积核高度,filter_weight 为卷积核宽度,in_channel 是图像通道数 ,和 input 的 in_channel 要保持一致,out_channel 是卷积核数量。
# 	strides,                    卷积步长,是一个长度为4的一维向量,[ 1, strides(height), strides(width), 1],第一位和最后一位一般是1
# 	padding, 					string类型,值为“SAME” 和 “VALID”,表示是否考虑边界填充。"SAME"是考虑边界,不足的时候用0去填充周围,"VALID"则不考虑    !!不同的值会造成不同的输出公式,详见https://blog.csdn.net/alxe_made/article/details/80834305
# 	use_cudnn_on_gpu, 	        bool类型,是否使用cudnn加速,默认为true(gpu真的贵,穷学生买不起)	
# 	name=None                   该函数操作的名字
# )
def conv2d(x,W):
    return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')

# #最大池化
# 参考https://blog.csdn.net/m0_37586991/article/details/84575325
# 参考https://blog.csdn.net/mao_xiao_feng/article/details/53453926
# tf.nn.max_pool(   			池化层
#     input,   					输入,一般池化层接在卷积层后面,所以输入通常是feature map,依然是[batch, in_height, in_width, in_channels]这样的shape 其中,in_height为卷积map的out_height, in_width卷积后map的out_width, in_channels=filter的out_channel
#     ksize,  					池化窗口的大小,取一个四维向量,一般是[1, height, width, 1],因为我们不想在batch和channels上做池化,所以这两个维度设为1
#     strides,  				池化滑动步长,是一个长度为4的一维向量,[ 1, strides, strides, 1],第一位和最后一位一般是1
#     padding,					string类型,值为“SAME” 和 “VALID”,表示是否考虑边界填充。"SAME"是考虑边界,不足的时候用0去填充周围,"VALID"则不考虑
#     name=None        			该函数操作的名字
# )

def max_pool_2x2(x):
    return tf.nn.max_pool2d(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')

# 初始化输入X
# 参考https://blog.csdn.net/kdongyi/article/details/82343712
# tensorflow.compat.v1  因为tensorflow2.x的版本不再支持placeholder,因此其中提供了tensorflow.compat.v1代码包来兼容原有1.x的代码,可以做到几乎不加修改的运行。
# 在tf1.x版本中,我们必须将图表分为两个阶段:
# 构建一个描述您要执行的计算的计算图。这个阶段实际上不执行任何计算;它只是建立了计算的符号表示。该阶段通常将定义一个或多个表示计算图输入的“占位符”(placeholder)对象。
# 多次运行计算图。 每次运行图形时(例如,对于一个梯度下降步骤),您将指定要计算的图形的哪些部分,并传递一个“feed_dict”字典,该字典将给出具体值为图中的任何“占位符”。
# tf.placeholder(    此函数可以理解为形参,用于定义过程,在执行的时候再赋具体的值
# 	dtype, 			 数据类型。常用的是tf.float32,tf.float64等数值类型
# 	shape=None, 	 数据形状。默认是None,就是一维值,也可以是多维(比如[2,3], [None, 3]表示列是3,行不定),因为MNIST数据为24*24*1图像,所以为784
# 	name=None        该函数操作的名字
# )
x = tf.placeholder(tf.float32, shape=[None,784])
# 初始化输出Y
# 因为MNIST为[0,9]共十个分类
y_ = tf.placeholder(tf.float32,shape=[None,10])

#第一个卷积层
#创建卷积核W_conv1,表示卷积核大小为5*5,第一层网络的输入和输出神经元个数分别为1和32
W_conv1 =  weight_variable([5,5,1,32])
b_conv1 = bias_variable([32])

# 参考https://blog.csdn.net/apengpengpeng/article/details/80579454
# 参考https://blog.csdn.net/m0_37592397/article/details/78695318
# tf.reshape(          改变张量形式
#     tensor,          tensor类型,即placeholder所处理得到的
#     shape,           输出的张量形式
#     name=None        该函数操作的名字
# )
#把输入x(二维张量,shape为[None, 784])变成4维的x_image,x_image的shape是[None,28,28,1]
#-1表示自动推测这个维度的size,即传给None
x_image = tf.reshape(x,[-1,28,28,1])
#relu激化和池化
# tf.nn.relu,使用relu激活函数
# 参考https://blog.csdn.net/tyhj_sf/article/details/79932893查看激活函数的作用
h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1) + b_conv1)
#h_pool1的输出即为第一层网络输出,shape为[batch,14,14,32]
h_pool1 = max_pool_2x2(h_conv1)

#第二个卷积层
W_conv2 = weight_variable([5,5,32,64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1,W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

#全连接层
#该层拥有1024个神经元(神经元个数可在0~4000间调参)
#W的第1维size为7*7*64,7*7是h_pool2输出的size,64是第2层输出神经元个数
w_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
# 将第2层的输出reshape成[batch, 7*7*64]的张量
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
# 参考https://www.cnblogs.com/AlvinSui/p/8987707.html
# tf.matmul(  矩阵相乘x*y
# 	x,    	  矩阵x    
# 	y 		  矩阵y
# )
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat,w_fc1) + b_fc1)

#Dropout减少过拟合
keep_prob = tf.placeholder(tf.float32)
#参考https://blog.csdn.net/huahuazhu/article/details/73649389
#参考https://blog.csdn.net/yangfengling1023/article/details/82911306
# tf.nn.dropout(
#     x,					输入,为tensor类型
#     keep_prob,			float类型,每个元素被保留下来的概率,设置神经元被选中的概率,在初始化时keep_prob只是一个占位符
#     noise_shape=None,     一个1维的int32张量,代表了随机产生“保留/丢弃”标志的shape。
#     seed=None,			随机数种子,为整型,设置后每次生成的随机数都一样。
#     name=None     		该函数操作的名字
# )
h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)


#读出层
W_fc2 = weight_variable([1024,10])
b_fc2 = bias_variable([10])
y_conv = tf.matmul(h_fc1_drop,W_fc2) + b_fc2


#用交叉熵作为代价函数
# 参考https://www.jianshu.com/p/cf235861311b
# 参考https://blog.csdn.net/mieleizhi0522/article/details/80200126
# 参考https://blog.csdn.net/zwqjoy/article/details/78952087
cross_entropy = tf.reduce_mean(
	# 参考https://blog.csdn.net/mao_xiao_feng/article/details/53382790(评论第一条有大用)
	# 参考https://blog.csdn.net/qq_35203425/article/details/79773459
	# tf.nn.softmax_cross_entropy_with_logits(
	# 	logits, 		神经网络最后一层的输出,如果有batch的话,它的大小就是[batchsize,num_classes],单样本的话,大小就是num_classes,需和标签大小一致
	# 	labels, 		实际的标签
	# 	name=None   	该函数操作的名字
	# )
    tf.nn.softmax_cross_entropy_with_logits(labels = y_,logits = y_conv)
    )

# 引入tf.train.AdamOptimizer().minimize进行梯度下降,学习率为0.0001
# 函数内容详见https://blog.csdn.net/TeFuirnever/article/details/88933368
# 		   及https://blog.csdn.net/lomodays207/article/details/84027365
# 		   及https://blog.csdn.net/polyhedronx/article/details/93405760及官方文档
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

#评估函数
# 参考https://www.jianshu.com/p/1aec70f77b36
# 参考https://blog.csdn.net/kdongyi/article/details/82390394
# tf.argmax(						返回最大的那个数值所在的下标
#     input,						输入矩阵
#     axis=None,				    axis可被设置为0或1,分别表示0:按列计算,1:行计算
#     name=None,					设置函数名称
#     dimension=None,
#     output_type=tf.int64			返回数据类型
#     )
# 参考https://blog.csdn.net/ustbbsy/article/details/79564529
# tf.equal(				判断输入x,y是否相等
# 	x, 				    输入x
# 	y,    				输入y
# 	name=None  			设置函数名称
# 	)
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_, 1))
# 参考https://blog.csdn.net/dcrmg/article/details/79747814
# tf.cast(         执行 tensorflow 中张量数据类型转换
#   input,         待转换的数据(张量)   
# 	dtype, 		   目标数据类型
# 	name=None      设置函数名称
# 	)
# 参考https://blog.csdn.net/dcrmg/article/details/79797826
# tf.reduce_mean(      				计算张量tensor沿着指定的数轴(tensor的某一维度)上的的平均值,主要用作降维或者计算tensor(图像)的平均值。
# 	input_tensor,					输入张量
#     axis=None,					指定的轴,如果不指定,则计算所有元素的均值;
#     keep_dims=False, 				是否降维度,设置为True,输出的结果保持输入tensor的形状,设置为False,输出结果会降低维度;
#     name=None,					设置函数名称
#     )
#计算正确预测项的比例,因为tf.equal返回的是布尔值,
#使用tf.cast把布尔值转换成浮点数,然后用tf.reduce_mean求平均值
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

#运行
with tf.Session() as sess:
	# 参考https://blog.csdn.net/qq_37285386/article/details/89054090
	# 参考https://blog.csdn.net/surserrr/article/details/89421925
	# sess.run(tf.global_variables_initializer())    初始化全局所有变量
    sess.run(tf.global_variables_initializer())
    for i in range(2000):
    	#train.next_batch(50)   每次随机从训练集中抓取50幅图像
    	# 参考https://blog.csdn.net/qq_33254870/article/details/81390897
    	# 参考https://blog.csdn.net/qq_41140351/article/details/92710525
        batch = mnist.train.next_batch(50)  
        if i % 20 == 0:
        	#feed_dict 和 placeholder的相生相灭
        	# 参考https://blog.csdn.net/lcczzu/article/details/91416211
        	# 参考https://www.cnblogs.com/itboys/p/8858172.html

        	# accuracy.eval(feed_dict = {x:batch[0], y_:batch[1], keep_prob:1.0 }) = sess.run(accuracy,feed_dict = {x:batch[0], y_:batch[1], keep_prob:1.0 })  
        	# 参考https://blog.csdn.net/zxyhhjs2017/article/details/82349535
        	# 参考https://blog.csdn.net/qq_31150463/article/details/84561478
        	# 参考https://segmentfault.com/a/1190000015287066?utm_source=tag-newest
            train_accuracy = accuracy.eval(feed_dict = {x:batch[0], y_:batch[1], keep_prob:1.0 })
            print('step %d, training accuracy %g' % (i, train_accuracy))
        train_step.run(feed_dict = {x:batch[0], y_:batch[1], keep_prob:0.5 })

CNN入门:
https://www.bilibili.com/video/av13260183?p=16
一个十分有意思的网站:
http://scs.ryerson.ca/~aharley/vis/conv/

你可能感兴趣的:(从MNIST入门tensorflow———超详细分析MNIST代码)