Tensorflow:理解Tensorflow中的sess.run原理机制,理解卷积神经网络输出形状shape
我们通过运行tensorflow中的程序,理解卷积和全连接操作的输出形状,并清楚为什么会是这个形状?
一、理解Tensorflow中的sess.run原理机制
首先,Tensorflow是google的开源深度学习框架,目前全球最流行的框架。其sess.run的运行原理可以简单地由下图来看。
其中ops是一个操作节点,Tensor可以理解成可以通过ops进行计算变换的矩阵。通过这种方式可以组成非常复杂的结构图(graph)来模拟人脑的神经元工作。
Tensorflow的图需要建立,图建立后需要在session中运行,也就是sess.run()。下面模拟了一个图,一个常量经过卷积操作后,输出。
import tensorflow as tf
import numpy as np
#建立Graph
inputs = tf.constant(np.random.rand(4,3,3,2))#创建4*3*3*2的随机张量
conv1 = tf.layers.conv2d(inputs, 5, (3, 3),padding = "same")#二维卷积操作
conv2 = tf.layers.conv2d(inputs, 5, (3, 3),padding = "valid")#二维卷积操作
#运行
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
[_conv1] = sess.run([conv1])
[_conv2] = sess.run([conv2])
print("当padding=same时:输出形状为:",_conv1.shape)
print("当padding=valid时:输出形状为:",_conv2.shape)
在上述代码中,sess.run()一共执行了两次,也就是图中的数据一共流动了两次。但是如果我们用下面的代码执行:
import tensorflow as tf
import numpy as np
#建立Graph
inputs = tf.constant(np.random.rand(4,3,3,2))#创建4*3*3*2的随机张量
conv1 = tf.layers.conv2d(inputs, 5, (3, 3),padding = "same")#二维卷积操作
conv2 = tf.layers.conv2d(inputs, 5, (3, 3),padding = "valid")#二维卷积操作
#运行
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
[_conv1, _conv2] = sess.run([conv1, conv2])
print("当padding=same时:输出形状为:",_conv1.shape)
print("当padding=valid时:输出形状为:",_conv2.shape)
这样sess.run()只执行了一次,也就是图中的数据只流动了一次。
通过这两种方式的区别我们可以清楚Tensorflow的数据流动机制,以以上例子,用图来理解。
现在的输入是一个常量,看不出来什么,如果真实的训练,就有差距了,比如我要看同一批输入,同时输出的conv1和conv2是什么,用两次sess.run()就不合理。因为这样输入已经变了,而且涉及到训练的时候可能已经反向传播了一次,参数w和b也变了。
sess.run()一次,数据只会流动一次,Tensorflow就会去分析图的构成。一个tensor可以流向两个地方的,就只用一个tensor就行。
通俗一点理解就是:如果两次卷积在一个sess.run()同时流动的话,输入是一样的,方便比较两次卷积的结果;如果两次卷积在两个sess.run()分别流动的话,两次卷积分别的输入值就不一样了。但因为例子里面是个常量,所以看着是一样的,实际是不一样的。
所以理解为什么叫Tensorflow了吗?我们run一次,Tensor就flow一次!
二、理解卷积的输出形状shape
理解了上述机制后,再继续看代码。
import tensorflow as tf
import numpy as np
#建立Graph
inputs = tf.constant(np.random.rand(4,3,3,2))#创建4*3*3*2的随机张量
conv1 = tf.layers.conv2d(inputs, 5, (3, 3),padding = "same")#二维卷积操作
conv2 = tf.layers.conv2d(inputs, 5, (3, 3),padding = "valid")#二维卷积操作
#运行
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
[_conv1, _conv2] = sess.run([conv1, conv2])
print("当padding=same时:输出形状为:",_conv1.shape)
print("当padding=valid时:输出形状为:",_conv2.shape)
我们调用Tensorflow中自带的conv包,对输入数据进行卷积操作。其中conv2d代表的是二维卷积操作,同样的conv3d就是三位卷积操作。在卷积过程中,tf.layers.conv2d()中的参数分别是输入数据、卷积核数目、卷积核形状和padding。padding通常去两个值,分别是“same”和“valid”,两种方式的输出结果区别如下:
接下来我们来着重理解一下,为什么输出的结果不同。
当padding=“same”时,会对原有的矩阵外圈进行均匀补零,见下图:
当padding=“valid”时,不会对原有矩阵进行扩充,将直接对原矩阵进行卷积操作,见下图:
三、理解全连接的输出形状shape
理解了上述的卷积的输出形状后,我们在此基础上,再加一个全连接层dense,输出结果的形状shape,并清楚为什么会是这个形状。
以卷积过程padding="same"为例。
import tensorflow as tf
import numpy as np
#建立Graph
inputs = tf.constant(np.random.rand(4,3,3,2))#创建4*3*3*2的随机张量
conv1 = tf.layers.conv2d(inputs, 5, (3, 3),padding = "same")#二维卷积操作
f_conv1 = tf.layers.flatten(conv1)#输出矩阵拉伸运算
dense = tf.layers.dense(f_conv1, 1024)#设置神经元个数为1024
#运行
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
[dense] = sess.run([dense])
print("全连接后的输出形状为:", dense.shape)
输出形状结果为:
以某一张图为输入层为例,从下图方便理解: