之前我们知道用卷积神经网络做图片识别
https://ieeexplore.ieee.org/document/726791/citations#citations
可是卷积神经网络是怎么工作的呢?
本文专门完整走一边神经网络,看看数据都发生了什么变化。
需要用到的库有tensorflow、matplotlib和cv2
import tensorflow as tf
import matplotlib.pyplot as plt
import cv2
首先我们读取一张图片,然后改成320×320的尺寸
img = cv2.imread('doll.png')
b,g,r = cv2.split(img) # cv2是bgr而不是rgb,所以需要重新排列
img = cv2.merge([r,g,b])
img = cv2.resize(img, (320, 320))
img = img / 255.0
可以查看一下图片是一个320×320×3的数组
array([[[0.6627451 , 0.67843137, 0.6745098 ],
[0.6627451 , 0.67843137, 0.6745098 ],
[0.6627451 , 0.67843137, 0.6745098 ],
...,
[0.49019608, 0.49019608, 0.48235294],
[0.49803922, 0.48235294, 0.47843137],
[0.49803922, 0.48235294, 0.47843137]],
[[0.6627451 , 0.68627451, 0.67843137],
[0.6627451 , 0.68627451, 0.67843137],
[0.6627451 , 0.68627451, 0.67843137],
...,
[0.49019608, 0.49019608, 0.48235294],
[0.49019608, 0.48627451, 0.47843137],
[0.49019608, 0.48627451, 0.47843137]],
[[0.6627451 , 0.69019608, 0.69019608],
[0.6627451 , 0.68627451, 0.68627451],
[0.6627451 , 0.68627451, 0.68627451],
...,
[0.49019608, 0.49019608, 0.48235294],
[0.49019608, 0.48627451, 0.47843137],
[0.49019608, 0.48627451, 0.47843137]],
...,
...
[0.6627451 , 0.67843137, 0.6745098 ],
...,
[0.32156863, 0.25098039, 0.20392157],
[0.31764706, 0.24705882, 0.2 ],
[0.31372549, 0.24313725, 0.19607843]]])
cv2的功能比较多,比如改变尺寸,如果不需要,只用自带的函数就可以完成
在matplatlib里面显示一下为
plt.imshow(img)
plt.show()
现在我们就把这张图片输入一下神经网络模型,看看每一步的变化。
总共如下重要参数
https://tensorflow.google.cn/api_docs/python/tf/keras/layers/Conv2D
tf.keras.backend.set_floatx('float64')
conv1 = tf.keras.layers.Conv2D(16, 2, input_shape=(320,320,3))
img_conv1 = conv1(tf.reshape(img, (1, 320, 320, 3)))
正常应该是输入一组320×320×3的图片
现在虽然只有一张也是要放入一个数组中所以reshape(1, 320, 320, 3)
输入的刚才的img,也就是320×320的三通道的1张图片
因为filters=16
,所以输出了16张图片
卷积核是2×2,所以320×320变成了319×319
TensorShape([1, 319, 319, 16])
注意,之前的3代表的是RGB三通道,现在的16代表了16个不同的卷积后的图片
返回的319×319的16张图片如下
我们修改一下参数,现在把kernel_size设置为4,步长设置为2,并且增加了relu为激活函数
conv2 = tf.keras.layers.Conv2D(16, 4, 2, activation=tf.nn.relu)
img_conv2 = conv2(img_conv1)
fig, axes = plt.subplots(4, 4, figsize=(6,6))
for m in range(4):
for n in range(4):
axes[m, n].imshow(img_conv2[0,:,:,m*4 + n])
plt.show()
TensorShape([1, 158, 158, 16])
图如下
因为步长为2,所以尺寸从之前的319减少到了158,少了一半
因为设置了relu激活函数,所以很多数据没到阈值直接变成0,所以很多图片直接“黑”了,因为0是黑色
池化是为了防止过拟合,表现效果就是尺寸更小
pool1 = tf.keras.layers.MaxPool2D()
img_pool1 = pool1(img_conv2)
fig, axes = plt.subplots(4, 4, figsize=(6,6))
for m in range(4):
for n in range(4):
axes[m, n].imshow(img_pool1[0,:,:,m*4 + n])
plt.show()
img_pool1.shape
TensorShape([1, 79, 79, 16])
池化之后从之前的158降低到了79
如图
再来一次卷积和池化,并且把深度加到64
conv3 = tf.keras.layers.Conv2D(32, 2, activation='relu')
img_conv3 = conv3(img_pool1)
conv4 = tf.keras.layers.Conv2D(64, 2, activation='relu')
img_conv4 = conv4(img_conv3)
pool2 = tf.keras.layers.MaxPool2D()
img_pool2 = pool2(img_conv4)
fig, axes = plt.subplots(8, 8, figsize=(8,8))
for m in range(8):
for n in range(8):
axes[m, n].imshow(img_pool2[0,:,:,m*8 + n])
plt.show()
img_pool2.shape
现在要把38×38×64的三维向量拉平,变成一维向量,计算机算的数据最好是一维
img_flatten = tf.keras.layers.Flatten()(img_pool2)
img_flatten.shape
TensorShape([1, 92416])
最后使用dense创建选链接,unit设置为多少就会变成多少个数据
img_dense1 = tf.keras.layers.Dense(32, activation=tf.nn.relu)(img_flatten)
img_dense1.shape
TensorShape([1, 32])
比如最终的图片分类是10个,那么最终就设置10个单位,一般留一个梯度,缓缓下降到最终的单位
cls = tf.keras.layers.Dense(10, activation=tf.nn.softmax)(img_dense1)
cls
可以看到,cls中的10个数据,就代表了每一个分类的概率
因为没有训练,数据比较平均
源代码:https://gitee.com/thales-ucas/deep-learning/blob/main/cnn.ipynb