卷积神经网络之AlexNet(一)

前面已经简单介绍了LeNet网络,同时用Pytorch框架搭建LeNet-5网络并在MNIST数据集进行了简单训练。LeNet-5于1994年提出,从提出到2012年长达近20年时间在计算机视觉领域具有一定的影响力,但神经网络一直被其他机器学习方法超越,并未处于领导地位。当时限制神经网络发展的因素大概有三个,一是硬件不足以支持大量参数的深层多通道卷积神经网络;二是数据集相对较小;三是缺少训练神经网络的一些关键技巧。
本文将对AlexNet网络结构做简单介绍,同时用Pytorch框架搭建该网络并在Fashion-Mnist数据集上训练

1.AlexNet简介

2012年,Alex Krizhevsky等人提出的AlexNet网络在ImageNet大赛中取得了很好的成绩,这是深度卷积神经网络的突破。该突破主要得益于两方面的因素:一是ImageNet数据集非常大,有100万个样本;二是应用GPU训练神经网络,打破了卷积神经网络中的计算瓶颈(卷积和矩阵乘法的计算)。

1.1网络结构

2012年,Alex Krizhevsky等人提出的AlexNet网络在ImageNet大赛中取得了很好的成绩,这是深度卷积神经网络的突破。
在这里插入图片描述
左边是LeNet,右边是AlexNet.。AlexNet与LeNet在结构非常类似,但也存在一点差异,一是AlexNet比LeNet要深,多了两个卷积层,二是激活函数使用ReLu函数而非Sigmoid函数。

1.2网络实现

本文暂不使用ImageNet数据集,依然使用MNIST数据集,MNIST数据集每张图片是(1,28,28),一共有10类图片,因此要对网络的输入输出层稍作修改即可(输入层的通道数,输出层的神经元个数,图像大小在加载数据时会处理为[1,244,244],这里不涉及)。

class AlexNet(nn.Module):
    def __init__(self):
        super(AlexNet, self).__init__()
        #input(1,244,244)
        self.conv1=nn.Conv2d(1,96,kernel_size=11,stride=4)#(1,244,244)-->(96,59,59)
        self.pool1=nn.MaxPool2d(kernel_size=3,stride=2)#(96,59,59)-->(96,29,29)
        self.conv2=nn.Conv2d(96,256,kernel_size=5,padding=2)#(96,29,29)-->(256,29,29)
        self.pool2=nn.MaxPool2d(kernel_size=3,stride=2)#(256,29,29)-->(256,14,14)
        self.conv3=nn.Conv2d(256,384,kernel_size=3,padding=1)#(256,14,14)-->(384,14,14)
        self.conv4 = nn.Conv2d(384, 384, kernel_size=3, padding=1)  # (384,14,14)-->(384,14,14)
        self.conv5 = nn.Conv2d(384, 256, kernel_size=3, padding=1)  # (384,14,14)-->(256,14,14)
        self.pool3=nn.MaxPool2d(kernel_size=3,stride=2)#(256,14,14)-->(256,6,6)
        self.fc1=nn.Linear(256*6*6,4096)
        self.fc2=nn.Linear(4096,4096)
        self.fc3=nn.Linear(4096,10)
    def forward(self,x):
        x=self.pool1(torch.relu(self.conv1(x)))
        x=self.pool2(torch.relu(self.conv2(x)))
        x=self.pool3(torch.relu(self.conv5(self.conv4(self.conv3(x)))))
        x=x.view(-1,256*6*6)
        x=torch.relu(self.fc1(x))
        x=torch.relu(self.fc2(x))
        x=self.fc3(x)
        return x

搭建好网络后,用一个[1,1,244,244]的tensor来验证一下:

net=AlexNet()
x=torch.randn(1,1,244,244)
Y=net(x)
print(Y.shape)

结果如下:

torch.Size([1, 10])

从输出张量的形状来看,网络没有问题,达到预期结果。

2.模型训练

2.1MNIST数据集

2.2数据迭代器

构建一个数据迭代器来加载训练数据和测试数据:

def load_data_fashion_mnist(batch_size):
    transform=transforms.Compose([transforms.Resize((244,244)),transforms.ToTensor()])
    mnist_train=torchvision.datasets.MNIST(root='data',train=True,transform=transform,download=True)
    mnist_test=torchvision.datasets.MNIST(root='data',train=False,transform=transform,download=True)
    return (data.DataLoader(mnist_train,batch_size,shuffle=True,num_workers=4),
            data.DataLoader(mnist_test,batch_size,shuffle=True,num_workers=4))

加入了transforms.Resize((244,244))将图片分辨率修改为(244,244)

指定batch_size=100,输出如下:

torch.Size([100, 1, 244, 244])

2.3训练器

与上篇LeNet中一样,不做修改:

def train(net,train_iter,test_iter,num_epochs,lr):

    def init(n):
        if type(n)==nn.Conv2d or type(n)==nn.Linear:
            nn.init.normal(n.weight.data)
    net.apply(init)

    optimizer =torch.optim.SGD(net.parameters(),lr=lr)
    loss=nn.CrossEntropyLoss()

    train_loss_list=[]
    train_acc_list=[]
    test_acc_list=[]

    for i in range(num_epochs):
        train_loss=0.0
        train_acc=0.0
        for j,(X,y) in enumerate(train_iter):
            X=X.to(device)
            y=y.to(device)
            optimizer.zero_grad()
            Y=net(X).to(device)
            l=loss(Y,y)
            l.backward()
            optimizer.step()
            train_loss+=l.item()
            train_acc+=accuracy(Y,y)/X.shape[0]
            if j%200==199:
                a=train_loss/200.0
                b=train_acc/200.0
                train_loss_list.append(a)
                train_acc_list.append(b)
                train_loss=0.0
                train_acc=0.0

                with torch.no_grad():#在测试数据集上测试
                    for m,(X_test,y_test) in enumerate(train_iter):
                        X_test=X_test.to(device)
                        y_test=y_test.to(device)
                        output=net(X_test).to(device)
                        acc=accuracy(Y,y)/X.shape[0]
                        test_acc_list.append(acc)


                print(f'train_loss:{a:.3f},train_acc:{b:.3f},test_acc:{acc:.3f}')

结果如下:

train_loss:nan,train_acc:0.098,test_acc:0.130
train_loss:nan,train_acc:0.099,test_acc:0.090
...
train_loss:nan,train_acc:0.098,test_acc:0.090
train_loss:nan,train_acc:0.098,test_acc:0.120

可以看到准确率和损失函数的值并没有过大的变化,损失函数的值nan,这说明学习过程异常,这个训练器在之前LeNet训练时没有问题,那么该异常是什么原因造成的?
查阅了一些资料,这种现象可能属于梯度消失/梯度爆炸,主要是权重初始化不恰当造成的。针对ReLu激活函数,Kaiming初始化方法能够有效缓解梯度消失和梯度爆炸:

nn.init.kaiming_normal_(n.weight.data)
train_loss:nan,train_acc:0.097,test_acc:0.090
train_loss:nan,train_acc:0.099,test_acc:0.120
...
train_loss:nan,train_acc:0.099,test_acc:0.120
train_loss:nan,train_acc:0.100,test_acc:0.060

结果问题依然没有解决!
最后把学习率减小了两个数量级,学习正常!

train_loss:0.566,train_acc:0.868,test_acc:0.970
train_loss:0.158,train_acc:0.956,test_acc:0.970
...
train_loss:0.043,train_acc:0.987,test_acc:0.990
train_loss:0.039,train_acc:0.988,test_acc:0.980

3.总结

本文首先介绍了AlexNet网络,比较了与LeNet的不同,然后用Pytorch搭建了该网络,但在MNIST数据集上训练时出现了异常,可能是梯度消失/梯度爆炸造成的,尝试了一些解决方案但并未成功,最后通过减小学习率成功解决问题。当然,解决梯度消失/爆炸的方法还有很多,后面再详细介绍。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值