CNN的基本原理与实战
CNN基本原理
主要包括卷积层,池化层,全连接层。
卷积层
1、二维卷积
(1)卷积(Conv2D)
主要负责提取图像钟的局部特征,卷积核进行卷积运算的过程如下:
注:蓝色图是输入,而青色图是输出,下同。
具体计算细节如下图:
每一个卷积核都可以从不同的角度提取图片中的信息。
假定输入图片大小 W×W,Filter大小 F×F ,步长 S,padding的像素数 P。
于是我们可以得出 输出图片大小为NxN,其中 N = (W − F + 2P )/S+1
(2)反卷积(Conv2DTranspose)
在进行反卷积之前先对图像进行填充,下图中的虚线表示填充,然后进行卷积:
卷积与反卷积的输入输出关系正好相反。
2、一维卷积(Conv1D)
在Conv1D中,内核沿一维滑动,如下图所示:
该数据是从一个人戴在手臂上的加速度计中收集的。数据表示所有三个轴上的加速度。Conv1D可以根据加速度计数据执行活动识别任务,例如人站着,走路,跳跃等。此数据有2个维度。第一维是时间步长,其他维是3轴上的加速度值。
下图说明了内核如何在加速度计数据上移动。每行代表某个轴的时间序列加速度。内核只能沿时间轴一维移动。
在tensorflow2.0中实现代码如下
Conv1D(1,kernel_size = 5,input_shape =(120,3))
参数input_shape(120,3)表示120个时间步,每个时间步中有3个数据点。这3个数据点是x,y和z轴的加速度。
参数kernel_size为5,表示内核的宽度,卷积核高度与每个时间步中的数据点数相同。
Conv1D可以用于音频和文本数据等时间序列数据。如下图所示:
3、三维卷积(Conv3D)
在Conv3D中,卷积核按3个维度滑动,如下所示:
Conv3D主要用于3D图像数据。例如磁共振成像(MRI)数据。MRI数据被广泛用于检查大脑,脊髓,内部器官等。
三者的总结如下:
- 在1D CNN中,内核沿1个方向移动。1D CNN的输入和输出数据是2维的。主要用于时间序列数据。
- 在2D CNN中,内核沿2个方向移动。2D CNN的输入和输出数据是3维的。主要用于图像数据。
- 在3D CNN中,内核在3个方向上移动。3D CNN的输入和输出数据是4维的。通常用于3D 图像数据(MRI,CT扫描,视频)
池化层
池化层(Pooling)用来大幅降低参数量级(降维,防止过拟合),本质上是一种下采样,其过程如下:
原始图片是20×20的,我们对其进行下采样,采样窗口为10×10,最终将其下采样成为一个2×2大小的特征图。
下面介绍两种池化的逆过程
(1)UnSampling
下图描述了上采样和下采样的过程,二者是一个相反的过程:
(2)UnPooling
下图描述了上池化的过程:
可以看出,两者的区别在于UnSampling阶段没有使用MaxPooling时的位置信息,而是直接将内容复制来扩充Feature Map。UnPooling的过程,特点是在Maxpooling的时候保留最大值的位置信息,之后在unPooling阶段使用该信息扩充Feature Map,除最大值位置以外,其余补0。
全连接层
全连接层类似传统神经网络的部分,用来输出想要的结果。
将卷积和池化的结果进行Flatten或者GlobalMaxPooling2D,生成一维结果向量。继续与全连接网络进行连接,输出想要的结果(回归或者分类)。
CNN的实现
手写数字识别
引入必要的库
from tensorflow.keras.layers import *
from tensorflow.keras.models import *
from tensorflow.keras import layers
from tensorflow.keras import models
import tensorflow as tf
from tensorflow import keras
from sklearn import datasets
from sklearn.preprocessing import OneHotEncoder
import os
os.environ['CUDA_VISIABLE_DEVICE'] = '0'
加载数据集
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
# 归一化
x_train = x_train.astype("float32") / 255
x_test = x_test.astype("float32") / 255
构建一个简单的CNN
# 定义输入
_input = Input(shape=(28,28,1),name='img')
# 开始二维卷积,池化
x = Conv2D(32,3,(1,1),padding='valid',activation='relu')(_input)
x = MaxPooling2D(2)(x)
x = Conv2D(64,3,(1,1),padding='same',activation='relu')(x)
x = MaxPooling2D(2)(x)
x = Conv2D(64,3,(1,1),padding='same',activation='relu')(x)
x = Flatten()(x) #(none,256)
#接入全连接层
x = Dense(32,activation='relu')(x)
output = Dense(10,activation='relu')(x)
# 定义模型
model_1 = Model(_input,output)
# 编译
model_1.compile(loss =keras.losses.SparseCategoricalCrossentropy(from_logits=True),
optimizer=keras.optimizers.RMSprop(),
metrics = ['accuracy'])
注:SparseCategoricalCrossentropy适用于y=[2,5,6,9],CategoricalCrossentropy适用于y进行onehot后作为输入。
模型的训练
# 注意保持输入与模型的输入一致
history = model_1.fit(tf.expand_dims(x_train,-1), y_train,
epochs=5,batch_size=512,
validation_data=(tf.expand_dims(x_test,-1),y_test))v
训练结果可视化
import matplotlib.pyplot as plt
# 绘制训练 & 验证的准确率值
plt.plot(history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()
# 绘制训练 & 验证的损失值
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()
图片的编码解码
下面定义一个模型,能够将图像输入转换为 16 维向量的 encoder
模型,以及用于训练的端到端 autoencoder
模型。同时体现在tensorflow2.0中所有的模型均可像层一样被调用。
定义一个编码器
定义图片一个编码器
encoder_input = Input(shape=(28,28,1),name='original_img')
x = layers.Conv2D(16,3,activation='relu')(encoder_input) # (26,26,16)
x = layers.Conv2D(32,3,activation='relu')(x) # (24,24,32)
x = layers.MaxPooling2D(3)(x) # 局部池化 # (8,8,32)
x = layers.Conv2D(16,3,activation='relu')(x) # (6,6,16)
x = layers.Conv2D(16,3,activation='relu')(x) # (4,4,16)
encoder_output = layers.GlobalMaxPooling2D()(x) #全局池化, (None,16)
# 声明Model,可以将结果当作一个网络层一样调用
encoder = keras.Model(encoder_input,encoder_output,name='encoder')
定义一个解码器
# 定义解码器
decoder_input = keras.Input(shape=(16,),name='encoder_img')
x = layers.Reshape((4,4,1))(decoder_input)
x = layers.Conv2DTranspose(16,3,activation='relu')(x) # (6, 6, 32)
x = layers.Conv2DTranspose(32,3,activation='relu')(x) # (8, 8, 32)
x = layers.UpSampling2D(3)(x) # (24, 24, 32)
x = layers.Conv2DTranspose(16,3,activation='relu')(x) #(26, 26, 16)
decoder_output = layers.Conv2DTranspose(1,3,activation='relu')(x) # 输出维度多少?28,28,1.
# 声明Model,可以将结果当作一个网络层一样调用
decoder = keras.Model(decoder_input,decoder_output,name='decoder')
定义完整的模型
autoencoder_input = keras.Input(shape=(28,28,1),name='img')
# 将模型像层一样调用
encoder_img = encoder(autoencoder_input)
decoder_img = decoder(encoder_img)
# 定义模型
autoencoder = keras.Model(autoencoder_input,decoder_img, name='autoencoder')
# 模型的编译
autoencoder.compile(loss = 'mse',
optimizer='RMSprop',
metrics=[tf.keras.metrics.Accuracy()])
模型的训练
autoencoder.fit(tf.expand_dims(x_train,-1), tf.expand_dims(x_train,-1),epochs=2,batch_size=512)
要注意模型的输入与输出分别是啥!!!
一个经典的图片编码解码器:
来自论文:Learning Deconvolution Network for Semantic Segmentation
参考:
一文看懂卷积神经网络-CNN(基本原理+独特价值+实际应用)
A technical report on convolution arithmetic in the context of deep learning
上一篇: 交换机的基本原理与配置