https://www.nowcoder.com/discuss/205843?type=2
1.IOU计算
求出左上角的最大值,右下角的最小值。
# -*- coding: utf-8 -*-
#
# This is the python code for calculating bbox IoU,
# By running the script, we can get the IoU score between pred / gt bboxes
#
# Author: hzhumeng01 2018-10-19
# copyright @ netease, AI group
from __future__ import print_function, absolute_import
import numpy as np
def get_IoU(pred_bbox, gt_bbox):
"""
return iou score between pred / gt bboxes
:param pred_bbox: predict bbox coordinate
:param gt_bbox: ground truth bbox coordinate
:return: iou score
"""
# bbox should be valid, actually we should add more judgements, just ignore here...
# assert ((abs(pred_bbox[2] - pred_bbox[0]) > 0) and
# (abs(pred_bbox[3] - pred_bbox[1]) > 0))
# assert ((abs(gt_bbox[2] - gt_bbox[0]) > 0) and
# (abs(gt_bbox[3] - gt_bbox[1]) > 0))
# -----0---- get coordinates of inters
ixmin = max(pred_bbox[0], gt_bbox[0])
iymin = max(pred_bbox[1], gt_bbox[1])
ixmax = min(pred_bbox[2], gt_bbox[2])
iymax = min(pred_bbox[3], gt_bbox[3])
iw = np.maximum(ixmax - ixmin + 1., 0.)
ih = np.maximum(iymax - iymin + 1., 0.)
# -----1----- intersection
inters = iw * ih
# -----2----- union, uni = S1 + S2 - inters
uni = ((pred_bbox[2] - pred_bbox[0] + 1.) * (pred_bbox[3] - pred_bbox[1] + 1.) +
(gt_bbox[2] - gt_bbox[0] + 1.) * (gt_bbox[3] - gt_bbox[1] + 1.) -
inters)
# -----3----- iou
overlaps = inters / uni
return overlaps
def get_max_IoU(pred_bboxes, gt_bbox):
"""
given 1 gt bbox, >1 pred bboxes, return max iou score for the given gt bbox and pred_bboxes
:param pred_bbox: predict bboxes coordinates, we need to find the max iou score with gt bbox for these pred bboxes
:param gt_bbox: ground truth bbox coordinate
:return: max iou score
"""
# bbox should be valid, actually we should add more judgements, just ignore here...
# assert ((abs(gt_bbox[2] - gt_bbox[0]) > 0) and
# (abs(gt_bbox[3] - gt_bbox[1]) > 0))
if pred_bboxes.shape[0] > 0:
# -----0---- get coordinates of inters, but with multiple predict bboxes
ixmin = np.maximum(pred_bboxes[:, 0], gt_bbox[0])
iymin = np.maximum(pred_bboxes[:, 1], gt_bbox[1])
ixmax = np.minimum(pred_bboxes[:, 2], gt_bbox[2])
iymax = np.minimum(pred_bboxes[:, 3], gt_bbox[3])
iw = np.maximum(ixmax - ixmin + 1., 0.)
ih = np.maximum(iymax - iymin + 1., 0.)
# -----1----- intersection
inters = iw * ih
# -----2----- union, uni = S1 + S2 - inters
uni = ((gt_bbox[2] - gt_bbox[0] + 1.) * (gt_bbox[3] - gt_bbox[1] + 1.) +
(pred_bboxes[:, 2] - pred_bboxes[:, 0] + 1.) * (pred_bboxes[:, 3] - pred_bboxes[:, 1] + 1.) -
inters)
# -----3----- iou, get max score and max iou index
overlaps = inters / uni
ovmax = np.max(overlaps)
jmax = np.argmax(overlaps)
return overlaps, ovmax, jmax
if __name__ == "__main__":
# test1
pred_bbox = np.array([50, 50, 90, 100]) # top-left: <50, 50>, bottom-down: <90, 100>, <x-axis, y-axis>
gt_bbox = np.array([70, 80, 120, 150])
print (get_IoU(pred_bbox, gt_bbox))
# test2
pred_bboxes = np.array([[15, 18, 47, 60],
[50, 50, 90, 100],
[70, 80, 120, 145],
[130, 160, 250, 280],
[25.6, 66.1, 113.3, 147.8]])
gt_bbox = np.array([70, 80, 120, 150])
print (get_max_IoU(pred_bboxes, gt_bbox))
代码解析
若不相交得到的结果中,必有ixmax < ixmin、iymax - iymin其一成立,此时iw、ih就为0了;
nms代码
https://blog.csdn.net/a1103688841/article/details/89711120
import numpy as np
boxes = np.array([[100, 100, 210, 210, 0.72],
[250, 250, 420, 420, 0.8],
[220, 220, 320, 330, 0.92],
[100, 100, 210, 210, 0.72],
[230, 240, 325, 330, 0.81],
[220, 230, 315, 340, 0.9]])
def py_cpu_nms(dets, thresh):
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]
areas = (y2 - y1 + 1) * (x2 - x1 + 1)
scores = dets[:, 4]
keep = []
# index = scores.argsort()[::-1]
index = np.argsort(scores)[::-1]
while index.size > 0:
i = index[0] # every time the first is the biggst, and add it directly
keep.append(i)
x11 = np.maximum(x1[i], x1[index[1:]]) # calculate the points of overlap
y11 = np.maximum(y1[i], y1[index[1:]])
x22 = np.minimum(x2[i], x2[index[1:]])
y22 = np.minimum(y2[i], y2[index[1:]])
w = np.maximum(0, x22 - x11 + 1) # the weights of overlap
h = np.maximum(0, y22 - y11 + 1) # the height of overlap
overlaps = w * h
ious = overlaps / (areas[i] + areas[index[1:]] - overlaps)
idx = np.where(ious <= thresh)[0]
index = index[idx + 1] # because index start from 1
return keep
res = py_cpu_nms(boxes, 0.5)
print(res)
>>> import numpy as np
>>> index = np.array([1, 2])
>>> a = np.array([2, 3, 4, 5])
>>> c = a[index]
>>> c
array([3, 4])
np.where
当数组是一维数组时,返回的值是一维的索引,所以只有一组索引数组
#-*-coding:utf-8-*-
import numpy as np
a = np.arange(8) #array([0, 1, 2, 3, 4, 5, 6, 7])
index = np.where(a > 4) #(array([5, 6, 7], dtype=int64),)
print(index[0]) #[5 6 7]
2. CNN反向传播细节,怎么过全联接层、池化层、卷积层
- 通过链式法则得到每个参数的偏导,乘以LR,就是每个参数的增量,每个参数减去增量就是变后的值。
- max pooling: 下一层的梯度会原封不动地传到上一层最大值所在位置的神经元,其他位置的梯度为0;average pooling: 下一层的梯度会平均地分配到上一层的对应相连区块的所有神经元。
3、优化器算法介绍
https://blog.csdn.net/pursuit_zhangyu/article/details/100067391
4. CNN多分类损失函数 softmax
https://blog.csdn.net/lvchunyang66/article/details/80076959
https://blog.csdn.net/u014380165/article/details/77284921
分类loss函数
交叉熵
以识别手写数字为例,0~9共十个类别。识别数字1,神经网络的输出结果越接近[0,1,0,0,0,0,0,0,0,0]越好。交叉上是最好的评判方法之一。交叉熵刻画了两个概率分布之间的距离,它是分类问题中使用比较广的一种损失函数。
p代表正确答案,q代表的是预测值。交叉熵值越小,两个概率分布越接近。
KL散度
KL散度,有时候也叫KL距离,一般被用于计算两个分布之间的不同。
https://www.zhihu.com/question/65288314/answer/244557337
softmax
softmax函数的作用,它是将Softmax将神经网络的输出变成了一个概率分布,这样子可以计算它和GT的距离。
举个例子:假设网络的输出=[1,2,3],那么经过softmax层后就会得到[0.09,0.24,0.67],这三个数字表示这个样本属于第1,2,3类的概率分别是0.09,0.24,0.67。
softmax loss函数
cross entropy loss函数
回归loss函数
5、smooth L1为什么更有效
https://www.zhihu.com/question/58200555/answer/621174180
6.过拟合的解决办法
https://zhuanlan.zhihu.com/p/42070435
- 增加原始数据集
- 使用数据增强
- 正则化
- Dropout
- 训练次数少一点
正则化
解释一
regularizer英文名字
向你的模型加入某些规则,加入先验,缩小解空间,减小求出错误解的可能性。
解释二
https://www.jianshu.com/p/c9bb6f89cfcc
原因1:来自知乎上一种比较直观和简单的理解, 模型过于复杂是因为模型尝试去兼顾各个测试数据点, 导致模型函数如下图,处于一种动荡的状态, 每个点的到时在某些很小的区间里,函数值的变化很剧烈。这就意味着函数在某些小区间里的导数值(绝对值)非常大,由于自变量值可大可小,所以只有系数足够大,才能保证导数值很大。
而加入正则能抑制系数过大的问题。如下公式, 是岭回归的计算公式。
如果发生过拟合, 参数θ一般是比较大的值, 加入惩罚项后, 只要控制λ的大小,当λ很大时,θ1到θn就会很小,即达到了约束数量庞大的特征的目的。
上面的红字可以使用代码(9.Regression.zip/9.3.ElasticNet.py)解释
https://github.com/zhudaoruyi/ChinaHadoop-ML-V
7.目标检测的指标:识别精度,识别速度,定位精度
https://www.zhihu.com/question/41540197
详细说一下mAP, 阈值怎么设定
https://zhuanlan.zhihu.com/p/70306015
mAP: mean Average Precision, 即各类别AP的平均值,每个类别的AP指的是根据recall和precision绘制一条曲线下面的面积。AP曲线包括精准率Precision: TP / (TP + FP),召回率Recall: TP / (TP + FN)。精确率是针对我们预测结果而言的,它表示的是预测为正的样本中有多少是对的。那么预测为正就有两种可能了,一种就是把正类预测为正类(TP),另一种就是把负类预测为正类(FP)。而召回率是针对我们原来的样本而言的,它表示的是样本中的正例有多少被预测正确了。
接下来是要统计出每个类别的统计出TP,FP,FN个数就可以了。
拿单张图片来说吧,首先遍历图片中ground truth对象,然后提取我们要计算的某类别的gt objects,之后读取我们通过检测器检测出的这种类别的检测框(其他类别的先不管),接着过滤掉置信度分数低于置信度阈值的框(也有的未设置信度阈值),将剩下的检测框按置信度分数从高到低排序,最先判断置信度分数最高的检测框与gt bbox的iou是否大于iou阈值,若iou大于设定的iou阈值即判断为TP,将此gt_bbox标记为已检测(后续的同一个GT的多余检测框都视为FP,这就是为什么先要按照置信度分数从高到低排序,置信度分数最高的检测框最先去与iou阈值比较,若大于iou阈值,视为TP,后续的同一个gt对象的检测框都视为FP),iou小于阈值的,直接规划到FP中去。
对于一个二分问题,可将实例(case)分成正类(positive)或负类(negative)。如果进行预测,会出现四种情况,即:实例是正类并且也被预测成正类,即为真正类TP(True positive),实例是负类被预测成正类,称之为假正类FP(False positive),实例是负类被预测成负类,称之为真负类TN(True negative),实例是正类被预测成负类,称之为假负类FN(false negative)。列表如下表所示,其中1代表正类,0代表负类。
8.ROC曲线
AUC是指曲线下面积,一般用于ROC(受试者工作特性曲线)围成的面积。
ROC是根据TPR、FPR进行绘制的。通过阈值的改变,得到敏感度和特异度画出ROC曲线。
(1)真正类率(True Postive Rate)TPR: TP/(TP+FN),代表分类器预测的正类中实际正实例占所有正实例的比例。Sensitivity(敏感度)和recall一样
(2)负正类率(False Postive Rate)FPR: FP/(FP+TN),代表分类器预测的正类中实际负实例占所有负实例的比例。1-Specificity(特异度)
9.ResNet介绍
ResNet 主要的创新在残差网络,其实这个网络的提出本质上还是要解决层次比较深的时候无法训练的问题。这种借鉴了Highway Network思想的网络相当于旁边专门开个通道使得输入可以直达输出,而优化的目标由原来的拟合输出H(x)变成输出和输入的差H(x)-x,其中H(X)是某一层原始的的期望映射输出,x是输入。
代码
class Res2d(nn.Module):
def __init__(self, n_in, n_out, stride = 1):
super(PostRes2d, self).__init__()
self.conv1 = nn.Conv2d(n_in, n_out, kernel_size = 3, stride = stride, padding = 1)
self.bn1 = nn.BatchNorm2d(n_out)
self.relu = nn.ReLU(inplace = True)
self.conv2 = nn.Conv2d(n_out, n_out, kernel_size = 3, padding = 1)
self.bn2 = nn.BatchNorm2d(n_out)
if stride != 1 or n_out != n_in:
self.shortcut = nn.Sequential(
nn.Conv2d(n_in, n_out, kernel_size = 1, stride = stride),
nn.BatchNorm2d(n_out))
else:
self.shortcut = None
def forward(self, x):
residual = x
if self.shortcut is not None:
residual = self.shortcut(x)
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out += residual
out = self.relu(out)
return out
10. 为什么会出现梯度消失和梯度爆炸
误差大于1,那么层数增多的时候,最终的求出的梯度更新将以指数形式增加,即发生梯度爆炸,如果此部分小于1,那么随着层数增多,求出的梯度更新信息将会以指数形式衰减,即发生了梯度消失
梯度消失/爆炸是什么?(反向传播中由于链式求导法则的连乘,如果乘数都比较小趋于0,最终传递到网络输入层的梯度会变得很小(梯度消失),如果乘数都很大,最终的梯度也会变得很大(梯度爆炸),其实二者都是因为网络太深导致权值更新不稳定,本质上是因为梯度反向传播中的连乘效应)
BP反向传播
https://zhuanlan.zhihu.com/p/22473137
深度学习相关
普通卷的参数量如何计算?
1.卷积后尺寸计算
out_height=(in_height+2pad-filter_height)/strides[1]+1
out_width=(in_width+2pad-filter_width)/strides[2] +1
如果换成depthwise conv呢?
大小为3*3,padding=0,stride=1的卷积核在经过2次计算后感受野为多少?
减小过拟合的方法有哪些?
YOLO v3的loss函数(讲错了,当时一紧张就讲成了Faster RCNN的loss函数,脑子发热了,难受~
smooth L1函数的作用为什么效果更好?
11.难例挖掘和在线难例挖掘
https://zhuanlan.zhihu.com/p/57440670
online hard negative mining顾名思义:negative,即负样本,其次是hard,说明是困难样本,也就是说在对负样本分类时候,loss比较大(label与prediction相差较大)的那些样本,也可以说是容易将负样本看成正样本的那些样本。
hard negative mining就是多找一些hard negative加入负样本集,进行训练,这样会比easy negative组成的负样本集效果更好。
hard negative mining思路在目标检测中的训练过程,简单来说有以下三个步骤
1、目标检测中如何根据有标签的数据划分正负训练集?
用带标签的图像随机生成图像块,iou大于某一个阈值的图像块做为正样本,否则为负样本。但一般负样本远远多于正样本,为避免训练出来的模型会偏向预测为负例,需要保持样本均衡,所以初始负样本训练集需要选择负样本集的子集,一般正:负=1:3。
2、有了正负训练集就可以训练神经网络了。经过训练后,就可以用这个训练出的模型预测其余的负样本了(就是没有加入训练集的那些负样本)。模型在预测一张图像块后会给出其属于正负的概率,在这里设置一个阈值,预测为正的概率大于这个阈值,就可以把这个图像块加入复样本训练集了。
3.正样本训练集不变,负样本训练集除了初始的那些,还有新加入的。拿着这个新的训练集,就可以开始新的一轮训练了。(这里运用了解决样本不平衡欠采样的方法之一)
跳到第二步(这个过程是重复的)
12、softmax交叉熵函数求导
14、L1、L2范数,L1趋向于0,但L2不会,为什么?
https://zhuanlan.zhihu.com/p/30147914
https://blog.csdn.net/jinping_shi/article/details/52433975
发生过拟合的时候参数会变得非常的大,参数变大才能更加attention细节。通过限制参数不要太大就可以防止过拟合,如何让模型更加关注参数的大小,需要在loss函数中加入,来抑制参数过大,即L1正则项,L2正则项。
他们都是可以防止过拟合,降低模型复杂度。L1 会产生稀疏的特征,L2 会产生更多地特征但是都会接近于0。L1在特征选择时候非常有用,而L2就只是一种规则化而已。
为什么L1使权重稀疏,L2使权重平滑?
从梯度叠加的角度考虑:因为目标函数是损失项和正则项的和,梯度也是两者的和。对于正则项来说,无论L1还是L2,都是希望权重变为0的。但是L1求导后是常量,它一般比损失项的绝对值大,因此参数更新的方向和L1正则项的方向是一致的,最后会变成0;而L2是平方,求导后是权重的固定比例, 每次减去的是一个逐渐变小的数,因此在原点附近的时候参数更新的方向不一定是最小化参数的绝对值了,那么最小值点就不会在原点。
从解空间形状角度考虑:假设二维情况,L2正则项约束的解空间是圆形,L1正则项约束的解空间是多边形,最优解是约束的解空间形状和原来目标函数等高线的交点。显然,多边形的解空间更容易在尖角处取得,也就是在坐标轴上取得,而圆形则不容易在坐标轴上与等高线相切。
L1 会产生稀疏的特征的其他解释
https://www.jianshu.com/p/475d2c3197d2
角度一:数学公式
这个角度从权值的更新公式来看权值的收敛结果。
首先来看看L1和L2的梯度(导数的反方向):
所以(不失一般性,我们假定:wi等于不为0的某个正的浮点数,学习速率η 为0.5):
L1的权值更新公式为wi= wi- η * 1 = wi- 0.5 * 1,也就是说权值每次更新都固定减少一个特定的值(比如0.5),那么经过若干次迭代之后,权值就有可能减少到0。
L2的权值更新公式为wi= wi- η * wi= wi- 0.5 * wi,也就是说权值每次都等于上一次的1/2,那么,虽然权值不断变小,但是因为每次都等于上一次的一半,所以很快会收敛到较小的值但不为0。
L1能产生等于0的权值,即能够剔除某些特征在模型中的作用(特征选择),即产生稀疏的效果。
L2可以得迅速得到比较小的权值,但是难以收敛到0,所以产生的不是稀疏而是平滑的效果。
角度二:几何空间
这个角度从几何位置关系来看权值的取值情况。
直接来看下面这张图:
高维我们无法想象,简化到2维的情形,如上图所示。其中,左边是L1图示,右边是L2图示,左边的方形线上是L1中w1/w2取值区间,右边得圆形线上是L2中w1/w2的取值区间,绿色的圆圈表示w1/w2取不同值时整个正则化项的值的等高线(凸函数),从等高线和w1/w2取值区间的交点可以看到,L1中两个权值倾向于一个较大另一个为0,L2中两个权值倾向于均为非零的较小数。这也就是L1稀疏,L2平滑的效果。
假设原先损失函数是C0,那么在L2和L1正则条件下对参数求导分别是:
可以想象用梯度下降的方法,当w小于1的时候,L2正则项的惩罚效果越来越小,L1正则项惩罚效果依然很大,L1可以惩罚到0,而L2很难。
15、激活函数有哪些
https://zhuanlan.zhihu.com/p/73214810
1.Sigmoid
sigmoid函数也称为Logistic函数,因为Sigmoid函数可以从Logistic回归(LR)中推理得到,也是LR模型指定的激活函数。具体推理参考:Datartisan:机器学习系列-广义线性模型
sigmod函数的取值范围在(0, 1)之间,可以将网络的输出映射在这一范围,方便分析。
Sigmoid公式及导数:
Sigmoid及其导数曲线:
Sigmoid作为激活函数的特点:
优点:平滑、易于求导。
缺点:
- 激活函数计算量大(在正向传播和反向传播中都包含幂运算和除法);
- 反向传播求误差梯度时,求导涉及除法;
- Sigmoid导数取值范围是[0, 0.25],由于神经网络反向传播时的“链式反应”,很容易就会出现梯度消失的情况。例如对于一个10层的网络, 根据,第10层的误差相对第一层卷积的参数的梯度将是一个非常小的值,这就是所谓的“梯度消失”。
- Sigmoid的输出不是0均值(即zero-centered);这会导致后一层的神经元将得到上一层输出的非0均值的信号作为输入,随着网络的加深,会改变数据的原始分布。
2. tanh
tanh为双曲正切函数,其英文读作Hyperbolic Tangent。tanh和 sigmoid 相似,都属于饱和激活函数,区别在于输出值范围由 (0,1) 变为了 (-1,1),可以把 tanh 函数看做是 sigmoid 向下平移和拉伸后的结果。
从公式2中,可以更加清晰看出tanh与sigmoid函数的关系(平移+拉伸)。
tanh及其导数曲线:
tanh作为激活函数的特点:
相比Sigmoid函数,
- tanh的输出范围时(-1, 1),解决了Sigmoid函数的不是zero-centered输出问题;
- 幂运算的问题仍然存在;
- tanh导数范围在(0, 1)之间,相比sigmoid的(0, 0.25),梯度消失(gradient vanishing)问题会得到缓解,但仍然还会存在。
3.ReLU
Relu(Rectified Linear Unit)——修正线性单元函数:该函数形式比较简单,
公式:relu=max(0, x)
ReLU及其导数曲线:
从上图可知,ReLU的有效导数是常数1,解决了深层网络中出现的梯度消失问题,也就使得深层网络可训练。同时ReLU又是非线性函数,所谓非线性,就是一阶导数不为常数;对ReLU求导,在输入值分别为正和为负的情况下,导数是不同的,即ReLU的导数不是常数,所以ReLU是非线性的(只是不同于Sigmoid和tanh,relu的非线性不是光滑的)。
ReLU在x>0下,导数为常数1的特点:
导数为常数1的好处就是在“链式反应”中不会出现梯度消失,但梯度下降的强度就完全取决于权值的乘积,这样就可能会出现梯度爆炸问题。解决这类问题:一是控制权值,让它们在(0,1)范围内;二是做梯度裁剪,控制梯度下降强度,如ReLU(x)=min(6, max(0,x))
ReLU在x<0下,输出置为0的特点:
描述该特征前,需要明确深度学习的目标:深度学习是根据大批量样本数据,从错综复杂的数据关系中,找到关键信息(关键特征)。换句话说,就是把密集矩阵转化为稀疏矩阵,保留数据的关键信息,去除噪音,这样的模型就有了鲁棒性。ReLU将x<0的输出置为0,就是一个去噪音,稀疏矩阵的过程。而且在训练过程中,这种稀疏性是动态调节的,网络会自动调整稀疏比例,保证矩阵有最优的有效特征。
但是ReLU 强制将x<0部分的输出置为0(置为0就是屏蔽该特征),可能会导致模型无法学习到有效特征,所以如果学习率设置的太大,就可能会导致网络的大部分神经元处于‘dead’状态,所以使用ReLU的网络,学习率不能设置太大。
ReLU作为激活函数的特点:
- 相比Sigmoid和tanh,ReLU摒弃了复杂的计算,提高了运算速度。
- 解决了梯度消失问题,收敛速度快于Sigmoid和tanh函数,但要防范ReLU的梯度爆炸
- 容易得到更好的模型,但也要防止训练中出现模型‘Dead’情况。
4. Leaky ReLU, PReLU(Parametric Relu), RReLU(Random ReLU)
ReLU及其变体图像:
16、数据不均衡怎么处理*-
1.通过调整样本权重来平衡模型在不同样本上的表现
2.采用代价敏感算法以提升模型在少数样本上的表现
3.通过采样技术平衡样本分布
18、感受野
https://www.jianshu.com/p/9997c6f5c01e
在卷积神经网络中,感受野(Receptive Field)的定义是卷积神经网络每一层输出的特征图(feature map)上每个像素点在原始图像上映射的区域大小,这里的原始图像是指网络的输入图像,是经过预处理(如resize,warp,crop)后的图像。
神经元之所以无法对原始图像的所有信息进行感知,是因为在卷积神经网络中普遍使用卷积层和pooling层,在层与层之间均为局部连接。
神经元感受野的值越大表示其能接触到的原始图像范围就越大,也意味着它可能蕴含更为全局,语义层次更高的特征;相反,值越小则表示其所包含的特征越趋向局部和细节。因此感受野的值可以用来大致判断每一层的抽象层次.
19、梯度法和牛顿法的区别?
https://zhuanlan.zhihu.com/p/78185057
20、目标检测的数据增强方法
1. 随机裁剪 (对标记的Boxes进行处理)
2. 随机变换亮度
3. 随机变换通道
4. 随机变换对比度
5. 随机变换饱和度
21、为什么需要onehot编码?
首先onehot应用在分类问题对label的编码中,它的特点是使得各个类别之间距离相等。如果不使用onehot而是直接使用0, 1,2这样的数值来代替类别,那么不同类之间的距离就不同了,那么这个问题就 变成了回归的问题。另外因为one-hot中一个维度对应一个特征的关系,特征选择后可以直接降低one-hot编码的维度。
但是需要注意的是,有些分类问题中不同类别之间的距离是不同的,这时直接使用onehot是不妥的,可以在计算损失时按照不同距离之间的比例关系,给不同类别之间赋予不同的权重。
22、BN,LN,IN,GN,WN,WS
Batch Normalization
- BN有效的原因:首先是归一化使得模型每一层的输出值变得稳定,易于训练;其次在归一化过程中使用的均值和标准差是在mini-batch上计算的,它们相对整个数据集的均值标准差,具有一定的噪声,这就使得BN有轻微的正则化效果。
- BN中 和 的作用:因为我们不知道每一层做归一化是不是一定会更好,也不知需要归一化到什么程度,所以设置这两个参数让模型自己去学到合适的归一化。
- 需要注意的是,在batch_size很小时,BN的效果会变得很差。因为均值和标准差的噪声太大了。
- 训练时和测试时的不同点:测试时用的均值和方差是全部训练数据的均值和方差。
BN、LN、IN、GN、WN、WS是如何归一化的?
- BN (Batch Normalization):对其求均值和方差时,将在 N、H、W上操作,而保留通道 C 的维度。具体来说,就是把第1个样本的第1个通道,加上第2个样本第1个通道 ...... 加上第 N 个样本第1个通道,求平均,得到通道 1 的均值(注意是除以 N×H×W 而不是单纯除以 N,最后得到的是一个代表这个 batch 第1个通道平均值的数字,而不是一个 H×W 的矩阵)。
- LN (Layer Normalization): 对每个样本的 C、H、W 维度上的数据求均值和标准差,保留 N 维度。适合训练数据长度不同的 RNN 模型。
- IN (Instance Normalization):对每个样本的 H、W 维度的数据求均值和标准差,保留 N 、C 维度,也就是说,它只在 channel 内部求均值和标准差。在GAN中应用较多,因为生成器生成的某张图和其他图是无关的,因此只需要使用自己的统计量进行归一化。但是用GAN做图像修补的时候IN可能效果不好,因为待修补的图片自身可能有大量的全黑或全白等异常像素,自己的统计量受异常值影响大。另外,图像风格迁移时用目标风格图片对应 channel 的均值和标准差“去归一化”,以期获得目标图片的风格。
- GN (Group Normalization) :计算均值和标准差时,把每一个样本 feature map 的 channel 分成 G 组,每组将有 C/G 个 channel,然后将这些 channel 中的元素求均值和标准差。各组 channel 用其对应的归一化参数独立地归一化。多用于检测分割等占用显存较大的任务。
- WN (Weight Normalization):思路是对每个神经元的weight vector进行权重g与方向v的分解,在BP过程中分别对g与v进行更新从而加速了收敛的速度。由于这个操作是定义在神经元层面,所以WN相对于BN也具有更大的适用范围。
- WS(Weight Standardization):对权重进行归一化。
代码:
# BN
import numpy as np
def batch_norm(x, gamma, beta):
# x_shape:[N, C, H, W]
results = 0.
eps = 1e-5
x_mean = np.mean(x, axis=(0, 2, 3), keepdims=True)
x_var = np.var(x, axis=(0, 2, 3), keepdims=True)
x_normalized = (x - x_mean) / np.sqrt(x_var + eps)
results = gamma * x_normalized + beta
return results
# GN
import numpy as np
def group_norm(x, gamma, beta, G=16):
# x_shape:[N, C, H, W]
results = 0.
eps = 1e-5
x = np.reshape(x, (x.shape[0], G, x.shape[1]//G, x.shape[2], x.shape[3]))
x_mean = np.mean(x, axis=(2, 3, 4), keepdims=True)
x_var = np.var(x, axis=(2, 3, 4), keepdims=True)
x_normalized = (x - x_mean) / np.sqrt(x_var + eps)
results = gamma * x_normalized + beta
return results
5.防止梯度消失的方法
-
预训练加微调
-
梯度剪切、权重正则(针对梯度爆炸)
-
使用不同的激活函数
-
使用batchnorm
-
使用残差结构
-
使用LSTM网络
23. 检测任务中可以提高点的方法
data-augment方法
使用FPN
换loss函数,使用focal loss函数
图像金字塔,作为输入
24. 目标检测——深度学习下的小目标检测(检测难的原因和Tricks)
https://www.cnblogs.com/E-Dreamer-Blogs/p/11442927.html
(1)小目标在原图中尺寸比较小,通用目标检测模型中,一般的基础骨干神经网络(VGG系列和Resnet系列)都有几次下采样处理,导致小目标在特征图的尺寸基本上只有个位数的像素大小,导致设计的目标检测分类器对小目标的分类效果差。
tricks
(1) data-augmentation.简单粗暴,比如将图像放大,利用 image pyramid多尺度检测,最后将检测结果融合.缺点是操作复杂,计算量大,实际情况中不实用;
(2) 特征融合方法:FPN这些,多尺度feature map预测,feature stride可以从更小的开始;
(3)设置更小更稠密的anchor,设计anchor match strategy等,参考S3FD;
https://zhuanlan.zhihu.com/p/83220498
2. 针对同一张图片里面包含小目标数量少的问题,在图片内用分割的Mask抠出小目标图片再使用复制粘贴的方法(当然,也加上了一些旋转和缩放,另外要注意不要遮挡到别的目标)。
25、空洞卷积
https://www.jianshu.com/p/f743bd9041b3
Dilated/Atrous Convolution(中文叫做空洞卷积或者膨胀卷积) 或者是 Convolution with holes 从字面上就很好理解,是在标准的 convolution map 里注入空洞,以此来增加 reception field。相比原来的正常convolution,dilated convolution 多了一个 hyper-parameter 称之为 dilation rate 指的是kernel的间隔数量(e.g. 正常的 convolution 是 dilatation rate 1)。
Deep CNN 对于其他任务还有一些致命性的缺陷。较为著名的是 up-sampling 和 pooling layer 的设计。
主要问题有:
- Up-sampling / pooling layer (e.g. bilinear interpolation) is deterministic. (参数不可学习)
- 内部数据结构丢失;空间层级化信息丢失。
- 小物体信息无法重建 (假设有四个pooling layer 则 任何小于 2^4 = 16 pixel 的物体信息将理论上无法重建。)
在这样问题的存在下,语义分割问题一直处在瓶颈期无法再明显提高精度, 而 dilated convolution 的设计就良好的避免了这些问题。
26、特征选择
27. 为什么将数据归一化
https://zhuanlan.zhihu.com/p/30358160
1)归一化后加快了梯度下降求最优解的速度
蓝色的圈圈图代表的是两个特征的等高线。其中左图两个特征X1和X2的区间相差非常大,X1区间是[0,2000],X2区间是[1,5],其所形成的等高线非常尖。当使用梯度下降法寻求最优解时,很有可能走“之字型”路线(垂直等高线走),从而导致需要迭代很多次才能收敛;
而右图对两个原始特征进行了归一化,其对应的等高线显得很圆,在梯度下降进行求解时能较快的收敛。
因此如果机器学习模型使用梯度下降法求最优解时,归一化往往非常有必要,否则很难收敛甚至不能收敛。
28. DNN和CNN的区别
CNN是局部感受野和参数共享,
- kernel通过在feature map上移动得到局部信息,随着不断的下采样下去,感受野不断变大,起初可能只是得到了鼻子、耳朵的信息,后面得到的就是人脸的信息了。
- 同一个卷积核在feature map内参数是共享的,图像通过卷积操作后仍然保留原先的位置关系。对于一张1000*1000的像素图片来说,全连接模型的隐层节点都有10w个输入,也就有对应的10w个权重参数。若使用9*9的卷积核进行卷积操作的话,每个节点的输入是81,对应100个权值,数量级大大减少
29. pytorch代码
#-*-coding:utf-8-*-
import torch
import torch.nn as nn
import torch.optim as optim
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
#layer1
self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=1)
self.bn1 = nn.BatchNorm2d(32)
self.relu1 = nn.ReLU()
self.maxpool1 = nn.MaxPool2d(2)
#layer2
self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=1)
self.bn2 = nn.BatchNorm2d(64)
self.relu2 = nn.ReLU()
self.maxpool2 = nn.MaxPool2d(2)
#layer3
self.conv3 = nn.Conv2d(in_channels=64, out_channels=32, kernel_size=3, padding=1)
self.bn3 = nn.BatchNorm2d(32)
self.relu3 = nn.ReLU()
self.maxpool3 = nn.MaxPool2d(2)
#layer4
self.layer4 = nn.Sequential(
nn.Conv2d(32, 32, 3),
nn.BatchNorm2d(32),
nn.ReLU(),
nn.MaxPool2d(2)
)
#fc
self.fc1 = nn.Linear(30752, 1024)
self.fc2 = nn.Linear(1024, 2)
self.sigmoid = nn.Sigmoid()
def forward(self, input):
in_size = input.size(0)
# layer1
out = self.conv1(input)
out = self.bn1(out)
out = self.relu1(out)
out = self.maxpool1(out)
# layer2
out = self.conv2(out)
out = self.bn2(out)
out = self.relu2(out)
out = self.maxpool2(out)
# layer3
out = self.conv3(out)
out = self.bn3(out)
out = self.relu3(out)
out = self.maxpool3(out)
# layer4
out = self.layer4(out)
# 展开
out = out.view(in_size, -1)
out = self.fc1(out)
out = self.fc2(out)
out = self.sigmoid(out)
return out
model = Net()
model = nn.DataParallel(model, device_ids=[0])
optimizer = optim.Adam(model.parameters(), lr=1e-2)
input = torch.ones(1, 3, 512, 512)
out = model(input)
print(out.shape)
# 数据预处理
transform = transforms.Compose([
transforms.Resize(size=(150, 150)),
transforms.ToTensor(),
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])
class MyDataset(Dataset):
def __init__(self, root_dir, transform=None):
# os.listdir函数读取路径下所以文件的文件名,并组成一个列表并返回
self.file = os.listdir(root_dir)
self.root_dir = root_dir
self.transform = transform
def __len__(self):
return len(self.file) # 返回这给列表的大小
def __getitem__(self, index):
# 将传入路径和文件名组成一个新的地址,这个数据就是单个数据的具体地址,方便之后以地址读取该数据
img_name = os.path.join(self.root_dir, self.file[index])
# 从文件名中获取标签(我的数据标签在文件名中,比如一张狗的图片名:cat.4.jpg,4是序号,cat是
# 标签)
if img_name[13:16] == 'dog':
label = 0
else:
label = 1
# 根据上面获得的具体地址,读取这张图片
image = Image.open(img_name)
# 对图片进行处理
image = self.transform(image)
# print(image.shape)
# numpy的三个维度顺序为:H * W * C
# 而torch的张量维度顺序:C * H * W ,所以模型要处理它必须转换成torch的形式
# 返回数据和标签
return image, label
dataset_train = MyDataset(root_dir='kaggle/train/', transform=transform)
dataset_test = MyDataset(root_dir='kaggle/test1/', transform=transform)
# 导入数据
train_loader = torch.utils.data.DataLoader(dataset_train, batch_size=BATCH_SIZE, shuffle=True)
# test_loader = torch.utils.data.DataLoader(dataset_test, batch_size=BATCH_SIZE, shuffle=True)
# 定义网络
class ConvNet(nn.Module):
def __init__(self):
super(ConvNet, self).__init__()
self.conv1 = nn.Conv2d(3, 32, 3)
self.max_pool1 = nn.MaxPool2d(2)
self.conv2 = nn.Conv2d(32, 64, 3)
self.max_pool2 = nn.MaxPool2d(2)
self.conv3 = nn.Conv2d(64, 128, 3)
self.max_pool3 = nn.MaxPool2d(2)
self.conv4 = nn.Conv2d(128, 128, 3)
self.max_pool4 = nn.MaxPool2d(2)
self.fc1 = nn.Linear(6272, 512)
self.fc2 = nn.Linear(512, 1)
def forward(self, x):
in_size = x.size(0)
x = self.conv1(x)
x = F.relu(x)
x = self.max_pool1(x)
x = self.conv2(x)
x = F.relu(x)
x = self.max_pool2(x)
x = self.conv3(x)
x = F.relu(x)
x = self.max_pool3(x)
x = self.conv4(x)
x = F.relu(x)
x = self.max_pool4(x)
# 展开
x = x.view(in_size, -1)
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
x = torch.sigmoid(x)
return x
# 实例化模型并且移动到GPU
model = ConvNet().to(DEVICE)
# 选择简单暴力的Adam优化器,学习率调低
optimizer = optim.Adam(model.parameters(), lr=1e-4)
# 定义训练过程
def train(model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
# print(data.shape)
# print(target.shape)
# print(target)
data, target = data.to(device), target.to(device).float().reshape(50, 1)
optimizer.zero_grad()
output = model(data)
loss = F.binary_cross_entropy(output, target)
loss.backward()
optimizer.step()
if (batch_idx + 1) % 10 == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, (batch_idx + 1) * len(data), len(train_loader.dataset),
100. * (batch_idx + 1) / len(train_loader), loss.item()))