一、用自己的语言解释以下概念
(一)、局部感知、权值共享
(1)局部感知
定义:在进行卷积计算的时候,将图片划分为一个个的区域进行计算/考虑;
由于越是接近的像素点之间的关联性越强, 反之则越弱. 所以我们选择先进行局部感知, 然后在更高层(FC层)将这些局部信息综合起来得到全局信息的方式.【来自深度学习之卷积神经网络(Convolutional Neural Networks, CNN) - 知乎 (zhihu.com)】
感知的大小只有滑动窗口那么大。如下图所示:
可以看到,每次卷积核都是针对某一局部的数据窗口进行卷积,这就是所谓的CNN中的局部感知机制。
(2)权值共享
定义:在卷积神经网络中,同一个卷积核在整个输入数据的不同位置共享相同的权重参数。这意味着无论卷积核移动到哪个位置,它所使用的权重参数都是一样的。
以上文参考卷积神经网络(CNN)的相关概念 - 掘金 (juejin.cn)
如下图所示,移动的蓝框是局部感知,在计算第一个output时,W0不改变,这就是权值共享
(二)、池化(子采样、降采样、汇聚)。会带来那些好处和坏处?
(1)池化对应定义
池化,也称子采样、降采样或汇聚,它的工作是取区域平均或最大,其目的是为了减少特征图,减少特征的数量,从而降低模型的计算量和参数量。
其中池化运算有以下几种:
- 最大池化(Max Pooling)。取4个点的最大值。这是最常用的池化方法。
- 均值池化(Mean Pooling)。取4个点的均值。
- 高斯池化。借鉴高斯模糊的方法。不常用。
- 可训练池化。训练函数 ff ,接受4个点为输入,出入1个点。不常用。
图来自【CS231n Convolutional Neural Networks for Visual Recognition】
上图是一个2*2且步长为2的最大池化。
文来自【卷积神经网络(CNN)的相关概念 - 掘金 (juejin.cn)】
(2)好处、坏处
上图可以看到池化操作具有特征不变性,可以保留主要特征,实现降维。
好处:
1)降维:池化减小图片的尺寸,主要用来降维。
2)减少计算量:通过池化操作,可以大幅降低后续层的计算复杂度,从而提高模型的训练速度。
3)保留主要特征:池化操作通常会采用最大池化或平均池化等方式,保留主要特征,降低了特征的冗余性。
4)减少过拟合:通过减少特征图的尺寸,池化层有助于减少模型对于训练数据中噪声和微小变化的敏感度,从而有助于防止过拟合。
图文来自【图像识别(七)| 池化层是什么?有什么作用? - 知乎 (zhihu.com)】
坏处:
池化层由于保留的是主要特征,会丧失一些细节信息,特别是在一些对细节敏感的任务中,可能需要谨慎使用或者考虑使用合适的池化方式。【文来自CNN中池化层的作用?-CSDN博客】
(三)、全卷积网络
定义:全卷积网络(FCN)是从抽象的特征中恢复出每个像素所属的类别。即从图像级别的分类进一步延伸到像素级别的分类。
文来自【机器学习基础系列笔记7—全卷积网络FCN&U-Net结构 - 知乎 (zhihu.com)】
相比于CNN:FCN相较于CNN来说,其将CNN最后几个用于输出概率的全连接层都改成了卷积层,这样网络的输出将是热力图而非类别;同时,为解决卷积和池化导致图像尺寸的变小,使用上采样方式对图像尺寸进行恢复。
它的基本思想是:1.不含全连接层(fc)的全卷积网络。可适应任意尺寸输入。 2.增大数据尺寸的反卷积(deconv)层。能够输出精细的结果。 3.结合不同深度层结果的跳级(skip)结构。同时确保鲁棒性和精确性。
文来自【卷积神经网络( CNN)与全卷积神经网络(FCN) (xjx100.cn)】
FCN网络结构:全卷积部分和反卷积部分。其中全卷积部分为一些经典的CNN网络(如VGG,ResNet等),用于提取特征;反卷积部分则是通过上采样得到原尺寸的语义分割图像。FCN的输入可以为任意尺寸的彩色图像,输出与输入尺寸相同,通道数为n(目标类别数)+1(背景)。
图文来自【FCN(全卷积神经网络)详解-菜鸟笔记 (coonote.com)】
(四)、低级特征、中级特征、高级特征
网络中靠前的部分提取的是初级特征:例如边缘特征,是直接从原数据集中提取到的。
网络中段提取的是中级特征,中级特征一定程度上是初级特征的再组合体现,比如纹理特征等。
而网络后端提取的是高级特征,高级特征可以看作中级特征的再组合,也更加抽象。
文来自【分层特征提取Hierarchical Feature Extraction (baidu.com)】
还是这个图:
形象来说,首先尽可能找到与这个头像相关的各种边,这些边就是底层的特征(Low-level features),也就是低级特征;然后对这些底层特征进行组合,就可以看到鼻子、眼睛、耳朵等,它们是中间层特征(Mid-level features),也就是中级特征;最后,对鼻子、眼睛等进行组合,就可以组成各种各样的头像,也就是高层特征(High-level features)。这个时候,它就可以识别出各种人的头像了。
图文来自【白话版,聊聊“深度学习” (qq.com)】
在搜集了其他的博客后【来自【22-23 春学期】人工智能基础--AI作业8-卷积2-CSDN博客】里边对于这几个特征是这么说的:
低级特征通常指一些较为基础的、直接从原始数据中提取的特征,如颜色、纹理、边缘等,它们通常对于物体的分类或识别任务并不十分有效,但是可以作为中级特征的基础。
中级特征则是指基于低级特征构建的一些更高层次的特征,如形状、轮廓、纹理组合等,这些特征能够更好地描述物体的形态和结构,因此对于分类或识别任务的效果会更好。
高级特征则是指基于中级特征构建的更加抽象和复杂的特征,如物体的部件、结构、语义等,这些特征能够更好地描述物体的高层次语义信息,因此对于更加复杂的任务(如目标检测、语义分割等)的效果会更好。
(五)、多通道。N输入,M输出是如何实现的?
1)多输入、单输出
如图所示,输入通道有两个,那么需要两个卷积核,进行卷积运算,然后累加,得到一个输出。
当输入通道有多个时,因为对各个通道的结果做了累加,所以不论输入通道数是多少,输出通道数总是为 1。
那么如何得到多个输出呢?
2)多输入、多输出
如上图所示,如果想要得到M个输出,那么只要准备M组卷积核,其中每一组卷积核的多少与有几个输入有关。 来自【精选】【从零开始学习深度学习】23. CNN中的多通道输入及多通道输出计算方式及1X1卷积层介绍_多通道cnn-CSDN博客
(六)、1×1的卷积核有什么作用
1)增加网络深度(增加非线性映射次数)
当1*1的卷积核但是是m层和n层的话,1×1卷积核可以起到一个跨通道聚合的作用。
2)升维/降维
1*1不会改变输出的尺寸,改变的是通道数。
1*1的卷积核将原本的数据量进行增加或者减少。这里看其他文章或者博客中都称之为升维、降维。但我觉得维度并没有改变,改变的只是 height × width × channels 中的 channels 这一个维度的大小而已。
上图文来自【1*1卷积核的作用-CSDN博客】
图来自【深度笔记|1x1卷积核的作用 - 知乎 (zhihu.com)】
3)跨通道的信息交互
4)减少卷积核参数(简化模型)
降维,其实也是减少了参数,因为特征图少了,参数也自然跟着就减少,相当于在特征图的通道数上进行卷积,压缩特征图,二次提取特征,使得新特征图的特征表达更佳。
上边这四个作用是相互联系的,归根到底,1*1的卷积核实现了输出的降维或者升维,才会出现其他作用。
二、使用CNN进行XO识别
(一)、复现参考资料中的代码
(1)数据集
(2)模型构建
如上图所示,构建模型:
class Net(nn.Module):def __init__(self):super(Net, self).__init__()#定义卷积层self.conv1 = nn.Conv2d(1, 9, 3)#最大池化层self.maxpool = nn.MaxPool2d(2, 2)self.conv2 = nn.Conv2d(9, 5, 3)self.relu = nn.ReLU()self.fc1 = nn.Linear(27 * 27 * 5, 1200)self.fc2 = nn.Linear(1200, 64)self.fc3 = nn.Linear(64, 2)def forward(self, x):#第一次x = self.maxpool(self.relu(self.conv1(x)))#第二次x = self.maxpool(self.relu(self.conv2(x)))x = x.view(-1, 27 * 27 * 5)#全连接+激活x = self.relu(self.fc1(x))x = self.relu(self.fc2(x))x = self.fc3(x)return x
(3)训练模型
把数据分成了10个批次,然后进行循环,循环中嵌套的循环是计算平均损失函数:
model = Net()criterion = torch.nn.CrossEntropyLoss() # 损失函数: 交叉熵损失函数
optimizer = optim.SGD(model.parameters(), lr=0.1) # 优化函数:随机梯度下降epochs = 10
for epoch in range(epochs):running_loss = 0.0#计算平均损失值【累加,每10次一输出一清零】for i, data in enumerate(data_loader):images, label = dataout = model(images)loss = criterion(out, label)#计算损失optimizer.zero_grad()#清空之前的梯度loss.backward()optimizer.step()running_loss += loss.item()if (i + 1) % 10 == 0:print('[%d %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 100))running_loss = 0.0print('finished train')# 保存模型
torch.save(model, 'model_name.pth') # 保存的是模型, 不止是w和b权重值
然后保存模型。
得到的输出为:
[1 10] loss: 0.069
[1 20] loss: 0.069
[2 10] loss: 0.068
[2 20] loss: 0.066
[3 10] loss: 0.058
[3 20] loss: 0.037
[4 10] loss: 0.023
[4 20] loss: 0.008
[5 10] loss: 0.004
[5 20] loss: 0.004
[6 10] loss: 0.003
[6 20] loss: 0.002
[7 10] loss: 0.001
[7 20] loss: 0.001
[8 10] loss: 0.001
[8 20] loss: 0.000
[9 10] loss: 0.001
[9 20] loss: 0.000
[10 10] loss: 0.001
[10 20] loss: 0.000
finished train
(4)测试模型
读取一张图片进行测试:
# 读取模型
model_load = torch.load('model_name.pth')
# 读取一张图片 images[0],测试
print("labels[0] truth:\t", labels[0])
x = images[0]
predicted = torch.max(model_load(x), 1)
print("labels[0] predict:\t", predicted.indices)img = images[0].data.squeeze().numpy() # 将输出转换为图片的格式
plt.imshow(img, cmap='gray')
plt.show()
如下图所示,预测正确:
(5)计算准确率
“预测正确的数目除以总的数目”,得到准确率:
# 读取模型
model_load = torch.load('model_name.pth')correct = 0
total = 0
with torch.no_grad(): # 进行评测的时候网络不更新梯度for data in data_loader_test: # 读取测试集images, labels = dataoutputs = model_load(images)_, predicted = torch.max(outputs.data, 1) # 取出 最大值的索引 作为 分类结果total += labels.size(0) # labels 的长度correct += (predicted == labels).sum().item() # 预测正确的数目
print('Accuracy of the network on the test images: %f %%' % (100. * correct / total))
得到准确率:
(6)查看训练好模型的特征图
# 看看每层的 卷积核 长相,特征图 长相
# 获取网络结构的特征矩阵并可视化
import torch
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
from torchvision import transforms, datasets
import torch.nn as nn
from torch.utils.data import DataLoader# 定义图像预处理过程(要与网络模型训练过程中的预处理过程一致)transforms = transforms.Compose([transforms.ToTensor(), # 把图片进行归一化,并把数据转换成Tensor类型transforms.Grayscale(1) # 把图片 转为灰度图
])
path = r'training_data_sm'
data_train = datasets.ImageFolder(path, transform=transforms)
data_loader = DataLoader(data_train, batch_size=64, shuffle=True)
for i, data in enumerate(data_loader):images, labels = dataprint(images.shape)print(labels.shape)breakclass Net(nn.Module):def __init__(self):super(Net, self).__init__()self.conv1 = nn.Conv2d(1, 9, 3) # in_channel , out_channel , kennel_size , strideself.maxpool = nn.MaxPool2d(2, 2)self.conv2 = nn.Conv2d(9, 5, 3) # in_channel , out_channel , kennel_size , strideself.relu = nn.ReLU()self.fc1 = nn.Linear(27 * 27 * 5, 1200) # full connect 1self.fc2 = nn.Linear(1200, 64) # full connect 2self.fc3 = nn.Linear(64, 2) # full connect 3def forward(self, x):outputs = []x = self.conv1(x)outputs.append(x)x = self.relu(x)outputs.append(x)x = self.maxpool(x)outputs.append(x)x = self.conv2(x)x = self.relu(x)x = self.maxpool(x)x = x.view(-1, 27 * 27 * 5)x = self.relu(self.fc1(x))x = self.relu(self.fc2(x))x = self.fc3(x)return outputs# create model
model1 = Net()# load model weights加载预训练权重
# model_weight_path ="./AlexNet.pth"
model_weight_path = "model_name1.pth"
model1.load_state_dict(torch.load(model_weight_path))# 打印出模型的结构
print(model1)x = images[0]# forward正向传播过程
out_put = model1(x)for feature_map in out_put:# [N, C, H, W] -> [C, H, W] 维度变换im = np.squeeze(feature_map.detach().numpy())# [C, H, W] -> [H, W, C]im = np.transpose(im, [1, 2, 0])print(im.shape)# show 9 feature mapsplt.figure()for i in range(9):ax = plt.subplot(3, 3, i + 1) # 参数意义:3:图片绘制行数,5:绘制图片列数,i+1:图的索引# [H, W, C]# 特征矩阵每一个channel对应的是一个二维的特征矩阵,就像灰度图像一样,channel=1# plt.imshow(im[:, :, i])plt.imshow(im[:, :, i], cmap='gray')plt.show()
输出:
实例化情况
torch.Size([64, 1, 116, 116])
torch.Size([64])
Net((conv1): Conv2d(1, 9, kernel_size=(3, 3), stride=(1, 1))(maxpool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(conv2): Conv2d(9, 5, kernel_size=(3, 3), stride=(1, 1))(relu): ReLU()(fc1): Linear(in_features=3645, out_features=1200, bias=True)(fc2): Linear(in_features=1200, out_features=64, bias=True)(fc3): Linear(in_features=64, out_features=2, bias=True)
)
(7)查看卷积核
# 看看每层的 卷积核 长相,特征图 长相
# 获取网络结构的特征矩阵并可视化
import torch
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
from torchvision import transforms, datasets
import torch.nn as nn
from torch.utils.data import DataLoaderplt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 #有中文出现的情况,需要u'内容
# 定义图像预处理过程(要与网络模型训练过程中的预处理过程一致)
transforms = transforms.Compose([transforms.ToTensor(), # 把图片进行归一化,并把数据转换成Tensor类型transforms.Grayscale(1) # 把图片 转为灰度图
])
path = r'training_data_sm'
data_train = datasets.ImageFolder(path, transform=transforms)
data_loader = DataLoader(data_train, batch_size=64, shuffle=True)
for i, data in enumerate(data_loader):images, labels = data# print(images.shape)# print(labels.shape)breakclass Net(nn.Module):def __init__(self):super(Net, self).__init__()self.conv1 = nn.Conv2d(1, 9, 3) # in_channel , out_channel , kennel_size , strideself.maxpool = nn.MaxPool2d(2, 2)self.conv2 = nn.Conv2d(9, 5, 3) # in_channel , out_channel , kennel_size , strideself.relu = nn.ReLU()self.fc1 = nn.Linear(27 * 27 * 5, 1200) # full connect 1self.fc2 = nn.Linear(1200, 64) # full connect 2self.fc3 = nn.Linear(64, 2) # full connect 3def forward(self, x):outputs = []x = self.maxpool(self.relu(self.conv1(x)))# outputs.append(x)x = self.maxpool(self.relu(self.conv2(x)))outputs.append(x)x = x.view(-1, 27 * 27 * 5)x = self.relu(self.fc1(x))x = self.relu(self.fc2(x))x = self.fc3(x)return outputs# create model
model1 = Net()# load model weights加载预训练权重
model_weight_path = "model_name1.pth"
model1.load_state_dict(torch.load(model_weight_path))x = images[0]# forward正向传播过程
out_put = model1(x)weights_keys = model1.state_dict().keys()
for key in weights_keys:print("key :", key)# 卷积核通道排列顺序 [kernel_number, kernel_channel, kernel_height, kernel_width]if key == "conv1.weight":weight_t = model1.state_dict()[key].numpy()print("weight_t.shape", weight_t.shape)k = weight_t[:, 0, :, :] # 获取第一个卷积核的信息参数# show 9 kernel ,1 channelplt.figure()for i in range(9):ax = plt.subplot(3, 3, i + 1) # 参数意义:3:图片绘制行数,5:绘制图片列数,i+1:图的索引plt.imshow(k[i, :, :], cmap='gray')title_name = 'kernel' + str(i) + ',channel1'plt.title(title_name)plt.show()if key == "conv2.weight":weight_t = model1.state_dict()[key].numpy()print("weight_t.shape", weight_t.shape)k = weight_t[:, :, :, :] # 获取第一个卷积核的信息参数print(k.shape)print(k)plt.figure()for c in range(9):channel = k[:, c, :, :]for i in range(5):ax = plt.subplot(2, 3, i + 1) # 参数意义:3:图片绘制行数,5:绘制图片列数,i+1:图的索引plt.imshow(channel[i, :, :], cmap='gray')title_name = 'kernel' + str(i) + ',channel' + str(c)plt.title(title_name)plt.show()
输出:
卷积核
(二)、重新设计网络结构
(1)至少增加一个卷积层,卷积层达到三层以上
由于我在加入一层卷积层后,计算特征图尺寸一一直不对,然后舍友茜借了我一段代码:
import torch
import torch.nn as nn# 定义卷积层和池化层
conv1 = nn.Conv2d(1, 9, 3)
maxpool = nn.MaxPool2d(2, 2)
conv2 = nn.Conv2d(9, 5, 3)
conv3 = nn.Conv2d(5, 3, 3)
# 假设输入图像尺寸为 64x64
W_in, H_in = 116, 116# 计算特征图尺寸
x = torch.rand(1, 1, W_in, H_in) # 构造一个输入张量
x = maxpool(nn.ReLU()(conv1(x)))
x = maxpool(nn.ReLU()(conv2(x)))
x = maxpool(nn.ReLU()(conv3(x)))output_size = x.size()[2:] # 获取特征图的尺寸
print("Output feature map size:", output_size)
可以得到对应的尺寸:
Output feature map size: torch.Size([12, 12])
在后边就不放代码了,只有构建模型部分进行了一点点修改:
import torch
import torch.nn as nn# 定义卷积层和池化层
conv1 = nn.Conv2d(1, 9, 3)
maxpool = nn.MaxPool2d(2, 2)
conv2 = nn.Conv2d(9, 5, 3)
conv3 = nn.Conv2d(5, 3, 3)
# 假设输入图像尺寸为 64x64
W_in, H_in = 116, 116# 计算特征图尺寸
x = torch.rand(1, 1, W_in, H_in) # 构造一个输入张量
x = maxpool(nn.ReLU()(conv1(x)))
x = maxpool(nn.ReLU()(conv2(x)))
x = maxpool(nn.ReLU()(conv3(x)))output_size = x.size()[2:] # 获取特征图的尺寸
print("Output feature map size:", output_size)
然后训练模型得到的损失:
[1 10] loss: 0.051
[1 20] loss: 0.045
[1 30] loss: 0.039
[2 10] loss: 0.045
[2 20] loss: 0.041
[2 30] loss: 0.044
[3 10] loss: 0.042
[3 20] loss: 0.046
[3 30] loss: 0.042
[4 10] loss: 0.040
[4 20] loss: 0.045
[4 30] loss: 0.043
[5 10] loss: 0.040
[5 20] loss: 0.044
[5 30] loss: 0.045
[6 10] loss: 0.045
[6 20] loss: 0.041
[6 30] loss: 0.044
[7 10] loss: 0.043
[7 20] loss: 0.047
[7 30] loss: 0.037
[8 10] loss: 0.037
[8 20] loss: 0.046
[8 30] loss: 0.044
[9 10] loss: 0.042
[9 20] loss: 0.046
[9 30] loss: 0.042
[10 10] loss: 0.041
[10 20] loss: 0.045
[10 30] loss: 0.041
finished train
效果不太好。
然后参考了一下美女学霸NNDL 作业7 相关语言解释+基于CNN的XO识别代码复现-CSDN博客的,多增加了10轮次,效果很好:
[1 10] loss: 0.069
[1 20] loss: 0.069
[2 10] loss: 0.069
[2 20] loss: 0.068
[3 10] loss: 0.056
[3 20] loss: 0.034
[4 10] loss: 0.012
[4 20] loss: 0.006
[5 10] loss: 0.005
[5 20] loss: 0.001
[6 10] loss: 0.002
[6 20] loss: 0.001
[7 10] loss: 0.001
[7 20] loss: 0.001
[8 10] loss: 0.001
[8 20] loss: 0.000
[9 10] loss: 0.000
[9 20] loss: 0.000
[10 10] loss: 0.000
[10 20] loss: 0.000
[11 10] loss: 0.000
[11 20] loss: 0.000
[12 10] loss: 0.000
[12 20] loss: 0.000
[13 10] loss: 0.000
[13 20] loss: 0.000
[14 10] loss: 0.000
[14 20] loss: 0.000
[15 10] loss: 0.000
[15 20] loss: 0.000
[16 10] loss: 0.000
[16 20] loss: 0.000
[17 10] loss: 0.000
[17 20] loss: 0.000
[18 10] loss: 0.000
[18 20] loss: 0.000
[19 10] loss: 0.000
[19 20] loss: 0.000
[20 10] loss: 0.000
[20 20] loss: 0.000
finished train
(2)去掉池化层,对比“有无池化”的效果
得到的结果:
[1 10] loss: 0.062
[1 20] loss: 0.051
[1 30] loss: 0.044
[2 10] loss: 0.041
[2 20] loss: 0.045
[2 30] loss: 0.042
[3 10] loss: 0.044
[3 20] loss: 0.043
[3 30] loss: 0.041
[4 10] loss: 0.043
[4 20] loss: 0.041
[4 30] loss: 0.044
[5 10] loss: 0.042
[5 20] loss: 0.042
[5 30] loss: 0.044
[6 10] loss: 0.041
[6 20] loss: 0.045
[6 30] loss: 0.042
[7 10] loss: 0.043
[7 20] loss: 0.043
[7 30] loss: 0.039
[8 10] loss: 0.046
[8 20] loss: 0.039
[8 30] loss: 0.043
[9 10] loss: 0.039
[9 20] loss: 0.044
[9 30] loss: 0.043
[10 10] loss: 0.037
[10 20] loss: 0.043
[10 30] loss: 0.044
[11 10] loss: 0.040
[11 20] loss: 0.042
[11 30] loss: 0.041
[12 10] loss: 0.039
[12 20] loss: 0.040
[12 30] loss: 0.042
[13 10] loss: 0.039
[13 20] loss: 0.041
[13 30] loss: 0.037
[14 10] loss: 0.038
[14 20] loss: 0.036
[14 30] loss: 0.035
[15 10] loss: 0.034
[15 20] loss: 0.033
[15 30] loss: 0.031
[16 10] loss: 0.030
[16 20] loss: 0.029
[16 30] loss: 0.029
[17 10] loss: 0.025
[17 20] loss: 0.024
[17 30] loss: 0.024
[18 10] loss: 0.014
[18 20] loss: 0.019
[18 30] loss: 0.021
[19 10] loss: 0.011
[19 20] loss: 0.009
[19 30] loss: 0.017
[20 10] loss: 0.006
[20 20] loss: 0.006
[20 30] loss: 0.008
finished train
可以看到,20轮次有较好的效果。但是速度比较慢,还有池化层的效率更高。
(3)修改“通道数”等超参数,观察变化
修改了通道数,改成11的:
[1 10] loss: 0.070
[1 20] loss: 0.069
[2 10] loss: 0.069
[2 20] loss: 0.067
[3 10] loss: 0.050
[3 20] loss: 0.038
[4 10] loss: 0.022
[4 20] loss: 0.010
[5 10] loss: 0.004
[5 20] loss: 0.003
[6 10] loss: 0.002
[6 20] loss: 0.002
[7 10] loss: 0.001
[7 20] loss: 0.002
[8 10] loss: 0.001
[8 20] loss: 0.001
[9 10] loss: 0.001
[9 20] loss: 0.000
[10 10] loss: 0.001
[10 20] loss: 0.000
finished train
Accuracy of the network on the test images: 99.666667 %
改为15,效果不好:
[1 10] loss: 0.070
[1 20] loss: 0.069
[2 10] loss: 0.069
[2 20] loss: 0.069
[3 10] loss: 0.069
[3 20] loss: 0.069
[4 10] loss: 0.069
[4 20] loss: 0.069
[5 10] loss: 0.069
[5 20] loss: 0.069
[6 10] loss: 0.069
[6 20] loss: 0.069
[7 10] loss: 0.069
[7 20] loss: 0.069
[8 10] loss: 0.069
[8 20] loss: 0.069
[9 10] loss: 0.069
[9 20] loss: 0.068
[10 10] loss: 0.065
[10 20] loss: 0.061
finished train
Accuracy of the network on the test images: 80.000000 %
(4)可视化低级特征、中级特征、高级特征
注重一下可视化的过程:
1.通过模型将输入数据`x`进行正向传播,得到输出的特征图`out_put`。
2.但是得到的特征图不能使用plt直接显示出来,需要对他们进行一些操作实现可视化【循环:`np.squeeze`函数将特征图的维度从`[N, C, H, W]`变换为`[C, H, W]`,其中`N`表示批次大小,`C`表示通道数,`H`和`W`表示特征图的高度和宽度;np.transpose`函数将特征图的维度从`[C, H, W]`变换为`[H, W, C]`;然后输出特征图形状】
3.创建图形窗口,循环使用`plt.imshow`函数绘制特征图的每个通道,其中`im[:, :, i]`表示特征图的第`i`个通道。
4.`plt.show`函数展示绘制好的特征图。
import torch
from torchvision import transforms
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import torch.nn as nn # 数据预处理
transforms = transforms.Compose([ transforms.ToTensor(), # 把图片进行归一化,并把数据转换成Tensor类型 transforms.Grayscale(), # 把图片转为灰度图
]) path = r'train_data'
data_train = datasets.ImageFolder(path, transform=transforms)
data_loader = DataLoader(data_train, batch_size=64, shuffle=True) for i, data in enumerate(data_loader): images, labels = data print(images.shape) # 输出:(batch_size, 1, height, width) 或者 (batch_size, num_channels, height, width)取决于你的图像数据 print(labels.shape) # 输出:(batch_size,) break class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 9, 3) # input: (batch_size, 1, height, width), output: (batch_size, 9, height-2, width-2) self.maxpool = nn.MaxPool2d(2, 2) # 2x2的最大池化层,输出:(batch_size, 9, height/2-1, width/2-1) self.conv2 = nn.Conv2d(9, 5, 3) # input: (batch_size, 9, height/2-1, width/2-1), output: (batch_size, 5, height/2-2, width/2-2) self.conv3 = nn.Conv2d(5, 5, 3) # input: (batch_size, 5, height/2-2, width/2-2), output: (batch_size, 5, height/2-3, width/2-3) self.relu = nn.ReLU() self.fc1 = nn.Linear(5 * 5 * 5, 480) # input: (batch_size, 5*5*5), output: (batch_size, 480) self.fc2 = nn.Linear(480, 320) # input: (batch_size, 480), output: (batch_size, 320) self.fc3 = nn.Linear(320, 2) # input: (batch_size, 320), output: (batch_size, 2) def forward(self, x): x = self.maxpool(self.relu(self.conv1(x))) # output: (batch_size, 9, height/2-1, width/2-1) x = self.maxpool(self.relu(self.conv2(x))) # output: (batch_size, 5, height/4-1, width/4-1) or (batch_size, 5*num_channels ..., height/4-1, width/4-1) depending on the num_channels of your input data x = self.maxpool(self.relu(self.conv3(x))) # output: (batch_size, 5, height/8-1, width/8-1) or (batch_size,# 打印出模型的结构
print(model1)x = images[0]# forward正向传播过程
out_put = model1(x)for feature_map in out_put:# [N, C, H, W] -> [C, H, W] 维度变换im = np.squeeze(feature_map.detach().numpy())# [C, H, W] -> [H, W, C]im = np.transpose(im, [1, 2, 0])print(im.shape)# show 9 feature mapsplt.figure()for i in range(5):ax = plt.subplot(3, 3, i + 1) # 参数意义:3:图片绘制行数,5:绘制图片列数,i+1:图的索引# [H, W, C]# 特征矩阵每一个channel对应的是一个二维的特征矩阵,就像灰度图像一样,channel=1# plt.imshow(im[:, :, i])plt.imshow(im[:, :, i], cmap='gray')plt.show()
可以看到,在最开始的图像中还有明确的⚪,到后来就没了,变成了一个个像素组的,因为初级特征关注的是直接从原数据集中提取到的。而在后期,也就是高级特征阶段,关注的更加抽象。
这一部分是参考的【NNDL 作业7 相关语言解释+基于CNN的XO识别代码复现-CSDN博客】
三、收获
1.首先是修改卷积层:在这一环节中,我多次尝试【10轮次时】,然后最后的准确率都在50%-60%左右,后来修改到20轮次,发现效果好了很多,可是在第7轮是就没有损失了,感觉很神奇。
2.关于提取这几个特征图,很难弄,在网上也没有搜到相关资料,一直卡着,后来看了美女学霸的【指路:NNDL 作业7 相关语言解释+基于CNN的XO识别代码复现-CSDN博客】,发现“out_put = model1(x)”这一步,也就是正向传播得到特征图,豁然开朗,后边我就懂了。
3.在最后可视化特征这部分,我用了三个卷积层+三个线性层【想让高级特征更明显点,但是好像不太行】
深层网络的感受野更大,大感受野下才存在一定的高阶语义。深层网络所积累的特征空间更大。
上边那句话我的理解是,神经网络越深,那么他的高级特征更抽象。
【来自(46 封私信 / 82 条消息) 为什么越深层的特征图具备更丰富的语义信息? - 知乎 (zhihu.com)】
我感觉我的高级特征图还不是很抽象,可能卷积层再多一点,网络再深一点效果会更明显。
XO识别参考:
【23-24 秋学期】NNDL 作业7 基于CNN的XO识别-CSDN博客
【2021-2022 春学期】人工智能-作业6:CNN实现XO识别_x = self.conv2(x)#请问经过conv2(x)之后,x的维度是多少-CSDN博客