前情提要
机器学习这一块光看书还是不行的,该动手搓的东西还是要动手搓。正在基于 pytorch
做模型复现项目,目前完成了LeNet
、AlexNet
、VGG
、InceptionNet
,后续还要做 ResNet
、SSD
、YOLO
。
仓库:My_Model_Zoo
当然也并不完全是闭门造车,还是参考了别人的代码。
约定
模型 - model.py
训练 - train.py
推理 - inference.py
LeNet
识别手写数字,万物之源 reference
由于是第一次写,所以还是要参照别人的代码 reference
不过他的代码并不太忠于原论文,我做了一些修改
概述
LeNet-5是一个较简单的卷积神经网络。输入的二维图像,先经过两次卷积层到池化层,再经过全连接层,最后使用softmax分类作为输出层。
LeNet-5 这个网络虽然很小,但是它包含了深度学习的基本模块:卷积层,池化层,全连接层。是其他深度学习模型的基础。
LeNet-5共有7层,不包含输入,每层都包含可训练参数;每个层有多个Feature Map,每个FeatureMap通过一种卷积滤波器提取输入的一种特征,然后每个FeatureMap有多个神经元。
逐层解析
INPUT层-输入层
首先是数据 INPUT 层,输入图像的尺寸统一归一化为32*32。
注意:本层不算LeNet-5的网络结构,传统上,不将输入层视为网络层次结构之一。
C1层-卷积层
输入图片:$32 \times 32$
卷积核大小:$5 \times 5$
卷积核种类:$6$
输出featuremap大小:$28 \times 28$ $(32-5+1)=28$
神经元数量:$28 \times 28 \times 6$
可训练参数:$(5 \times 5 + 1)\times 6$(每个滤波器$5 \times 5=25$个unit参数和一个bias参数,一共6个滤波器)
连接数:$(5\times 5+1)\times 6\times 28\times 28=122304$
S2层-池化层
输入:$28 \times 28$
采样区域:$2 \times 2$
采样方式:4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid。可以发现和现在的主流Pooling和激活函数设置是不一样的。
采样种类:$6$
输出featureMap大小:$14 \times 14$ $(28 / 2)$
神经元数量:$14 \times 14 \times 6$
可训练参数:$2 \times 6$ (6个通道分别的权重weight和偏置bias)
连接数:$(2 \times 2 + 1) \times 6 \times 14 \times 14$
C3层-卷积层
卷积核大小:$5 \times 5$
卷积核种类:$16$
输出featureMap大小:$10 \times 10$ $(14 - 5 + 1) = 10$
注意这里有特殊的组合计算。
C3的前6个feature map与S2层相连的3个feature map相连接,后面6个feature map与S2层相连的4个feature map相连接,后面3个feature map与S2层部分不相连的4个feature map相连接,最后一个与S2层的所有feature map相连。卷积核大小依然为$5\times 5$,所以总共有$6\times (3\times 5\times 5+1)+6\times (4\times 5\times 5+1)+3\times (4\times 5\times 5+1)+1\times (6\times 5\times 5+1)=1516$个参数。而图像大小为10*10,所以共有151600个连接。
(实际上是采用池化层featureMap的channel的不同的组合模式)
论文中给出了两个如此设置的原因:1)减少参数,2)这种不对称的组合连接的方式有利于提取多种组合特征。
S4层-池化层(下采样层)
输入:$10 \times 10$
采样区域:$2 \times 2$
采样方式:4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid。
采样种类:$16$
输出featureMap大小:$5 \times 5$ $(10 / 2)$
神经元数量:$5 \times 5 \times 16 = 400$
可训练参数:$2 \times 16$ (16个通道分别的权重weight和偏置bias)
连接数:$(2 \times 2 + 1) \times 6 \times 5 \times 5 = 2000$
C5层-卷积层
输入:$5 \times 5$
卷积核大小:$5 \times 5$
卷积核种类:$120$
输出featuremap大小:$1 \times 1$ $(5-5+1)=1$
可训练参数:$(16 \times 5 \times 5 + 1)\times 120$
F6层-全连接层
输入:c5 120维向量
计算方式:计算输入向量和权重向量之间的点积,再加上一个偏置,结果通过sigmoid函数输出。
可训练参数:84*(120+1)=10164
Output层-全连接层
Output层也是全连接层,共有10个节点,分别代表数字0到9,且如果节点i的值为0,则网络识别的结果是数字i。采用的是径向基函数(RBF)的网络连接方式。假设x是上一层的输入,y是RBF的输出,则RBF输出的计算方式是:
\[y_i = \sum_i(x_j - w_{ij})^2\]AlexNet
介绍
AlexNet是由Alex Krizhevsky、Ilya Sutskever和Geoffrey Hinton在2012年ImageNet图像分类竞赛中提出的一种经典的卷积神经网络。当时,AlexNet在 ImageNet 大规模视觉识别竞赛中取得了优异的成绩,把深度学习模型在比赛中的正确率提升到一个前所未有的高度。因此,它的出现对深度学习发展具有里程碑式的意义。
网络结构
Input
AlexNet输入为RGB三通道的224 × 224 × 3大小的图像(也可填充为227 × 227 × 3 )
Conv + Pooling
Conv1
input: $224 \times 224 \times 3$
kernel size: $11 \times 11 \times 3$
channel: $96$
stride: $4$
feature map: split $55 \times 55 \times 96$ into 2 $55 \times 55 \times 48$ submaps, connect to
ReLU1
Pool1
size: $3 \times 3$
stride: $2$
output: $2 \times 27 \times 27 \times 48$
LRN1
Conv2
kernel size: $5 \times 5 \times 48$
channel: $256$
ReLU2
Pool2
LRN2
Conv3
kernel size: $3 \times 3 \times 256$
channel: $384$
ReLU3
Conv4
kernel size: $3 \times 3 \times 192$
channel: $384$
ReLU4
Conv5
kernel size: $3 \times 3 \times 192$
channel: $256$
ReLU5
Pool5
size: $3 \times 3$
stride: $2$
Full
F6
Get $4096$ channels of $1 \times 1$ feature map from pooling. Used
ReLU6
Dropout
F7
ReLU7
Dropout
F8
Softmax
$1000$ dimension output.
VGG
介绍
VGG是Oxford的Visual Geometry Group的组提出的(大家应该能看出VGG名字的由来了)。该网络是在ILSVRC 2014上的相关工作,主要工作是证明了增加网络的深度能够在一定程度上影响网络最终的性能。VGG有两种结构,分别是VGG16和VGG19,两者并没有本质上的区别,只是网络深度不一样。
原理
VGG16相比AlexNet的一个改进是采用连续的几个3x3的卷积核代替AlexNet中的较大卷积核(11x11,7x7,5x5)。对于给定的感受野(与输出有关的输入图片的局部大小),采用堆积的小卷积核是优于采用大的卷积核,因为多层非线性层可以增加网络深度来保证学习更复杂的模式,而且代价还比较小(参数更少)。
简单来说,在VGG中,使用了3个3x3卷积核来代替7x7卷积核,使用了2个3x3卷积核来代替5*5卷积核,这样做的主要目的是在保证具有相同感知野的条件下,提升了网络的深度,在一定程度上提升了神经网络的效果。
网络结构
VGG16
input: 224 * 224 RGB
conv3-64
conv3-64
maxpool
conv3-128
conv3-128
maxpool
conv3-256
conv3-256
conv3-256
maxpool
conv3-512
conv3-512
conv3-512
maxpool
conv3-512
conv3-512
conv3-512
maxpool
FC-4096
FC-4096
FC-1000
softmax
VGG19
input: 224 * 224 RGB
conv3-64
conv3-64
maxpool
conv3-128
conv3-128
maxpool
conv3-256
conv3-256
conv3-256
conv3-256
maxpool
conv3-512
conv3-512
conv3-512
conv3-512
maxpool
conv3-512
conv3-512
conv3-512
conv3-512
maxpool
FC-4096
FC-4096
FC-1000
softmax
GoogLeNet
该网络的细节在之前post里已经介绍详尽,最关键的部分是写有四个分支的 Inception
模块,这也是 GoogLeNet
的灵魂所在。
code reference 1 code reference 2
ResNet
“网络越深越好”,但是越深越难训练。
ResNet的核心是通过把之前的输入连接到后面来实现数据的跨层流动,动态调整各层权重在最后结果中的权重。
在cv概念blog里已经叙述详尽。
可以试着换一个数据集
SSD
SSD是两种单步目标识别网络之一。可以参考之前的blog
主要是按照ssd.pytorch的源码进行实现。由于只想关心模型的主干部分,原项目的辅助函数我就直接引用了。这个仓库的代码解析能找到很多,issue
区也是十分精彩,百家争鸣。
可以说原作者在竭尽所能地确保代码的优美性,对于每个神经网络模块,总是采用传入config列表然后识别列表进行模块的产生,使用了大量的迭代和分类讨论。但是这样可读性非常糟糕,再加上论文中的SSD是一个固定的模型,我不太想关注它的可拓展性,所以我干脆把他的这种代码全部改写成了直接列举的形式,希望更加直观。这样以后复习的时候也能一眼就看出它的结构。
SSD的 Loss
是 MultiBoxLoss
。计算方式如下:
定义$x_{ij}^p$为“第$i$个default box和第$j$个ground truth box匹配且类别为$p$”的指示器变量(indicator)。loss function为:
\[L(x, c, l, g) = \frac{1}{N}(L_{conf}(x, c) + \alpha L_{loc}(x, l, g))\]其中$L_{loc}$(定位损失)和之前讲Faster R-CNN时提到的bounding-box regression类似。定义:$l$为预测框,$g$为ground-truth框,用这两个参数产生smooth L1 loss。回归默认框($d$)的中心($cx, cy$)和宽度、高度($w, h$)。
\[L_{loc}(x, l, g) = \sum^N_{i \in Pos}\sum_{m \in \{cx, cy, w, g\}}x^k_{ij}\mathrm{smooth L1}(\^m_i - \hat{g}^m_j)\] \[\hat{g}^{cx}_j = (g^{cx}_j - d^{cx}_i) / d^w_i\;\;\;\;\hat{g}^{cy}_j = (g^{cy}_j - d_i^{cy}) / d^h_i =\] \[\hat{g}^h_j = \log(\frac{g^h_j}{d^h_i})\;\;\;\;\hat{g}^h_j = \log(\frac{g^h_j}{d^h_i})\]置信度损失是在多类别置信度($c$)上的softmax损失。
\[L_{conf}(x, c) = -\sum^N_{i\in Pos} x^p_{ij}\log(\hat{c}^p_i) - \sum_{i \in Neg}\log(\hat{c}_i^0)\] \[\hat{c}^p_i = \frac{\exp(c_i^p)}{\sum_p \exp(c^p_i)}\]YOLO
之前的blog已经讲过YOLO,所以不再重复劳动
YOLO官网: