python人脸识别项目之学习笔记(五):TensorFlow实现卷积,激励,池化 +全连接
需要学习的知识点
- 如何使用卷积,激励,池化
- 如何使用全连接
最近看了一位大佬写的对卷积的理解写得非常详细,可以参考这篇博客
https://blog.csdn.net/v_JULY_v/article/details/51812459 - 如何用代码实现卷积
1. 卷积(tf.nn.conv2d)
# conv2d(input, filter, strides, padding, use_cudnn_on_gpu=True, data_format="NHWC", name=None)
每一个参数代表一个形态shape,比如第一个参数【batch, h, w, channels】,里面的元素有4个,说明它是一个4维的张量,也就是4位的数组
如果形态不懂的话可以看看之前写的博客
https://editor.csdn.net/md/?articleId=107565145
如果通过形态得到一个4维的张量
第一个参数是输入,它是一个4维度的张量
【batch, h, w, channels】
batch 就是说一次输入多少张图片, 可以是1, 或者其他数
h 输入的高度
w 输入的宽度
channels 就是一张图片输入的通道数, 彩色是3, 黑白是1
第二个参数是一个卷积核,也是一个4维的张量
【k_h, k_w, in, out】
k_h 卷积核的高度
k_w 卷积核的宽度
in 卷积需要作用输入图片的通道数,可以是第一个参数的 channels 的值
out 卷积核的个数,也可以是输出的通道数
第三个参数是卷积核的移动步长,也是一个4维的张量
【1, s_h, s_w, 1】
s_h 就是高度方向移动的步长
s_w 就是宽度方向移动的步长
第四个参数是补0的方法,可以选择二个之中的其中一个
【“SAME"或者"VALID”】
SAME TensorFlow会自动补0,这样会保证输出大小和输入大小相同
VALID 不会自动补0
输入一张 3 * 3 的图片对他进行卷积操作
import tensorflow as tf
# 这是一张 3 *3 的彩色图片
input = tf.constant(
[ # 一张图片
[ # 图片的宽
[ # 图片的高
[100., 100., 100.], # 里面那一层就是图片的通道
[100., 100., 100.],
[100., 100., 100.]
],
[
[100., 100., 100.], # 里面那一层就是图片的通道
[100., 100., 100.],
[100., 100., 100.]
],
[
[100., 100., 100.], # 里面那一层就是图片的通道
[100., 100., 100.],
[100., 100., 100.]
]
]
]
)
# 做一个过滤器(卷积核)
filter = tf.constant(
[ # 卷积核的宽度为2
[ # 卷积核的高度为 2
[ # 通道数 为 3 (这里的通道就是输入图片的通道)彩色图片为3
[0.5, 0.1], # 卷积核的个数
[0.5, 0.1],
[0.5, 0.1]
],
[
[0.5, 0.1],
[0.5, 0.1],
[0.5, 0.1]
]
],
[
[
[0.5, 0.1],
[0.5, 0.1],
[0.5, 0.1]
],
[
[0.5, 0.1],
[0.5, 0.1],
[0.5, 0.1]
]
]
]
)
# 这是一个封装的函数
def conv2d(x, w):
# 可以看出每次宽移动1格,高移动1格
# padding='SAME' 让其主动补0
return tf.nn.conv2d(x, w, strides=[1, 1, 1, 1], padding='SAME')
result = conv2d(x, filter)
sess = tf.Session()
res = sess.run(result)
print(res)
sess.close()
执行的结果
可以看出他是一个 4 维的张量
输出是 2 个 3 * 3 的矩阵
为什么是 2 个 3 * 3 的矩阵???
2. ReLU激励层(tf.nn.relu)
- 明白为什么需要经过这个激励层,他的作用是什么?
不理解的可以看看这个大佬的博客
https://blog.csdn.net/a6333230/article/details/80887062
他非常请求的解释了激励层的作用。
我的理解:激励层的作用就是将线性的问题变化为非线性的问题。
先说下卷积层,学深度学习的应该都是知道卷积层的,那卷积层具体指什么呢?其实卷积层就是我们所做的一大堆特定的滤波器,该滤波器会对某些特定的特征进行强烈的响应,一般情况下是结果值非常大。而对一些无关特性,其响应很小,大部分是结果相对较小,或者几乎为0。这样就可以看做为激活,当特定卷积核发现特定特征时,就会做出响应,输出大的数值,而响应函数的存在把输出归为0~1,那么大的数值就接近1,小的数值就接近0。因此在最后计算每种可能所占比重时,自然大的数值比重大。
TF-激活函数 tf.nn.relu
将上面卷积得到的特征值得到,将这个特征值进行激活,代码如下
import tensorflow as tf
x = tf.constant(
[ # 一张图片
[ # 图片的宽
[ # 图片的高
[100., 100., 100.], # 里面那一层就是图片的通道
[100., 100., 100.],
[100., 100., 100.]
],
[
[100., 100., 100.], # 里面那一层就是图片的通道
[100., 100., 100.],
[100., 100., 100.]
],
[
[100., 100., 100.], # 里面那一层就是图片的通道
[100., 100., 100.],
[100., 100., 100.]
]
]
]
)
filter = tf.constant(
[ # 卷积核的宽度为2
[ # 卷积核的高度为 2
[ # 通道数 为 3
[0.5, 0.1],
[0.5, 0.1],
[0.5, 0.1]
],
[
[0.5, 0.1],
[0.5, 0.1],
[0.5, 0.1]
]
],
[
[
[0.5, 0.1],
[0.5, 0.1],
[0.5, 0.1]
],
[
[0.5, 0.1],
[0.5, 0.1],
[0.5, 0.1]
]
]
]
)
def conv2d(x, w):
return tf.nn.conv2d(x, w, strides=[1, 1, 1, 1], padding='SAME')
res = conv2d(x, filter)
with tf.Session() as sess:
print(sess.run(res))
b = tf.nn.relu(res)
print(sess.run(b))
执行结果
3. 池化(tf.nn.max_pool)
tf.nn.max_pool(value, ksize, strides, padding, name=None)
vale 是第一个参数,通常是特征图,一般都经过卷积he激励层得到的特征值
ksize是第二个参数,【1, height, width, 1】
strides第三个参数:和卷积类似,窗口在每一个维度上滑动的步长,一般也是 [1, stride,stride, 1]
padding是第四个参数:和卷积类似,可以取 ‘VALID’ 或者 ‘SAME’
根据上面的代码
import tensorflow as tf
x = tf.constant(
[ # 一张图片
[ # 图片的宽
[ # 图片的高
[100., 100., 100.], # 里面那一层就是图片的通道
[100., 100., 100.],
[100., 100., 100.]
],
[
[100., 100., 100.], # 里面那一层就是图片的通道
[100., 100., 100.],
[100., 100., 100.]
],
[
[100., 100., 100.], # 里面那一层就是图片的通道
[100., 100., 100.],
[100., 100., 100.]
]
]
]
)
filter = tf.constant(
[ # 卷积核的宽度为2
[ # 卷积核的高度为 2
[ # 通道数 为 3
[0.5, 0.1],
[0.5, 0.1],
[0.5, 0.1]
],
[
[0.5, 0.1],
[0.5, 0.1],
[0.5, 0.1]
]
],
[
[
[0.5, 0.1],
[0.5, 0.1],
[0.5, 0.1]
],
[
[0.5, 0.1],
[0.5, 0.1],
[0.5, 0.1]
]
]
]
)
def conv2d(x, w):
return tf.nn.conv2d(x, w, strides=[1, 1, 1, 1], 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')
res_conv2d = conv2d(x, filter)
res_relu = tf.nn.relu(res_conv2d)
res_pool = max_pool_2x2(res_relu)
sess = tf.Session()
res = sess.run(res_pool)
print(res)
sess.close()
执行结果
4. 全连接(tf.layers.dense)
要明白全连接层之前的作用是提取特征,全连接层的作用是分类。
全连接还是非常好理解的
这里有一个大佬的博客,写的非常容易理解
https://www.cnblogs.com/Terrypython/p/11147665.html
tf.layers.dense 函数的学习
tf.layers.dense(
inputs,
units,
activation=None,
use_bias=True,
kernel_initializer=None,
bias_initializer=tf.zeros_initializer(),
kernel_regularizer=None,
bias_regularizer=None,
activity_regularizer=None,
kernel_constraint=None,
bias_constraint=None,
trainable=True,
name=None,
reuse=None
)
inputs: 该层的输入
units: 输出的大小(维数),整数或long
activation: 使用什么激活函数(神经网络的非线性层),默认为None,不使用激活函数
use_bias: 使用bias为True(默认使用),不用bias改成False即可
这里有一道题,让我加深如何实现全连接
import tensorflow as tf
x = tf.constant([[90.0, 80.0, 70.0]])
w1 = tf.constant([[0.1, 0.2, 0.3, 0.4], [0.2, 0.3, 0.4, 0.5], [0.3, 0.4, 0.5, 0.6]])
b1 = tf.constant(0.1)
y1 = tf.matmul(x, w1) + b1
w1 = tf.constant_initializer([[0.1, 0.2, 0.3, 0.4], [0.2, 0.3, 0.4, 0.5], [0.3, 0.4, 0.5, 0.6]])
b1 = tf.constant_initializer(value=0.1)
y2 = tf.layers.dense(x, units=4, kernel_initializer=w1, bias_initializer=b1)
def func1(sess):
print('TF 矩阵乘法', sess.run(y1))
def func2(sess):
sess.run(tf.global_variables_initializer())
print('TF dense()', sess.run(y2))
if __name__ == '__main__':
with tf.Session() as sess:
func1(sess)
func2(sess)
执行结果