PyTorch卷积神经网络基础篇

卷积神经网络基础篇(刘二大人PyTorch第十讲)

       本节讲解了卷积相对于全连接神经网络的区别与优势、卷积神经网络进行图片特征提取的过程、卷积与下采样过程中通道数量与特征图张量大小的变化过程,最后搭建了一个简单的两层卷积,在MNIST数据集上进行性能测试,准确率比上一讲中全连接神经网络的准确率提升了1%。
       全连接神经网络将图片张量展平为一维向量作为输入进行特征提取,存在的缺陷是,会损坏图片的空间结构(原本在空间位置上相邻的两个点在被展平后可能会相隔很远)。卷积神经网络是直接把图像张量作为输入,保存了图片原始的空间结构信息,卷积的实质可以看作是学习周围点对中心点的影响(只关注局部信息,不像全连接神经网络那样关注图片上任意两个点之间的影响)。
       卷积神经网络进行图片分类的过程是:对于输入的图片张量(每张图片可以看作一个大的矩阵),经过卷积(可以看一个小矩阵,卷积的过程可以看作小矩阵在大矩阵上按一定步长滑动、对应位置点乘,学习周围点对中心点的影响)、下采样(关注局部范围内最显著的特征(最大池化)或平均特征(平均池化),减少神经元的数量,避免网络过于复杂、影响训练速度,同时避网络因为过于复杂而导致过拟合),最后得到多个通道的特征张量,将其展平为一维向量,接入全连接层进行分类。(对于分类任务,在最终分类层之前都是需要将所有神经元展平成一阶的一维向量(因为最后要做全连接进行分类))。


卷积过程
  • 关于卷积核:
    (1)每个卷积核的通道数必须与输入数据的通道数相同。在处理多通道数据时,卷积核要具有相同的通道数(一个通道配一个卷积)
    (2)卷积核的数量与输入通道数相同。在卷积层中使用多个卷积核,每个卷积核会生成一个独立的特征图,通过增加卷积核的数量,可以让模型学到更多不同的特征。
    (3)卷积核的大小自定义。卷积核大小的定义需要考虑感受野与任务需求、的匹配。
    (4)卷积核的参数,(out_channel, in_channel, width, height),(输出通道数(卷积核的数量), 输入通道数(卷积核的通道数),卷积核的宽,卷积核的高)

  • 关于卷积层:
    (1)卷积层要求输入输出是一个4维的张量(B, C, W, H)。输入:(batch_size, in_channel, width, height),输出 (batch_size, out_channel, width, height)。卷积后channel可变,width、height可变(取决于padding和是否下采样)。下采样后,channel不变,width、height变。

  • 关于全连接层:
    全连接层的输入张量(batch_size, 特征数量),输出张量(batch_size,类别数量)。
    在进行全连接层之前,需要将特征提取后得到的特征图展平为一维向量(使用.view(batch_size, features)进行展平,其中features的值需要推导)。再接入全连接层,计算得到属于各个类别的原始分数。

1. 自定义卷积核代码

import torch

input = [3,4,5,6,7,
         2,4,6,8,2,
         1,6,7,8,4,
         9,7,4,6,2,
         3,7,5,4,1]
input = torch.Tensor(input).view(1,1,5,5)  # (batch_size, in_channel, width, height)
print(input.shape)
conv_layer = torch.nn.Conv2d(1,3,kernel_size=3,padding=1,bias=False)  # conv2d参数(in_channel,out_channel,kernel_size,padding等)(in_channel与图像的输入通道一样,out_channel和卷积核的数量一样)

kernel = torch.Tensor([1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9]).view(3,1,3,3) 
print(kernel.shape)
conv_layer.weight.data = kernel

output = conv_layer(input)

print(output.shape)

# 下采样
maxpooling_layer = torch.nn.MaxPool2d(kernel_size=2)  # 核大小为2,步长也就默认为2
output = maxpooling_layer(output)
print(output)

说明:
(1)输入input是一个四维张量(batch_size,in_channel,width,height)
(2)nn.Conv2d用来管理每个卷积核及其对应的权重参数,其输入的数据张量形状是(batch_size, in_channel, width, height),输出张量(batch_size, out_channel, width, height),batch_size不变,out_channel改变(和卷积核数量一致),width, height改变(和padding、下采样有关)。nn.Conv2d的参数有(in_channel,out_channel, kernel_size, padding, stride, ...),其中,kernel_size可以是一个正方形,也可以是一个长方形(元组形式),如kernel_size=3,或kernel_size=(5,3)
(3)卷积核权重的定义:kernel = torch.Tensor([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]).view(3,1,3,3)
卷积核的参数:(out_channel,in_channel, width, height)
这里在一个卷积层里定义了3个相同大小的、通道数为1的卷积核(在实际的训练过程中,卷积核中每个通道的参数模型自己学习的,所以卷积核也就是可学习的权重参数)。在大多数卷积神经网络的实现中,一个卷积层的多个卷积核大小是一样的,但在理论和一些特殊架构中也可以不同,如GoogLeNet(inception架构)就采用了不同大小卷积核并行的方式(但要注意,按通道的维度进行拼接时,每个分支最终输出的的批量大小、特征图大小必须一致才能拼接)

  • 犯的错误:
         不小心将torch.Tensor()写成了torch.tensor()。运行时的错误提示“TypeError: cross_entropy_loss(): argument 'input' (position 1) must be Tensor, not NoneType”。
    原因:
    关于torch.tensor 和 torch.Tensor
        • torch.tensor:它是一个函数,用于根据传入的数据创建新的张量对象。你可以使用 Python 列表、NumPy 数组等作为输入,该函数会根据输入数据的类型和内容创建对应的张量。
        • torch.Tensor:它是一个类,确切地说是 torch.FloatTensor 的别名。当你调用 torch.Tensor() 时,实际上是在创建一个 torch.FloatTensor 类型的张量对象,若不传入数据,会创建一个未初始化的张量。
        当要将一个需要梯度计算的数据设置为张量时,数据类型必须是浮点型(如torch.float32、torch.float64)或者复数类型(如torch.complex64、torch.complex128)。
    原因分析:这里的输入、卷积核的权重参数都得是浮点类型的张量,torch.Tensor会将输入数据自动转换为浮点型,写成torch.tensor后,torch.tensor识别出输入数据是整型,而整型不能不进行梯度计算,所以会报错)

2. 使用卷积神经网络进行图片分类代码

import torch
import torch.nn.functional as F

from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.optim as optim 


# 1.加载数据
class CustomMNIST(datasets.MNIST):
    mirrors = [
        'https://ossci-datasets.s3.amazonaws.com/mnist/',
    ]

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,),(0.3081,))
])

batch_size = 64

train_data = CustomMNIST(root=r'./dataset/mnist/', train=True, download=True, transform=transform)
train_loader = DataLoader(train_data, shuffle=True, batch_size=batch_size)
test_data = CustomMNIST(root=r'./dataset/mnist/', train=False, download=True, transform=transform)
test_loader = DataLoader(test_data, batch_size=batch_size)


# 2. 模型设计
class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5)
        self.pooling = torch.nn.MaxPool2d(kernel_size=2)
        self.fc= torch.nn.Linear(320,10)
    
    def forward(self, x):
        batch_size = x.size(0)   # x:(batch_size,in_channel,28,28)
        x = F.relu(self.pooling(self.conv1(x)))   # 卷积层输入是(B,C,W,H),输出是(B,C',W',H')
        x = F.relu(self.pooling(self.conv2(x)))   
        x = x.view(batch_size, -1)   
        x = self.fc(x)    # 全连接层的输入是(Batch_size,Feature), 输出是(Batch_size,Feature')
        return x      

model = Net()

# 3. 损失函数与优化器
criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)

# 4. 训练与测试
def train(epoch):
    running_loss = 0.0
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, targets = data

        optimizer.zero_grad()
        # 正向,反向,更新
        outputs = model(inputs)
        loss = criterion(outputs, targets)
        loss.backward()
        optimizer.step()

        # 计算损失
        running_loss = running_loss + loss.item()
        if batch_idx %300 == 299:
            print('epoch:%d, batch:%5d, loss:%.3f'%(epoch+1, batch_idx+1, running_loss/300))

def test():
    correct, total = 0.0, 0.0
    with torch.no_grad():
        for data in test_loader:
            images, labels = data
            outputs = model(images)
            _,predicted = torch.max(outputs.data, dim=1)
            total = total + labels.size(0)
            correct = correct + (predicted==labels).sum().item()
    
    print('Accuracy on test set: %d %%'%(100*correct/total))


if __name__ == "__main__":
    for epoch in range(10):
        train(epoch)
        test()

运行结果:

epoch:1, batch:  300, loss:0.622
epoch:1, batch:  600, loss:0.822
epoch:1, batch:  900, loss:0.958
Accuracy on test set: 96 %
epoch:2, batch:  300, loss:0.111
...
epoch:10, batch:  900, loss:0.108
Accuracy on test set: 98 %
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容