3.卷积神经网络基础
- VGG-16网络
VGG-16的网络结构:
- 网络改进
- 网络规模进一步增大,参数数量约为1.38亿。
- 由于各卷积层,池化层的超参数基本相同,整体结构呈现出规整的特点
- 普遍规律
- 随着网络深入,高和宽衰减,通道数增多。
VGG-16的网络结构实现代码如下所示
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)
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))
conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))
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())
VGG训练结果如下:
- 残差网络
- 为什么需要残差网络
(1)对于非残差网络,层数的增加是有上限的,随着层数的增加训练误差不增反降。
![](https://img-blog.csdnimg.cn/direct/a5c0443e39ba4b2babe657368f53b9a0.png#pic_center)
而残差网络能够做到随着层数的增加,训练误差逐渐减少。
![](https://img-blog.csdnimg.cn/direct/4734a7580b7b4251af77767abf62cc7e.png#pic_center)
对于如下神经网络:
根据链式法则,给出
∂
C
/
∂
b
1
\partial C/\partial {b_1}
∂C/∂b1的具体表达式:
∂ C ∂ b 1 = σ ′ ( z 1 ) × w 2 × σ ′ ( z 2 ) × w 3 × σ ′ ( z 3 ) × w 4 × σ ′ ( z 4 ) × ∂ C ∂ a 4 \frac{{\partial C}}{{\partial {b_1}}} = \sigma '({z_1}) \times {w_2} \times \sigma '({z_2}) \times {w_3} \times \sigma '({z_3}) \times {w_4} \times \sigma '({z_4}) \times \frac{{\partial C}}{{\partial {a_4}}} ∂b1∂C=σ′(z1)×w2×σ′(z2)×w3×σ′(z3)×w4×σ′(z4)×∂a4∂C
对于sigmoid函数 σ \sigma σ,其导数 σ ′ ( 0 ) = 1 / 4 \sigma '({0})=1/4 σ′(0)=1/4时达到最高。如果我们使用标准方法来初始化网络的权重,那么会使用一个均值为0标准差为1的高斯分布。因此所有的权重通常都会满足 ∣ w j ∣ < 1 \left| {{w_j}} \right| < 1 ∣wj∣<1,若对权重项进行乘积,最终的结果肯定会指数级下降,即出现梯度消失问题。
- 残差块的网络结构
- 残差网络的特点
- 普通网络的基准模型受VGG网络的启发
- 卷积层主要有3*3的过滤器,并遵循两个简单的设计规则:(1)对输出特征图的尺寸相同的各层,都有相同数量的过滤器;(2)如果特征图的大小减半,那么过滤器的数量就增加一倍,以保证每一层的时间复杂度相同
- ResNet模型比VGG网络更少的过滤器和更低的复杂性。ResNet具有34层权重层,有36亿FLOPs,知识VGG的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
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))
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())
3.常用数据集
-
MNIST数据集
MNIST数据集主要由一些手写数字的图片和相应的标签组成,图片一共有10类,分别对应从0~ -
CIFAR-10数据集
- CIFAR-10数据集由10个类的60000个32×32彩色图像组成,每个类有6000个图像。
- 数据集分为五个训练批次和一个测试批次,每个批次有10000个图像。
-
PASCAL VOC数据集
- 是目标分类 识别 、检测、分割最常用的数据集之一。
- 第一届PASCAL VOC举办于2005年,2012年终止。常用的是PASCAL 2012
- 数据集中对应的是XML标注信息
-
MS COCO数据集
- 数据集以scene understanding为目标,主要从复杂的日常场景中截取
- 包含目标分类识别 、检测、分割、语义标注等数据集
- 提供的标注类别有80类,有超过33万张图片,其中20万张有标注,整个数据集中个体的数目超过150万个。
-
ImageNet数据集
- 总图像数据:14197122,总类别数21841,带有标记框的图像数:1034908
-
ISLVRC 2012子数据集
- 训练集:1281167张图片标签
- 类别数:1000
- 验证集:50000张图片标签
- 测试集:100000张图片
4.深度学习视觉应用
4.1数据集与评价指标
- 算法评估相关概念
-
TP:被正确地划分为正例的个数,即实际为正例且被分类器划分为正例的实例数
-
FP:被错误地划分为正例的个数,即实际为负例但被分类器划分为正例的实例数
-
FN:被错误地划分为负例的个数,即实际为正例但被分类器划分为负例的实例数
-
TN:被正确地划分为负例的个数,即实际为负例且被分类器划分为负例的实例数
得到混淆矩阵:
预测值=1 | 预测值=0 | |
---|---|---|
真实值=1 | TP | FN |
真实值=0 | FP | TN |
其中具体指标:
-
P精确率:标识“挑剔”的程度
P = T P T P + F P P = \frac{{TP}}{{TP + FP}} P=TP+FPTP -
R召回率:召回率越高,准确度越低,标识“通过”的程度
R = T P T P + F N R = \frac{{TP}}{{TP + FN}} R=TP+FNTP -
Accuracy精度:对算法的综合评估指标
A c c u r a c y = T P + T N T P + F P + T N + F N Accuracy = \frac{{TP + TN}}{{TP + FP + TN + FN}} Accuracy=TP+FP+TN+FNTP+TN -
P-R曲线:表示了召回率和准确率之间的关系。具体曲线如下所示:
-
mAP:均值平均准确率
A P = ∑ k = 1 N P ( k ) Δ r ( k ) AP = \sum\limits_{k = 1}^N {P(k)\Delta r(k)} AP=k=1∑NP(k)Δr(k)
4.2目标检测与YOLO
目标检测是在给定的图片中精确找到物体所在位置,并标注出物体的类别。
目标检测的发展:
- R-CNN
- SPP NET
- Fast R-CNN
- Faster R-CNN
- YOLO
- 目标检测问题
目标检测的目标是检测出图中存在的目标以及该目标在图中的什么位置
目标检测问题的解决方案:
- 目标检测的基本思想
滑动窗口存在的问题:
- 滑动次数太多,计算太慢
- 目标大小不同,每一个滑动位置需要用很多框
对于滑动窗口存在的问题作出改进:
-
一般图片中,大多数位置都不存在目标。可以确定那些更有可能出现目标的位置,再有针对性的用CNN进行检测
-
进一步减少出现目标的位置,而且将目标分类检测和定位问题合在一个网络里
将目标检测问题转化为多个二分类问题。即只检测是否为这一类目标。使用独热编码扩展类别数。
4.3YOLO的网络结构
yolo的网络结构包含24个卷积层和2个全连接层;其中前20个卷积层用来做预训练,后面4个是随机初始化的卷积层,和2个全连接层。
YOLO对输入的图片进行了7×7的网格划分:
- 将图片分割为 S 2 {S^2} S2个grid(𝑆=7),每个grid cell的大小都是相等的
- 每个格子都可以检测是否包含目标
- YOLO v1中,每个格子只能检测一种物体(但可以不同大小)。
YOLO的网络输出为一个7×7×30的张量,对应着7×7个cell
- 每个cell对应2个包围框(bounding box, bb),预测不同大小和宽高比对应检测不同目标。每个bb有5个分量,分别是物体的中心位置 (𝑥,𝑦)和它的高(ℎ)和宽(𝑤),以及这次预测的置信度。在下图中,每个框代表1个预测的bb,粗细代表不同的置信度,越粗得越高。
4.4包围框和置信度
包围框包含四个关于位置的值,四个关于位置的值,分别是 x , y , h x,y,h x,y,h和 w w w,均为整数,实际预测中收敛慢因此,需要对数据进行归一化,0-1之间。置信度的计算公式为 C = Pr ( o b j ) × I O U t r u t h p r e d C = \Pr (obj) \times IOU_{truth}^{pred} C=Pr(obj)×IOUtruthpred
- P r ( o b j ) Pr(obj) Pr(obj)是一个grid物体的概率。
- I O U IOU IOU是预测的bb和真实的物体位置的交并比。
4.5 yolo损失函数
yolo损失函数一共有五项:
4.6训练与NMS
非极大值抑制NMS算法要点:
- 首先丢弃概率小于预定IOU阈值(例如0.5)的所有边界框;对于剩余的边界框。
- 选择具有最高概率的边界框并将其作为输出预测。
- 计算 “作为输出预测的边界框”,与其他边界框的相关联IoU值;舍去IoU大于阈值的边界框;其实就是舍弃与“作为输出预测的边界框” 很相近的框。
- 重复步骤2,直到所有边界框都被视为输出预测或被舍弃。
预训练与训练:
- YOLO先使用ImageNet数据集对前20层卷积网络进行预训练,然后使用完整的网络,在PASCAL VOC数据集上进行对象识别和定位的训练和预测。
- 训练中采用了drop out和数据增强来防止过拟合。
- YOLO的最后一层采用线性激活函数(因为要回归bb位置),其它层都是采用Leaky ReLU激活函数:
ϕ ( x ) = { ∗ 20 c x , i f x > 0 0.1 x , o t h e r w i s e \phi (x) = \left\{ {\begin{matrix}{*{20}{c}} {x,}&{{\rm{if}}x > 0}\\ {0.1x,}&{otherwise} \end{matrix}} \right. ϕ(x)={∗20cx,0.1x,ifx>0otherwise
5.语义分割与FCN
5.1 语义分割与FCN的基本思想
语义分割:找到同一画面中的不同类型目标区域
与目标检测区别:
- 实例分割:同一类型目标要分出来具体实例
- 目标检测:标出来外包围矩形
语义分割目标:对图中每一个像素进行分类,得到对应标签
基本思想:滑动窗口
FCN网络结构的分为两个部分:全卷积部分和反卷积部分。全卷积部分借用了一些经典的CNN网络,并把最后的全连接层换成卷积,用于提取特征,形成热点图;反卷积部分则是将小尺寸的热点图上采样得到原尺寸的语义分割图像。
5.2 FCN的具体实现
- 卷积部分
FCN中第6、7、8层都是通过1×1卷积得到的第6层的输出通道是4096第7层的输出通是4096第8层的输出是1000类即1000个特征图称为heatmap
- 反卷积部
使用了跳级结构。图中蓝色表示卷积层,绿色表示Max Pooling层,黄色表示求和运算,灰色表示裁剪。