AcWing
  • 首页
  • 活动
  • 题库
  • 竞赛
  • 应用
  • 其它
    • 题解
    • 分享
    • 商店
    • 问答
  • 吐槽
  • 登录/注册

$$\Large资深入门者关于PyTorch和深度学习的碎碎念——卷积神经网络模型实战$$

作者: 作者的头像   Okamasa店老板 ,  2023-01-26 02:49:32 ,  所有人可见 ,  阅读 87


2


<<< $\color{blue}{ (●’◡’●) 点赞 <(- ●’◡’●) }$

    /\\       //>>>> 
   /__\\     //        关注加RP,AC更容易!
  /    \\   //>>>>>

<<< $\color{blue}{ (●’◡’●) 收藏 <(- ●’◡’●) }$


这里实现几个经典的pytorch模型,并在FASION-MNIST数据集中进行测试和验证

数据集及输入输出

// 数据载入
import torchvision

mnist_train = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', train=True, download=True, transform=transforms.ToTensor())
mnist_test = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', train=False, download=True, transform=transforms.ToTensor())

batch_size = 256
if sys.platform.startswith('win'):
    num_workers = 0  # 0表示不用额外的进程来加速读取数据
else:
    num_workers = 4
train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=1)
test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=1)
// 训练函数
def train(model, loss, opt, max_iter=10):
    cnt = 0
    for epoch in range(max_iter):
        train_l_sum = 0
        batch_count = 0
        for X, y in train_iter:
            y_hat = net(X)
            l = loss(y_hat, y)
            opt.zero_grad()
            l.backward()
            opt.step()
            train_l_sum += l.item()
            batch_count += 1
        print('epoch %d, loss %.4f'
              % (epoch + 1, train_l_sum / batch_count))

第一个卷积神经网络:LeNet

5.5_lenet.png

网络结构如上图所示.

网络分为卷积层和全连接层两部分。
卷积层部分,根据图片,第一层的维度变化:(1, 32, 32) -> (6, 28, 28)
可以知道这是个655的卷积操作;然后是subsample,可以使用22的池化操作进行降采样。
每个节点使用sigmoid作为激活函数。
全连接层部分,第一层将所有维度”摊平”到120维,它的输入维度与输入数据有关。假设输入数据维度为N
N,
则这一层输入维度为(16, ((N -4) / 2 - 4) / 2, ((N - 4) / 2 - 4) / 2)

这类没有旁路的网络可以直接用Sequential实现:

class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(1, 6, 5), # in_channels, out_channels, kernel_size
            nn.Sigmoid(),
            nn.MaxPool2d(2, 2), # kernel_size, stride
            nn.Conv2d(6, 16, 5),
            nn.Sigmoid(),
            nn.MaxPool2d(2, 2)
        )
        self.fc = nn.Sequential(
            nn.Linear(16*4*4, 120),
            nn.Sigmoid(),
            nn.Linear(120, 84),
            nn.Sigmoid(),
            nn.Linear(84, 10)
        )

    def forward(self, img):
        feature = self.conv(img)
        output = self.fc(feature.view(img.shape[0], -1))
        return output

LeNet训练及结果:

net = LeModel()
loss_func = nn.CrossEntropyLoss()
opt_func = torch.optim.Adam(net.parameters(), lr = 0.001, weight_decay = 0.0005)
train(
    net,
    loss_func,
    opt_func
)

训练结果:

epoch 1, loss 0.6551
epoch 2, loss 0.6172
epoch 3, loss 0.5909
epoch 4, loss 0.5683
epoch 5, loss 0.5511
epoch 6, loss 0.5383
epoch 7, loss 0.5268
epoch 8, loss 0.5167
epoch 9, loss 0.5069
epoch 10, loss 0.4977

测试准确率0.8


残差神经网络ResNet:

ResNet引入了残差块设计,用来解决网络过深时产生的退化问题。结构如下:
res_block.png
具体原理这里不细究,实现如下:

from torch import nn, optim
import torch.nn.functional as F

class ResBlock(nn.Module):
    def __init__(self, input_dim, output_dim, stride, use_1x1 = False):
        super(ResBlock, self).__init__()
        self.conv1 = nn.Conv2d(input_dim, output_dim, 3, padding = 1, stride = stride)
        self.conv2 = nn.Conv2d(output_dim, output_dim, 3, padding = 1)
        if (use_1x1):
            self.conv3 = nn.Conv2d(input_dim, output_dim, 1, stride = stride)
        else:
            self.conv3 = None
        self.bn1 = nn.BatchNorm2d(output_dim)
        self.bn2 = nn.BatchNorm2d(output_dim)
    def forward(self, X):
        Y = F.relu(self.bn1(self.conv1(X)))
        Y = self.bn2(self.conv2(Y))
        if self.conv3:
            X = self.conv3(X)
        return F.relu(Y + X)

接下来实现传统的ResNet18模型
ResNet-18前两层在输出通道数为64、步幅为2的7×7卷积层后接步幅为2的3×3的最大池化层;使用ReLU作为激活函数。
和Block里一样,每次卷积层后都加上 BatchNorm2d进行归一化。
之后是4个Res块,最后接全局平均池化层。

resnet = nn.Sequential(
    nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
    nn.BatchNorm2d(64),
    nn.ReLU(),
    nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
)
resnet.add_module('res_layer1', nn.Sequential(
    ResBlock(64, 64, 1, False),
    ResBlock(64, 64, 1, False))
)
resnet.add_module('res_layer2', nn.Sequential(
    ResBlock(64, 128, 2, True),
    ResBlock(128, 128, 1, False))
    )
resnet.add_module('res_layer3', nn.Sequential(
    ResBlock(128, 256, 2, True),
    ResBlock(256, 256, 1, False))
    )
resnet.add_module('res_layer4', nn.Sequential(
    ResBlock(256, 512, 2, True),
    ResBlock(512, 512, 1, False))
    )
resnet.add_module("global_avg_pool",  nn.AdaptiveAvgPool2d(1))
resnet.add_module("fc", nn.Sequential(Flatten(), nn.Linear(512, 10))) 

Flatten只是做了shape的改变,把上一层BatchNum * 512 * 1 * 1的shape变为BatchNum * 512, 实现如下:

class Flatten(nn.Module):
    def __init__(self):
        super(Flatten, self).__init__()

    def forward(self, X):
        return X.view((X.shape[0], X.shape[1]))

训练:

loss_func = nn.CrossEntropyLoss()
opt_func = torch.optim.Adam(net.parameters(), lr = 0.001, weight_decay = 0.0005)
train(resnet, loss_func, opt_func)

epoch 1, loss 0.4420
epoch 2, loss 0.2978
epoch 3, loss 0.2690
epoch 4, loss 0.2460
epoch 5, loss 0.2284
epoch 6, loss 0.2155
epoch 7, loss 0.2005
epoch 8, loss 0.1923
epoch 9, loss 0.1813
epoch 10, loss 0.1721

测试的准确率为0.90,优于朴素的LeNet

0 评论

你确定删除吗?
1024
x

© 2018-2023 AcWing 版权所有  |  京ICP备17053197号-1
用户协议  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标
请输入绑定的邮箱地址
请输入注册信息