卷积神经网络理解(4)
1、CNN中常见的名词
- padding:padding(填充)参数的作用是决定在进行卷积或池化操作时,是否对输入的图像矩阵边缘补0
- stride:滑动卷积核时的步长stride(例如每次滑动一个或两个)
- kernal:卷积核,通常为3x3或者5x5
- filter:卷积核的数量(神经元的数量)。这个地方怎么理解呢,一个3x3的卷积核有9个参数,这些参数是通过learning出来的,一个卷积核扫过一幅图后,会生成一幅新的图,因为卷积核的参数是不同的,因此生成的图片也是不同的。但是卷积核的作用是侦查patten的对吧,一个图片中不可能只有一个patten吧,比如说一幅图片有100个patten,鸟嘴、鸟爪、牛蹄、猪头等特征都是patten,当将一头猪的图片输入到网络中,猪头这个patten的权重就会非常大,相反鸟嘴、鸟爪、牛蹄的patten权重就很低。所以filter的个数也就代表了神经元的数目。
- pooling:池化
2、整体流程理解
卷积(Conv2d) -> BN(batch normalization) -> 激励函数(ReLU) -> 池化(MaxPooling) ->
全连接层(Linear) -> 输出
1 # 定义网络结构
2 class CNNnet(torch.nn.Module):
3 def __init__(self):
4 super(CNNnet,self).__init__()
5 self.conv1 = torch.nn.Sequential(
6 torch.nn.Conv2d(in_channels=1,
7 out_channels=16,
8 kernel_size=3,
9 stride=2,
10 padding=1),
11 torch.nn.BatchNorm2d(16),
12 torch.nn.ReLU()
13 )
14 self.conv2 = torch.nn.Sequential(
15 torch.nn.Conv2d(16,32,3,2,1),
16 torch.nn.BatchNorm2d(32),
17 torch.nn.ReLU()
18 )
19 self.conv3 = torch.nn.Sequential(
20 torch.nn.Conv2d(32,64,3,2,1),
21 torch.nn.BatchNorm2d(64),
22 torch.nn.ReLU()
23 )
24 self.conv4 = torch.nn.Sequential(
25 torch.nn.Conv2d(64,64,2,2,0),
26 torch.nn.BatchNorm2d(64),
27 torch.nn.ReLU()
28 )
29 self.linear = torch.nn.Linear(2*2*64,100)
30 self.linear = torch.nn.Linear(100,10)
31 def forward(self, x):
32 x = self.conv1(x)
33 x = self.conv2(x)
34 x = self.conv3(x)
35 x = self.conv4(x)
36 x = self.linear(x.view(x.size(0),-1))
37 x = self.linear(x)
38 return x
39 model = CNNnet()
40 print(model)
首先图片经过卷积层。卷积层第一个参数是input channel ,如果是RGB图像,就是3维,如果是灰度图就是1维。
第二个参数是output channel,也就是filter,设置多少个卷积核,每个卷积核做运算后输出新的“图片”,后续的参数再是卷积核的大小、步长、填充等等。
重点要理解这个output channel
那么问题来了,怎么判断做完卷积后新图片的大小?假设输入的tensor size为C H W,若in_channels=C,out_channels=Cout,kernel_size=k,stride=s,padding=p,那么输出的tensor size是:
Cout*((H + 2*p - k)/s+1) * ((W + 2*p - k)/s+1)
这种方法需要手动计算,有时候还可能计算错误,
这里再提供一种方法,执行完下述程序后,加上一句 print(x.size())
将数据打印出来,
然后再填到torch.nn.Linear(input,100)里
1 def forward(self, x):
2 x = self.conv1(x)
3 x = self.conv2(x)
4 x = self.conv3(x)
5 x = self.conv4(x)
6 print(x.size())
3、关于Pytorch默认的参数初始化问题
用自己的话总结一下:Pytorch中默认会初始化参数,这个初始化在调用神经网络时已经给初始好了,不需要自己手动初始化。系统默认初始化参数时也是用random
的相关函数,这也是为什么我们需要设置seed了,因为设置seed后,每次初始化的参数值是一模一样的。
此外,也可以手动初始化参数。自动初始化参数是可以满足大多数情况的,但是针对有些特定的RNN、CNN的网络,有更适合收敛的参数,因此可以默认,但也可以手动初始化参数。
random.seed() | random模块的随机数种子 |
torch.manual_seed() | 为CPU设置随机数种子 |
torch.cuda.manual_seed() | 为GPU设置随机数种子 |
torch.cuda.manual_seed_all() |
为所有的GPU设置随机数种子 |
4、transforms.ToTensor()
这个函数的用法?
ToTensor()
将shape
为(H, W, C)
的nump.ndarray
或img
转为shape
为(C, H, W)
的tensor
,
其将每一个数值归一化到[0,1]
,其归一化方法比较简单,直接除以255即可。
总结一下:① 、起到了一个reshape的作用 ;②、起到归一化的作用。
其实不难理解为什么起到这两点作用,因为PIL的读取的图片格式本来就是(H, W, C),所以肯定要进行一步转换;
且transforms这个库就是针对图像的,而将图像像素的归一化是最基础的操作之一,所以totensor都替我们做好了。
5、卷积的理解
- 卷积核参数量怎么算?
假设现在有一张单通道的灰度图,卷积核大小是3×3,输出通道是10。
那么参数量就是3×3×1×10
假设现在有一张三通道的彩色图,卷积核大小是3×3,输出通道是10。
那么参数量就是3×3×3×10
因此要搞清楚一个概念:不是一个卷积核对三个通道做运算,而是三个卷积核对三通道做运算(即每个卷积核只对一个通道运算)。
因此一幅图有3个通道,就需要3个卷积核,每个卷积核大小又是3×3,这3个卷积核对3个通道计 算完后,会合并成一张新的图(把原先这3个通道分别运算的结果,再加起来,合成一个新通 道),因此输出通道是10,因此就需要重复10次这样的过程。也就是再×10。
- 如何高效的并行运算卷积滑窗?
我们计算卷积时,不可能一张张滑动吧,原理是这个原理,但是要考虑GPU并行运算啊,一张张滑动,那么运算就太慢了,
所以在实际的程序里是将其转换为矩阵的形式并行处理。什么意思呢,看下面这张图。
输入一个4×4的图片,卷积核是3×3,那么每次就有一部分取不到,取不到的那部分就用0代替,如上图的红黄绿紫矩阵。把这四个矩阵拉成向量,就如下图所示,将输入的图片拉成向量,将卷积核及补0的图片也拉成向量,然后就可以当成一个矩阵运算了,这样运算效率就高了。
假设本来是4×4是输入图片,就被拉成了1×16,然后卷积核及补零后,也被拉成了16维,总共有4组(总共可以滑动4次),所以就成了上图这样。这就是实际计算的方法,因为计算时不可能用一张张滑动的方式,这样太低效了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏