目录
基本卷积神经网络
AlexNet
网络提出
在2012年前,图像特征都是机械地计算出来的。事实上,设计一套新的特征函数、改进结果,并撰写论文是盛极一时的潮流。SIFT(Lowe, 2004)、SURF(Bay et al., 2006)、HOG(定向梯度直方图)(Dalal and Triggs, 2005)、bags of visual words和类似的特征提取方法占据了主导地位。
另一组研究人员,包括Yann LeCun、Geoff Hinton、Yoshua Bengio、Andrew Ng、Shun ichi Amari和Juergen Schmidhuber,他们认为特征本身应该被学习。此外他们还认为在合理地复杂性前提下,特征应该由多个共同学习的神经网络层组成,每个层都有可学习的参数。在机器视觉中,最底层可能检测边缘、颜色和纹理。事实上,Alex Krizhevsky、Ilya Sutskever和Geoff Hinton提出了一种新的卷积神经网络变体AlexNet。2012年在全球知名的图像识别竞赛ILSVRC中,AlexNet将错误率降低了近 10个百分点,这是之前所有机器学习模型无法做到的。AlexNet以Alex Krizhevsky的名字命名,他是论文(Krizhevsky et al., 2012)的第一作者。
AlexNet跟LeNet-5类似也是一个用于图像识别的卷积神经网络。AlexNet网络结构更加复杂,参数更多。在ILSVRC比赛中,AlexNet所用的数据集是ImageNet,总共识别1000个类别。
网络结构
AlexNet由八层组成:五个卷积层、两个全连接隐藏层和一个全连接输出层。
池化层均采用最大池化;
AlexNet使用ReLU而不是sigmoid作为其激活函数;
网络规模扩大,参数数量接近6000万;
出现“多个卷积层+一个池化层”的结构。
随网络深入,宽、高衰减,通道数增加。
改进
输入样本
最简单、通用的图像数据变形的方式
- 从原始图像( 256,256 )中,随机的 crop 出一些图像( 224,224 )。【平移变换,crop】
- 水平翻转图像。【反射变换,flip】
- 给图像增加一些随机的光照。【光照、彩色变换,color jittering】
激活函数
采用ReLU替代Tan Sigmoid;用于卷积层与全连接层之后。
Dropout
在每个全连接层后面使用一个Dropout层,以概率p随机关闭激活函数。
双GPU 策略
AlexNet使用两块GTX580显卡进行训练,两块显卡只需要在特定的层进行通信。
import torch
from torch import nn
from d2l import torch as d2l
net = nn.Sequential(
# 这里使用一个11*11的更大窗口来捕捉对象。
# 同时,步幅为4,以减少输出的高度和宽度。
# 另外,输出通道的数目远大于LeNet
nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=1), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
# 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,且增大输出通道数
nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
# 使用三个连续的卷积层和较小的卷积窗口。
# 除了最后的卷积层,输出通道的数量进一步增加。
# 在前两个卷积层之后,汇聚层不用于减少输入的高度和宽度
nn.Conv2d(256, 384, kernel_size=3, padding=1), nn.ReLU(),
nn.Conv2d(384, 384, kernel_size=3, padding=1), nn.ReLU(),
nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Flatten(),
# 这里,全连接层的输出数量是LeNet中的好几倍。使用dropout层来减轻过拟合
nn.Linear(6400, 4096), nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(4096, 4096), nn.ReLU(),
nn.Dropout(p=0.5),
# 最后是输出层。由于这里使用Fashion-MNIST,所以用类别数为10,而非论文中的1000
nn.Linear(4096, 10))
X = torch.randn(1, 1, 224, 224)
for layer in net:
X=layer(X)
print(layer.__class__.__name__,'output shape:\t',X.shape)
用cpu训练是真的慢,这速度我哭了。
这是gpu训练的速度,都不是一个量级的。。。
VGG-16
VGG是Oxford的Visual Geometry Group的组提出的。该网络是在ILSVRC 2014上的相关工作,主要工作是证明了增加网络的深度能够在一定程度上影响网络最终的性能。VGG有两种结构,分别是VGG16和VGG19,两者并没有本质上的区别,只是网络深度不一样。
VGG-16网络结构
与AlexNet、LeNet一样,VGG网络可以分为两部分:第一部分主要由卷积层和汇聚层组成,第二部分由全连接层组成。从AlexNet到VGG,它们本质上都是块设计。
网络说明
改进
- 网络规模进一步增大,参数数量约为1.38亿
- 由于各卷积层、池化层的超参数基本相同,整体结构呈现出规整的特点。
普遍规律:随网络深入,高和宽衰减,通道数增多。
import torch
from torch import nn
from d2l import torch as d2l
def vgg_block(num_convs, in_channels, out_channels):
layers = []
for _ in range(num_convs):
layers.append(nn.Conv2d(in_channels, out_channels,
kernel_size=3, padding=1))
layers.append(nn.ReLU())
in_channels = out_channels
layers.append(nn.MaxPool2d(kernel_size=2,stride=2))
return nn.Sequential(*layers)
conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))
def vgg(conv_arch):
conv_blks = []
in_channels = 1
# 卷积层部分
for (num_convs, out_channels) in conv_arch:
conv_blks.append(vgg_block(num_convs, in_channels, out_channels))
in_channels = out_channels
return nn.Sequential(
*conv_blks, nn.Flatten(),
# 全连接层部分
nn.Linear(out_channels * 7 * 7, 4096), nn.ReLU(), nn.Dropout(0.5),
nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(0.5),
nn.Linear(4096, 10))
net = vgg(conv_arch)
X = torch.randn(size=(1, 1, 224, 224))
for blk in net:
X = blk(X)
print(blk.__class__.__name__,'output shape:\t',X.shape)
ratio = 4
small_conv_arch = [(pair[0], pair[1] // ratio) for pair in conv_arch]
net = vgg(small_conv_arch)
lr, num_epochs, batch_size = 0.05, 10, 128
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
cpu训练效率太低,此处就放弃训练了。
残差网络
残差操作这一思想起源于论文《Deep Residual Learning for Image Recognition》,这篇文章发现,如果存在某个K层的网络f是当前最优网络,那么可以构造一个更深的网络,其最后几层仅是该网络f第K层输出的恒等映射(Identity Mapping),就可以取得与f一致的结果;也许K还不是所谓“最佳层数”,那么更深的网络就可以取得更好的结果。总而言之,与浅层网络相比,更深的网络的表现不应该更差。更深的网络在训练过程中的难度更大,因此作者提出了残差网络的思想。
残差网络在普通网络的基础上,将浅层的激活项通过支路直接传向深层,克服深层神经网络中梯度消失的问题,为训练极深的神经网络提供便利。
梯度消失
目前优化神经网络的方法都是基于BP,即根据损失函数计算的误差通过梯度反向传播的方式,指导深度网络权值的更新优化。其中将误差从末层往前传递的过程需要链式法则,链式法则是一个连乘的形式,所以当层数越深的时候,梯度将以指数形式传播。
梯度消失问题一般随着网络层数的增加会变得越来越明显。在根据损失函数计算的误差通过梯度反向传播的方式对深度网络权值进行更新时,得到的梯度值接近0,也就是梯度消失。
残差块
残差网络依旧让非线形层满足
H
(
x
,
w
h
)
H\left( x,{{w}_{h}} \right)
H(x,wh),然后从输入直接引入一个短连接到非线形层的输出上,使得整个映射变为
y
=
H
(
x
,
w
h
)
+
x
y=H\left( x,{{w}_{h}} \right)+x
y=H(x,wh)+x
这就是残差网路的核心公式,任何使用了这种操作的网络都可以称之为残差网络。
残差网络
- 普通网络的基准模型受VGG网络的启发
- 卷积层主要有3×3的过滤器,并遵循两个简单的设计规则①对输出特征图的尺寸相同的各层,都有相同数量的过滤器;②如果特征图的大小减半,那么过滤器的数量就增加一倍,以保证每一层的时间复杂度相同。
- ResNet模型比VGG网络更少的过滤器和更低的复杂性。ResNet具有34层的权重层,有36亿FLOPs,只是VGG-19(19.6亿FLOPs)的18%。
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l
class Residual(nn.Module): #@save
def __init__(self, input_channels, num_channels,
use_1x1conv=False, strides=1):
super().__init__()
self.conv1 = nn.Conv2d(input_channels, num_channels,
kernel_size=3, padding=1, stride=strides)
self.conv2 = nn.Conv2d(num_channels, num_channels,
kernel_size=3, padding=1)
if use_1x1conv:
self.conv3 = nn.Conv2d(input_channels, num_channels,
kernel_size=1, stride=strides)
else:
self.conv3 = None
self.bn1 = nn.BatchNorm2d(num_channels)
self.bn2 = nn.BatchNorm2d(num_channels)
def forward(self, X):
Y = F.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3:
X = self.conv3(X)
Y += X
return F.relu(Y)
blk = Residual(3,3)
X = torch.rand(4, 3, 6, 6)
Y = blk(X)
Y.shape
blk = Residual(3,6, use_1x1conv=True, strides=2)
blk(X).shape
b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
nn.BatchNorm2d(64), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
def resnet_block(input_channels, num_channels, num_residuals,
first_block=False):
blk = []
for i in range(num_residuals):
if i == 0 and not first_block:
blk.append(Residual(input_channels, num_channels,
use_1x1conv=True, strides=2))
else:
blk.append(Residual(num_channels, num_channels))
return blk
b2 = nn.Sequential(*resnet_block(64, 64, 2, first_block=True))
b3 = nn.Sequential(*resnet_block(64, 128, 2))
b4 = nn.Sequential(*resnet_block(128, 256, 2))
b5 = nn.Sequential(*resnet_block(256, 512, 2))
net = nn.Sequential(b1, b2, b3, b4, b5,
nn.AdaptiveAvgPool2d((1,1)),
nn.Flatten(), nn.Linear(512, 10))
X = torch.rand(size=(1, 1, 224, 224))
for layer in net:
X = layer(X)
print(layer.__class__.__name__,'output shape:\t', X.shape)
lr, num_epochs, batch_size = 0.05, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
和上面的VGG-16一样,cpu训练效率太低,没有进行训练。
常用数据集
常见的数据集包括VOC和COCO;ImageNet较大
MNIST
MNIST 数据集
MNIST数据集是从NIST的两个手写数字数据集:Special Database 3和Special Database 1中分别取出部分图像,并经过一些图像处理后得到的,共有70000张图像,其中训练集60000张,其中30000张来NIST的Special Database 3,30000张来自NIST的Special Database 1;测试集10000张,其中5000张来自NIST的Special Database 3,5000张来自NIST的Special Database 1。所有图像都是28×28的灰度图像,每张图像包含一个手写数字。
共10个类别,每个类别代表0~9之间的一个数字,每张图像只有一个类别。
Fashion MNIST 数据集
FashionMNIST是一个替代MNIST手写数字集 的图像数据集 。它是由 Zalando旗下的研究部门提供,涵盖了来自10种类别的共7万个不同商品的正面图片。
FashionMNIST的大小 、格式和训练集/测试集划分与原始的MNIST完全一致。60000/10000的训练测试数据划分28 x 28的灰度图片。可以直接用它来测试机器学习和深度学习算法性能且不需要改动任何代码。
CIFAR 10数据集
CIFAR 10数据集由10个类的60000个32x32彩色图像组成,每个类有6000个图像。有50000个训练图像和10000个测试图像。
数据集分为五个训练批次和一个测试批次,每个批次有10000个图像。测试批次包含来自每个类别的恰好1000个随机选择的图像。
PASCAL VOC数据集
PASCAL的全称是Pattern Analysis,Statistical Modelling and Computational Learning
VOC的全称是Visual Object Classes
目标分类(识别 )、检测、分割最常用的数据集之一
第一届PASCAL VOC举办于2005年,2012年终止。常用的是PASCAL 2012
一共分成20类:
- person
- bird,cat,cow,dog,horse,sheep
- aeroplane,bicycle,boat,bus,car,motorbike,train
- bottle,chair,dining table,potted plant,sofa,tv/monitor
MS COCO数据集
PASCAL的全称是Microsoft Common Objects in Context,起源于微软于2014年出资标注的 Microsoft COCO数据集;
数据集以scene understanding为目标,主要从复杂的日常场景中截取;
包含目标分类(识别) 、检测、分割、语义标注等数据集;
ImageNet竞赛停办后,COCO竞赛就成为是当前目标识别、检测等领域的一个最权威、最重要的标杆。
提供的标注类别有80类,有超过33万张图片,其中20万张有标注,整个数据集中个体的数目超过150万个。
ImageNet数据集与ILSVRC
ImageNet数据集
始于2009年,李飞飞与 Google 的合作:“ImageNet: A Large Scale Hierarchical Image Database”
总图像数据:14,197,122
总类别数:21841
带有标记框的图像数:1,034,908
ISLVRC 2012子数据集
训练集:1,281,167张图片+标签
类别数:1,000
验证集: 50,000 张图片+标签
测试集:100,000张图片
深度学习视觉应用
评价指标
算法评估相关概念
TP:被正确地划分为正例的个数,即实际为正例且被分类器划分为正例的实例数
FP:被错误地划分为正例的个数,即实际为负例但被分类器划分为正例的实例数
FN:被错误地划分为负例的个数,即实际为正例但被分类器划分为负例的实例数
TN:被正确地划分为负例的个数,即实际为负例且被分类器划分为负例的实例数
P(精确率):TP/TP+FP
R(召回率):TP/(TP+FN)。
置信度与准确率:精度(准确率)越高,召回率越低;召回率越高,准确度越低。
目标检测与 YOLO
目标检测问题
目标检测是在给定的图片中精确找到物体所在位置,并标注出物体的类别。物体的尺寸变化范围很大,摆放物体的角度,姿态不定,而且可以出现在图片的任何地方,并且物体还可以是多个类别。
数据集输出表达
- 分类:是否有目标;有什么目标
- 定位:定位方式1 对角线顶点坐标1/对角线顶点坐标2;定位方式2 中心点坐标/长/宽
YOLO家族发展
一步法(two stage)无需候选框,直接出最终结果。
突出优点:快
目标检测基本思想
滑动窗口
滑动窗口的问题1:滑动次数太多,计算太慢
假设图片为𝑤宽,ℎ高,识别一幅图片需要𝑇时间,则需要:𝑤∗ℎ∗𝑇的总时间。
滑动窗口的问题2:目标大小不同,每一个滑动位置需要用很多框
图片宽度和高度都不相同,比例也不相同,因此需要取很多框。
滑动窗口的改进
- 一般图片中,大多数位置都不存在目标。
- 可以确定那些更有可能出现目标的位置,再有针对性的用CNN进行检测——两步法(Region Proposal)
- 两步法依然很费时
- 进一步减少出现目标的位置,而且将目标分类检测和定位问题合在一个网络里——一步法(YOLO)
一步法基本思想
简化的二分类问题:只检测一类
分成互补重叠的cell;产生问题:目标的大小和位置?
分类问题扩展为回归+分类问题
问题1:有一个框里有多个,有个多个框里有一个,怎么办?
多个框里有一个目标,取目标中心点所在框
一个框里有多个,暂不能解决
问题2:多类目标怎么办?
使用独热编码扩展类别数
问题3:小目标怎么办?
使用单独网络拟合小目标,即设置多个bounding box.
文中代码来源:《动手学深度学习》7.1 7.2 7.6