• 【23-24 秋学期】NNDL 作业7 基于CNN的XO识别


    一、用自己的语言解释以下概念


    (一)、局部感知、权值共享

    (1)局部感知

    定义:在进行卷积计算的时候,将图片划分为一个个的区域进行计算/考虑;

    由于越是接近的像素点之间的关联性越强, 反之则越弱. 所以我们选择先进行局部感知, 然后在更高层(FC层)将这些局部信息综合起来得到全局信息的方式.【来自深度学习之卷积神经网络(Convolutional Neural Networks, CNN) - 知乎 (zhihu.com)

    感知的大小只有滑动窗口那么大。如下图所示:

    可以看到,每次卷积核都是针对某一局部的数据窗口进行卷积,这就是所谓的CNN中的局部感知机制。

    (2)权值共享

    定义:在卷积神经网络中,同一个卷积核在整个输入数据的不同位置共享相同的权重参数。这意味着无论卷积核移动到哪个位置,它所使用的权重参数都是一样的。

    以上文参考卷积神经网络(CNN)的相关概念 - 掘金 (juejin.cn)

    如下图所示,移动的蓝框是局部感知,在计算第一个output时,W0不改变,这就是权值共享

    【图来自https://cs231n.github.io/convolutional-networks/】

    (二)、池化(子采样、降采样、汇聚)。会带来那些好处和坏处?

    (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)

    相比于CNNFCN相较于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)模型构建

    如上图所示,构建模型:

    1. class Net(nn.Module):
    2. def __init__(self):
    3. super(Net, self).__init__()
    4. #定义卷积层
    5. self.conv1 = nn.Conv2d(1, 9, 3)
    6. #最大池化层
    7. self.maxpool = nn.MaxPool2d(2, 2)
    8. self.conv2 = nn.Conv2d(9, 5, 3)
    9. self.relu = nn.ReLU()
    10. self.fc1 = nn.Linear(27 * 27 * 5, 1200)
    11. self.fc2 = nn.Linear(1200, 64)
    12. self.fc3 = nn.Linear(64, 2)
    13. def forward(self, x):
    14. #第一次
    15. x = self.maxpool(self.relu(self.conv1(x)))
    16. #第二次
    17. x = self.maxpool(self.relu(self.conv2(x)))
    18. x = x.view(-1, 27 * 27 * 5)
    19. #全连接+激活
    20. x = self.relu(self.fc1(x))
    21. x = self.relu(self.fc2(x))
    22. x = self.fc3(x)
    23. return x

     (3)训练模型

    把数据分成了10个批次,然后进行循环,循环中嵌套的循环是计算平均损失函数:

    1. model = Net()
    2. criterion = torch.nn.CrossEntropyLoss() # 损失函数: 交叉熵损失函数
    3. optimizer = optim.SGD(model.parameters(), lr=0.1) # 优化函数:随机梯度下降
    4. epochs = 10
    5. for epoch in range(epochs):
    6. running_loss = 0.0#计算平均损失值【累加,每10次一输出一清零】
    7. for i, data in enumerate(data_loader):
    8. images, label = data
    9. out = model(images)
    10. loss = criterion(out, label)#计算损失
    11. optimizer.zero_grad()#清空之前的梯度
    12. loss.backward()
    13. optimizer.step()
    14. running_loss += loss.item()
    15. if (i + 1) % 10 == 0:
    16. print('[%d %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 100))
    17. running_loss = 0.0
    18. print('finished train')
    19. # 保存模型
    20. torch.save(model, 'model_name.pth') # 保存的是模型, 不止是w和b权重值

     然后保存模型。

    得到的输出为:
     

    1. [1 10] loss: 0.069
    2. [1 20] loss: 0.069
    3. [2 10] loss: 0.068
    4. [2 20] loss: 0.066
    5. [3 10] loss: 0.058
    6. [3 20] loss: 0.037
    7. [4 10] loss: 0.023
    8. [4 20] loss: 0.008
    9. [5 10] loss: 0.004
    10. [5 20] loss: 0.004
    11. [6 10] loss: 0.003
    12. [6 20] loss: 0.002
    13. [7 10] loss: 0.001
    14. [7 20] loss: 0.001
    15. [8 10] loss: 0.001
    16. [8 20] loss: 0.000
    17. [9 10] loss: 0.001
    18. [9 20] loss: 0.000
    19. [10 10] loss: 0.001
    20. [10 20] loss: 0.000
    21. finished train

    (4)测试模型

    读取一张图片进行测试:

    1. # 读取模型
    2. model_load = torch.load('model_name.pth')
    3. # 读取一张图片 images[0],测试
    4. print("labels[0] truth:\t", labels[0])
    5. x = images[0]
    6. predicted = torch.max(model_load(x), 1)
    7. print("labels[0] predict:\t", predicted.indices)
    8. img = images[0].data.squeeze().numpy() # 将输出转换为图片的格式
    9. plt.imshow(img, cmap='gray')
    10. plt.show()

    如下图所示,预测正确:

    (5)计算准确率 

     “预测正确的数目除以总的数目”,得到准确率:

    1. # 读取模型
    2. model_load = torch.load('model_name.pth')
    3. correct = 0
    4. total = 0
    5. with torch.no_grad(): # 进行评测的时候网络不更新梯度
    6. for data in data_loader_test: # 读取测试集
    7. images, labels = data
    8. outputs = model_load(images)
    9. _, predicted = torch.max(outputs.data, 1) # 取出 最大值的索引 作为 分类结果
    10. total += labels.size(0) # labels 的长度
    11. correct += (predicted == labels).sum().item() # 预测正确的数目
    12. print('Accuracy of the network on the test images: %f %%' % (100. * correct / total))

    得到准确率:

    (6)查看训练好模型的特征图

    1. # 看看每层的 卷积核 长相,特征图 长相
    2. # 获取网络结构的特征矩阵并可视化
    3. import torch
    4. import matplotlib.pyplot as plt
    5. import numpy as np
    6. from PIL import Image
    7. from torchvision import transforms, datasets
    8. import torch.nn as nn
    9. from torch.utils.data import DataLoader
    10. # 定义图像预处理过程(要与网络模型训练过程中的预处理过程一致)
    11. transforms = transforms.Compose([
    12. transforms.ToTensor(), # 把图片进行归一化,并把数据转换成Tensor类型
    13. transforms.Grayscale(1) # 把图片 转为灰度图
    14. ])
    15. path = r'training_data_sm'
    16. data_train = datasets.ImageFolder(path, transform=transforms)
    17. data_loader = DataLoader(data_train, batch_size=64, shuffle=True)
    18. for i, data in enumerate(data_loader):
    19. images, labels = data
    20. print(images.shape)
    21. print(labels.shape)
    22. break
    23. class Net(nn.Module):
    24. def __init__(self):
    25. super(Net, self).__init__()
    26. self.conv1 = nn.Conv2d(1, 9, 3) # in_channel , out_channel , kennel_size , stride
    27. self.maxpool = nn.MaxPool2d(2, 2)
    28. self.conv2 = nn.Conv2d(9, 5, 3) # in_channel , out_channel , kennel_size , stride
    29. self.relu = nn.ReLU()
    30. self.fc1 = nn.Linear(27 * 27 * 5, 1200) # full connect 1
    31. self.fc2 = nn.Linear(1200, 64) # full connect 2
    32. self.fc3 = nn.Linear(64, 2) # full connect 3
    33. def forward(self, x):
    34. outputs = []
    35. x = self.conv1(x)
    36. outputs.append(x)
    37. x = self.relu(x)
    38. outputs.append(x)
    39. x = self.maxpool(x)
    40. outputs.append(x)
    41. x = self.conv2(x)
    42. x = self.relu(x)
    43. x = self.maxpool(x)
    44. x = x.view(-1, 27 * 27 * 5)
    45. x = self.relu(self.fc1(x))
    46. x = self.relu(self.fc2(x))
    47. x = self.fc3(x)
    48. return outputs
    49. # create model
    50. model1 = Net()
    51. # load model weights加载预训练权重
    52. # model_weight_path ="./AlexNet.pth"
    53. model_weight_path = "model_name1.pth"
    54. model1.load_state_dict(torch.load(model_weight_path))
    55. # 打印出模型的结构
    56. print(model1)
    57. x = images[0]
    58. # forward正向传播过程
    59. out_put = model1(x)
    60. for feature_map in out_put:
    61. # [N, C, H, W] -> [C, H, W] 维度变换
    62. im = np.squeeze(feature_map.detach().numpy())
    63. # [C, H, W] -> [H, W, C]
    64. im = np.transpose(im, [1, 2, 0])
    65. print(im.shape)
    66. # show 9 feature maps
    67. plt.figure()
    68. for i in range(9):
    69. ax = plt.subplot(3, 3, i + 1) # 参数意义:3:图片绘制行数,5:绘制图片列数,i+1:图的索引
    70. # [H, W, C]
    71. # 特征矩阵每一个channel对应的是一个二维的特征矩阵,就像灰度图像一样,channel=1
    72. # plt.imshow(im[:, :, i])
    73. plt.imshow(im[:, :, i], cmap='gray')
    74. plt.show()

    输出:

    实例化情况

    1. torch.Size([64, 1, 116, 116])
    2. torch.Size([64])
    3. Net(
    4. (conv1): Conv2d(1, 9, kernel_size=(3, 3), stride=(1, 1))
    5. (maxpool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    6. (conv2): Conv2d(9, 5, kernel_size=(3, 3), stride=(1, 1))
    7. (relu): ReLU()
    8. (fc1): Linear(in_features=3645, out_features=1200, bias=True)
    9. (fc2): Linear(in_features=1200, out_features=64, bias=True)
    10. (fc3): Linear(in_features=64, out_features=2, bias=True)
    11. )

     

    (7)查看卷积核

    1. # 看看每层的 卷积核 长相,特征图 长相
    2. # 获取网络结构的特征矩阵并可视化
    3. import torch
    4. import matplotlib.pyplot as plt
    5. import numpy as np
    6. from PIL import Image
    7. from torchvision import transforms, datasets
    8. import torch.nn as nn
    9. from torch.utils.data import DataLoader
    10. plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
    11. plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 #有中文出现的情况,需要u'内容
    12. # 定义图像预处理过程(要与网络模型训练过程中的预处理过程一致)
    13. transforms = transforms.Compose([
    14. transforms.ToTensor(), # 把图片进行归一化,并把数据转换成Tensor类型
    15. transforms.Grayscale(1) # 把图片 转为灰度图
    16. ])
    17. path = r'training_data_sm'
    18. data_train = datasets.ImageFolder(path, transform=transforms)
    19. data_loader = DataLoader(data_train, batch_size=64, shuffle=True)
    20. for i, data in enumerate(data_loader):
    21. images, labels = data
    22. # print(images.shape)
    23. # print(labels.shape)
    24. break
    25. class Net(nn.Module):
    26. def __init__(self):
    27. super(Net, self).__init__()
    28. self.conv1 = nn.Conv2d(1, 9, 3) # in_channel , out_channel , kennel_size , stride
    29. self.maxpool = nn.MaxPool2d(2, 2)
    30. self.conv2 = nn.Conv2d(9, 5, 3) # in_channel , out_channel , kennel_size , stride
    31. self.relu = nn.ReLU()
    32. self.fc1 = nn.Linear(27 * 27 * 5, 1200) # full connect 1
    33. self.fc2 = nn.Linear(1200, 64) # full connect 2
    34. self.fc3 = nn.Linear(64, 2) # full connect 3
    35. def forward(self, x):
    36. outputs = []
    37. x = self.maxpool(self.relu(self.conv1(x)))
    38. # outputs.append(x)
    39. x = self.maxpool(self.relu(self.conv2(x)))
    40. outputs.append(x)
    41. x = x.view(-1, 27 * 27 * 5)
    42. x = self.relu(self.fc1(x))
    43. x = self.relu(self.fc2(x))
    44. x = self.fc3(x)
    45. return outputs
    46. # create model
    47. model1 = Net()
    48. # load model weights加载预训练权重
    49. model_weight_path = "model_name1.pth"
    50. model1.load_state_dict(torch.load(model_weight_path))
    51. x = images[0]
    52. # forward正向传播过程
    53. out_put = model1(x)
    54. weights_keys = model1.state_dict().keys()
    55. for key in weights_keys:
    56. print("key :", key)
    57. # 卷积核通道排列顺序 [kernel_number, kernel_channel, kernel_height, kernel_width]
    58. if key == "conv1.weight":
    59. weight_t = model1.state_dict()[key].numpy()
    60. print("weight_t.shape", weight_t.shape)
    61. k = weight_t[:, 0, :, :] # 获取第一个卷积核的信息参数
    62. # show 9 kernel ,1 channel
    63. plt.figure()
    64. for i in range(9):
    65. ax = plt.subplot(3, 3, i + 1) # 参数意义:3:图片绘制行数,5:绘制图片列数,i+1:图的索引
    66. plt.imshow(k[i, :, :], cmap='gray')
    67. title_name = 'kernel' + str(i) + ',channel1'
    68. plt.title(title_name)
    69. plt.show()
    70. if key == "conv2.weight":
    71. weight_t = model1.state_dict()[key].numpy()
    72. print("weight_t.shape", weight_t.shape)
    73. k = weight_t[:, :, :, :] # 获取第一个卷积核的信息参数
    74. print(k.shape)
    75. print(k)
    76. plt.figure()
    77. for c in range(9):
    78. channel = k[:, c, :, :]
    79. for i in range(5):
    80. ax = plt.subplot(2, 3, i + 1) # 参数意义:3:图片绘制行数,5:绘制图片列数,i+1:图的索引
    81. plt.imshow(channel[i, :, :], cmap='gray')
    82. title_name = 'kernel' + str(i) + ',channel' + str(c)
    83. plt.title(title_name)
    84. plt.show()

     输出:

    卷积核

     

     

     

    (二)、重新设计网络结构

    (1)至少增加一个卷积层,卷积层达到三层以上

    由于我在加入一层卷积层后,计算特征图尺寸一一直不对,然后舍友茜借了我一段代码:

    1. import torch
    2. import torch.nn as nn
    3. # 定义卷积层和池化层
    4. conv1 = nn.Conv2d(1, 9, 3)
    5. maxpool = nn.MaxPool2d(2, 2)
    6. conv2 = nn.Conv2d(9, 5, 3)
    7. conv3 = nn.Conv2d(5, 3, 3)
    8. # 假设输入图像尺寸为 64x64
    9. W_in, H_in = 116, 116
    10. # 计算特征图尺寸
    11. x = torch.rand(1, 1, W_in, H_in) # 构造一个输入张量
    12. x = maxpool(nn.ReLU()(conv1(x)))
    13. x = maxpool(nn.ReLU()(conv2(x)))
    14. x = maxpool(nn.ReLU()(conv3(x)))
    15. output_size = x.size()[2:] # 获取特征图的尺寸
    16. print("Output feature map size:", output_size)

     可以得到对应的尺寸:

    Output feature map size: torch.Size([12, 12])

    在后边就不放代码了,只有构建模型部分进行了一点点修改:

    1. import torch
    2. import torch.nn as nn
    3. # 定义卷积层和池化层
    4. conv1 = nn.Conv2d(1, 9, 3)
    5. maxpool = nn.MaxPool2d(2, 2)
    6. conv2 = nn.Conv2d(9, 5, 3)
    7. conv3 = nn.Conv2d(5, 3, 3)
    8. # 假设输入图像尺寸为 64x64
    9. W_in, H_in = 116, 116
    10. # 计算特征图尺寸
    11. x = torch.rand(1, 1, W_in, H_in) # 构造一个输入张量
    12. x = maxpool(nn.ReLU()(conv1(x)))
    13. x = maxpool(nn.ReLU()(conv2(x)))
    14. x = maxpool(nn.ReLU()(conv3(x)))
    15. output_size = x.size()[2:] # 获取特征图的尺寸
    16. print("Output feature map size:", output_size)

    然后训练模型得到的损失:

    1. [1 10] loss: 0.051
    2. [1 20] loss: 0.045
    3. [1 30] loss: 0.039
    4. [2 10] loss: 0.045
    5. [2 20] loss: 0.041
    6. [2 30] loss: 0.044
    7. [3 10] loss: 0.042
    8. [3 20] loss: 0.046
    9. [3 30] loss: 0.042
    10. [4 10] loss: 0.040
    11. [4 20] loss: 0.045
    12. [4 30] loss: 0.043
    13. [5 10] loss: 0.040
    14. [5 20] loss: 0.044
    15. [5 30] loss: 0.045
    16. [6 10] loss: 0.045
    17. [6 20] loss: 0.041
    18. [6 30] loss: 0.044
    19. [7 10] loss: 0.043
    20. [7 20] loss: 0.047
    21. [7 30] loss: 0.037
    22. [8 10] loss: 0.037
    23. [8 20] loss: 0.046
    24. [8 30] loss: 0.044
    25. [9 10] loss: 0.042
    26. [9 20] loss: 0.046
    27. [9 30] loss: 0.042
    28. [10 10] loss: 0.041
    29. [10 20] loss: 0.045
    30. [10 30] loss: 0.041
    31. finished train

    效果不太好。 

    然后参考了一下美女学霸NNDL 作业7 相关语言解释+基于CNN的XO识别代码复现-CSDN博客的,多增加了10轮次,效果很好:

    1. [1 10] loss: 0.069
    2. [1 20] loss: 0.069
    3. [2 10] loss: 0.069
    4. [2 20] loss: 0.068
    5. [3 10] loss: 0.056
    6. [3 20] loss: 0.034
    7. [4 10] loss: 0.012
    8. [4 20] loss: 0.006
    9. [5 10] loss: 0.005
    10. [5 20] loss: 0.001
    11. [6 10] loss: 0.002
    12. [6 20] loss: 0.001
    13. [7 10] loss: 0.001
    14. [7 20] loss: 0.001
    15. [8 10] loss: 0.001
    16. [8 20] loss: 0.000
    17. [9 10] loss: 0.000
    18. [9 20] loss: 0.000
    19. [10 10] loss: 0.000
    20. [10 20] loss: 0.000
    21. [11 10] loss: 0.000
    22. [11 20] loss: 0.000
    23. [12 10] loss: 0.000
    24. [12 20] loss: 0.000
    25. [13 10] loss: 0.000
    26. [13 20] loss: 0.000
    27. [14 10] loss: 0.000
    28. [14 20] loss: 0.000
    29. [15 10] loss: 0.000
    30. [15 20] loss: 0.000
    31. [16 10] loss: 0.000
    32. [16 20] loss: 0.000
    33. [17 10] loss: 0.000
    34. [17 20] loss: 0.000
    35. [18 10] loss: 0.000
    36. [18 20] loss: 0.000
    37. [19 10] loss: 0.000
    38. [19 20] loss: 0.000
    39. [20 10] loss: 0.000
    40. [20 20] loss: 0.000
    41. finished train


    (2)去掉池化层,对比“有无池化”的效果

    得到的结果:

    1. [1 10] loss: 0.062
    2. [1 20] loss: 0.051
    3. [1 30] loss: 0.044
    4. [2 10] loss: 0.041
    5. [2 20] loss: 0.045
    6. [2 30] loss: 0.042
    7. [3 10] loss: 0.044
    8. [3 20] loss: 0.043
    9. [3 30] loss: 0.041
    10. [4 10] loss: 0.043
    11. [4 20] loss: 0.041
    12. [4 30] loss: 0.044
    13. [5 10] loss: 0.042
    14. [5 20] loss: 0.042
    15. [5 30] loss: 0.044
    16. [6 10] loss: 0.041
    17. [6 20] loss: 0.045
    18. [6 30] loss: 0.042
    19. [7 10] loss: 0.043
    20. [7 20] loss: 0.043
    21. [7 30] loss: 0.039
    22. [8 10] loss: 0.046
    23. [8 20] loss: 0.039
    24. [8 30] loss: 0.043
    25. [9 10] loss: 0.039
    26. [9 20] loss: 0.044
    27. [9 30] loss: 0.043
    28. [10 10] loss: 0.037
    29. [10 20] loss: 0.043
    30. [10 30] loss: 0.044
    31. [11 10] loss: 0.040
    32. [11 20] loss: 0.042
    33. [11 30] loss: 0.041
    34. [12 10] loss: 0.039
    35. [12 20] loss: 0.040
    36. [12 30] loss: 0.042
    37. [13 10] loss: 0.039
    38. [13 20] loss: 0.041
    39. [13 30] loss: 0.037
    40. [14 10] loss: 0.038
    41. [14 20] loss: 0.036
    42. [14 30] loss: 0.035
    43. [15 10] loss: 0.034
    44. [15 20] loss: 0.033
    45. [15 30] loss: 0.031
    46. [16 10] loss: 0.030
    47. [16 20] loss: 0.029
    48. [16 30] loss: 0.029
    49. [17 10] loss: 0.025
    50. [17 20] loss: 0.024
    51. [17 30] loss: 0.024
    52. [18 10] loss: 0.014
    53. [18 20] loss: 0.019
    54. [18 30] loss: 0.021
    55. [19 10] loss: 0.011
    56. [19 20] loss: 0.009
    57. [19 30] loss: 0.017
    58. [20 10] loss: 0.006
    59. [20 20] loss: 0.006
    60. [20 30] loss: 0.008
    61. finished train

    可以看到,20轮次有较好的效果。但是速度比较慢,还有池化层的效率更高。 

    (3)修改“通道数”等超参数,观察变化

    修改了通道数,改成11的:
     

    1. [1 10] loss: 0.070
    2. [1 20] loss: 0.069
    3. [2 10] loss: 0.069
    4. [2 20] loss: 0.067
    5. [3 10] loss: 0.050
    6. [3 20] loss: 0.038
    7. [4 10] loss: 0.022
    8. [4 20] loss: 0.010
    9. [5 10] loss: 0.004
    10. [5 20] loss: 0.003
    11. [6 10] loss: 0.002
    12. [6 20] loss: 0.002
    13. [7 10] loss: 0.001
    14. [7 20] loss: 0.002
    15. [8 10] loss: 0.001
    16. [8 20] loss: 0.001
    17. [9 10] loss: 0.001
    18. [9 20] loss: 0.000
    19. [10 10] loss: 0.001
    20. [10 20] loss: 0.000
    21. finished train
    Accuracy of the network on the  test images: 99.666667 %

     改为15,效果不好:

    1. [1 10] loss: 0.070
    2. [1 20] loss: 0.069
    3. [2 10] loss: 0.069
    4. [2 20] loss: 0.069
    5. [3 10] loss: 0.069
    6. [3 20] loss: 0.069
    7. [4 10] loss: 0.069
    8. [4 20] loss: 0.069
    9. [5 10] loss: 0.069
    10. [5 20] loss: 0.069
    11. [6 10] loss: 0.069
    12. [6 20] loss: 0.069
    13. [7 10] loss: 0.069
    14. [7 20] loss: 0.069
    15. [8 10] loss: 0.069
    16. [8 20] loss: 0.069
    17. [9 10] loss: 0.069
    18. [9 20] loss: 0.068
    19. [10 10] loss: 0.065
    20. [10 20] loss: 0.061
    21. 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`函数展示绘制好的特征图。

    1. import torch
    2. from torchvision import transforms
    3. from torch.utils.data import DataLoader
    4. from torchvision import datasets, transforms
    5. import torch.nn as nn
    6. # 数据预处理
    7. transforms = transforms.Compose([
    8. transforms.ToTensor(), # 把图片进行归一化,并把数据转换成Tensor类型
    9. transforms.Grayscale(), # 把图片转为灰度图
    10. ])
    11. path = r'train_data'
    12. data_train = datasets.ImageFolder(path, transform=transforms)
    13. data_loader = DataLoader(data_train, batch_size=64, shuffle=True)
    14. for i, data in enumerate(data_loader):
    15. images, labels = data
    16. print(images.shape) # 输出:(batch_size, 1, height, width) 或者 (batch_size, num_channels, height, width)取决于你的图像数据
    17. print(labels.shape) # 输出:(batch_size,)
    18. break
    19. class Net(nn.Module):
    20. def __init__(self):
    21. super(Net, self).__init__()
    22. self.conv1 = nn.Conv2d(1, 9, 3) # input: (batch_size, 1, height, width), output: (batch_size, 9, height-2, width-2)
    23. self.maxpool = nn.MaxPool2d(2, 2) # 2x2的最大池化层,输出:(batch_size, 9, height/2-1, width/2-1)
    24. 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)
    25. 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)
    26. self.relu = nn.ReLU()
    27. self.fc1 = nn.Linear(5 * 5 * 5, 480) # input: (batch_size, 5*5*5), output: (batch_size, 480)
    28. self.fc2 = nn.Linear(480, 320) # input: (batch_size, 480), output: (batch_size, 320)
    29. self.fc3 = nn.Linear(320, 2) # input: (batch_size, 320), output: (batch_size, 2)
    30. def forward(self, x):
    31. x = self.maxpool(self.relu(self.conv1(x))) # output: (batch_size, 9, height/2-1, width/2-1)
    32. 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
    33. x = self.maxpool(self.relu(self.conv3(x))) # output: (batch_size, 5, height/8-1, width/8-1) or (batch_size,
    34. # 打印出模型的结构
    35. print(model1)
    36. x = images[0]
    37. # forward正向传播过程
    38. out_put = model1(x)
    39. for feature_map in out_put:
    40. # [N, C, H, W] -> [C, H, W] 维度变换
    41. im = np.squeeze(feature_map.detach().numpy())
    42. # [C, H, W] -> [H, W, C]
    43. im = np.transpose(im, [1, 2, 0])
    44. print(im.shape)
    45. # show 9 feature maps
    46. plt.figure()
    47. for i in range(5):
    48. ax = plt.subplot(3, 3, i + 1) # 参数意义:3:图片绘制行数,5:绘制图片列数,i+1:图的索引
    49. # [H, W, C]
    50. # 特征矩阵每一个channel对应的是一个二维的特征矩阵,就像灰度图像一样,channel=1
    51. # plt.imshow(im[:, :, i])
    52. plt.imshow(im[:, :, i], cmap='gray')
    53. 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博客

  • 相关阅读:
    Linux安装Python3.10与部署flask项目实战详细记录
    目标检测应用场景和发展趋势
    电商运营该如何做 AB 测试
    LeetCode算法题解|LeetCode738. 单调递增的数字、LeetCode968. 监控二叉树
    洛谷刷题C语言:数字统计、你的飞碟在这儿、哥德巴赫猜想、数字翻转、低洼地
    一篇文章带你掌握性能测试工具——Jmeter
    5V升压充电8.4V芯片
    项目重构演进之路
    Codeforces Round 836 (Div. 2) A - C
    mysql 中的锁
  • 原文地址:https://blog.csdn.net/weixin_61838030/article/details/134347110