卷积神经网络CNN
目录
一、CNN概述
二、图像基础知识
三、卷积层
3.1 卷积的计算
3.2 Padding
3.3 Stride
3.4 多通道卷积计算
3.5 多卷积核卷积计算
3.6 特征图大小计算
3.7 Pytorch 卷积层API
四、池化层
4.1 池化计算
4.2 Stride
4.3 Padding
4.4 多通道池化计算
4.5 Pytorch 池化层API
一、CNN概述
卷积神经网络是深度学习在计算机视觉领域的突破性成果。在计算机视觉领域,往往输入图像都很大,若使用全连接网络,计算代价较高。图像也很难保留原有的特征,导致图像处理的准确率不高
卷积神经网络(Convolutional Neural Network)是含有卷积层的神经网络。卷积层的作用就是用来自动学习、提取图像的特征
CNN网络主要有三部分构成:卷积层、池化层和全连接层构成,其中卷积层负责提取图像中的局部特征;池化层用来大幅降低参数量级(降维);全连接层用来输出想要的结果
二、图像基础知识
图像是由像素点组成的,每个像素点的值范围为[0, 255],像素值越大意味着较亮。一张 200x200 的图像,则是由 40000 个像素点组成,若每个像素点都是 0,意味着这是一张全黑的图像
彩色图一般都是多通道的图像,所谓多通道可以理解为图像由多个不同的图像层叠加而成。平常的彩色图像一般都是由 RGB 三个通道组成的,还有一些图像具有 RGBA 四个通道,最后一个通道为透明通道,该值越小,则图像越透明
import numpy as np
import matplotlib.pyplot as plt
def test01():
# 构建200 * 200, 像素值全为0的图像
image = np.zeros([200, 200])
plt.imshow(image, cmap='gray', vmin=0, vmax=255)
plt.show()
# 构建200 * 200, 像素值全为255的图像
image = np.full([200, 200], 255)
plt.imshow(image, cmap='gray', vmin=0, vmax=255)
plt.show()
def test02():
image = plt.imread('data/彩色图片.png')
print(image.shape)
# (640, 640, 4) 图像为 RGBA 四通道
# 修改数据的维度, 将通道维度放在第一位
image = np.transpose(image, [2, 0, 1])
# 打印所有通道
for channel in image:
print(channel)
plt.imshow(channel)
plt.show()
# 修改透明度
image[3] = 0.05
image = np.transpose(image, [1, 2, 0])
plt.imshow(image)
plt.show()
if __name__ == "__main__":
test01()
test02()
三、卷积层
3.1 卷积的计算
- input 表示输入的图像
- filter 表示卷积核, 也叫做滤波器
- input 经过 filter 的得到输出为最右侧的图像,即特征图
卷积运算本质上就是在滤波器和输入数据的局部区域间做点积
左上角的点计算方法:
按照上面的计算方法可以得到最终的特征图为:
3.2 Padding
通过上面的卷积计算过程,最终的特征图会比原始图像小很多,若想要保持经过卷积后的图像大小不变,可以在原图周围添加 padding 再进行卷积来实现
3.3 Stride
按照步长为1来移动卷积核,计算特征图如下所示:
若将 Stride 增大为2,也是可以提取特征图的,如下图所示:
3.4 多通道卷积计算
实际中的图像都是多个通道组成的
计算方法如下:
- 当输入有多个通道(Channel),如 RGB 三个通道,此时要求卷积核需要拥有相同的通道数
- 每个卷积核通道与对应的输入图像的各个通道进行卷积
- 将每个通道的卷积结果按位相加得到最终的特征图
3.5 多卷积核卷积计算
实际对图像进行特征提取时,需要使用多个卷积核进行特征提取。可以理解为从不同到的视角、不同的角度对图像特征进行提取
3.6 特征图大小计算
输出特征图的大小与以下参数息息相关:
- size:卷积核大小,一般会选择为奇数,如:1*1,3*3,5*5*
- Padding:零填充的方式
- Stride:步长
那计算方法如下图所示:
- 输入图像大小:W * W
- 卷积核大小: F * F
- Stride:S
- Padding:P
- 输出图像大小:N x N
样例
- 图像大小:5 * 5
- 卷积核大小:3 * 3
- Stride:1
- Padding:1
- (5 - 3 + 2) / 1 + 1 = 5,即得到的特征图大小为:5 * 5
3.7 Pytorch 卷积层API
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
def show(image):
plt.imshow(image)
plt.axis('off')
plt.show()
# 单个多通道卷积核
def test01():
# 读取图片, 形状(640, 640, 4) HWC
image = plt.imread('data/彩色图片.png')
show(image)
# 构建卷积层
conv = nn.Conv2d(in_channels=4, out_channels=1, kernel_size=3, stride=1, padding=1)
# 卷积层对输入数据的形状有要求,(batch_size, channel, height, weight)
image = torch.tensor(image).permute(2, 0, 1)
image = image.unsqueeze(0)
print(image.shape)
# 输入
output_image = conv(image)
print(output_image.shape)
# 调整形状为正常图像形状
output_image = output_image.squeeze(0).permute(1, 2, 0)
show(output_image.detach().numpy())
# 多个多通道卷积核
def test02():
# 读取图片, 形状(640, 640, 4) HWC
image = plt.imread('data/彩色图片.png')
show(image)
# 构建卷积层
# 由于out_channels为3, 相当于有3个4通道卷积核
conv = nn.Conv2d(in_channels=4, out_channels=3, kernel_size=3, stride=1, padding=1)
# 卷积层对输入数据的形状有要求,(batch_size, channel, height, weight)
image = torch.tensor(image).permute(2, 0, 1)
image = image.unsqueeze(0)
# 输入
output_image = conv(image)
print(output_image.shape)
# 调整形状为正常图像形状
output_image = output_image.squeeze(0).permute(1, 2, 0)
print(output_image.shape)
# 打印三个特征图
# 每组卷积核的参数不同, 在与输入图像进行卷积运算时会提取出不同的特征信息
show(output_image[:, :, 0].unsqueeze(2).detach().numpy())
show(output_image[:, :, 1].unsqueeze(2).detach().numpy())
show(output_image[:, :, 2].unsqueeze(2).detach().numpy())
if __name__ == "__main__":
test01()
test02()
四、池化层
池化层 (Pooling) 降低维度,缩减模型大小,提高计算速度。主要对卷积层学习到的特征图进行下采样(SubSampling)处理
池化层主要有两种:最大池化、平均池化
4.1 池化计算
最大池化:
- max(0,1,3,4)
- max(1,2,4,5)
- max(3,4,6,7)
- max(4,5,7,8)
平均池化:
- mean(0,1,3,4)
- mean(1,2,4,5)
- mean(3,4,6,7)
- mean(4,5,7,8)
4.2 Stride
最大池化:
- max(0,1,4,5)
- max(2,3,6,7)
- max(8,9,12,13)
- max(10,11,14,15)
平均池化:
- mean(0,1,4,5)
- mean(2,3,6,7)
- mean(8,9,12,13)
- mean(10,11,14,15)
4.3 Padding
最大池化:
- max(0,0,0,0)
- max(0,0,0,1)
- max(0,0,1,2)
- max(0,0,2,0)
- ... 以此类推
平均池化:
- mean(0,0,0,0)
- mean(0,0,0,1)
- mean(0,0,1,2)
- mean(0,0,2,0)
- ... 以此类推
4.4 多通道池化计算
在处理多通道输入数据时,池化层对每个输入通道分别池化,而不是像卷积层那样将各个通道的输入相加。这意味着池化层的输出和输入的通道数是相等
即:卷积会改变通道数,池化不会改变通道数
4.5 Pytorch 池化层API
import torch
import torch.nn as nn
# 基本使用
def test01():
inputs = torch.tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]]).float()
inputs = inputs.unsqueeze(0).unsqueeze(0)
# (1,1,3,3)
print(inputs.shape)
# 最大池化, 输入形状(batch_size, channel, height, weight)
polling = nn.MaxPool2d(kernel_size=2, stride=1, padding=0)
output = polling(inputs)
print(output)
# 平均池化
polling = nn.AvgPool2d(kernel_size=2, stride=1, padding=0)
output = polling(inputs)
print(output)
# stride
def test02():
inputs = torch.tensor([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]]).float()
inputs = inputs.unsqueeze(0).unsqueeze(0)
# 最大池化, 输入形状(batch_size, channel, height, weight)
polling = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)
output = polling(inputs)
print(output)
# 平均池化
polling = nn.AvgPool2d(kernel_size=2, stride=2, padding=0)
output = polling(inputs)
print(output)
# padding
def test03():
inputs = torch.tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]]).float()
inputs = inputs.unsqueeze(0).unsqueeze(0)
# 最大池化, 输入形状(batch_size, channel, height, weight)
polling = nn.MaxPool2d(kernel_size=2, stride=1, padding=1)
output = polling(inputs)
print(output)
# 平均池化
polling = nn.AvgPool2d(kernel_size=2, stride=1, padding=1)
output = polling(inputs)
print(output)
# 多通道池化
def test04():
inputs = torch.tensor([[[0, 1, 2], [3, 4, 5], [6, 7, 8]],
[[10, 20, 30], [40, 50, 60], [70, 80, 90]],
[[11, 22, 33], [44, 55, 66], [77, 88, 99]]]).float()
inputs.unsqueeze(0)
# 最大池化, 输入形状(batch_size, channel, height, weight)
polling = nn.MaxPool2d(kernel_size=2, stride=1, padding=0)
output = polling(inputs)
print(output)
# 平均池化
polling = nn.AvgPool2d(kernel_size=2, stride=1, padding=0)
output = polling(inputs)
print(output)
if __name__ == "__main__":
# test01()
# test02()
# test03()
test04()