TensorFlow基础——深入理解AlexNet代码中的每一个TF函数
本文关注如何使用TensorFlow中的函数来构建AlexNet,详细介绍每一个TF函数的应用方法,适合基础入门。
一. AlexNet的TensowFlow代码
首先给出AlexNet的代码,但是此处不会对AlexNet的原理做详细解析,而是将重点关注TensorFlow的函数使用
# 加载模块
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 导入数据
mnist = input_data.read_data_sets("MNIST_data",one_hot=True)
# 相关子函数定义 ###################################################
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))
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})
return result
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1,1,1,1], padding="SAME") # padding="SAME"用零填充边界
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1,2,2,1], padding="SAME")
#处理图片 ###################################################
xs = tf.placeholder(tf.float32, [None,784]) # 训练x
ys = tf.placeholder(tf.float32, [None,10]) # 训练y
keep_prob = tf.placeholder(tf.float32) # dropout的保留率
x_image = tf.reshape(xs, [-1, 28, 28, 1]) # 向量转矩阵:将1*784的向量reshape为28*28,-1表示不关注这一维度
#构建网络 ###################################################
## convl layer ##
W_conv1 = weight_variable([5,5,1,32]) # kernel 5*5, channel is 1, out size 32
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1) + b_conv1) # output size 28*28*32
h_pool1 = max_pool_2x2(h_conv1) # output size 14*14*32
## conv2 layer ##
W_conv2 = weight_variable([5,5,32,64]) # kernel 5*5, in size 32, out size 64
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1,W_conv2) + b_conv2) # output size 14*14*64
h_pool2 = max_pool_2x2(h_conv2) # output size 7*7*64
## funcl layer ##
W_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])
# [n_samples,7,7,64]->>[n_samples, 7*7*64]
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
## func2 layer ##
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2)+b_fc2)
#优化神经网络###################################################
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys*tf.log(prediction),reduction_indices=[1])) #定义损失函数
train_step = tf.train.GradientDescentOptimizer(1e-4).minimize(cross_entropy) # 定义求解器
sess =tf.Session() # 创建会话前台Python与后台C++之间的会话
sess.run(tf.initialize_all_variables()) # 计算图的初始化
# 主函数开始##################################################
if __name__ == '__main__':
for i in range(1000):
batch_xs,batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={xs:batch_xs,ys:batch_ys, keep_prob:0.5})
if i % 1 ==0:
# print(sess.run(prediction,feed_dict={xs:batch_xs}))
print(compute_accuracy(mnist.test.images,mnist.test.labels))
二. AlexNet的TensowFlow函数解析
此处将从头到尾,依次解析以上代码中用到的TF函数。
1. tf.nn、tf.layer、tf.contrib的区别
三者都是TF中定义的函数模块,但是使用有所不同。
- Tf.nn:提供每一层的底层实现的API,比如编辑一个myConv2d需要自行调用基础接口进行操作
- Tf.layer:是基于tf.nn封装的高级函数,比如定义conv2d可以直接调用tf.layers.conv2d
- Tf.contrib:也是封装完好的高级函数,但是tf.contrib包含不稳定和实验代码,有可能以后API会改变,比如tf.contrib**.layers.conv2d
2. tf.placeholder创建占位符
- 作用:Placeholder用于占位,等待计算过程中持续馈入的输入数据
- 格式:tf.placeholder(dtype, shape=None, name=None)
举例:x_train = tf.placeholder(tf.float32,shape=(None,28,28,1),name=“x_train”)
其中:● None用于表示batchsize;
● 28*28表示图片的高宽;
● 1表示图片的channel数,黑白图片为1,RGB图片为3
PS:为什么已经通过函数左值给定变量名了,还需要通过函数的name属性再次定义变量名。 例如x_train = tf.placeholder(tf.float32,shape=(None,28,28,1),name=“x_train1”)。
原因是:
● 函数左值x_train是Python语言中的变量名空间,这个变量名只是在tensorflow图的Python脚本运行过程中指向tensorflow图中的变量的临时指针,这个命名空间在脚本运行完毕之后就会自动删除。
● 而函数中定义的name="x_train1"是tensorflow命名空间中属于这个图变量的真实属性,这个属性不会随着Python代码运行结束而消除,并且在tensorboard中显示的是name="x_train1"这个属性。
3. tf.reshape 修改tensor的形状
- 格式:tf.reshape(tensor,shape,name=None)
4. tf.truncated_normal 创建截断式正态分布
-
作用:tf.truncated_normal作用是利用截断式(truncated截断式的)正态分布输出随机数
PS:截断式:如果生成的某个随机数大于了2倍标准差,就会重新生成,也就是保证生成的随机数处于【-2σ,2σ】区间内。
PS:正态分布中处于【-3σ,3σ】的概率为99.7%;正态分布中处于【-2σ,2σ】的概率为95.6%;正态分布中处于【-2σ,2σ】的概率为68.2% -
格式:tf.random.truncated_normal(shape,mean=0.0, stddev=1.0, dtype=tf.dtypes.float32, seed=None, name=None)
举例:w1_conv = tf.random.truncated_normal([5,5,1,32],mean=0.2,stddev=0.5,dtype=tf.flaot32,seed=1,name=‘w1_conv’)
说明:
(1)mean和stddev分别定义正态分布的均值和标准差,
(2)seed用于定义随机数的种子,对于正态分布而言,只要定义了相同的随机数种子,则每次输出的结果都是一样的。如下面的代码实验:只要两者有相同的随机数种子,则生成的随机数的前半部分都是相同的。
PS:这个shape对应着卷积核的shape,分别是【width,height,in_chinnels,out_chinnels】 -
补充:tf.random_normal()函数是生成普通正态分布的函数,tf.random_normal与tf.truncated_normal的参数格式相同,区别是前者不截断,后者阶段
-
实验代码:
# 加载模块
a = tf.Variable(tf.random_normal([2,2],seed=1)) # 普通的正态分布
b = tf.Variable(tf.truncated_normal([2,3],seed=1)) # 截断式正态分布
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
print(sess.run(a))
print(sess.run(b))
# 分别输出:
a:
[[-0.8113182 1.4845988 ]
[ 0.06532937 -2.4427042 ]]
b:
[[-0.8113182 1.4845988 0.06532937]
[ 0.0992484 0.6396971 1.6108712 ]]
# 可见只要随机数种子相同,就会生成相同的随机数。
5. tf.constant() 创建常量形式的张量
-
作用:tf.constant()基于给定的常数创建张量
-
格式:tb1 = tf.constant(value, dtype=None, shape=None, name=‘Const’,verify_shape=False )
其中value是给定的常量值或者常量的list。
举例:tensor = tf.constant([1, 2, 3, 4, 5, 6, 7]) 得到[1 2 3 4 5 6 7]
tensor = tf.constant(-1.0, shape=[2, 3])得到 [[-1. -1. -1.],[-1. -1. -1.]] -
实验代码:
# 加载模块
a = tf.Variable(tf.constant(1.2,shape=[3,3])) # 生成一个3*3的常量矩阵,所有值都是1.2
b = tf.Variable(tf.constant([1.2,1.3,1.4],shape=[7])) # 生成一个1*7的常量矩阵,前三个值是1.2,1.3,1.4,后续的值用1.4 填补
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
print(sess.run(a))
print(sess.run(b))
# 分别输出:
a:
[[1.2 1.2 1.2]
[1.2 1.2 1.2]
[1.2 1.2 1.2]]
b:
[1.2 1.3 1.4 1.4 1.4 1.4 1.4]
6. tf.Varible() 创建TF变量的方式
- 作用:tf.Varible() 创建TF变量的方式。
● tensorflow中的图变量需要先定义、然后初始化(tf.global_variables_initializer。),然后在赋值计算(tf.Session的run)
● 常用的变量定义函数有tf.Varible()或者tf.get_varible(),这两个函数非常重要,并且两者在变量域方面有较大差别,因此我么将在后续的文章中详细讲解。
7. tf.nn.relu() relu激活函数
- 作用:tf.nn.relu(features,name=None) 就是最普通的relu函数 relu = max(feature,0)
- PS:tf中还有另外几个relu系列函数
(1)tf.nn.relu6(features,name=None) 算法是:relu6 = min(max(feature,0),6),可以避免正方向激活后的值过大
(2)tf.nn.leaky_relu(features,alpha=0.2,name=None) 算法是 lrelu = max(feature,alpha*feature)alpha用于定义负区间的斜率
(3)tf.nn.crelu(features,name=None,axis=-1) 算法是CReLU(x)=[ReLU(x),ReLU(−x)],用于补足relu所抹去的负响应
8. f.nn.dropout() dropout方法
- 作用:
● tf.nn.dropout用于在训练过程中随机屏蔽1-keep_prob的比例的神经元,将这部分神经元的前馈值变成0,在本次训练中不参与更新与计算。
● 而剩下的keep_prob比例的神经元的值被乘以一个1/keep_prob的系数,这样保证 变换以后,最终的输出值相当于没变化。
● 说白了,tensorflow中的dropout就是:使输入tensor中某些元素变为0,其它没变0的元素变为原来的1/keep_prob大小! - 格式:tf.nn.dropout(x,keep_prob=None,noise_shape=None,seed=None,name=None,rate=None)
其中value是给定的常量值或者常量的list。 - PS:dropout在大型网络中比较实用,在小型网络中用不好就是累赘
9. tf.reduce_sum() 压缩求和函数
-
作用:tf.reduce_sum()用于压缩求和
-
格式:tf.reduce_sum(input_tensor,axis=None,keepdims=None,name=None,reduction_indices=None,keep_dims=None)
● axis 用于指明压缩方向,如果没有指明,就会直接压缩为1个常数
● keepdims表示按照某一维数就行压缩契合 -
实验代码:
import tensorflow as tf
import numpy as np
x = tf.constant([[1,1,1],[2,2,2]])
with tf.Session() as sess:
print(sess.run(tf.reduce_sum(x))) #所有求和 #输出 9
print(sess.run(tf.reduce_sum(x,0))) #按 列 求和 # 输出 [3 3 3]
print(sess.run(tf.reduce_sum(x,1))) #按 行 求和 # 输出 [3 6]
print(sess.run(tf.reduce_sum(x,1,keepdims=True))) #按维度 行 求和 #输出[[3],[6]]
print(sess.run(tf.reduce_sum(x,[0,1]))) #行列求和 # 输出9
print(sess.run(tf.reduce_sum(x,reduction_indices=[1]))) # 输出[3,6]
10. tf.reduce_mean() 压缩求均值函数
- 作用:tf.reduce_mean()用于压缩求均值
- 格式:其参数类似于reduce_sum
tf.reduce_mean(input_tensor,axis=None,keepdims=None,name=None,reduction_indices=None,keep_dims=None)
● axis 用于指明压缩方向,如果没有指明,就会直接压缩为1个常数
● keepdims表示按照某一维数就行压缩契合
11. f.train.GradientDescentOptimizer(1e-4).minimize(cross_entropy) # 求解器
- 作用:tf.train包含许多求解器,GradientDescentOptimizer(lr).minimize(loss)是一种基于梯度下降法的优化求解算法。
这是一种个比较大的模块,将在后面开辟新的章节专门讲解
12. tf.session 定义会话
-
作用:
● tensorflow的内核使用更加高效的C++作为后台,以支撑它的密集计算。
● tensorflow把前台(即python程序)与后台程序之间的连接称为"会话(Session)"
● tensorflow的世界里,变量的定义与计算时分开的,比如之前的tf.constant,tf.nn.relu的函数都只是定义了一个变量,而真正要开始幅值和计算还需要对其进行初始化
● sess=tf.session表示创建python与c++之间的会话,也就是启动之前定义好的图,并将这个会话命名为sess -
格式:tf.Session作为会话,主要功能是指定操作对象的执行环境,Session类构造函数有3个可选参数。
● target(可选):指定连接的执行引擎,多用于分布式场景。
● graph(可选):指定要在Session对象中参与计算的图(graph)。
● config(可选):辅助配置Session对象所需的参数(限制CPU或GPU使用数目,设置优化参数以及设置日志选项等)。
以上参数都是可选,一般一句sess=tf.session()就可以启动了 -
PS:
除了tf.session外,还可以使用tf.InteractiveSession()来创建会话,后者将当前会话设置为默认会话,这样在进行eval和run等操作的时候就不用指定名称了
● 对于tf.Session()
sess=tf.Session()
sess.run(op1,feed_dict(x:x1,y:y1))
● 对于tf.InteractiveSession()
sess=tf.InteractiveSession()
op1.eval(feed_dict(x:x1,y:y1))
● 也就是说:tf.InteractiveSession()相当于
sess=tf.Session()
with sess.as_default():
13. tf.session.run 用于运算
- 作用:tf.session.run 会话创建完成后,使用run方法来进行图中的op计算
- 格式:run(fetches,feed_dict=Noneoptions=None,run_metadata=None)
● fetches表示数据流图中能接收的任意数据流图元素,包括各类Op/Tensor对象
● feed_dict可选项,给数据流图中的placeholder提供运行时数据。feed_dict的数据结构为python中的字典,其元素为各种键值对。
● "key"为各种Tensor对象的句柄;"value"很广泛,但必须和“键”的类型相匹配,或能转换为同一类型 - 举例:
● sess.run(tf.initialize_all_variables()) 启动对所有算子的初始化,tf.session只是创建图,tf.initialize_all_variables()用于初始化所有op,等待输入;这个函数在新版本中将逐步被tf.global_variables_initializer()取代。
● sess.run(train_step, feed_dict={xs:batch_xs,ys:batch_ys, keep_prob:0.5}) # feed进计算图中的参数都是python的字典格式