卷积神经网络理解(4)

1、CNN中常见的名词

  • paddingpadding(填充)参数的作用是决定在进行卷积或池化操作时,是否对输入的图像矩阵边缘补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.ndarrayimg转为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次),所以就成了上图这样。这就是实际计算的方法,因为计算时不可能用一张张滑动的方式,这样太低效了。

 

posted @ 2024-01-31 17:27  taohuaxiaochunfeng  阅读(13)  评论(0编辑  收藏  举报