AlexNet

AlexNet是2012年提出的一个模型,并且赢得了ImageNet图像识别挑战赛的冠军.首次证明了由计算机自动学习到的特征可以超越手工设计的特征,对计算机视觉的研究有着极其重要的意义.

AlexNet的设计思路和LeNet是非常类似的.不同点主要有以下几点:

  • 激活函数由sigmoid改为Relu
  • AlexNet使用了dropout,LeNet没有使用
  • AlexNet引入了大量的图像增广,如翻转、裁剪和颜色变化,从而进一步扩大数据集来缓解过拟合

激活函数

relu

\[\text{ReLU}(x) = \max(x, 0).
\]

其曲线及导数的曲线图绘制如下:



sigmoid

其曲线及导数的曲线图绘制如下:

\[\text{sigmoid}(x) = \frac{1}{1 + \exp(-x)}.
\]



relu的好处主要有两点:

  • 计算更简单,没有了指数运算
  • 在正数区间,梯度恒定为1,而sigmoid在接近0和1时,梯度几乎为0,会导致模型参数更新极为缓慢

现在大多模型激活函数都选择Relu了.

模型结构

AlexNet结构如下:



早期的硬件设备计算能力不足,所以上面的图分成了两部分,把不同部分的计算分散到不同的gpu上去,现在已经完全没有这种必要了.比如第一个卷积层的channel是48 x 2 = 96个.

细化的结构图:

根据上图,我们来写模型的定义:

首先,用一个11 x 11的卷积核,对224 x 224的输入,卷积后得到55 x 55 x 96的输出.

由于我们的图片是单通道的,那么我们有代码:

nn.Conv2d(1, 96, kernel_size=11, stride=4)

然而,我们测试一下他的输出.

X=torch.randn((1,1,224,224)) #batch x c x h x w
net = nn.Conv2d(1, 96, kernel_size=11, stride=4)
out = net(X)
print(out.shape)

输出

torch.Size([1, 96, 54, 54])

我们将代码修改为

nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2)

再用相同的代码测试输出

X=torch.randn((1,1,224,224)) #batch x c x h x w
net = nn.Conv2d(1, 96, kernel_size=11, stride=4,padding=2)
out = net(X)
print(out.shape)

输出

torch.Size([1, 96, 55, 55])

由此可见,第一个卷积层的实现应该是

nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2)

搜索pytorch的padding策略,https://blog.csdn.net/g11d111/article/details/82665265基本都是抄这篇的,这篇里指出

 显然,padding=1的效果是:原来的输入层基础上,上下左右各补了一行!

然而实测和文章中的描述不一致.

net = nn.Conv2d(1, 1, 3,padding=0)
X= torch.randn((1,1,5,5))
print(X)
out=net(X)
print(out) net2 = nn.Conv2d(1, 1, 3,padding=1)
out=net(X)
print(out)

输出

tensor([[[[-2.3052,  0.7220,  0.3106,  0.0605, -0.8304],
[-0.0831, 0.0168, -0.9227, 2.2891, 0.6738],
[-0.7871, -0.2234, -0.2356, 0.2500, 0.8389],
[ 0.7070, 1.1909, 0.2963, -0.7580, 0.1535],
[ 1.0306, -1.1829, 3.1201, 1.0544, 0.3521]]]])
tensor([[[[-0.1129, 0.7711, -0.6452],
[-0.3387, 0.1025, 0.3039],
[ 0.1604, 0.2709, 0.0740]]]], grad_fn=<MkldnnConvolutionBackward>)
tensor([[[[-0.1129, 0.7711, -0.6452],
[-0.3387, 0.1025, 0.3039],
[ 0.1604, 0.2709, 0.0740]]]], grad_fn=<MkldnnConvolutionBackward>)

到目前,我也没有把torch的padding策略搞清楚.知道的同学请评论留言.我的torch版本是1.2.0

AlexNet的激活函数采用的是Relu,所以

nn.ReLU(inplace=True)

接下来用一个3 x 3的卷积核去做最大池化,步幅为2,得到[27,27,96]的输出

nn.MaxPool2d(kernel_size=3, stride=2)

我们测试一下:

X=torch.randn((1,1,224,224)) #batch x c x h x w
net = nn.Sequential(
nn.Conv2d(1, 96, kernel_size=11, stride=4,padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3,stride=2)
)
out = net(X)
print(out.shape)

输出

torch.Size([1, 96, 27, 27])

至此,一个基本的卷积单元的定义就完成了,包括卷积,激活,池化.类似的,我们可以写出后续各层的定义.

AlexNet有5个卷积层,第一个卷积层的卷积核大小为11x11,第二个卷积层的卷积核大小为5x5,其余的卷积核均为3x3.第一二五个卷积层后做了最大池化操作,窗口大小为3x3,步幅为2.

这样,卷积层的部分如下:

net = nn.Sequential(
nn.Conv2d(1, 96, kernel_size=11, stride=4,padding=2), #[1,96,55,55] order:batch,channel,h,w
nn.ReLU(),
nn.MaxPool2d(kernel_size=3,stride=2), #[1,96,27,27] nn.Conv2d(96, 256, kernel_size=5, stride=1,padding=2),#[1,256,27,27]
nn.ReLU(),
nn.MaxPool2d(kernel_size=3,stride=2), #[1,256,13,13] nn.Conv2d(256, 384, kernel_size=3, stride=1,padding=1), #[1,384,13,13]
nn.ReLU(), nn.Conv2d(384, 384, kernel_size=3, stride=1,padding=1), #[1,384,13,13]
nn.ReLU(), nn.Conv2d(384, 256, kernel_size=3, stride=1,padding=1), #[1,256,13,13]
nn.ReLU(),
nn.MaxPool2d(kernel_size=3,stride=2), #[1,256,6,6]
)

接下来是全连接层的部分:

net = nn.Sequential(
nn.Linear(256*6*6,4096),
nn.ReLU(),
nn.Dropout(0.5),  #dropout防止过拟合
nn.Linear(4096,4096),
nn.ReLU(),
nn.Dropout(0.5),  #dropout防止过拟合
nn.Linear(4096,10) #我们最终要10分类
)

全连接层的参数数量过多,为了防止过拟合,我们在激活层后面加入了dropout层.

这样的话我们就可以给出模型定义:

class AlexNet(nn.Module):
def __init__(self):
super(AlexNet, self).__init__()
self.conv = nn.Sequential(
nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=2), # [1,96,55,55] order:batch,channel,h,w
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2), # [1,96,27,27] nn.Conv2d(96, 256, kernel_size=5, stride=1,padding=2), # [1,256,27,27]
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2), # [1,256,13,13] nn.Conv2d(256, 384, kernel_size=3, stride=1,padding=1), # [1,384,13,13]
nn.ReLU(), nn.Conv2d(384, 384, kernel_size=3, stride=1,padding=1), # [1,384,13,13]
nn.ReLU(), nn.Conv2d(384, 256, kernel_size=3, stride=1,padding=1), # [1,256,13,13]
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2), # [1,256,6,6]
) self.fc = nn.Sequential(
nn.Linear(256*6*6, 4096),
nn.ReLU(),
nn.Dropout(0.5), # dropout防止过拟合
nn.Linear(4096, 4096),
nn.ReLU(),
nn.Dropout(0.5), # dropout防止过拟合
nn.Linear(4096, 10) # 我们最终要10分类
) def forward(self, img):
feature = self.conv(img)
output = self.fc(feature.view(img.shape[0], -1))#输入全连接层之前,将特征展平
return output

加载数据

batch_size,num_workers=32,4
train_iter,test_iter = learntorch_utils.load_data(batch_size,num_workers,resize=224)

其中load_data定义于learntorch_utils.py.

def load_data(batch_size,num_workers,resize):
trans = []
if resize:
trans.append(torchvision.transforms.Resize(size=resize))
trans.append(torchvision.transforms.ToTensor())
transform = torchvision.transforms.Compose(trans) mnist_train = torchvision.datasets.FashionMNIST(root='/home/sc/disk/keepgoing/learn_pytorch/Datasets/FashionMNIST',
train=True, download=True,
transform=transform)
mnist_test = torchvision.datasets.FashionMNIST(root='/home/sc/disk/keepgoing/learn_pytorch/Datasets/FashionMNIST',
train=False, download=True,
transform=transform) train_iter = torch.utils.data.DataLoader(
mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
test_iter = torch.utils.data.DataLoader(
mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers) return train_iter,test_iter

这里,构造了一个transform.对图像做一次resize.

    if resize:
trans.append(torchvision.transforms.Resize(size=resize))
trans.append(torchvision.transforms.ToTensor())
transform = torchvision.transforms.Compose(trans)

定义模型

net = AlexNet().cuda()

由于全连接层的存在,AlexNet的参数还是非常多的.所以我们使用GPU做运算

定义损失函数

loss = nn.CrossEntropyLoss()

定义优化器

opt = torch.optim.Adam(net.parameters(),lr=0.01)

定义评估函数

def test():
acc_sum = 0
batch = 0
for X,y in test_iter:
X,y = X.cuda(),y.cuda()
y_hat = net(X)
acc_sum += (y_hat.argmax(dim=1) == y).float().sum().item()
batch += 1
print('acc_sum %d,batch %d' % (acc_sum,batch)) return 1.0*acc_sum/(batch*batch_size)

验证在测试集上的准确率.

训练

num_epochs = 3
def train():
for epoch in range(num_epochs):
train_l_sum,batch = 0,0
start = time.time()
for X,y in train_iter:
X,y = X.cuda(),y.cuda()
y_hat = net(X)
l = loss(y_hat,y)
opt.zero_grad()
l.backward() opt.step()
train_l_sum += l.item() batch += 1
test_acc = test()
end = time.time()
time_per_epoch = end - start
print('epoch %d,train_loss %f,test_acc %f,time %f' %
(epoch + 1,train_l_sum/(batch*batch_size),test_acc,time_per_epoch)) train()

输出

acc_sum 6297,batch 313
epoch 1,train_loss 54.029241,test_acc 0.628694,time 238.983008
acc_sum 980,batch 313
epoch 2,train_loss 0.106785,test_acc 0.097843,time 239.722055
acc_sum 1000,batch 313
epoch 3,train_loss 0.071997,test_acc 0.099840,time 239.459902

明显出现了过拟合.我们把学习率调整为0.001后,把batch_size调整为128

opt = torch.optim.Adam(net.parameters(),lr=0.001)

再训练,输出

acc_sum 8714,batch 79
epoch 1,train_loss 0.004351,test_acc 0.861748,time 156.573509
acc_sum 8813,batch 79
epoch 2,train_loss 0.002473,test_acc 0.871539,time 201.961380
acc_sum 8958,batch 79
epoch 3,train_loss 0.002159,test_acc 0.885878,time 202.349568

过拟合消失. 同时可以看到AlexNet由于参数过多,训练还是挺慢的.

从头学pytorch(十五):AlexNet的更多相关文章

  1. 从头学pytorch(十二):模型保存和加载

    模型读取和存储 总结下来,就是几个函数 torch.load()/torch.save() 通过python的pickle完成序列化与反序列化.完成内存<-->磁盘转换. Module.s ...

  2. 从头学pytorch(十六):VGG NET

    VGG AlexNet在Lenet的基础上增加了几个卷积层,改变了卷积核大小,每一层输出通道数目等,并且取得了很好的效果.但是并没有提出一个简单有效的思路. VGG做到了这一点,提出了可以通过重复使⽤ ...

  3. 从头学pytorch(十八):GoogLeNet

    GoogLeNet GoogLeNet和vgg分别是2014的ImageNet挑战赛的冠亚军.GoogLeNet则做了更加大胆的网络结构尝试,虽然深度只有22层,但大小却比AlexNet和VGG小很多 ...

  4. 从头学pytorch(十九):批量归一化batch normalization

    批量归一化 论文地址:https://arxiv.org/abs/1502.03167 批量归一化基本上是现在模型的标配了. 说实在的,到今天我也没搞明白batch normalize能够使得模型训练 ...

  5. 从头学pytorch(十四):lenet

    卷积神经网络 在之前的文章里,对28 X 28的图像,我们是通过把它展开为长度为784的一维向量,然后送进全连接层,训练出一个分类模型.这样做主要有两个问题 图像在同一列邻近的像素在这个向量中可能相距 ...

  6. 从头学pytorch(一):数据操作

    跟着Dive-into-DL-PyTorch.pdf从头开始学pytorch,夯实基础. Tensor创建 创建未初始化的tensor import torch x = torch.empty(5,3 ...

  7. 从头学pytorch(二十):残差网络resnet

    残差网络ResNet resnet是何凯明大神在2015年提出的.并且获得了当年的ImageNet比赛的冠军. 残差网络具有里程碑的意义,为以后的网络设计提出了一个新的思路. googlenet的思路 ...

  8. 从头学pytorch(五) 多层感知机及其实现

    多层感知机 上图所示的多层感知机中,输入和输出个数分别为4和3,中间的隐藏层中包含了5个隐藏单元(hidden unit).由于输入层不涉及计算,图3.3中的多层感知机的层数为2.由图3.3可见,隐藏 ...

  9. 从头学pytorch(二十二):全连接网络dense net

    DenseNet 论文传送门,这篇论文是CVPR 2017的最佳论文. resnet一文里说了,resnet是具有里程碑意义的.densenet就是受resnet的启发提出的模型. resnet中是把 ...

随机推荐

  1. 2018-8-10-win10-uwp-如何打包Nuget给其他人

    title author date CreateTime categories win10 uwp 如何打包Nuget给其他人 lindexi 2018-08-10 19:16:50 +0800 20 ...

  2. 为更强大而生的开源关系型数据库来了!阿里云RDS for MySQL 8.0 正式上线!

    2019年5月29日15时,阿里云RDS for MySQL 8.0正式上线,使得阿里云成为紧跟社区步伐,发布MySQL最新版本的云厂商.RDS for MySQL 8.0 产品是阿里云推出的 MyS ...

  3. Open Source Projects Released By Google

    Open Source Projects Released By Google Google has released over 20 million lines of code and over 9 ...

  4. H3C IP网络的结构

  5. meta标签、常用的文字类标签及其区别

    常用的文字类基本标签 段落:p标题文字 :h1~h6超链接:a,必须属性href,后跟跳转地址图片:img,必须属性src,后跟图片地址字体斜体:em.i 文字加粗:b.strong文字下划线:u文字 ...

  6. Activity学习(一):生命周期

    一. 认识Activity Activity是Android的四大组件之一,那么它是什么呢?如果简单的理解,可以把它当成应用的一个显示的屏幕. Activity类处于android.app包中,继承体 ...

  7. C#将可编译为本地机器码

    微软宣布了.net native的开发者预览版,详见这里. 这是一个大家期待了很多年的特性.每年在技术论坛上都有无数的人问,C#能否编译成本地机器码. 有了这个特性之后,更多开发商会开始选择C#来开发 ...

  8. Python工程编译成跨平台可执行文件(.pyc)

    原文:https://blog.csdn.net/zylove2010/article/details/79593655 在某些场景下,若不方便将python编写的源码工程直接给到其他人员,则可以将p ...

  9. H5 数据存储localStorage

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. H3C 链路聚合分类