前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >要学习的第一个现代CNN:AlexNet

要学习的第一个现代CNN:AlexNet

作者头像
数据派THU
发布2023-05-11 17:46:09
发布2023-05-11 17:46:09
19400
代码可运行
举报
文章被收录于专栏:数据派THU数据派THU
运行总次数:0
代码可运行
代码语言:javascript
代码运行次数:0
复制
来源:PyTorch研习社本文约1800字,建议阅读5分钟本文为你介绍AlexNet。

从上图可以看到相对于LeNet(左侧),AlexNet(右侧)更深了,也应用了新的技术:

  • AlexNet有5层卷积层(Conv层)
  • 使用Max Pool
  • 使用了ReLU激活函数

AlexNet支持更高分辨率的彩色图像(3x224x224)。

AlexNet是为了基于ImageNet数据集的ISLVRC比赛而设计的。ImageNet是一个计算机视觉系统识别项目,是目前世界上图像识别最大的数据库,它包含了1000个类别的超过140万张图像。所以相对于为了解决手写数字识别(0到9共10个类别)的LeNet,AlexNet的最后输出层就有了1000个输出。

为什么用ReLU激活函数

首先ReLU函数不含有指数计算,在计算上比Sigmoid函数简单。

其次如果Sigmoid函数的输出接近0或1时会出现梯度(导数)接近0,这使得我们无法依赖SGD来调整模型参数。相反,ReLU激活函数在正区间的梯度总是1。因此,如果模型参数没有正确初始化,sigmoid函数可能在正区间内得到几乎为0的梯度,从而使模型无法得到有效的训练。

用PyTorch实现AlexNet  

现在蓝图有了,最关键的就是根据公式计算出每层计算后的输出数据维度了。Pooling层的计算公式和卷积层的计算公式一样,再提一下计算公式为:

[(nh - kh + 2*padding + stride) / stride] * [(nw - kw + 2*padding + stride) / stride]

以上除法为向下取整,padding指的是PyTorch中nn.Conv2d()和nn.MaxPool2d()中的padding参数值。

据此我们的AlexNet实现代码为:

代码语言:javascript
代码运行次数:0
复制
import torch.nn as nnclass AlexNet(nn.Module):    def __init__(self):        super().__init__()        # 卷积层        self.conv = nn.Sequential(            # 这次输入图像RGB彩色图像,所以in_channels=3            nn.Conv2d(in_channels=3, out_channels=96, kernel_size=11, stride=4),            nn.ReLU(),            nn.MaxPool2d(kernel_size=3, stride=2),            nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, padding=2),            nn.ReLU(),            nn.MaxPool2d(kernel_size=3, stride=2),            nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, padding=1),            nn.ReLU(),            nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, padding=1),            nn.ReLU(),            nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, padding=1),            nn.MaxPool2d(kernel_size=3, stride=2)        )        self.flatten = nn.Flatten()        # 全连接层        self.fc = nn.Sequential(            # 这一层是卷积层到全连接层            nn.Linear(256*5*5, 4096),            nn.ReLU(),            nn.Dropout(p=0.5),            nn.Linear(4096, 4096),            nn.ReLU(),            nn.Dropout(p=0.5),            nn.Linear(4096, 1000)        )        def forward(self, X):        X = self.conv(X)        X = self.flatten(X)        return self.fc(X)br

有一点需要注意的是,我们在AlexNet的结构图里没有卷积层到全连接层的连接,但是实现时一定要加上。为了防止过拟合,还应用了Dropout。

初始化模型,查看模型摘要

代码语言:javascript
代码运行次数:0
复制
model = AlexNet()def init_weights(m):    if type(m) == nn.Linear or type(m) == nn.Conv2d:        nn.init.xavier_uniform_(m.weight, gain=1.0)model.apply(init_weights)import model_summarymodel_summary.summary(model, (3, 224, 224))br

从头开始训练AlexNet

我要训练一个基于AlexNet的花朵品种分类,插入公众号的图像数据集加载链接。

由于这个数据集里就5种花朵,所以我直接将AlexNet的最后一层输出从1000改成5,其他的不动。

数据集在加载自己的图像数据集一文中说过了。

模型已经初始化过了,现在只需要定义优化器和损失函数:

代码语言:javascript
代码运行次数:0
复制
import torchoptimizer = torch.optim.SGD(model.parameters(), lr=0.01)# reduction='mean'表示取所有样本损失的均值loss_func = nn.CrossEntropyLoss(reduction='mean')br

开始训练:

代码语言:javascript
代码运行次数:0
复制
val_accs = train_loop(model, loss_func, optimizer, 10, train_dl, val_dl)br

从这个图的走势可以看出如果我加大训练次数epoch,那我会得到性能更好的模型。

现在可以看下模型在验证集上的准确率:

代码语言:javascript
代码运行次数:0
复制
plt.xlabel('Epoch')plt.ylabel('Val Accuracy')plt.plot(val_accs)br

在测试集上测试模型

模型在测试集上的准确率,也是50%左右:

代码语言:javascript
代码运行次数:0
复制
test_acc(model, test_dl)# Test accuracy: 48.76%br

你是不是觉得就50%的准确率,但是我们仅仅只训练10轮,而且有5个类别。

下面我从测试数据中选择了10朵花,每种花2朵来看看我们应该怎么使用训练好的模型去预测现实世界中的花朵种类:

代码语言:javascript
代码运行次数:0
复制
imgs = ['./data/flower/test/tulips/unnamed.jpg',        './data/flower/test/tulips/images.jpg',        './data/flower/test/sunflowers/sunflower.jpg',        './data/flower/test/sunflowers/images.jpg',        './data/flower/test/roses/1549462726-pink-and-yellow-roses-flower-basket.jpg',        './data/flower/test/roses/images.jpg',        './data/flower/test/dandelion/DandelionFlower.jpg',        './data/flower/test/dandelion/images.jpg',        './data/flower/test/daisy/LolaFlora-Two-Petals-Daisies-600x334.jpg',        './data/flower/test/daisy/images.jpg']                        for img in imgs:    predict_label = predict(model, test_transform, train_dataset.classes, img)    print('Actual label: {} ... Predict label:{}'.format(        os.path.split(os.path.split(img)[0])[1], predict_label))br

输出:

代码语言:javascript
代码运行次数:0
复制
Actual label: tulips ... Predict label:tulipsActual label: tulips ... Predict label:tulipsActual label: sunflowers ... Predict label:sunflowersActual label: sunflowers ... Predict label:sunflowersActual label: roses ... Predict label:tulipsActual label: roses ... Predict label:tulipsActual label: dandelion ... Predict label:sunflowersActual label: dandelion ... Predict label:dandelionActual label: daisy ... Predict label:daisyActual label: daisy ... Predict label:dandelionbr

看这个结果,感觉还行。

train_loop、test_acc与predict函数代码都可以在训练分类神经网络的关键代码3.0找到。

编辑:文婧

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-05-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据派THU 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档