我们现在开始了解卷积神经网络,卷积神经网络是深度学习在计算机视觉领域的突破性成果,在计算机视觉领域,往往我们输入的图像都很大,使用全连接网络的话,计算的代价较高,图像也很难保留原有的特征,导致图像处理的准确率不高。
CNN
卷积神经网络(Convolutional Neural Networks,简称CNN)是一种特殊类型的神经网络,特别适合于处理具有网格结构的数据,如图像和视频。CNN的设计灵感来源于生物视觉系统,特别是大脑视觉皮层对视觉信息的处理方式。
关键组件:
-
卷积层(Convolutional Layer):这是CNN的核心,使用一组可学习的滤波器(或称卷积核)扫描输入数据,以检测特定特征,如边缘、纹理等。滤波器的移动步长(stride)和填充(padding)是可以调整的参数,以控制输出特征图的尺寸。
-
激活函数(Activation Function):如ReLU(Rectified Linear Unit)等,为网络引入非线性,使得模型能学习复杂的模式。
-
池化层(Pooling Layer):通常紧随卷积层之后,用于降低数据的空间维度,减少计算量,同时保持重要特征不变,常见的有最大池化(Max Pooling)。
-
全连接层(Fully Connected Layer):位于网络的较深层,所有特征被整合并用于最终的分类或回归任务。
图像
图像是由像素点组成的,每个像素点的值范围为: [0, 255]
- 0 表示最暗的黑色,即没有光亮。
- 255 表示最亮的白色,即光亮度最大。
我们看到的彩色图一般都是多通道的图像, 所谓多通道可以理解为图像由多个不同的图像层叠加而成,最常见的RGB彩色图像由三个通道组成——红色、绿色、蓝色,每个通道都有自己的像素值(也是0到255的范围),三者的组合可以表示出数百万种不同的颜色。
我们通过一个简单的例子来使用Python的matplotlib
库来加载并显示一张图像,进而理解像素和通道的概念。
from PIL import Image
import matplotlib.pyplot as plt
# 加载图像
image_path = 'your_image_path.jpg' # 替换为你的图片路径
img_pil = Image.open(image_path)
# 使用matplotlib显示图像
plt.figure(figsize=(8, 8)) # 设置显示窗口大小
plt.imshow(img_pil) # 显示图像
# 如果是RGB图像,可以分离通道查看
if img_pil.mode == 'RGB':
r, g, b = img_pil.split()
plt.figure(figsize=(8, 8))
plt.imshow(r, cmap='Reds') # 显示红色通道
plt.figure(figsize=(8, 8))
plt.imshow(g, cmap='Greens') # 显示绿色通道
plt.figure(figsize=(8, 8))
plt.imshow(b, cmap='Blues') # 显示蓝色通道
plt.show() # 显示所有图像窗口
卷积层
卷积神经网络(Convolutional Neural Network)是含有卷积层的神经网络. 卷积层的作用就是用来自动学习、提取图像的特征。CNN网络主要有三部分构成:卷积层、池化层和全连接层构成,其中卷积层负责提取图像中的局部特征;池化层用来大幅降低参数量级(降维);全连接层类似人工神经网络的部分,用来输出想要的结果。
卷积核
- 卷积核是一个小型矩阵,通常尺寸较小,如3x3或5x5,也有其他尺寸如7x7或更大。每个元素代表权重,用于与输入图像(或前一层的特征图)的对应部分相乘。
- 卷积核的大小、深度(即通道数)必须与输入数据的深度匹配。例如,处理RGB图像时,卷积核也应有3个通道,分别对应红、绿、蓝通道。
计算步骤:
-
对齐: 首先,将卷积核与输入数据的某个区域精确对齐。对齐的起始点可以是从输入数据的左上角开始。
-
点乘与求和: 对于卷积核覆盖的每个位置,将卷积核的每个元素与输入数据对应位置的元素进行逐元素相乘,然后将所有乘积相加得到一个标量值。这个过程称为内积或点积。
-
滑动与重复: 完成一次点乘求和后,卷积核按照预定的步长(stride)在输入数据上水平或垂直滑动到下一个位置,重复上述计算过程。步长决定了卷积核每次移动的距离。
-
边缘处理: 为了处理边缘问题,可以在输入数据的边缘周围添加一圈或几圈零值,这个过程称为填充(padding)。填充可以保持输出特征图的尺寸或者增加尺寸。
-
输出特征图: 经过上述操作,每个卷积核在输入数据上滑动并计算后,会生成一个新的二维矩阵,称为特征图(Feature Map),表示了输入数据在该卷积核下的响应强度分布。
PyTorch 卷积层 API
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
# 显示图像
def show(img):
# 输入形状: (Height, Width, Channel)
plt.imshow(img)
plt.axis('off')
plt.show()
# 1. 单个多通道卷积核
def test01():
# 读取图像, 形状: (640, 640, 4)
img = plt.imread('data/彩色图片.png')
show(img)
# 构建卷积层
# 由于 out_channels 为 1, 相当于只有一个4通道卷积核
conv = nn.Conv2d(in_channels=4, out_channels=1, kernel_size=3, stride=1, padding=1)
# 输入形状: (BatchSize, Channel, Height, Width)
# mg形状: torch.Size([4, 640, 640])
img = torch.tensor(img).permute(2, 0, 1)
# img 形状: torch.Size([1, 4, 640, 640])
img = img.unsqueeze(0)
# 输入卷积层, new_img 形状: torch.Size([1, 1, 640, 640])
new_img = conv(img)
# new_img 形状: torch.Size([640, 640, 1])
new_img = new_img.squeeze(0).permute(1, 2, 0)
show(new_img.detach().numpy())
# 2. 多个多通道卷积核
def test02():
# 读取图像, 形状: (640, 640, 4)
img = plt.imread('data/彩色图片.png')
show(img)
# 构建卷积层
# 由于 out_channels 为 3, 相当于只有 3 个4通道卷积核
conv = nn.Conv2d(in_channels=4, out_channels=3, kernel_size=3, stride=1, padding=1)
# 输入形状: (BatchSize, Channel, Height, Width)
# img形状: torch.Size([3, 640, 640])
img = torch.tensor(img).permute(2, 0, 1)
# img 形状: torch.Size([1, 3, 640, 640])
img = img.unsqueeze(0)
# 输入卷积层, new_img 形状: torch.Size([1, 3, 640, 640])
new_img = conv(img)
# new_img 形状: torch.Size([640, 640, 3])
new_img = new_img.squeeze(0).permute(1, 2, 0)
# 打印三个特征图
show(new_img[:, :, 0].unsqueeze(2).detach().numpy())
show(new_img[:, :, 1].unsqueeze(2).detach().numpy())
show(new_img[:, :, 2].unsqueeze(2).detach().numpy())
池化层
池化层(Pooling Layer)在卷积神经网络(CNN)中紧随卷积层之后,其主要目的是降低特征图的空间维度,减少计算量,同时保持或提炼重要的特征信息。
最常见的池化类型有两种:
- 最大池化(Max Pooling):在池化窗口内取最大值作为输出。
- 平均池化(Average Pooling):在池化窗口内取所有值的平均值作为输出。
计算过程
- 初始化: 确定池化窗口大小、步幅和输入特征图尺寸。
- 窗口滑动: 从输入特征图的左上角开始,窗口按照步幅S逐行逐列地滑动。
- 计算输出: 对于窗口覆盖的每个区域:
- 如果是最大池化,选择窗口内最大的元素作为输出值。
- 如果是平均池化,计算窗口内所有元素的平均值作为输出值。
- 构建输出特征图: 将每个窗口计算出的输出值按照窗口滑动的位置放置在输出特征图上,最终形成一个维度减小的特征图。
边缘处理
与卷积层类似,池化层也可以通过添加边界填充(padding)来处理边缘,但实践中通常较少使用,因为池化的目的是降维而非保持尺寸不变。
PyTorch 池化 API 使用
在PyTorch中,使用池化层主要通过torch.nn
模块中的类来实现,比如nn.MaxPool2d
用于最大池化,nn.AvgPool2d
用于平均池化,以及nn.AdaptiveMaxPool2d
和nn.AdaptiveAvgPool2d
用于自适应池化。
最大池化 (Max Pooling)
import torch
import torch.nn as nn
# 创建一个最大池化层实例
max_pool = nn.MaxPool2d(kernel_size=2, stride=2) # 核大小为2x2,步长也为2
# 假设我们有一个张量作为输入,形状为(batch_size, channels, height, width)
input_tensor = torch.randn(1, 3, 32, 32) # 例如,一个批次、3个通道、32x32大小的图像
# 通过池化层传递输入张量
output_tensor = max_pool(input_tensor)
print("Output shape:", output_tensor.shape) # 输出张量的形状会根据池化核大小和步长变化
平均池化 (Average Pooling)
avg_pool = nn.AvgPool2d(kernel_size=2, stride=2) # 同样的核大小和步长设置
output_tensor_avg = avg_pool(input_tensor)
print("Average Pool Output shape:", output_tensor_avg.shape)
自适应池化 (Adaptive Pooling)
adaptive_max_pool = nn.AdaptiveMaxPool2d(output_size=(8, 8)) # 输出尺寸为8x8
output_adaptive_max = adaptive_max_pool(input_tensor)
print("Adaptive Max Pool Output shape:", output_adaptive_max.shape)
adaptive_avg_pool = nn.AdaptiveAvgPool2d(output_size=(8, 8))
output_adaptive_avg = adaptive_avg_pool(input_tensor)
print("Adaptive Avg Pool Output shape:", output_adaptive_avg.shape)