(一)模型结构
Deep Learning里面大名鼎鼎的AlexNet基本的结构如下:
AlexNet包含8层变换,其中有5层卷积和2层全连接隐藏层,以及1个全连接输出层。
AlexNet中层的设计(网图):
(二)核心创新点和效果
查阅一些资料,了解了一下为什么AlexNet能够刷新Image Classification的几率。
首先是AlexNet 把sigmoid激活函数改成了简单的ReLU函数:
①sigmoid函数求幂运算比较复杂,而ReLU函数运算很简单
②ReLU激活函数在不同的参数初始化方法下使模型更容易训练
如上图所示,当sigmoid激活函数输出极接近0或1时,这些区域的梯度几乎为0,从而造成反向传播无法继续更新部分模型参数;而ReLU激活函数在正区间的梯度恒为1。因此,若模型参数初始化不当,sigmoid函数可能在正区间得到几乎为0的梯度,从而令模型无法得到有效训练。
③ReLU比tanh的收敛速度更快
AlexNet采用**局部反应标准化(local response normalization)**实现相同位置的神经元之间的相互抑制。
其次AlexNet通过丢弃法来控制全连接层的模型复杂度。对于某一层神经元,通过定义的概率来随机删除一些神经元,同时保持输入层和输出层的神经元个数不变,然后按照神经网络的学习方法进行参数更新。同样缓解了过拟合。
AlexNet引入了数据增强(Data Augmentation),从而进一步扩大数据集来缓解过拟合。
- 方法1:生成平移图像和水平翻转图像。做法就是从256x256的图像中提取随机的224x224大小的块(以及它们的水平翻转),然后基于这些提取的块训练网络。softmax层对这十个块做出的预测取均值。
- 方法2:改变训练图像的RGB通道的强度。特别的,本文对整个ImageNet训练集的RGB像素值进行了PCA。对每一幅训练图像,本文加上多倍的主成分,倍数的值为相应的特征值乘以一个均值为0标准差为0.1的高斯函数产生的随机变量。
AlexNet采用重叠池化层(OverlappingPooling)。池化层步长为2,池化区域为3×3,这种重叠池化层不容易过拟合。
(三)简化的AlexNet代码实现:
(1)定义层
import d2lzh as d2l
from mxnet import gluon, init, nd
from mxnet.gluon import data as gdata, nn
import os # os模块主要处理操作系统相关的功能
import sys # sys模块主要处理系统相关的功能
net = nn.Sequential()
# 使用较大的11 x 11窗口来捕获物体。同时使用步幅4来较大幅度减小输出高和宽。这里使用的输出通
# 道数比LeNet中的也要大很多
net.add(nn.Conv2D(96, kernel_size=11, strides=4, activation='relu'),
nn.MaxPool2D(pool_size=3, strides=2),
# 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,且增大输出通道数
nn.Conv2D(256, kernel_size=5, padding=2, activation='relu'),
nn.MaxPool2D(pool_size=3, strides=2),
# 连续3个卷积层,且使用更小的卷积窗口。除了最后的卷积层外,进一步增大了输出通道数。
# 前两个卷积层后不使用池化层来减小输入的高和宽
nn.Conv2D(384, kernel_size=3, padding=1, activation='relu'),
nn.Conv2D(384, kernel_size=3, padding=1, activation='relu'),
nn.Conv2D(256, kernel_size=3, padding=1, activation='relu'),
nn.MaxPool2D(pool_size=3, strides=2),
# 这里全连接层的输出个数比LeNet中的大数倍。使用丢弃层来缓解过拟合
nn.Dense(4096, activation="relu"), nn.Dropout(0.5),
nn.Dense(4096, activation="relu"), nn.Dropout(0.5),
# 输出层。由于这里使用Fashion-MNIST,所以用类别数为10,而非论文中的1000
nn.Dense(10))
(2)观察每一层的输出情况:
X = nd.random.uniform(shape=(1, 1, 227, 227))
net.initialize()
for layer in net:
X = layer(X)
print(layer.name, 'output shape:\t', X.shape)
结果和上面的层的设计结果类似。
我们仍然使用Fashion-MNIST数据集来演示AlexNet。
(3)定义下载数据集的函数
先来看一下预备知识:
class mxnet.gluon.data.vision.transforms.Resize(size, keep_ratio=False, interpolation=1)
将图像或一批图像按给定的大小调整大小。应该在mxnet.gluon.data.vision.transforms.to sensor之前应用。
class mxnet.gluon.data.vision.transforms.ToTensor()
将图像数组或一批图像数组转换为张量数组。
将[0,255]范围内的形状(h×w×c)的图像数组转换为[0,1]范围内的float32张量形状(c×h×w)数组。
如果是批处理输入,将[0,255]范围内的批处理图像形状数组(n×h×w×c)转换为float32张量形状数组(n×c×h×w)。
class mxnet.gluon.data.vision.transforms.Compose(transforms)
依次组成多个变换。
在实践中,数据读取经常是训练的性能瓶颈,特别当模型较简单或者计算硬件性能较高时。Gluon的DataLoader中一个很方便的功能是允许使用多进程来加速数据读取(暂不支持Windows操作系统)。这里我们通过参数num_workers来设置4个进程读取数据。
现在来看一下我们定义的load_data_fashion_mnist()函数:
# 本函数已保存在d2lzh包中方便以后使用
def load_data_fashion_mnist(batch_size, resize=None, root=os.path.join(
'~', '.mxnet', 'datasets', 'fashion-mnist')):
#利用os.path.join(path,name):连接目录与文件名或目录,结果为path/name
root = os.path.expanduser(root) # 把path中包含的“~”和“~user”转换成用户目录
transformer = []
if resize:
transformer += [gdata.vision.transforms.Resize(resize)]
# 利用Resize函数改变形状
transformer += [gdata.vision.transforms.ToTensor()]
# 利用ToTensor函数变成张量
transformer = gdata.vision.transforms.Compose(transformer)
# 利用Compose函数把步骤结合起来
mnist_train = gdata.vision.FashionMNIST(root=root, train=True)
#从root下载数据集,通过train参数决定是训练还是测试数据集
mnist_test = gdata.vision.FashionMNIST(root=root, train=False)
num_workers = 0 if sys.platform.startswith('win32') else 4
# sys.paltform各个平台的返回值,windows返回的值为win32
# 0表示不用额外的进程来加速读取数据
train_iter = gdata.DataLoader(
mnist_train.transform_first(transformer), batch_size, shuffle=True,
num_workers=num_workers)
test_iter = gdata.DataLoader(
mnist_test.transform_first(transformer), batch_size, shuffle=False,
num_workers=num_workers)
return train_iter, test_iter
(四)训练数据
batch_size = 128
# 如出现“out of memory”的报错信息,可减小batch_size或resize
train_iter, test_iter = load_data_fashion_mnist(batch_size, resize=227)
lr, num_epochs, ctx = 0.01, 5, d2l.try_gpu()
net.initialize(force_reinit=True, ctx=ctx, init=init.Xavier())
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': lr})
d2l.train_ch5(net, train_iter, test_iter, batch_size, trainer, ctx, num_epochs)
AlexNet跟LeNet结构类似,但使用了更多的卷积层和更大的参数空间来拟合大规模数据集ImageNet。它是浅层神经网络和深度神经网络的分界线。