本文首发于 github,如果看完文章有所收获,一定要先点赞后收藏。毕竟,赠人玫瑰,手有余香。 最后,更多面经和干货文章,欢迎关注我的公众号-嵌入式视觉!
“kernel”)就相当于滤波器(“filter”),这两个概念是可以互换的。一个 “Kernel” 更倾向于是 2D 的权重矩阵。而 “filter” 则是指多个 kernel 堆叠的 3D 结构。如果是一个 2D 的 filter,那么两者就是一样的。但是一个3D filter,在大多数深度学习的卷积中,它是包含 kernel 的。每个卷积核都是独一无二的,主要在于强调输入通道的不同方面。
不管是TensorFlow、Keras、Caffe还是Pytorch,其卷积层和池化层的参数默认值可能有所不同,但是最终的卷积输出大小计算公式是一样的。
Kernal 大小(在 Tensorflow/keras 框架中也称为filter);Padding ;Stride;Channels。Pytorch 框架中,图片(feature map)经卷积 Conv2D 后输出大小计算公式如下:$\left \lfloor N = \frac{W-F+2P}{S}+1 \right \rfloor$,其中 $\lfloor \rfloor$ 是向下取整符号,用于结果不是整数时进行向下取整(Pytorch 的 Conv2d 卷积函数的默认参数 ceil_mode = False,即默认向下取整, dilation = 1)。
W×W(默认输入尺寸为正方形)Filter 大小 F×FSPN×Nkeras-Conv2DTranspose(pytorch-ConvTranspose2d) 后得到的特征图大小计算方式:$out = (in - 1) *s -2p + k$,还有另外一个写法:$W = (N - 1)*S - 2P + F$,可由卷积输出大小计算公式反推得到。$in$ 是输入大小, $k$ 是卷积核大小, $s$ 是滑动步长, padding 的像素数 $p$,$out$ 是输出大小。
反卷积也称为转置卷积,一般主要用来还原 feature map 的尺寸大小,在 cnn 可视化,fcn 中达到 pixel classification,以及 gan 中从特征生成图像都需要用到反卷积的操作。反卷积输出结果计算实例。例如,输入:2x2, 卷积核大小:4x4, 滑动步长:3,填充像素为 0, 输出:7x7 ,其计算过程就是, (2 - 1) * 3 + 4 = 7。
3,池化层如果设置为不填充像素(对于 Pytorch,设置参数padding = 0,对于 Keras/TensorFlow,设置参数padding="valid"),池化得到的特征图大小计算方式: $N=(W-F)/S+1$,这里公式表示的是除法结果向下取整再加 1。
总结:对于Pytorch 和 tensorflow 的卷积和池化函数,卷积函数 padding 参数值默认为 0/"valid"(即不填充),但在实际设计的卷积神经网络中,卷积层一般会填充像素(same),池化层一般不填充像素(valid),输出 shape 计算是向下取整。注意:当 stride为 1 的时候,kernel为 3、padding为 1 或者 kernel为 5、padding为 2,这两种情况可直接得出卷积前后特征图尺寸不变。
注意不同的深度学习框架,卷积/池化函数的输出 shape 计算会有和上述公式有所不同,我给出的公式是简化版,适合面试题计算,实际框架的计算比这复杂,因为参数更多。
Keras/TensorFlow 设置卷积层的过程中可以设置 padding 参数值为 “valid” 或 “same”。“valid” 代表只进行有效的卷积,对边界数据不处理。“same” 代表 TensorFlow 会自动对原图像进行补零(表示卷积核可以停留在图像边缘),也就是自动设置 padding 值让输出与输入形状相同。
channel-first)和通道在后(channel-last)的约定,常用深度学习框架使用的数据张量形状总结如下:
Pytorch/Caffe: (N, C, H, W);TensorFlow/Keras: (N, H, W, C)。Pytorch 的卷积层和池化层的输入 shape 格式为 (N, C, H, W),Keras 的卷积层和池化层的输入 shape 格式为 (N, H, W, C)。
OpenCV 读取图像后返回的矩阵 shape 的格式是 (H, W, C)格式。当 OpenCV 读取的图像为彩色图像时,返回的多通道的 BGR 格式的矩阵(HWC),在内存中的存储如下图:
注意:对于Pytorch、Keras的卷积层和池化层函数,其padding参数值都默认为不填充像素,默认值为0和valid。
class torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
二维池化层,默认输入的尺度是(N, C_in,H,W),输出尺度(N,C_out,H_out,W_out)。池化层输出尺度的 Width 默认计算公式如下(ceil_mode= True 时是向上取整,Height 计算同理):
$$\left\lfloor \frac{W_{in} + 2 * \text{padding}[0] - \text{dilation}[0] \times (\text{kernel_size}[0] - 1) - 1}{\text{stride[0]}} + 1 \right\rfloor$$
主要参数解释:
kernel_size(int or tuple):max pooling 的窗口大小。stride(int or tuple, optional):max pooling的窗口移动的步长。默认值是kernel_size`。padding(int or tuple, optional):默认值为 0,即不填充像素。输入的每一条边补充 0 的层数。dilation:滑动窗中各元素之间的距离。ceil_mode:默认值为 False,即上述公式默认向下取整,如果设为 True,计算输出信号大小的时候,公式会使用向上取整。示例代码:Pytorch中池化层默认ceil mode = false,而Caffe只实现了ceil mode= true的计算方式。
import torch
import torch.nn as nn
import torch.autograd as autograd
# 大小为3,步幅为2的正方形窗口池
m = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
# pool of non-square window
input = autograd.Variable(torch.randn(20, 16, 50, 32))
output = m(input)
print(output.shape) # torch.Size([20, 16, 25, 16])
keras.layers.MaxPooling2D(pool_size=(2, 2), strides=None, padding='valid', data_format=None)
对于 2D 空间数据的最大池化。默认输入尺寸是 (batch_size, rows, cols, channels)/(N, H, W, C_in) 的 4D 张量,默认输出尺寸是 (batch_size, pooled_rows, pooled_cols, channels) 的 4D 张量。
padding = valid:池化层输出的特征图大小为:$N=(W-F)/S+1$,这里表示的是向下取整再加 1。padding = same: 池化层输出的特征图大小为 $N = W/S$,这里表示向上取整。pool_size: 整数,或者 2 个整数表示的元组, 沿(垂直,水平)方向缩小比例的因数。(2,2)会把输入张量的两个维度都缩小一半。 如果只使用一个整数,那么两个维度都会使用同样的窗口长度。strides: 整数,2 个整数表示的元组,或者是 None。 表示步长值。 如果是 None,那么默认值是 pool_size。padding: "valid" 或者 "same"(区分大小写)。data_format: 字符串,channels_last (默认)或 channels_first 之一。 表示输入各维度的顺序。 channels_last 代表尺寸是 (batch, height, width, channels) 的输入张量, 而 channels_first 代表尺寸是 (batch, channels, height, width) 的输入张量。 默认值根据 Keras 配置文件 ~/.keras/keras.json 中的 image_data_format 值来设置。如果还没有设置过,那么默认值就是 "channels_last"。class torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
二维卷积层, 输入的尺度是(N, C_in, H, W),输出尺度(N,C_out,H_out,W_out)。卷积层输出尺度的 Weight 计算公式如下(Height 同理):
$$\left\lfloor \frac{W_{in} + 2 \times \text{padding}[0] - \text{dilation}[0] \times (\text{kernel_size}[0] - 1) - 1}{\text{stride}[0]} + 1\right\rfloor$$
Pytorch/Caffe框架输入输出数据的尺寸都是 ((N, C, H, W)),常规卷积的卷积核权重shape都为(C_out, C_in, kernel_height, kernel_width),常规卷积是这样,但是分组卷积的卷积核权重shape为(C_out, C_in/g, kernel_height, kernel_width)和DW卷积的卷积核 权重shape为(C_in, 1, kernel_height, kernel_width)。
kernel_size, stride, padding, dilation 参数可以是以下两种形式( Maxpool2D 也一样):
a single int:同样的参数值被应用与 height 和 width 两个维度。a tuple of two ints:第一个 int 值应用于 height 维度,第二个 int 值应用于 width 维度,也就是说卷积输出后的 height 和 width 值是不同的,要分别计算。in_channels(int) – 输入信号的通道。out_channels(int) – 卷积产生的通道。kerner_size(int or tuple) - 卷积核的尺寸。stride(int or tuple, optional) - 卷积步长,默认值为 1 。padding(int or tuple, optional) - 输入的每一条边补充 0 的层数,默认不填充。dilation(int or tuple, optional) – 卷积核元素之间的间距,默认取值 1 。groups(int, optional) – 从输入通道到输出通道的阻塞连接数。bias(bool, optional) - 如果 bias=True,添加偏置。###### Pytorch卷积层输出大小验证
import torch
import torch.nn as nn
import torch.autograd as autograd
# With square kernels and equal stride
# output_shape: height = (50-3)/2+1 = 24.5,卷积向下取整,所以 height=24.
m = nn.Conv2d(16, 33, 3, stride=2)
# # non-square kernels and unequal stride and with padding
# m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2)) # 输出shape: torch.Size([20, 33, 28, 100])
# # non-square kernels and unequal stride and with padding and dilation
# m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1)) # 输出shape: torch.Size([20, 33, 26, 100])
input = autograd.Variable(torch.randn(20, 16, 50, 100))
output = m(input)
print(output.shape) # 输出shape: torch.Size([20, 16, 24, 49])
keras.layers.Conv2D(filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None)
2D 卷积层 (例如对图像的空间卷积)。输入输出尺寸格式要求和池化层函数一样。输入尺寸:(N, H, W, C),卷积核尺寸:(K, K, C_in, C_out)。
当使用该层作为模型第一层时,需要提供 input_shape 参数(整数元组,不包含 batch 轴),例如,input_shape=(128, 128, 3) 表示 128x128 的 RGB 图像,在 data_format="channels_last" 时。
主要参数解释:
filters: 整数,输出空间的维度 (即卷积中滤波器的输出数量)。kernel_size: 一个整数,或者 2 个整数表示的元组或列表,指明 2D 卷积窗口的宽度和高度。 可以是一个整数,为所有空间维度指定相同的值。strides: 一个整数,或者 2 个整数表示的元组或列表,指明卷积核模板沿宽度和高度方向的移动步长。 可以是一个整数,为所有空间维度指定相同的值。 指定任何 stride 值 != 1 与指定 dilation_rate 值 != 1 两者不兼容,默认取值 1,即代表会不遗漏的滑过输入图片(Feature Map)的每一个点。padding: "valid" 或 "same" (大小写敏感),默认valid,这里的 "same" 代表给边界加上 Padding 让卷积的输出和输入保持同样("same")的尺寸(即填充像素)。data_format: 字符串, channels_last (默认) 或 channels_first 之一,表示输入中维度的顺序。 channels_last 对应输入尺寸为 (batch_size, height, width, channels), channels_first 对应输入尺寸为 (batch_size, channels, height, width)。 它默认为从 Keras 配置文件 ~/.keras/keras.json 中 找到的 image_data_format 值。 如果你从未设置它,将使用 channels_last。dilation_rate: 一个整数或 2 个整数的元组或列表, 指定膨胀卷积(空洞卷积 dilated convolution)的膨胀率。 可以是一个整数,为所有空间维度指定相同的值。 当前,指定任何 dilation_rate 值 != 1 与 指定 stride 值 != 1 两者不兼容。Pytorch 的 Conv2d 函数不要求提供 输入数据的大小 (H,W),但是要提供输入深度,Keras 的 Conv2d 函数第一层要求提供 input_shape 参数 (H,W, C),其他层不需要。
softmax 回归解决了以上两个问题,它将输出值变换为值为正且和为 1 的概率分布,公式如下:
$$
softmax(y){i} = y{i}^{'} = \frac{e^{yi}}{\sum_{j=1}^{n}e^{yj}}
$$
softmax 回归一起使用,公式如下:
$$L = -\sum_{c=1}^{M}y_{c}log(p_{c})或者H(p,q)=-\sum p(x)logq(x)$$
0 或 1),如果该类别和样本的类别相同就是 1,否则是 0;KL 散度,而因为训练数据的分布是固定的,因此最小化 KL 散度等价于最小化交叉熵,而且交叉熵计算更简单,所以机器/深度学习中常用交叉熵 cross-entroy 作为分类问题的损失函数。
Adam、AdaGrad、RMSProp优化算法具有自适应性。
Receptive Field)是指后一层神经元在前一层神经元的感受空间,也可以定义为卷积神经网络中每层的特征图(Feature Map)上的像素点在原始图像中映射的区域大小,即如下图所示:
注意:感受野在 CNN 中是呈指数级增加的。小卷积核(如 3*3)通过多层叠加可取得与大卷积核(如 7*7)同等规模的感受野,此外采用小卷积核有两个优势:
model capacity)和复杂度(model complexity)。参考 感受野(receptive file)计算
top to down),这里只讲解后者。正常卷积(且不带 padding)感受野计算公式如下:
$$F(i, j-1) = (F(i, j)-1)*stride + kernel_size$$
其中 $F(i, j)$ 表示第 i 层对第 j 层的局部感受野,所以这个公式是从上层向下层计算感受野的。仔细看这个公式会发现和反卷积输出大小计算公式一模一样,实际上感受野计算公式就是 feature_map 计算公式的反向推导。
以下 Python 代码可以实现计算 Alexnet zf-5 和 VGG16 网络每层输出 feature map 的感受野大小,卷积核大小和输入图像尺寸默认定义好了,代码如下:
# !/usr/bin/env python
# [filter size, stride, padding]
net_struct = {'alexnet': {'net':[[11,4,0],[3,2,0],[5,1,2],[3,2,0],[3,1,1],[3,1,1],[3,1,1],[3,2,0]],
'name':['conv1','pool1','conv2','pool2','conv3','conv4','conv5','pool5']},
'vgg16': {'net':[[3,1,1],[3,1,1],[2,2,0],[3,1,1],[3,1,1],[2,2,0],[3,1,1],[3,1,1],[3,1,1],
[2,2,0],[3,1,1],[3,1,1],[3,1,1],[2,2,0],[3,1,1],[3,1,1],[3,1,1],[2,2,0]],
'name':['conv1_1','conv1_2','pool1','conv2_1','conv2_2','pool2','conv3_1','conv3_2',
'conv3_3', 'pool3','conv4_1','conv4_2','conv4_3','pool4','conv5_1','conv5_2','conv5_3','pool5']},
'zf-5':{'net': [[7,2,3],[3,2,1],[5,2,2],[3,2,1],[3,1,1],[3,1,1],[3,1,1]],
'name': ['conv1','pool1','conv2','pool2','conv3','conv4','conv5']}}
def outFromIn(isz, net, layernum):
"""
计算feature map大小
"""
totstride = 1
insize = isz
# for layer in range(layernum):
fsize, stride, pad = net[layernum]
outsize = (insize - fsize + 2*pad) / stride + 1
insize = outsize
totstride = totstride * stride
return outsize, totstride
def inFromOut(net, layernum):
"""
计算感受野receptive file大小
"""
RF = 1
for layer in reversed(range(layernum)): # reversed 函数返回一个反向的迭代器
fsize, stride, pad = net[layer]
RF = ((RF -1)* stride) + fsize
return RF
if __name__ == '__main__':
imsize = 224
feature_size = imsize
print ("layer output sizes given image = %dx%d" % (imsize, imsize))
for net in net_struct.keys():
feature_size = imsize
print ('************net structrue name is %s**************'% net)
for i in range(len(net_struct[net]['net'])):
feature_size, stride = outFromIn(feature_size, net_struct[net]['net'], i)
rf = inFromOut(net_struct[net]['net'], i+1)
print ("Layer Name = %s, Output size = %3d, Stride = % 3d, RF size = %3d" % (net_struct[net]['name'][i], feature_size, stride, rf))
程序输出结果如下:
layer output sizes given image = 224x224
************net structrue name is alexnet**************
Layer Name = conv1, Output size = 54, Stride = 4, RF size = 11
Layer Name = pool1, Output size = 26, Stride = 2, RF size = 19
Layer Name = conv2, Output size = 26, Stride = 1, RF size = 51
Layer Name = pool2, Output size = 12, Stride = 2, RF size = 67
Layer Name = conv3, Output size = 12, Stride = 1, RF size = 99
Layer Name = conv4, Output size = 12, Stride = 1, RF size = 131
Layer Name = conv5, Output size = 12, Stride = 1, RF size = 163
Layer Name = pool5, Output size = 5, Stride = 2, RF size = 195
************net structrue name is vgg16**************
Layer Name = conv1_1, Output size = 224, Stride = 1, RF size = 3
Layer Name = conv1_2, Output size = 224, Stride = 1, RF size = 5
Layer Name = pool1, Output size = 112, Stride = 2, RF size = 6
Layer Name = conv2_1, Output size = 112, Stride = 1, RF size = 10
Layer Name = conv2_2, Output size = 112, Stride = 1, RF size = 14
Layer Name = pool2, Output size = 56, Stride = 2, RF size = 16
Layer Name = conv3_1, Output size = 56, Stride = 1, RF size = 24
Layer Name = conv3_2, Output size = 56, Stride = 1, RF size = 32
Layer Name = conv3_3, Output size = 56, Stride = 1, RF size = 40
Layer Name = pool3, Output size = 28, Stride = 2, RF size = 44
Layer Name = conv4_1, Output size = 28, Stride = 1, RF size = 60
Layer Name = conv4_2, Output size = 28, Stride = 1, RF size = 76
Layer Name = conv4_3, Output size = 28, Stride = 1, RF size = 92
Layer Name = pool4, Output size = 14, Stride = 2, RF size = 100
Layer Name = conv5_1, Output size = 14, Stride = 1, RF size = 132
Layer Name = conv5_2, Output size = 14, Stride = 1, RF size = 164
Layer Name = conv5_3, Output size = 14, Stride = 1, RF size = 196
Layer Name = pool5, Output size = 7, Stride = 2, RF size = 212
************net structrue name is zf-5**************
Layer Name = conv1, Output size = 112, Stride = 2, RF size = 7
Layer Name = pool1, Output size = 56, Stride = 2, RF size = 11
Layer Name = conv2, Output size = 28, Stride = 2, RF size = 27
Layer Name = pool2, Output size = 14, Stride = 2, RF size = 43
Layer Name = conv3, Output size = 14, Stride = 1, RF size = 75
Layer Name = conv4, Output size = 14, Stride = 1, RF size = 107
Layer Name = conv5, Output size = 14, Stride = 1, RF size = 139
卷积核池化的定义核过程理解是不难的,但是其作用却没有一个标准的答案,我在网上看了众多博客和魏秀参博士的书籍,总结了以下答案。卷积层和池化层的理解可参考魏秀参的《解析卷积神经网络》书籍,卷积(
convolution )操作的作用如下:
局部感知,参数共享 的特点大大降低了网络参数,保证了网络的稀疏性。pooling )操作作用如下:
CNN 具有局部连接、权值共享、池化操作(简单说就是下采样)和多层次结构的特点。
translation invariant),且可以学到模式的空间层次结构。数据增强, 增加数据多样性;L1, L2正则化;dropout;BN ,batch normalization;80% 用于训练集,20% 用于验证集(训练集和验证集一定不能相交);训练都时候每隔一定 Epoch 比较验证集但指标和训练集是否一致,如果不一致,并且变坏了,那么意味着过拟合。learning curve 来判别过拟合,参考此博客。underfitting 欠拟合的表现就是模型不收敛,原因有很多种,这里以神经网络拟合能力不足问题给出以下参考解决方法:
he_normal,深度学习框架都内置了很多权重初始化方法;ReLu,循环神经网络中的循环层使用的激活函数一般为 tanh,或者 ReLu;SGD 优化器速度慢但是会达到最优.loss 值依然很大甚至与初始值没有太大区别,且精度很低,测试集亦如此。根据我的总结,原因一般有以下几种:
L1 norm)是指向量中各个元素绝对值之和,也有个美称叫“稀疏规则算子”(Lasso regularization)。 比如 向量 A=[1,-1,3], 那么 A 的 L1 范数为 |1|+|-1|+|3|。简单总结一下就是:
L1 范数可以使权值参数稀疏,方便特征提取。 L2 范数可以防止过拟合,提升模型的泛化能力。
Tensorflow 是一个通过计算图的形式来表述计算的编程系统,计算图也叫数据流图,可以把计算图看做是一种有向图,Tensorflow 中的每一个计算都是计算图上的一个节点,而节点之间的边描述了计算之间的依赖关系。
scale 不一致,实际上每层需要的学习率是不一样的,同一层不同维度的 scale 往往也需要不同大小的学习率,通常需要使用最小的那个学习率才能保证损失函数有效下降,Batch Normalization 将每层、每维的 scale 保持一致,那么我们就可以直接使用较高的学习率进行优化。
(2). 移除或使用较低的 dropout。 dropout 是常用的防止 overfitting 的方法,而导致 overfitting 的位置往往在数据边界处,如果初始化权重就已经落在数据内部,overfitting现象就可以得到一定的缓解。论文中最后的模型分别使用10%、5%和0%的dropout训练模型,与之前的 40%-50% 相比,可以大大提高训练速度。
(3). 降低 L2 权重衰减系数。 还是一样的问题,边界处的局部最优往往有几维的权重(斜率)较大,使用 L2 衰减可以缓解这一问题,现在用了 Batch Normalization,就可以把这个值降低了,论文中降低为原来的 5 倍。
(4). 代替Local Response Normalization层。 由于使用了一种 Normalization,再使用 LRN 就显得没那么必要了。而且 LRN 实际上也没那么 work。
(5). Batch Normalization调整了数据的分布,不考虑激活函数,它让每一层的输出归一化到了均值为0方差为1的分布,这保证了梯度的有效性,可以解决反向传播过程中的梯度问题。目前大部分资料都这样解释,比如 BN 的原始论文认为的缓解了 Internal Covariate Shift(ICS) 问题。
关于训练阶段和推理阶段 BN 的不同可以参考 Batch Normalization详解 。
BP 算法中的链式求导逐层传递逐层减小,最后趋近于0,导致对某些层的训练失效;BP 算法中的链式求导逐层传递逐层增大,最后趋于无穷,导致某些层无法收敛;在反向传播过程中需要对激活函数进行求导,如果导数大于 1,那么随着网络层数的增加,梯度更新将会朝着指数爆炸的方式增加这就是梯度爆炸。同样如果导数小于 1,那么随着网络层数的增加梯度更新信息会朝着指数衰减的方式减少这就是梯度消失。
relu、leakrelu 等激活函数BN 批归一化xavierCNN 中的残差结构LSTM 结构RNN 可以看做作是同一神经网络结构在时间序列上被复制多次的结果,这个被复制多次的结构称为循环体,如何设计循环体的网络结构是 RNN 解决实际问题的关键。RNN 的输入有两个部分,一部分为上一时刻的状态,另一部分为当前时刻的输入样本。
(2). 更多的非线性变换。2 个 3×3 卷积层拥有比 1 个 5×5 卷积层更多的非线性变换(前者可以使用两次 ReLU 激活函数,而后者只有一次),使得卷积神经网络对特征的学习能力更强。
paper中给出的相关解释:三个这样的层具有 7×7 的有效感受野。那么我们获得了什么?例如通过使用三个 3×3 卷积层的堆叠来替换单个 7×7 层。首先,我们结合了三个非线性修正层,而不是单一的,这使得决策函数更具判别性。其次,我们减少参数的数量:假设三层 3×3 卷积堆叠的输入和输出有 C 个通道,堆叠卷积层的参数为 3×(3×3C) = 27C 个权重;同时,单个 7×7 卷积层将需要 7×7×C = 49C 个参数,即参数多 81%。这可以看作是对 7×7 卷积滤波器进行正则化,迫使它们通过 3×3 滤波器(在它们之间注入非线性)进行分解。
此回答可以参考 TensorFlow 实战 p110,网上很多回答都说的不全。
Sigmoid 函数公式如下:
$\sigma (x)=\frac{1}{1+exp(-x)}$
ReLU激活函数公式如下:
$$f(x) = max(x, 0) =
\begin{cases}
x, & \text{if $x$ $\geq$ 0} \
0, & \text{if $x$ < 0}
\end{cases}$$
ReLU 的输出要么是 0, 要么是输入本身。虽然方程简单,但实际上效果更好。在网上看了很多版本的解释,有从程序实例分析也有从数学上分析,我找了个相对比较直白的回答,如下:
ReLU 函数计算简单,可以减少很多计算量。反向传播求误差梯度时,涉及除法,计算量相对较大,采用 ReLU 激活函数,可以节省很多计算量;sigmoid 函数反向传播时,很容易就会出现梯度消失问题(在sigmoid接近饱和区时,变换太缓慢,导数趋于 0,这种情况会造成信息丢失),从而无法完成深层网络的训练。ReLU 会使一部分神经元的输出为 0,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生。sigmoid 型函数,ReLU 函数有助于随机梯度下降方法收敛。dropout效果跟bagging效果类似(bagging是减少方差variance,而boosting是减少偏差bias)
(2). 在测试模型阶段
预测模型的时候,输入是当前输入,每个神经单元的权重参数要乘以概率p。
HOG 的核心思想是所检测的局部物体外形能够被光强梯度或边缘方向的分布所描述。通过将整幅图像分割成小的连接区域(称为cells),每个 cell 生成一个方向梯度直方图或者 cell 中pixel 的边缘方向,这些直方图的组合可表示(所检测目标的目标)描述子。
为改善准确率,局部直方图可以通过计算图像中一个较大区域(称为block)的光强作为 measure 被对比标准化,然后用这个值(measure)归一化这个 block 中的所有 cells 。这个归一化过程完成了更好的照射/阴影不变性。与其他描述子相比,HOG 得到的描述子保持了几何和光学转化不变性(除非物体方向改变)。因此HOG描述子尤其适合人的检测。
HOG 特征提取方法就是将一个image:
cells(2*2)
总结:颜色空间归一化——>梯度计算——>梯度方向直方图——>重叠块直方图归一化———>HOG特征
ReLU 函数、sigmoid 函数、tanh 函数等,其计算公式如下:
Sigmoid 激活函数,可用作分类任务的输出层。可以看出经过sigmoid激活函数后,模型输出的值域被压缩到 0到1 区间(这和概率的取值范围一致),这正是分类任务中sigmoid很受欢迎的原因。
tanh(x) 型函数是在 sigmoid 型函数基础上为解决均值问题提出的激活函数。tanh 的形状和 sigmoid 类似,只不过tanh将“挤压”输入至区间(-1, 1)。至于梯度,它有一个大得多的峰值1.0(同样位于z = 0处),但它下降得更快,当|x|的值到达 3 时就已经接近零了。这是所谓梯度消失(vanishing gradients)问题背后的原因,会导致网络的训练进展变慢。
ReLU 处理了sigmoid、tanh中常见的梯度消失问题,同时也是计算梯度最快的激励函数。但是,ReLU函数也有自身缺陷,即在 x < 0 时,梯度便为 y。换句话说,对于小于 y 的这部分卷积结果响应,它们一旦变为负值将再无法影响网络训练——这种现象被称作“死区"。
cross entroy)是常用的评判方法之一。交叉熵刻画了两个概率分布之间的距离,是分类问题中使用比较广泛的一种损失函数。给定两个概率分布 p 和 q ,通过 q 来表示 p 的交叉熵公式为:$H(p,q)=−∑p(x)logq(x)$
Fliplr,Flipud。不同于旋转180度,这是类似镜面的翻折,跟人在镜子中的映射类似,常用水平、上下镜面翻转。rotate。顺时针/逆时针旋转,最好旋转 90-180 度,否则会出现边缘缺失或者超出问题,如旋转 45 度。zoom。图像可以被放大或缩小,imgaug库可用Scal函数实现。crop。一般叫随机裁剪,操作步骤是:随机从图像中选择一部分,然后降这部分图像裁剪出来,然后调整为原图像的大小。根本上理解,图像crop就是指从图像中移除不需要的信息,只保留需要的部分translation。平移是将图像沿着x或者y方向(或者两个方向)移动。我们在平移的时候需对背景进行假设,比如说假设为黑色等等,因为平移的时候有一部分图像是空的,由于图片中的物体可能出现在任意的位置,所以说平移增强方法十分有用。Affine。包含:平移(Translation)、旋转(Rotation)、放缩(zoom)、错切(shear)。imgaug 图像增强库使用 GaussianBlur函数。Sharpen。imgaug库使用Sharpen函数。faster rcnn 将 roi pooling 替换为 roi align 效果有所提升。
RPN 生成的 ROI 区域大小是对应与输入图像大小(每个roi区域大小各不相同),为了能够共享权重,所以需要将这些 ROI 映射回特征图上,并固定大小。ROI Pooling操作过程如下图:
ROI Pooling 具体操作如下:
Conv layers 使用的是 VGG16,feat_stride=32(即表示,经过网络层后图片缩小为原图的 1/32),原图 $800\times 800$,最后一层特征图 feature map 大小: $25\times 25$region proposal,大小为 $665\times 665$,这样,映射到特征图中的大小:$665/32=20.78$,即大小为 $20.78\times 20.78$,源码中,在计算的时候会进行取整操作,于是,进行所谓的第一次量化,即映射的特征图大小为20*20;pooled_w=7、pooled_h=7,即 pooling 后固定成 $7\times 7$ 大小的特征图,所以,将上面在 feature map上映射的 $20\times 20$ 的 region proposal 划分成 49个同等大小的小区域,每个小区域的大小 $20/7=2.86$,,即 $2.86\times 2.86$,此时,进行第二次量化,故小区域大小变成 $2\times 2$;feature map。region proposal,偏差成大小为 $7\times 7$ 的,这样的像素偏差势必会对后层的回归定位产生影响。所以,产生了后面的替代方案: RoiAlign。
ROI Align 的工作原理如下图。
ROI Align 是在 Mask RCNN 中使用以便使生成的候选框region proposal 映射产生固定大小的 feature map 时提出的,根据上图,有着类似的映射:
Conv layers 使用的是 VGG16,feat_stride=32 (即表示,经过网络层后图片缩小为原图的1/32),原图 $800\times 800$,最后一层特征图feature map大小: $25*\times 25$;RoiPooling 那样就行取整操作,保留浮点数;pooled_w=7,pooled_h=7,即 pooling 后固定成 $7\times 7$ 大小的特征图,所以,将在 feature map 上映射的 $20.78\times 20.78$ 的 region proposal 划分成 49 个同等大小的小区域,每个小区域的大小 $20.78/7=2.97$,即 $2.97\times 2.97$;4,即表示,对于每个 $2.97\times 2.97$ 的小区域,平分四份,每一份取其中心点位置,而中心点位置的像素,采用双线性插值法进行计算,这样,就会得到四个点的像素值,如下图:
上图中,四个红色叉叉‘×’的像素值是通过双线性插值算法计算得到的。
最后,取四个像素值中最大值作为这个小区域(即:$2.97\times 2.97$ 大小的区域)的像素值,如此类推,同样是 49 个小区域得到 49 个像素值,组成 $7\times 7$ 大小的 feature map。
RoiPooling 和 RoiAlign 实现原理,在以后的项目中可以根据实际情况进行方案的选择;对于检测图片中大目标物体时,两种方案的差别不大,而如果是图片中有较多小目标物体需要检测,则优先选择 RoiAlign,更精准些。
Focal Loss 被提出来了,即:
$$
CE = \left{\begin{matrix}
-\alpha (1-p)^\gamma log(p), & if \quad y=1\
-(1-\alpha) p^\gamma log(1-p), & if\quad y=0
\end{matrix}\right.
$$
实验表明 $\gamma$ 取 2, $\alpha$ 取 0.25 的时候效果最佳。
Focal loss 成功地解决了在单阶段目标检测时,正负样本区域极不平衡而目标检测 loss 易被大批量负样本所左右的问题。RetinaNet 达到更高的精度的原因不是网络结构的创新,而是损失函数的创新!conv layers(VGG/ResNet) 权重。在所有的 RoIs 都被 pooling 成(512×7×7)的feature map后,将它 reshape 成一个一维的向量,就可以利用 VGG16 的预训练的权重来初始化前两层全连接。
nearest)、双线性插值算法(bilinear)、双三次插值算法(bicubic)等,这是传统图像处理方法。Conv2dTranspose2d等)Unpooling 的方法(简单的补零或者扩充操作)计算效果:最近邻插值算法 < 双线性插值 < 双三次插值。计算速度:最近邻插值算法 > 双线性插值 > 双三次插值
scale and shift 操作则是为了让因训练所需而“刻意”加入的BN能够有可能还原最初的输入。不加也可以。
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal
目录第1题连续问题分析:解法:第2题分组问题分析:解法:第3题间隔连续问题分析:解法:第4题打折日期交叉问题分析:解法:第5题同时在线问题分析:解法:第1题连续问题如下数据为蚂蚁森林中用户领取的减少碳排放量iddtlowcarbon10012021-12-1212310022021-12-124510012021-12-134310012021-12-134510012021-12-132310022021-12-144510012021-12-1423010022021-12-154510012021-12-1523.......找出连续3天及以上减少碳排放量在100以上的用户分析:遇到这类
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
如何学习ruby的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/
深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG
文章目录1、自相关函数ACF2、偏自相关函数PACF3、ARIMA(p,d,q)的阶数判断4、代码实现1、引入所需依赖2、数据读取与处理3、一阶差分与绘图4、ACF5、PACF1、自相关函数ACF自相关函数反映了同一序列在不同时序的取值之间的相关性。公式:ACF(k)=ρk=Cov(yt,yt−k)Var(yt)ACF(k)=\rho_{k}=\frac{Cov(y_{t},y_{t-k})}{Var(y_{t})}ACF(k)=ρk=Var(yt)Cov(yt,yt−k)其中分子用于求协方差矩阵,分母用于计算样本方差。求出的ACF值为[-1,1]。但对于一个平稳的AR模型,求出其滞
写在之前Shader变体、Shader属性定义技巧、自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用,方便后续回顾查看,如有以偏概全、不祥不尽之处,还望海涵。1、Shader变体先看一段代码......Properties{ [KeywordEnum(on,off)]USL_USE_COL("IsUseColorMixTex?",int)=0 [Toggle(IS_RED_ON)]_IsRed("IsRed?",int)=0}......//中间省略,后续会有完整代码 #pragmamulti_c