CNN基础论文 精读+复现----AlexNet(三)

文章目录

  • 程序完善
  • 结果分析
  • 总结
    • 完整代码

程序完善

前面两篇文章已经把论文中大部分的东西都说完了,前篇直达:CNN基础论文复现----AlexNet(一),网络也搭建起来了,到论文中的第5章了,就是 Details of learning。

上次留了个问题,就是有个均值,标准差和全连接隐层神经元偏置初始化的问题,不是很明白,然后查了一下网上大佬的代码。。。。。

文章中说到 2,4,5卷积层和全连接层初始化偏置为1,其余为0.
可以直接使用(self.C1.bias.data = nn.torch.zeros(self.C1.bias.data.size()),

后面加上

for m in self.modules(): #权重以及线性层偏置初始化
            if isinstance(m, nn.Conv2d) or isinstance(m, nn.Linear):
                m.weight.data = torch.normal(torch.zeros(m.weight.data.size()), torch.ones(m.weight.data.size()) * 0.01) 
                if isinstance(m, nn.Linear):
                    m.bias.data = torch.ones(m.bias.data.size())

但是这一段还有一块没弄明白,所以暂且搁置。

就先不使用文中说的初始化权重和偏置了。

由于ImageNet 数据集太大,所以我直接使用CIFAR-10进行代替,刚好电脑上之前做过CIFAR10的训练,数据包没删 ,可以继续用了。。

如果使用ImageNet 数据集,则均值和标准差像下面这样设置:

    transforms.Normalize((0.229, 0.224, 0.225,), (0.485, 0.456, 0.40)),

如果像我一样改用CIFAR-10 就设置成下面这样:

    transforms.Normalize((0.4915, 0.4823, 0.4468,), (1.0, 1.0, 1.0)),

文中提到不用标准差进行处理,所以这里设置的都是1,0。
这些参数直接谷歌都有现成的,都是大佬们不断优化之后的结果,拿来用就行了。

然后就写个循环开始训练就行了,我这直接拿上一次复现LeNet的代码。

def train(epoch):
    runing_loss = 0.0
    i = 1
    for i, data in enumerate(train_loader):
        x, y = data
        x, y = x.cuda(), y.cuda()
        i +=1
        if i % 10 == 0:
            print("运行中,当前运行次数:",i)
        # 清零 正向传播  损失函数  反向传播 更新
        optimizer.zero_grad()
        y_pre = model(x)
        loss = criterion(y_pre, y)
        loss.backward()
        optimizer.step()
        runing_loss += loss.item()
    print("这是第 %d轮训练,当前损失值 %.5f" % (epoch + 1, runing_loss / 391))

    return runing_loss / 391

这里是391 因为CIFAR10有 5W个训练样本,50000/128 约等于 391。
其余的地方和LeNet一样, 测试集是一模一样的,我就不贴在这了,最后我会贴一个完整的代码。

结果分析

程序我已经拿到谷歌云上去跑了,但是跑的无比的慢,我猜是加入了Relu和LRN层的原因,运行速度大概10-20分钟训练一次,等他跑完,我把结果放上来。

我设置了20轮训练,跑了两个小时才跑了9轮,实在是等不下去了,想去睡觉了,

来看一下9轮的结果把,正确率基本已经到了70%,这个网络确实厉害,之前我训练CIFAR10的时候(文章在这),30轮才接近64%的正确率。
CNN基础论文 精读+复现----AlexNet(三)_第1张图片
在这里插入图片描述

总结

  • 文章中在谈数据增强的时候说到了PCA叠加,由于我用的CIFAR10并没有用作者的那个数据集,所以这里就没有深究,不过我查了一下在sklearn库里有现成的API可以用。

  • 前面作者提到使用Relu比sigmoid或者tanh的速度提高了6倍,额 提高6倍之后还这么慢嘛,之前做CIFAR10训练的时候并没有每一层都加激活函数,所以并不知道他这个提高6倍速度是个什么概念,?但是通过公式可以看出来,Relu函数要么为0要么为本身,计算量倒是比sig和tan要少的多。

  • 之前也提到了 如果参数设置不合理,使用Relu函数会导致训练时间大幅增加,现在我这个模型的训练时间我觉得就很不正常,因为我并没有完全性复现网络,包括LRN层都是自己找的API,里面的size参数设置不一定正确,所以我后面会尝试撤掉LRN层再跑一遍。

  • 文章中的SGD加入了动量,就是momentum这个参数,这个参数相当于给参数加入了 ‘惯性’ 且会保留历史信息。通俗的理解为:假如当前进入局部最优点,加入惯性之后,有可能冲出局部最优点进而达到全局最优点,但并不是所有模型都适于加入动量。

  • 权重衰减,也是文中提到的一个东西,就是这个参数 weight_decay用来防止过拟合的,看名字也能理解嘛,权重衰减,将权重减小了,拟合度降低喽~ 数学层面理解 可以看一下这篇文章你们想看就看吧,反正我是不想看。。。。。

  • LRN层,这也是作者提到的,起 ‘抑制’ 作用,增加泛化能力,查阅资料得知,后续的人员研究后发现LRN对于模型的泛化能力并没有显著的作用,并且还有可能会降低模型准确率,还要引入更多的超参数。

这篇论文到这也就差不多了,其实也不算很好的复现了论文,毕竟有几个点都没有用上,比如将多路GPU改为单路(单纯觉得没必要用多路)。还有那个偏置和初始权重的设置问题并没有加进去(源码不熟 ,放假期间~比较懒散,不想深究。)

代码 以及AlexNet的中英文对照版 和 原版 都放在了GitHub上:
https://github.com/shitbro6/paper

完整代码

import torch
from torch import optim
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn as nn
import matplotlib.pyplot as plt




batch_size = 128
transform = transforms.Compose([
    transforms.Resize(224),
    transforms.ToTensor (),
    transforms.Normalize((0.4915, 0.4823, 0.4468,), (1.0, 1.0, 1.0)),
])

train_dataset = datasets.CIFAR10(root='../data/', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
test_dataset = datasets.CIFAR10(root='../data/', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)

print("训练集长度",len(train_dataset))
print("测试集长度",len(test_dataset))

# 模型类设计

class AlexNet(nn.Module):
    def __init__(self):
        super(AlexNet, self).__init__()
        self.mode1 = nn.Sequential(

            nn.Conv2d(in_channels=3, out_channels=96, kernel_size=11, stride=4,padding=2),
            nn.ReLU(True),
            nn.LocalResponseNorm(size=96,alpha=0.0001,beta=0.75,k=2),
            nn.MaxPool2d(kernel_size=3,stride=2),


            nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, stride=1,padding=2),
            nn.ReLU(True),
            nn.LocalResponseNorm(size=256,alpha=0.0001,beta=0.75,k=2),
            nn.MaxPool2d(kernel_size=3,stride=2),

            nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, stride=1,padding=1),
            nn.ReLU(True),
            nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, stride=1,padding=1),
            nn.ReLU(True),
            nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, stride=1,padding=1),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=3,stride=2),

            nn.Flatten(),
            nn.Linear(in_features=6*6*256, out_features=4096),
            nn.ReLU(True),
            nn.Dropout2d(p=0.5),
            nn.Linear(in_features=4096, out_features=4096),
            nn.ReLU(True),
            nn.Dropout2d(p=0.5),
            nn.Linear(in_features=4096, out_features=1000),

        )

    def forward(self, input):

        x = self.mode1(input)
        return x


model = AlexNet().cuda()
# 损失函数
criterion = torch.nn.CrossEntropyLoss().cuda()
# 优化器
optimizer = optim.SGD(model.parameters(),lr=0.01,weight_decay=0.0005,momentum=0.9)


def train(epoch):
    runing_loss = 0.0
    i = 1
    for i, data in enumerate(train_loader):
        x, y = data
        x, y = x.cuda(), y.cuda()
        i +=1
        if i % 10 == 0:
            print("运行中,当前运行次数:",i)
        # 清零 正向传播  损失函数  反向传播 更新
        optimizer.zero_grad()
        y_pre = model(x)
        loss = criterion(y_pre, y)
        loss.backward()
        optimizer.step()
        runing_loss += loss.item()
    # 每轮训练一共训练1W个样本,这里的runing_loss是1W个样本的总损失值,要看每一个样本的平均损失值, 记得除10000

    print("这是第 %d轮训练,当前损失值 %.5f" % (epoch + 1, runing_loss / 391))

    return runing_loss / 391

def test(epoch):
    correct = 0
    total = 0
    with torch.no_grad():
        for data in test_loader:
            x, y = data
            x, y = x.cuda(), y.cuda()
            pre_y = model(x)
            # 这里拿到的预测值 每一行都对应10个分类,这10个分类都有对应的概率,
            # 我们要拿到最大的那个概率和其对应的下标。

            j, pre_y = torch.max(pre_y.data, dim=1)  # dim = 1 列是第0个维度,行是第1个维度

            total += y.size(0)  # 统计方向0上的元素个数 即样本个数

            correct += (pre_y == y).sum().item()  # 张量之间的比较运算
    print("第%d轮测试结束,当前正确率:%d %%" % (epoch + 1, correct / total * 100))
    return correct / total * 100
if __name__ == '__main__':
    plt_epoch = []
    loss_ll = []
    corr = []
    for epoch in range(20):
        plt_epoch.append(epoch+1) # 方便绘图
        loss_ll.append(train(epoch)) # 记录每一次的训练损失值 方便绘图
        corr.append(test(epoch)) # 记录每一次的正确率

    plt.rcParams['font.sans-serif'] = ['KaiTi']
    plt.figure(figsize=(12,6))
    plt.subplot(1,2,1)
    plt.title("训练模型")
    plt.plot(plt_epoch,loss_ll)
    plt.xlabel("循环次数")
    plt.ylabel("损失值loss")


    plt.subplot(1,2,2)
    plt.title("测试模型")
    plt.plot(plt_epoch,corr)
    plt.xlabel("循环次数")
    plt.ylabel("正确率")
    plt.show()

你可能感兴趣的:(论文精读+复现,个人笔记,深度学习,python)