NNDL 作业7 基于CNN的XO识别

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

1、局部感知、权值共享

局部感知

指在卷积神经网络中,每个神经元只与输入数据的一小部分相连,这一小部分被称为局部感受野。通过这种方式,神经元只关注输入数据的局部特征,而不是整个输入数据。这样可以大大减少参数数量,提高模型的效率。

权值共享:

是指卷积神经网络中的每个卷积核(也称为滤波器或权重)在整个输入数据上共享相同的参数。也就是说,每个卷积核都具有相同的权重,权值共享可以大大减少网络的参数数量,并且有助于提高网络的泛化能力。

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

池化是一种在卷积神经网络中常用的技术,它通过对特征图进行降采样来减少参数数量,提高计算效率,并增强模型的泛化能力。

池化的好处主要包括:

  1. 减少数据维度:通过池化操作,可以将输入特征图的尺寸减小,从而减少了模型中需要训练的参数数量,降低了计算复杂度。
  2. 提高计算效率:池化层可以缩小特征图的尺寸,从而减少网络中卷积层的计算量,提高模型的计算效率。
  3. 增强泛化能力:池化层可以抑制过拟合现象,使模型更加健壮,提高模型的泛化能力。
  4. 特征平移不变性:通过池化操作,可以使得输入特征图对于轻微的平移变化具有一定的不变性,使模型更鲁棒和稳定。

然而,池化也存在一些缺点:

  1. 池化层会丢弃一部分特征激活信息,这可能会导致模型丢失一些位置信息,从而影响模型的精度。
  2. 空间失配:如果池化操作的大小和步幅设置不合理,可能会导致模型在处理空间信息时出现失配,无法精确地捕捉到特定位置的特征。
  3. 过度采样:在某些情况下,过度使用池化操作可能会导致特征图的尺寸过小,从而丢失了一些重要的细节信息

3、全卷积网络(课上讲的这个概念不准确,同学们查资料纠正一下)

 四、全卷积网络FCN详细讲解(超级详细哦)_fcn模型-CSDN博客

全卷积网络的特点在于将传统CNN后面的全连接层换成了卷积层,这样网络的输出将是热力图而非类别。此外,为解决卷积和池化导致图像尺寸的变小,全卷积网络使用上采样方式对图像尺寸进行恢复。

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

 低级特征:通常指底层的卷积层输出或者简单的图像特征通常包含图像的底层信息,如颜色、纹理等。它们是图像的基本特征,由于低级特征具有较小的感受野和局部性质,它们更关注图像的局部细节,并往往具有较高的空间分辨率。

中级特征:中级特征通过多个卷积层的组合和整合而得,可以将低级特征组合起来,形成更具有代表性和抽象性的特征表示。中级特征能够捕捉到更复杂的图像结构和形状,并且对于物体的部分特征具有较好的响应。

高级特征:高级特征通常是在较深层的卷积层或全连接层中产生的。往往包含对象内在的语义信息,这些信息是关于对象的高级抽象,通常包括对象类别、属性等更深层次的信息。它们通过对中级特征的进一步组合和整合,能够对整体图像的语义信息和高层次结构进行建模。

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

多通道的N输入,M输出可以通过使用1×1卷积层来实现。1×1卷积层可以用于改变通道数,从而实现多通道的输入和输出。具体来说,可以使用1×1卷积层来将N个输入通道转换为M个输出通道。这样,就可以实现多通道的N输入,M输出。

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

  1. 降维和增维:通过使用1×1的卷积核,可以改变特征图的通道数。当需要减少通道数时,1×1卷积核可以将输入特征图的通道数进行降维操作,从而减少参数量和计算复杂度。相反,如果需要增加通道数,1×1卷积核可以将输入特征图的通道数进行增维操作,以提供更多的表示能力。

  2. 特征交互和组合:1×1的卷积核可以用于特征图之间的交互和组合。通过在不同通道之间进行卷积操作,可以实现特征图之间的信息交流和融合,以捕捉更丰富的特征表示。这有助于提高模型的表达能力和判别能力。

  3. 非线性映射:1×1的卷积核通常会应用非线性激活函数,如ReLU等,以引入非线性变换。这样可以使得网络能够学习更复杂和非线性的特征表示,从而提高模型对数据的建模能力。

  4. 空间信息保持:由于1×1的卷积核的感受野只涵盖了一个像素点,所以它在卷积操作时可以保持输入特征图的空间维度不变。这对于一些需要保留细节信息和精确定位的任务,如目标检测和语义分割,具有重要意义

  5. 减少计算量:通过使用1×1卷积核,可以在保持feature map尺寸不变的前提下减少计算量,从而优化网络的性能。

  6. 与全连接层等价:在某些情况下,如在fast-rcnn中,1×1卷积核可以实现在每一个位置上对n个通道组成的n维vector进行全连接操作,这与全连接层的效果等价。

二、使用CNN进行XO识别 

1、复现

1、数据集
from torch.utils.data import DataLoader
from torchvision import transforms, datasets

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

data_train = datasets.ImageFolder('train_data', transforms)
data_test = datasets.ImageFolder('test_data', transforms)

train_loader = DataLoader(data_train, batch_size=64, shuffle=True)
test_loader = DataLoader(data_test, batch_size=64, shuffle=True)
for i, data in enumerate(train_loader):
    images, labels = data
    print(images.shape)
    print(labels.shape)
    break

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

可视化

2. 构建模型  
import torch.nn as nn
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 9, 3)
        self.pool = 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.pool(self.relu(self.conv1(x)))
        x = self.pool(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、训练模型 
model = CNN()

loss = nn.CrossEntropyLoss()
opti = torch.optim.SGD(model.parameters(), lr=0.1)

epochs = 10

for epoch in range(epochs):
    total_loss = 0
    for i, data in enumerate(train_loader):
        images, labels = data
        out = model(images)
        one_loss = loss(out, labels)
        opti.zero_grad()
        one_loss.backward()
        opti.step()
        total_loss += one_loss
        if (i + 1) % 10 == 0:
            print('[%d  %5d]   loss: %.3f' % (epoch + 1, i + 1, total_loss / 100))
            total_loss = 0.0

print('finished train')

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

4、模型测试  
import matplotlib.pyplot as plt
 
# 读取模型
model_load = torch.load('model.pth')
# 读取一张图片 images[0],测试
print("labels[0] truth:\t", labels[0])
x = images[0].unsqueeze(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.pth')

correct = 0
total = 0
with torch.no_grad():  # 进行评测的时候网络不更新梯度
    for data in test_loader:  # 读取测试集
        images, labels = data
        outputs = 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.optim
from torch.utils.data import DataLoader
from torchvision import transforms, datasets
 
transforms = transforms.Compose([
    transforms.ToTensor(),  # 把图片进行归一化,并把数据转换成Tensor类型
    transforms.Grayscale(1)  # 把图片 转为灰度图
])
 
data_train = datasets.ImageFolder('train_data', transforms)
data_test = datasets.ImageFolder('test_data', transforms)
 
train_loader = DataLoader(data_train, batch_size=64, shuffle=True)
test_loader = DataLoader(data_test, batch_size=64, shuffle=True)
for i, data in enumerate(train_loader):
    images, labels = data
    print(images.shape)
    print(labels.shape)
    break
 
for i, data in enumerate(test_loader):
    images, labels = data
    print(images.shape)
    print(labels.shape)
    break
 
import torch.nn as nn
 
 
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 9, 3)
        self.pool = 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):
        outputs = []
        x = self.conv1(x)
        outputs.append(x)
        x = self.relu(x)
        outputs.append(x)
        x = self.pool(x)
        outputs.append(x)
        x = self.conv2(x)
 
        x = self.relu(x)
 
        x = self.pool(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
 
 
 
 
 
import matplotlib.pyplot as plt
import numpy as np
# 读取模型
model = torch.load('model.pth')
print(model)
x = images[0].unsqueeze(0)
 
# forward正向传播过程
out_put = model(x)
for feature_map in out_put:
    # [N, C, H, W] -> [C, H, W]    维度变换
    im = np.squeeze(feature_map.detach().numpy())
    print(im.shape)
    # [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()
 

 

7. 查看训练好的模型的卷积核  
import matplotlib.pyplot as plt

# 读取模型
model = torch.load('model.pth')
print(model)
x = images[0].unsqueeze(0)

# forward正向传播过程
out_put = model(x)
weights_keys = model.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 = model.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 = model.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()

2.重新设计网络结构 

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

self.conv3 = nn.Conv2d(9, 7, 3)  # 更改第三个卷积层的输出通道数为7,这是我的第三层
import torch.nn as nn
import torch

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

# 将输入的图片先转换为灰度图,然后将其转换为 Tensor,并进行归一化处理。这样处理后的数据可以直接作为模型的输入。
transforms = transforms.Compose([
    transforms.ToTensor(),  # 把图片进行归一化,并把数据转换成Tensor类型
    transforms.Grayscale(1)  # 把图片 转为灰度图
])

# dataset.ImageFolder 将数据集封装为dataset类型
# root (string): 数据集的根目录路径。在这个根目录下,每个类别的图像应该被存放在一个单独的子文件夹中。每个子文件夹的名称将被视为一个类别,并且其中的图像将被标记为该类别。
# transform (callable, optional): 一个对图像进行变换的函数或转换操作。可以使用 transforms 模块中的函数来对图像进行预处理、数据增强等操作,例如将图像转换为 Tensor 类型、调整大小、裁剪等。
data_train = datasets.ImageFolder('train_data', transforms)
data_test = datasets.ImageFolder('test_data', transforms)

train_loader = DataLoader(data_train, batch_size=64, shuffle=True)
test_loader = DataLoader(data_test, batch_size=64, shuffle=True)

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

for i, data in enumerate(test_loader):
    images, labels = data
    print(images.shape)
    print(labels.shape)
    break
import torch.nn as nn

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 9, 3)
        self.conv2 = nn.Conv2d(9, 9, 3)
        self.conv3 = nn.Conv2d(9, 7, 3)  # 更改第三个卷积层的输出通道数为7
        self.pool = nn.MaxPool2d(2, 2)
        self.relu = nn.ReLU()

        self.fc1 = nn.Linear(12 * 12 * 7, 1200)  # 根据新的维度更新全连接层的输入
        self.fc2 = nn.Linear(1200, 64)
        self.fc3 = nn.Linear(64, 2)

    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, 12 * 12 * 7)  # 根据新的维度进行reshape
        x = self.relu(self.fc1(x))
        x = self.relu(self.fc2(x))
        x = self.fc3(x)
        return x
model = CNN()
loss = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
epochs = 30

for epoch in range(epochs):
    total_loss = 0.0
    for i, data in enumerate(train_loader):
        images, labels = data
        out = model(images)
        one_loss = loss(out, labels)
        optimizer.zero_grad()
        one_loss.backward()
        optimizer.step()
        total_loss += one_loss
        if (i + 1) % 10 == 0:
            print('[%d  %5d]   loss: %.3f' % (epoch + 1, i + 1, total_loss / 100))
            total_loss = 0.0

print('train finish')

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

correct = 0
total = 0
with torch.no_grad():  # 进行评测的时候网络不更新梯度
    for data in test_loader:  # 读取测试集
        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))

震惊,第一次100%,

这应该就是陷入局部最小值,在达到一个局部最小值后,模型可能会在该点附近震荡,导致损失函数的值在一定范围内波动。第一次感受到

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

  • import torch.nn as nn
    import torch
    
    from torch.utils.data import DataLoader
    from torchvision import transforms, datasets
    
    # 将输入的图片先转换为灰度图,然后将其转换为 Tensor,并进行归一化处理。这样处理后的数据可以直接作为模型的输入。
    transforms = transforms.Compose([
        transforms.ToTensor(),  # 把图片进行归一化,并把数据转换成Tensor类型
        transforms.Grayscale(1)  # 把图片 转为灰度图
    ])
    
    # dataset.ImageFolder 将数据集封装为dataset类型
    # root (string): 数据集的根目录路径。在这个根目录下,每个类别的图像应该被存放在一个单独的子文件夹中。每个子文件夹的名称将被视为一个类别,并且其中的图像将被标记为该类别。
    # transform (callable, optional): 一个对图像进行变换的函数或转换操作。可以使用 transforms 模块中的函数来对图像进行预处理、数据增强等操作,例如将图像转换为 Tensor 类型、调整大小、裁剪等。
    data_train = datasets.ImageFolder('train_data', transforms)
    data_test = datasets.ImageFolder('test_data', transforms)
    
    train_loader = DataLoader(data_train, batch_size=64, shuffle=True)
    test_loader = DataLoader(data_test, batch_size=64, shuffle=True)
    
    for i, data in enumerate(train_loader):
        images, labels = data
        print(images.shape)
        print(labels.shape)
        break
    
    for i, data in enumerate(test_loader):
        images, labels = data
        print(images.shape)
        print(labels.shape)
        break
    import torch.nn as nn
    import torch
    
    from torch.utils.data import DataLoader
    from torchvision import transforms, datasets
    
    # 将输入的图片先转换为灰度图,然后将其转换为 Tensor,并进行归一化处理。这样处理后的数据可以直接作为模型的输入。
    transforms = transforms.Compose([
        transforms.ToTensor(),  # 把图片进行归一化,并把数据转换成Tensor类型
        transforms.Grayscale(1)  # 把图片 转为灰度图
    ])
    
    # dataset.ImageFolder 将数据集封装为dataset类型
    # root (string): 数据集的根目录路径。在这个根目录下,每个类别的图像应该被存放在一个单独的子文件夹中。每个子文件夹的名称将被视为一个类别,并且其中的图像将被标记为该类别。
    # transform (callable, optional): 一个对图像进行变换的函数或转换操作。可以使用 transforms 模块中的函数来对图像进行预处理、数据增强等操作,例如将图像转换为 Tensor 类型、调整大小、裁剪等。
    data_train = datasets.ImageFolder('train_data', transforms)
    data_test = datasets.ImageFolder('test_data', transforms)
    
    train_loader = DataLoader(data_train, batch_size=64, shuffle=True)
    test_loader = DataLoader(data_test, batch_size=64, shuffle=True)
    
    for i, data in enumerate(train_loader):
        images, labels = data
        print(images.shape)
        print(labels.shape)
        break
    
    for i, data in enumerate(test_loader):
        images, labels = data
        print(images.shape)
        print(labels.shape)
        break
    
    
    class CNN(nn.Module):
        def __init__(self):
            super(CNN, self).__init__()
            self.conv1 = nn.Conv2d(in_channels=1, out_channels=9, kernel_size=3)
            self.conv2 = nn.Conv2d(in_channels=9, out_channels=5, kernel_size=3)
            self.relu = nn.ReLU()
    
            self.fc1 = nn.Linear(in_features=112 * 112 * 5, out_features=1200)
            self.fc2 = nn.Linear(in_features=1200, out_features=64)
            self.fc3 = nn.Linear(in_features=64, out_features=2)
    
        def forward(self, input):
            output = self.relu(self.conv1(input))
            output = self.relu(self.conv2(output))
            output = output.view(-1, 112 * 112 * 5)
            output = self.relu(self.fc1(output))
            output = self.relu(self.fc2(output))
            output = self.fc3(output)
            return output
    
    
    model = CNN()
    loss = nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
    epochs = 10
    
    for epoch in range(epochs):
        total_loss = 0.0
        for i, data in enumerate(train_loader):
            images, labels = data
            out = model(images)
            one_loss = loss(out, labels)
            optimizer.zero_grad()
            one_loss.backward()
            optimizer.step()
            total_loss += one_loss
            if (i + 1) % 10 == 0:
                print('[%d  %5d]   loss: %.3f' % (epoch + 1, i + 1, total_loss / 100))
                total_loss = 0.0
    
    print('train finish')
    
    torch.save(model, 'model.pth')  # 保存的是模型, 不止是w和b权重值
    torch.save(model.state_dict(), 'model_name1.pth')  # 保存的是w和b权重值
    
    correct = 0
    total = 0
    with torch.no_grad():  # 进行评测的时候网络不更新梯度
        for data in test_loader:  # 读取测试集
            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))
    
    # 损失降低的很快,但是最后的时候收敛很慢
    model = CNN()
    loss = nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
    epochs = 30
    
    for epoch in range(epochs):
        total_loss = 0.0
        for i, data in enumerate(train_loader):
            images, labels = data
            out = model(images)
            one_loss = loss(out, labels)
            optimizer.zero_grad()
            one_loss.backward()
            optimizer.step()
            total_loss += one_loss
            if (i + 1) % 10 == 0:
                print('[%d  %5d]   loss: %.3f' % (epoch + 1, i + 1, total_loss / 100))
                total_loss = 0.0
    
    print('train finish')
    
    torch.save(model, 'model.pth')  # 保存的是模型, 不止是w和b权重值
    torch.save(model.state_dict(), 'model_name1.pth')  # 保存的是w和b权重值
    
    correct = 0
    total = 0
    with torch.no_grad():  # 进行评测的时候网络不更新梯度
        for data in test_loader:  # 读取测试集
            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))
    

  • 99.666667%

  • 结果一样,池化层可以防止过拟合,结果一样,说明我们去掉后么有过拟合

  • 对比看出在到全连接层依旧还有 112*112*5的大小,那么就依旧需要112*112*5的参数,相比于具有池化层27*27*5,不仅大大增加了参数的数量,并且降低了运行的效率。所以池化还是很必要的。

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

  • 将第一个卷积层的输出通道改为10
  • import torch.nn as nn
    class CNN(nn.Module):
        def __init__(self):
            super(CNN, self).__init__()
            self.conv1 = nn.Conv2d(in_channels=1, out_channels=10, kernel_size=3)
            self.pool = nn.MaxPool2d(kernel_size=2, stride=2)
            self.conv2 = nn.Conv2d(in_channels=10, out_channels=5, kernel_size=3)
            self.relu = nn.ReLU()
    
            self.fc1 = nn.Linear(in_features=27 * 27 * 5, out_features=1200)
            self.fc2 = nn.Linear(in_features=1200, out_features=64)
            self.fc3 = nn.Linear(in_features=64, out_features=2)
    
        def forward(self, input):
            output = self.pool(self.relu(self.conv1(input)))
            output = self.pool(self.relu(self.conv2(output)))
            output = output.view(-1, 27 * 27 * 5)
            output = self.relu(self.fc1(output))
            output = self.relu(self.fc2(output))
            output = self.fc3(output)
            return output

  • 从99.6667到100,提高了一些,我就把通道数改成了20,结果结果又变回去了。

  • 增加卷积层的通道数在某些情况下可能会导致准确率上升,这可能是因为增加通道数可以提供更多的信息,使模型能够更详细地理解输入数据。每个通道对应于输入数据的一种特定特征,增加通道数意味着模型可以检测到更多的特征,这有助于提高模型的表达能力。

    然而,当通道数增加到一定程度时,准确率可能会开始下降。这可能是由于过拟合问题。增加通道数可能会增加模型的复杂性,使模型更容易过拟合。

    此外,增加通道数也会增加模型的参数数量,这可能会使模型难以优化。更多的参数意味着需要更多的数据来训练模型,否则模型可能会出现欠拟合问题。

  4 可视化 

  • 选择自己的最优模型
  • 可视化部分卷积核和特征图
  • 这两个上面有呢在复现那部分。
  • 探索低级特征、中级特征、高级特征 
import torch
import torch.nn as nn
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from PIL import Image

# 加载图片并转为灰度图
img = Image.open('C:\\Users\\hp\\Desktop\\Screenshot_2022_0603_085623.png').convert('L')

# 定义转换器
transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
])

# 对图片进行转换
img_tensor = transform(img)

# 定义模型
model = nn.Sequential(
    nn.Conv2d(1, 8, kernel_size=3, stride=1, padding=1),
    nn.ReLU(),
    nn.MaxPool2d(kernel_size=2, stride=2),
    nn.Conv2d(8, 16, kernel_size=3, stride=1, padding=1),
    nn.ReLU(),
    nn.MaxPool2d(kernel_size=2, stride=2),
    nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1),
    nn.ReLU(),
    nn.MaxPool2d(kernel_size=2, stride=2),
)

# 提取初级特征
with torch.no_grad():
    x = img_tensor.unsqueeze(0)
    x = model[:4](x)
    x = x.squeeze(0)
    for i in range(8):
        plt.subplot(2, 4, i+1)
        plt.imshow(x[i], cmap='gray')
        plt.axis('off')
    plt.show()

# 提取中级特征
with torch.no_grad():
    x = img_tensor.unsqueeze(0)
    x = model[:8](x)
    x = x.squeeze(0)
    for i in range(16):
        plt.subplot(4, 4, i+1)
        plt.imshow(x[i], cmap='gray')
        plt.axis('off')
    plt.show()

# 提取高级特征
with torch.no_grad():
    x = img_tensor.unsqueeze(0)
    x = model[:12](x)
    x = x.squeeze(0)
    for i in range(32):
        plt.subplot(8, 4, i+1)
        plt.imshow(x[i], cmap='gray')
        plt.axis('off')
    plt.show()

总结: 

1、ToTenser可以进行图片的归一化,图片进行归一化后归一化通过将数据归一化到较小的范围内,可以减小这种限制,使得梯度更容易传播,加速模型的收敛过程。归一化可以降低模型对输入数据的依赖性,使得模型对输入数据的微小变化具有更好的鲁棒性。这样可以减少模型的过拟合风险,提升模型的泛化能力。

2、在CNN中首先,输入x经过卷积层conv1进行卷积操作,然后通过ReLU激活函数进行非线性变换,再经过池化层pool进行降采样。接着,输出经过卷积层conv2和ReLU激活函数,再次经过池化层pool进行降采样。之后,通过展平操作将特征图转换为一维向量,然后经过两个全连接层fc1和fc2并分别使用ReLU激活函数进行非线性变换。最后,通过全连接层fc3得到最终的预测结果x。

3、通过取出最大值的索引作为分类结果,可以选择具有最高概率的类别作为最终的预测结果。这是基于假设:预测概率最高的类别是最有可能的类别。(注意,这种方法仅适用于单标签分类任务,即每个样本只属于一个类别。对于多标签分类任务或需要输出类别分数的情况,通常需要采用其他策略来处理分类结果。)

4、

4、在卷积神经网络(CNN)中,初级特征通常对应网络的低层,这些层通常包含较小的卷积核(例如3x3或5x5),并且卷积核的数量可能较少。这是因为低层网络主要关注提取图像的局部特征,如边缘和颜色。

随着网络深度的增加,中级特征对应于网络的中层,这些层通常使用较大的卷积核(例如7x7或11x11),并且卷积核的数量可能增加。这些层主要关注提取更复杂的特征,如纹理和形状。

高级特征对应于网络的高层,这些层通常使用更大的卷积核(例如13x13或更大的维度),并且卷积核的数量可能更多。这些层主要关注提取全局特征和抽象概念,如物体的部分和整体。

参考:【精选】NNDL 作业6:基于CNN的XO识别-CSDN博客

           卷积神经网络中的批归一化(Batch Normalization)技术解析-CSDN博客

          系统学习Pytorch笔记三:Pytorch数据读取机制(DataLoader)与图像预处理模块(transforms)         _dataloader 输入两个变量-CSDN博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值