一、用自己的语言解释以下概念
1.局部感知、权值共享
这两个名词都比较容易理解。
局部感知:就是神经元对一个全局图的局部先进行感知,然后再通过后续的操作随着感受野的扩大,将这些局部感知结合起来就能得到全局信息。本关键词和下一个词有着密不可分的关联。在CNN中神经元的形象可能没有那么的明显,但是我认为神经元就是经过卷积后的每一个值,都是一个神经元。然后就产生了很多神经元只不过是以矩阵的形式排列的。
权值共享,权值(weight)相信大家在代码中见过很多次这个单词了,在前馈神经网络中,这个权值是一个比较神秘并且很多的数据,但是在我们的CNN中,这个就会变成卷积核,卷积核在大,对比于同等规模的前馈神经网络的权值,也是要有少的优势的,这种少正是CNN的优势之一,并且可以使得整体的结果有相似的特点。
2.池化(子采样、降采样、汇聚)。会带来那些好处和坏处?
池化,括号中的全部都是池化的其他名称,一开始接触这个词pooling得知的含义就是池化,光看这个名字确实不太懂是什么意思,但是看到采样、汇聚等等词汇,我就明白了,这个名词的含义。至于其带来的好处和坏处,如下
好处:有效的减少神经元的数量,加速CNN的进程。可以让更小的神经元有更大的感受野。
坏处:可以从最大汇聚以及平均汇聚这两个名词中体会他的缺点,就是细节信息的丢失,如果不是为了方便完全没有必要多加一层计算减少一些神经元,如果我们的计算机能力充分,完全可以不丢失这些最真实的细节信息,而不是去使用劣质的信息。
3.全卷积网络(课上讲的这个概念不准确,同学们查资料纠正一下)
给出的网址介绍的是全卷积网络对于图像语义分割的一种框架,也就是框架如其名,全是卷积,丢掉了CNN里的在输出最终结果的全连接层,而是采用了卷积层,使得输出的并非只是一个向量,而是和输入相同的一个图,这个图是由第一部分卷积-编码,然后再第二部分卷积-译码,将数据在变为和输出图相同的突出特征的图。也可以说成是卷积部分与反卷积部分两个部分。在coonote中,也给出了fnc的基本过程,大概概括一下就是,先进行CNN,然后去掉全连接层,加入我们的fnc-32s网络,输出大图,然后再在之前的cnn中挑选出fnc-16s网络以及fnc-8s网络,通过跳级结构将这三部分结合起来可以得到一个比较好的像素级别的预测结果。
4.低级特征、中级特征、高级特征
低级特征:网络前期提取出的原始特征,也就是最开始的局部感知。卷积神经网络的初始卷积层通过一系列卷积操作和激活函数,提取出边缘、角点、纹理等低级特征。体现出的是图像的基本结构和局部信息特征。
中级特征:网络中间层次中提取的特征,它们对于物体的形状、纹理和部分结构等更高级的信息进行建模。多个卷积层和池化层的堆叠后提取出更加抽象和复杂的特征。例如,对于图像分类任务,中级特征可以表示物体的轮廓、纹理组合和局部特征等。
高级特征:网络较深层次中提取的更加抽象和语义化(语义化将计算机语言处理成人类更容易理解的东西)的特征。在深层卷积层和全连接层之间的部分,网络可以学习到更加高级的特征表示,如物体的整体形状、部分组合、物体类别等。高级特征具有更强的语义信息,对于最终的分类、检测或回归任务非常关键。
高级特征由于各有各的不同所以不能使用权值共享,需要做到全连接层来进行进一步的学习,这里也可以看出通FNC的不同之处。得到的结果形式是不同的。
5.多通道。N输入,M输出是如何实现的?
多通道实现的原理是输入数据的多维性,如果一个数据只有二维,那么多通道将无从谈起,当然卷积核的数量也同理。n个通道就代表size = x1*x2*n 的数据,然后m个输出就是y1*y2*p*m个卷积核,针对于p可以有p维的卷积核同原始数据卷积,得到z1*z2*p的特征图。实现过程就是每一维的每一层都有相应的数据和卷积核卷积然后分组相加,最后得到几个组的特征图。
6.1×1的卷积核有什么作用
1×1的卷积核又称网中网
1、升维与降维:当数据集的第三维的size增加时,1*1的卷积核,可以使特征图升维;同理也可以使用非线性函数进行降维,因为1*1不能改变前两维的size。
2、特征融合和交互:1x1卷积核可以用于特征图之间的融合和交互。通过在1x1卷积层中使用多个通道的输入和输出,可以实现对不同通道之间的特征进行线性组合和交互操作。这有助于网络学习更丰富和抽象的特征表示。
3、保留空间信息:1*1卷积核只能对单个的单元进行操作,可以保留最原始的真实数据。
二、使用CNN进行XO识别
1.复现参考资料中的代码
在这里我们要先引入两个公式
对于卷积操作
输出大小 = floor((输入大小 + 2*填充 - 卷积核大小) / 步幅) + 1
对于池化操作
输出大小 = floor((输入大小 - 池化窗口大小) / 步幅) + 1
根据输入图像为116*116可以按照以上公式一一计算出,扁平化的一个输入通道数为27*27*5,5为第三维的size为5
这样有利于理解为什么参数可以那样设置,也更容易进行一下的运算。
由于是代码的复现所以这里不提供源代码,只展示结果。
但是我有一个疑惑,如果仅仅是以这些公式理解的话,当输入图像为单数*单数时,输出的图像会丢失掉最后一列和最后一行的数据(针对于2*2池化窗口以及步幅为2),希望高人指点为什么会这样。由于结果的呈现和公式计算出的结果一直所以暂时我只能这样理解了。
训练结果:
测试结果:
查看训练好的模型的特征图:
第一层:conv1 = Conv2d(1,9,3)
经过Maxpool2d(2,2)
经过Relu()激活函数:
查看训练好的模型的卷积核:
key : conv1.weight
weight_t.shape (9, 1, 3, 3) 输出通道为1
key : conv2.weight
weight_t.shape (5, 9, 3, 3) 输出通道为9
2.重新设计网络结构
至少增加一个卷积层,卷积层达到三层以上:
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 9, 3) # in_channel , out_channel , kennel_size , stride
self.maxpool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(9, 5, 3) # in_channel , out_channel , kennel_size , stride
self.conv3 = nn.Conv2d(5, 3, 3) # in_channel , out_channel , kennel_size , stride
self.relu = nn.ReLU()
self.fc1 = nn.Linear(12 * 12 * 3, 600) # full connect 1
self.fc2 = nn.Linear(600, 32) # full connect 2
self.fc3 = nn.Linear(32, 2) # full connect 3
def forward(self, x):
x = self.maxpool(self.relu(self.conv1(x)))
x = self.maxpool(self.relu(self.conv2(x)))
x = self.maxpool(self.relu(self.conv3(x)))
x = x.view(-1, 12 * 12 * 3)
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.fc3(x)
return x
去掉池化层,对比“有无池化”的效果
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 9, 3) # in_channel , out_channel , kennel_size , stride
# self.maxpool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(9, 5, 3) # in_channel , out_channel , kennel_size , stride
self.conv3 = nn.Conv2d(5, 3, 3) # in_channel , out_channel , kennel_size , stride
self.relu = nn.ReLU()
self.fc1 = nn.Linear(110 * 110 * 3, 5000) # full connect 1
self.fc2 = nn.Linear(5000, 500) # full connect 2
self.fc3 = nn.Linear(500, 32)
self.fc4 = nn.Linear(32, 2) # full connect 3
def forward(self, x):
x = self.relu(self.conv1(x))
x = self.relu(self.conv2(x))
x = self.relu(self.conv3(x))
x = x.view(-1, 110 * 110 * 3)
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.relu(self.fc3(x))
x = self.fc4(x)
return x
结果很差,训练时间很长,全连接层太大了,而且还有三个用看来缩小。由于效果很差基本loss没有减小,所以就不予展示了。
修改“通道数”等超参数,观察变化
这里更改了卷积层的输入通道与输出通道以及不用maxpool改用avgpool(失败了,用这个avgpool2d直接loss不变化了,预测出来的结果也很差),然后又用回maxpool,池化窗口大小改为3
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 10, 3) # in_channel , out_channel , kennel_size , stride
self.pool = nn.MaxPool2d(3, 2)
self.conv2 = nn.Conv2d(10, 6, 3) # in_channel , out_channel , kennel_size , stride
self.conv3 = nn.Conv2d(6, 3, 3) # in_channel , out_channel , kennel_size , stride
self.relu = nn.ReLU()
self.fc1 = nn.Linear(11 * 11 * 3, 400) # full connect 1
self.fc2 = nn.Linear(400, 32) # full connect 2
self.fc3 = nn.Linear(32, 2) # full connect 3
def forward(self, x):
x = self.pool(self.relu(self.conv1(x)))
x = self.pool(self.relu(self.conv2(x)))
x = self.pool(self.relu(self.conv3(x)))
x = x.view(-1, 11 * 11 * 3)
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.fc3(x)
return x
以下是预测结果:
size of train_data: 1700
size of test_data: 300
27
5
Accuracy of the network on the test images: 99.666667 %
就用这个模型做步骤三了。
3.可视化
可视化部分卷积核和特征图(这里选择的是第一、二、三层卷积核以及第二次卷积前后的特征图)
卷积核:
key : conv1.weight
weight_t.shape (10, 1, 3, 3)
key : conv2.weight
weight_t.shape (6, 10, 3, 3) 这里代码稍微除了点问题,应该是十个的,但是我只写了6个循环,不影响查看,就不做调整了。
key : conv3.weight
weight_t.shape (3, 6, 3, 3)
特征图:
(54, 54, 6)
(54, 54, 6)
(26, 26, 6)
探索低级特征、中级特征、高级特征
分别选取的是:第一层卷积层结果、第二层卷积层结果、第三层卷积层结果(技术力有限,我这些图不是一个test,但是整体不影响观看):
总感觉怪怪的,特征好像从整体局部发展了。但是确实特征也是越来越明显了。
心得:
本次实验,由于有前人的代码,所以我主要进行了更改模型,以及更改输出的参数。但是针对于卷积神经网络,我有了更加深刻的理解。在卷积神经网络中,仍然用到了全连接层,这些全连接层使得模型会更好的预测,但是针对于FNC的话,是直接输出的热力图像,更加具有语义化,我认为两者各有千秋,使用的场景不同。针对于网络中的各种层,在代码中其实更容易体会,因为结构真的太明了了,发明这个的人太有才了。对于一些问题我总是想找到图像化的解释,就像我在进行池化层运算的时候产生的那个问题,我按照课上的思路走下来就是和公式算出来不一样,望高人指点。
参考:
【23-24 秋学期】NNDL 作业7 基于CNN的XO识别-CSDN博客
【2021-2022 春学期】人工智能-作业6:CNN实现XO识别_x = self.conv2(x)#请问经过conv2(x)之后,x的维度是多少-CSDN博客