NNDL 作业7 基于CNN的XO识别

目录

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

1. 局部感知、权值共享

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

3. 全卷积网络:参考  全卷积神经网络详解

4. 低级特征、中级特征、高级特征

5. 多通道。N输入,M输出是如何实现的?

6. 1×1的卷积核有什么作用 

 二、使用CNN进行XO识别

1.复现参考资料中的代码

训练模型

测试训练好的模型

测试模型(计算模型在测试集上的准确率)

2.重新设计网络结构

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

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

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

3.可视化

选择自己的最优模型

可视化特征图

 查看训练好的模型的卷积核

探索低级特征、中级特征、高级特征 

计算卷积、池化层后的特征图尺寸:

 画出训练过程损失和准确率变化的图像:

总结:


 

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

  1. 局部感知、权值共享
  2. 池化(子采样、降采样、汇聚)。会带来那些好处和坏处?
  3. 全卷积网络(课上讲的这个概念不准确,同学们查资料纠正一下)
  4. 低级特征、中级特征、高级特征
  5. 多通道。N输入,M输出是如何实现的?
  6. 1×1的卷积核有什么作用
  • 1. 局部感知、权值共享

局部感知:是指在卷积神经网络中,每个神经元不需要对整个图像的数据都进行感知,只需要对局部数据进行感知,(如图所示:感知的数据大小只有卷积核那么大)找出局部特征即可,随后在比当前层更高的层将局部特征综合起来就可以了。所谓感知,是指神经元对输入数据的处理和响应,具体来讲就是神经元通过卷积核对输入数据进行加权求和,并通过激活函数激活等一系列操作得到输出的过程。

权值共享:在卷积神经网络中,要共享的权值是卷积核中的数。卷积核在进行卷积操作时,是用相同的卷积核对整个图像的全局数据进行扫描,这就是权值共享。相对的,如果某种情况下设定卷积核都有不同的权值,就是权值独立,但是这种情况会带来很大的计算量。


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

池化:图像进行卷积后,对输出的特征图进行约减,缩小图像(称为下采样或降采样),同时保留特征图中的主要特征。一般有平均汇聚和最大汇聚:即用采样区间的最大值或平均值来代替整个区间,以达到缩小数据量的效果。

池化的好处:

  1. 降低计算量:池化操作可以减小特征图的尺寸和数量,从而降低后续层的计算量,加快模型的训练速度。
  2. 减少过拟合:池化操作可以减少特征图的维度,降低模型的参数数量,有助于减少过拟合的风险,提高模型的泛化能力。
  3. 提取主要特征:最大池化可以保留最显著的特征信息,有助于提取图像中最重要的特征,提高模型的表达能力。

池化的坏处:

池化操作会丢失一部分特征信息,可能会损失一些细节信息,导致模型在某些任务上的性能下降。过多的池化操作会导致特征图的尺寸变得很小,可能会导致分辨率下降,影响模型对细节的捕捉能力。

核心:

  1. 不含全连接层的全卷积网络,可适应任意尺寸输入;
  2. 反卷积层增大图像尺寸,输出精细结果;
  3. 结合不同深度层结果的跳级结构:即下文中通过跳级结构将最后一层的预测(富有全局信息)和更浅层(富有局部信息)的预测结合起来,在遵守全局预测的同时进行局部预测,确保鲁棒性和精确性。

全卷积神经网络一般步骤:

阶段1:以经典的分类网络为初始化,最后两级为全连接(红色),参数弃去不用。

阶段2:FCN-32s 网络—从特征小图预测分割小图,之后直接升采样为大图。

阶段3:FCN-16s 网络—上采样分为两次完成。在第二次升采样前,把第4个pooling层的预测结果融合进来,使用跳级结构提升精确性。

阶段4:FCN-8s 网络—升采样分为三次完成。 进一步融合了第3个pooling层的预测结果


  • 4. 低级特征、中级特征、高级特征

  1. 低级特征: 低级特征通常指的是由网络的底层卷积层提取的特征,这些特征包括边缘、纹理、颜色等局部的基本特征。低级特征具有较小的感受野,能够捕捉到图像中的细微细节,但缺乏全局的语义信息。

  2. 中级特征: 中级特征是在网络的中间层提取的特征,它们能够捕捉到更加抽象的图像特征,例如简单的形状、纹理组合等。中级特征具有较大的感受野,能够捕捉到一定程度的全局信息,但还不具备高级语义信息。

  3. 高级特征:高级特征能够捕捉到图像的全局语义信息, 是由网络的顶层卷积层或全连接层提取的特征,它们能够表征图像中的抽象概念和语义信息,如物体的类别、位置等。


  • 5. 多通道。N输入,M输出是如何实现的?

N输入是指输入N通道的图像,经过M个卷积核,卷积操作后得到M个通道的特征图。(对于卷积核来说:一共有M个卷积核,每个卷积核都有N个通道。)

如图所示:N通道的输入图像是要和每个卷积核的通道相对应的,N通道的输入图像和N通道的卷积核进行卷积,然后将特征图上对应位置的值相加,得到单通道的特征图,然后M个卷积核的结果集合而成的就是M通道的特征图。


  • 6. 1×1的卷积核有什么作用 

1. 增加网络深度(增加非线性映射次数):1x1卷积核的卷积过程相当于全连接的计算过程,不改变感受野和图像大小,但是内含的非线性激活函数被逐层引入,增加网络的非线性,增强神经网络的表达能力,使得网络可以表达更复杂的特征。

卷积核越大,它生成的特征图对应的感受野就越大,随着网络深度的增加,越靠后的特征图上的节点感受野也越大。因此特征也越来越抽象。但有的时候,我们想在不增加感受野的情况下,让网络加深,只是为了引入更多的非线性。1x1卷积核正好可以解决这个问题。

2. 升维/降维

可以通过改变1x1卷积核的个数,改变该层卷积输出特征图通道的个数,以达到升维降维的效果。虽然其他的卷积核也有这个作用,但是如果只想单纯的去提升或者降低特征图的通道,选用1x1卷积核最为合适, 1x1卷积核会使用更少的权重参数数量

3. 跨通道的信息交互

1x1卷积核只有一个参数,当它作用在多通道的特征图上时,相当于不同通道上的一个线性组合,实际上就是加起来再乘以一个系数,但是这样输出的特征图就是多个通道的整合信息了,能够使网络提取的特征更加丰富。

比如:在尺寸3x3,64通道个数的卷积核后面添加一个尺寸1x1,28通道个数的卷积核,就变成了尺寸3x3,28尺寸的卷积核。 原来的64个通道就可以理解为跨通道线性组合变成了28通道(只是通道处的线性组合),这就是通道间的信息交互。

4. 减少卷积核参数(简化模型)

 在该卷积过程中,通过再增加一层1x1的卷积核,先将输入通道减少,再进行非1x1卷积核的卷积操作,大大的降低了计算量。在GoogLeNet的3a模块和ResNet网络中都使用了1x1的卷积核来减少参数数目,降低计算量。

 二、使用CNN进行XO识别

1.复现参考资料中的代码

2.重新设计网络结构

  • 至少增加一个卷积层,卷积层达到三层以上
  • 去掉池化层,对比“有无池化”的效果
  • 修改“通道数”等超参数,观察变化

3.可视化

  • 选择自己的最优模型
  • 可视化部分卷积核和特征图
  • 探索低级特征、中级特征、高级特征

1.复现参考资料中的代码

训练模型
import torch
from torchvision import transforms, datasets
import torch.nn as nn
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import torch.optim as optim

transforms = transforms.Compose([
    transforms.ToTensor(),  # 把图片进行归一化,并把数据转换成Tensor类型
    transforms.Grayscale(1)  # 把图片 转为灰度图
])

path = r'train_data'
path_test = r'test_data'

data_train = datasets.ImageFolder(path, transform=transforms)
data_test = datasets.ImageFolder(path_test, transform=transforms)

print("size of train_data:", len(data_train))
print("size of test_data:", len(data_test))

data_loader = DataLoader(data_train, batch_size=64, shuffle=True)
data_loader_test = DataLoader(data_test, batch_size=64, shuffle=True)

for i, data in enumerate(data_loader):
    images, labels = data
    print(images.shape)
    print(labels.shape)
    break

for i, data in enumerate(data_loader_test):
    images, labels = data
    print(images.shape)
    print(labels.shape)
    break


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.relu = nn.ReLU()
        self.fc1 = nn.Linear(27 * 27 * 5, 1200)  # full connect 1
        self.fc2 = nn.Linear(1200, 64)  # full connect 2
        self.fc3 = nn.Linear(64, 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 = x.view(-1, 27 * 27 * 5)
        x = self.relu(self.fc1(x))
        x = self.relu(self.fc2(x))
        x = self.fc3(x)
        return x


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
    for i, data in enumerate(data_loader):
        images, label = data
        out = 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.0

print('finished train')

# 保存模型 torch.save(model.state_dict(), model_path)
torch.save(model.state_dict(), 'model_name1.pth')  # 保存的是模型, 不止是w和b权重值

# 读取模型
model = torch.load('model_name1.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()

 

测试模型(计算模型在测试集上的准确率)
import torch
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'train_data'
path_test = r'test_data'

data_train = datasets.ImageFolder(path, transform=transforms)
data_test = datasets.ImageFolder(path_test, transform=transforms)

print("size of train_data:", len(data_train))
print("size of test_data:", len(data_test))

data_loader = DataLoader(data_train, batch_size=64, shuffle=True)
data_loader_test = DataLoader(data_test, batch_size=64, shuffle=True)
print(len(data_loader))
print(len(data_loader_test))


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.relu = nn.ReLU()
        self.fc1 = nn.Linear(27 * 27 * 5, 1200)  # full connect 1
        self.fc2 = nn.Linear(1200, 64)  # full connect 2
        self.fc3 = nn.Linear(64, 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 = x.view(-1, 27 * 27 * 5)
        x = self.relu(self.fc1(x))
        x = self.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 读取模型
model = Net()
model.load_state_dict(torch.load('model_name1.pth', map_location='cpu'))  # 导入网络的参数

# model_load = torch.load('model_name1.pth')
correct = 0
total = 0
with torch.no_grad():  # 进行评测的时候网络不更新梯度
    for data in data_loader_test:  # 读取测试集
        images, labels = data
        outputs = model(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))

测试结果:

以上是对老师HBU_David 代码的复现。


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)
        self.relu = nn.ReLU()
        self.fc1 = nn.Linear(25*25*3, 500)  # full connect 1
        self.fc2 = nn.Linear(500, 64)  # full connect 2
        self.fc3 = nn.Linear(64, 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.relu(self.conv3(x))
        x = x.view(-1, 25*25*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.relu = nn.ReLU()
        self.fc1 = nn.Linear(112 * 112 * 5, 1200)  # full connect 1
        self.fc2 = nn.Linear(1200, 64)  # full connect 2
        self.fc3 = nn.Linear(64, 2)  # full connect 3

    def forward(self, x):

        x = self.relu(self.conv1(x))
        x = self.relu(self.conv2(x))
        x = x.view(-1, 112 * 112 * 5)
        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, 15, 3)  # in_channel , out_channel , kennel_size , stride
        self.maxpool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(15, 8, 3)  # in_channel , out_channel , kennel_size , stride

        self.relu = nn.ReLU()
        self.fc1 = nn.Linear(27*27 * 8, 1200)  # full connect 1
        self.fc2 = nn.Linear(1200, 64)  # full connect 2
        self.fc3 = nn.Linear(64, 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 = x.view(-1, 27*27 * 8)
        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, 5, 3)  # in_channel , out_channel , kennel_size , stride
        self.maxpool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(5, 3, 3)  # in_channel , out_channel , kennel_size , stride

        self.relu = nn.ReLU()
        self.fc1 = nn.Linear(27*27 * 3, 1200)  # full connect 1
        self.fc2 = nn.Linear(1200, 64)  # full connect 2
        self.fc3 = nn.Linear(64, 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 = x.view(-1, 27*27 * 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, 3, 3)  # in_channel , out_channel , kennel_size , stride
        self.maxpool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(3, 1, 3)  # in_channel , out_channel , kennel_size , stride

        self.relu = nn.ReLU()
        self.fc1 = nn.Linear(27*27 * 1, 1200)  # full connect 1
        self.fc2 = nn.Linear(1200, 64)  # full connect 2
        self.fc3 = nn.Linear(64, 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 = x.view(-1, 27*27 * 1)
        x = self.relu(self.fc1(x))
        x = self.relu(self.fc2(x))
        x = self.fc3(x)
        return x

根据训练实验结果可以看出修改通道数的参数,当增多或减少一些训练效果差不多,只有调整到第一个卷积层有三个通道,第二个有一个通道,它的收敛速度是最快的。在第五次训练就达到了0.006,最后也收敛到了0.000。


3.可视化

  • 选择自己的最优模型
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 15, 3)  # in_channel , out_channel , kennel_size , stride
        self.maxpool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(15, 9, 3)  # in_channel , out_channel , kennel_size , stride
        self.conv3 = nn.Conv2d(9, 5, 3)
        self.relu = nn.ReLU()
        self.fc1 = nn.Linear(25*25*5, 1200)  # full connect 1
        self.fc2 = nn.Linear(1200, 64)  # full connect 2
        self.fc3 = nn.Linear(64, 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.relu(self.conv3(x))
        x = x.view(-1, 25*25*5)
        x = self.relu(self.fc1(x))
        x = self.relu(self.fc2(x))
        x = self.fc3(x)
        return x

  • 可视化特征图
# 看看每层的 卷积核 长相,特征图 长相
# 获取网络结构的特征矩阵并可视化
import torch
import matplotlib.pyplot as plt
import numpy as np
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'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)
    print(labels.shape)
    break


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.relu = nn.ReLU()
        self.fc1 = nn.Linear(27 * 27 * 5, 1200)  # full connect 1
        self.fc2 = nn.Linear(1200, 64)  # full connect 2
        self.fc3 = nn.Linear(64, 2)  # full connect 3

    def 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 maps
    plt.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()

卷积后特征图
激活后特征图
池化后特征图
 查看训练好的模型的卷积核
# 看看每层的 卷积核 长相,特征图 长相
# 获取网络结构的特征矩阵并可视化
import torch
import matplotlib.pyplot as plt

from torchvision import transforms, datasets
import torch.nn as nn
from torch.utils.data import DataLoader

plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号 #有中文出现的情况,需要u'内容
#  定义图像预处理过程(要与网络模型训练过程中的预处理过程一致)
transforms = transforms.Compose([
    transforms.ToTensor(),  # 把图片进行归一化,并把数据转换成Tensor类型
    transforms.Grayscale(1)  # 把图片 转为灰度图
])
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)
    # print(labels.shape)
    break


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.relu = nn.ReLU()
        self.fc1 = nn.Linear(27 * 27 * 5, 1200)  # full connect 1
        self.fc2 = nn.Linear(1200, 64)  # full connect 2
        self.fc3 = nn.Linear(64, 2)  # full connect 3

    def 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 channel
        plt.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()

运行结果:

conv1.weight
conv1.bias
conv2.weight
conv2.bias
fc1.weight
fc1.bias
fc2.weight
fc2.bias
fc3.weight
fc3.bias
  • 探索低级特征、中级特征、高级特征 
  # 输出第一个卷积层的特征图
        conv1_feature = model.conv1(images)
        plt.figure()
        for i in range(9):
            ax = plt.subplot(3, 3, i + 1)
            plt.imshow(conv1_feature[0, i, :, :].detach().numpy(), cmap='gray')
        plt.show()

        # 输出第二个卷积层的特征图
        conv2_feature = model.conv2(model.maxpool(model.relu(model.conv1(images))))
        plt.figure()
        for i in range(5):
            ax = plt.subplot(1, 5, i + 1)
            plt.imshow(conv2_feature[0, i, :, :].detach().numpy(), cmap='gray')
        plt.show()

        # 输出全连接层的特征图
        x =model.maxpool( model.conv2(model.maxpool(model.relu(model.conv1(images)))))
        x = x.view(-1, 27 * 27 * 5)
        fc_feature = model.fc3(model.relu(model.fc2(model.relu(model.fc1(x))))).detach().numpy()
        plt.figure()
        plt.bar(range(2), fc_feature[0])
        plt.xticks(range(2), ['class 0', 'class 1'])
        plt.show()
第一层卷积后的特征图
第二层卷积后的特征图

 线性层的输出:那个种类输出的概率大选哪个。

 对于这个实验来说,随着层数的增加,低级特征,中级特征,高级特征的特征图表现变化不是很明显,可以说对于整个图像来说,直接跳到了高级特征,可能是图片相对简单。但是图中物体的轮廓相对逐层增加了,在线性层后可以看出输出的就是分类结果了。

计算卷积、池化层后的特征图尺寸:

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 = nn.ReLU()(conv1(x))
x = nn.ReLU()(conv2(x))
x = nn.ReLU()(conv3(x))
output_size = x.size()[2:]  # 获取特征图的尺寸
print("Output feature map size:", output_size)

 画出训练过程损失和准确率变化的图像:


import matplotlib.pyplot as plt

# 可视化模型的训练过程
losses = []  # 用于存储每个epoch的损失值
accuracies = []  # 用于存储每个epoch的准确率

epochs = 10
for epoch in range(epochs):
    running_loss = 0.0
    correct = 0
    total = 0
    for i, data in enumerate(data_loader):
        images, label = data
        out = model(images)
        loss = criterion(out, label)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        _, predicted = torch.max(out, 1)
        total += label.size(0)
        correct += (predicted == label).sum().item()

    epoch_loss = running_loss / len(data_loader)
    accuracy = correct / total
    losses.append(epoch_loss)
    accuracies.append(accuracy)

    print(f'Epoch [{epoch + 1}/{epochs}], Loss: {epoch_loss:.4f}, Accuracy: {accuracy * 100:.2f}%')

# 绘制损失值和准确率随着epoch变化的曲线
plt.figure()
plt.plot(range(1, epochs + 1), losses, label='Training Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Training Loss over epochs')
plt.show()

plt.figure()
plt.plot(range(1, epochs + 1), accuracies, label='Training Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.title('Training Accuracy over epochs')
plt.show()

 

总结:

在这个作业中,主要就是对卷积神经网络的相关概念进行总结,然后进行了应用,利用CNN进行XO识别,在XO识别的过程中:

  1. 首先就是要对数据进行处理,导入数据,划分训练集和测试集。
  2. 然后构建模型,调用函数定义卷积层和池化层,并且调好参数,然后前向传播。
  3. 定义损失函数和优化器。
  4. 进行训练,加上反向传播。

要实现这些过程首要的就是了解卷积和池化层函数参数的调用。注意要会查看torch官网的解释最权威:torch函数官网

​​​​​​​​​​​​​​​​​这次主要使用的这两个:

  • torch.nn.Conv2d(in_channelsout_channelskernel_sizestride=1padding=0dilation=1groups=1bias=Truepadding_mode='zeros'device=Nonedtype=None)
  • torch.nn.MaxPool2d(kernel_sizestride=Nonepadding=0dilation=1return_indices=Falseceil_mode=False)

这次实验主要就是了解卷积神经网络的过程,并且熟悉这两个函数,会调参,并解决出现的各种问题。

 

深度学习基础学习-1x1卷积核的作用(CNN中)

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

HBU_David【2021-2022 春学期】人工智能-作业6:CNN实现XO识别

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值