1. 机器学习
1.1 SVM支持向量积
https://blog.csdn.net/qq_34069667/article/details/106793766
SVM是support vector machine,支持向量机,是数据分析里面一种常用的算法。
支持向量机是一种二分类模型,它的基本模型定义在特征空间上的间隔最大的线性分类器,间隔最大使他有别于感知机;支持向量机还包括核技巧,这使得它称为实质上的非线性分类器。支持向量机的学习策略就是间隔最大化,可形式化为一个求解凸二次规划的问题,也等价于正则化的合页损失函数的最大化问题。支持向量机的学习算法是求解凸二次规划的最优化算法。简单来说SVM就是,在特征空间上找到最优超平面使得数据的正负样本间隔最大。
SVM为什么采取间隔最大化?
支持向量机学习的基本思想是求解能够正确划分训练集并且几何间隔最大的最大超平面。对于线性可分数据集而言,线性可分分离超平面有无穷多个,但是几何间隔最大的分离超平面是唯一的。
对训练集找到几何间隔最大的超平面意味着以充分的确信度对训练数据进行分类。也就是说,不仅将正负实例点分开,而且对最难分的实例点(离超平面最近的点)也有最大的确信度将它们分开。这样的超平面应该对未知的新实例有很好的分类预测能力(具有良好的鲁棒性)。
SVM为什么要引入核函数?(将线性不可分变为线性可分)
当样本在原始空间线性不可分时,可将样本从原始空间映射到一个更高维的特征空间,使得样本在这个特征空间内线性可分。
没有使用过SVM,我说一说我对他的了解。SVM全名是support vector machine,支持向量机的意思。我的理解它是数据分析里面一种常用的算法。基本的定义呢是,支持向量机是一种二分类模型,它求解了能使得特征空间间隔最大的最大超平面。定义中的这个特征空间可以把他理解为对样本空间,我们要将样本进行二分类,分出正样本和负样本;间隔最大的意思是,SVM找到的这个最大超平面,可以使得模型以最大的置信度对样本进行分类。想象一堆二维点,有正样本有负样本,随便一条线也就是最大超平面都能将样本进行划分,但是能使得与正负样本的几何距离最大的最大超平面是唯一的,SVM就是要求解这个。如果训练样本足够丰富,那么这个超平面在应对新的样本时也可以具有很好的预测能力,也就是鲁棒性。
1.2 K-means算法
https://blog.csdn.net/fuqiuai/article/details/79458331
kmeans算法是非监督学习中的聚类算法。
算法思想:
选择需要分类的类变数K。 选择K个点作为初始质心 repeat 将每个点指派到最近的质心,形成K个簇 重新计算每个簇的质心 until 簇不再发生变化或者达到了最大迭代次数
kmeans算法是无监督学习中的聚类算法。首先他是一个无监督学习算法,这意味着不需要对样本进行标注。聚类就是将样本数据进行分组,组内的差别越大,则聚类的效果越好。我们知道比较有名的目标检测算法YOLO,它里面的anchor框就是通过kmeans聚类得到的。
kmeans算法的关键有两个,一个是分类的个数,也就是K,第二个是K个点的初始值。拿平面上的一堆二维点举例子,先选择K个点作为初始点,然后计算每个数据点离初始点的距离,然后归类到初始点所代表的簇中。所有的点归类完后,重新计算每个簇的质心,也就是更新初始值。再循环这个计算过程。直到簇的中心不再发生变化或者达到了最大迭代次数。
1.3 朴素贝叶斯
https://blog.csdn.net/fuqiuai/article/details/79458943
1.4 分类与回归
分类和回归的区别在于输出变量的类型。
定量输出称为回归,或者说是连续变量预测;
定性输出称为分类,或者说是离散变量预测。
举个例子:
预测明天的气温是多少度,这是一个回归任务;
预测明天是阴、晴还是雨,就是一个分类任务。
人脸识别是一个分类任务,是这个人还是不是这个人;
年龄预测是一个回归任务,预测这个人多少岁。
2. 深度学习
2.1 常见指标
TP, FP, TN, FN, ACC, P, R, TPR, FPR, F-SCORE, P-R曲线,ROC曲线,AUC
P和N是预测的结果。T和F是告诉你预测的对不对。
- TP(true positive,真正): 预测为正,实际为正
- FP(false positive,假正): 预测为正,实际为负
- TN(true negative,真负):预测为负,实际为负
- FN(false negative,假负): 预测为负,实际为正
- ACC(accuracy,准确率):ACC = (TP+TN)/(TP+TN+FN+FP)
- P(precision精确率、精准率、查准率P = TP/ (TP+FP),预测为正的样本中有多少是真的正样本
- R(recall,召回率、查全率): R = TP/ (TP+FN),真的正样本中,有多少真正样本被预测出来了
- TPR(true positive rate,,真正类率同召回率、查全率):TPR = TP/ (TP+FN)注:Recall = TPR
- FPR(false positive rate,假正类率):FPR =FP/ (FP+TN)
- F-Score: F-Score = (1+β^2) x (PxR) / (β^2x(P+R)) = 2xTP/(2xTP + FP + FN)
- 当β=1是,F1-score = ,F1score同时考虑了精确率和召回率
- P-R曲线(precision-recall,查准率-查全率曲线)
- ROC曲线(receiver operating characteristic,接收者操作特征曲线)
- AUC(area under curve)值
P-R曲线:
PR曲线中的P代表的是precision(精准率),R代表的是recall(召回率),其代表的是精准率与召回率的关系,一般情况下,将recall设置为横坐标,precision设置为纵坐标。
通过PR曲线判断模型性能:
如果一个模型A的PR曲线完全包含了B的PR曲线,则可断言A比B好;如果发生交叉,则可以通过判断PR曲线的面积。更常用的是平衡点F1,平衡点(BEP)是P=R时的取值(斜率为1),F1值越大,我们可以认为该学习器的性能较好。F1的计算如下所示:
F1 = 2 P R /( P + R )
ROC曲线:
ROC的全名叫做Receiver Operating Characteristic,中文名字叫“受试者工作特征曲线”,其主要分析工具是一个画在二维平面上的曲线——ROC 曲线。平面的横坐标是false positive rate(FPR=FP/P),纵坐标是true positive rate(TPR=TP/P)。对某个分类器而言,我们可以根据其在测试样本上的表现得到一个TPR和FPR点对。这样,此分类器就可以映射成ROC平面上的一个点。调整这个分类器分类时候使用的阈值,我们就可以得到一个经过(0, 0),(1, 1)的曲线,这就是此分类器的ROC曲线。
AUC曲线:
AUC全名是area under roc curve,意思是ROC曲线下面的区域,那么这个区域面积就代表AUC的值。我们用这个数值来表示分类器的性能,其大小在0-1之间,当auc=0.5的时候可以判断这是一个随机分类器。
为什么用ROC而不用PR?
ROC曲线有个很好地特性,当测试集的正负样本分布发生变化的时候,ROC曲线能够保持不变,而PR曲线会发生比较严重的变化。因此,在面对正负样本数量不均匀的场景下,ROC曲线(auc的值)会是一个更加稳定的能反映模型好坏的指标。
什么是EER(等错误率):
EER是等错误率,就是FPR=FNR的时候的值。怎么获得这个值呢,画一条TPR=1-FPR(y=1-x)的线,与ROC曲线的交点所对应的点的FPR就是EER,我们一般取该点所对应的阈值作为分类器参数的阈值。
2.2 正则化
正则化,regularization,是代数几何中的一个概念。在深度学习里常常被用来解决模型过拟合的问题。常见的正则化方法有L1正则化和L2正则化。
在线性代数里面,有多种向量范数,有p范数、无穷范数、L1范数、L2范数,其中的L1范数和L2范数就被用来当做正则化的方法。
L1范数是向量元素绝对值之和。对应到深度学习里面就是网络权重参数绝对值之和。
L2范数是向量元素平方和再开方。对应到深度学习里面就是网络权重参数平方和再开方。
那么L1正则化和L2正则化的具体实现方法是改变目标函数,使得目标函数=损失函数+比例系数lambda * L1范数或者L2范数。
我们知道监督学习其实就是要让目标函数的值下降,然后在目标函数中加入L1范数之后,也会尽量让L1范数值变小。
根据定义,L1范数变小,即是的权重参数的绝对值变小,这意味着使得网络趋向于产生更少的特征,而让其他的权重参数为0,这样就使得网络稀疏化。
L2范数变小,可以使得每一个元素都很小,这意味着网络趋向于选择更多的特征,并且使得这些特征都接近于0.
在pytorch中的实现方式,L1范数我没试过,但是现在一般使用的优化器,比如SGD、Adam等加上了L2正则化功能,优化器函数提供了weight-decay这个参数,这个参数就是比例系数的值,=0的时候就不使用L2正则化
2.3 过拟合
过拟合:模型完全拟合出了训练集的特征分布,导致样本中的误差都被当成特征进行拟合了。具体表现是训练集上误差小,测试集上误差大。
比如二维平面上有一堆点,它们分布在一个二次抛物线周围,由于数据有噪声,所以存在波动。
欠拟合就是完全没有拟合出来数据点的特征,可能结果是一条直线;
过拟合就是完全拟合了数据点,包括数据中的噪声,以致于预测结果是一条波动的高次曲线;
比较好的拟合效果是拟合出数据的大致走向,输出一个二次抛物线
原因:
- 数据集相关的:
- 训练集的数据太少,模型完全拟合了这些数据
- 训练集不够丰富,训练集和新数据(测试集)的特征分布不一致(严重)
- 训练集中存在较严重的噪声,噪声大到模型过分的记住了噪声的特征,反而忽略了需要关注的特征。
- 模型相关的:
- 模型太大,参数过多以至于能完全拟合训练集的数据
- 模型本身不具备泛化性能
- 训练相关的:
- 权值学习(训练)迭代次数足够多,拟合了训练数据中的噪声和训练数据中没有代表性的特征。
解决方法:
- 数据集相关的:
- 丰富数据集,数据增强
- 随机添加噪声
- 模型相关的:
- 简化模型
- 加入批归一化Batch normalization
- 训练相关的
- 正则化L1正则和L2正则
- early stopping,在训练集loss不断下降但测试集指标不再提升时结束训练
- dropout
- 集成方法、bagging、随机森林、boosting、贝叶斯方法
欠拟合:
- 数据相关的:
- 数据及过于丰富,无法拟合
- 噪声过多,无法收敛
- 模型相关的:
- 模型过于简单
- 模型设计不合理,无法提取特征
- 训练相关的:
- 正则化比例系数过高
- 训练批次过少
2.4 反向传播
反向传播就是链式法则的巧妙运用
链式法则:复合函数的导数等于子函数导数之积
sigmoid: ,求导为x(1-x)
relu大于零的部分剃度不变,小于等于0的部分梯度为02.5 CNN
2.5.1 权值共享
所谓的权值共享就是说给一张输入图片,用一个filter去扫这张图,filter里面的数就是权重,这张图每个位置都是被同样的filter扫描,所以权重是一样的,也就是共享。2.5.2 卷积
卷积层的参数量
卷积层的计算量
卷积后的特征图大小2.5.3 池化
平均池化:
mean pooling的前向传播就是把一个patch中的值求取平均来做pooling,那么反向传播的过程也就是把某个元素的梯度等分为n份分配给前一层,这样就保证池化前后的梯度(残差)之和保持不变,还是比较理解的,图示如下
最大池化(Max Pooling):
max pooling也要满足梯度之和不变的原则,max pooling的前向传播是把patch中最大的值传递给后一层,而其他像素的值直接被舍弃掉。那么反向传播也就是把梯度直接传给前一层某一个像素,而其他像素不接受梯度,也就是为0。所以max pooling操作和mean pooling操作不同点在于需要记录下池化操作时到底哪个像素的值是最大,也就是max id,这个可以看caffe源码的pooling_layer.cpp,下面是caffe框架max pooling部分的源码
2.5.4 感受野
感受野(receptive field)的定义是卷积神经网络每一层输出的特征图上的像素点在原始输入图片上映射的区域大小。
感受野的作用:- 一般task要求感受野越大越好,如图像分类中最后卷积层的感受野要大于输入图像,网络深度越深感受也越大性能越好
- 密集预测task要求输出像素的感受野足够的大,确保做出决策时没有忽略重要信息,一般也是越深越好
- 目标检测task中,一般要求anchor<有效感受野<理论感受野
感受野的计算:
si是累乘后的stride,比如32倍下采样的累乘si就是32
什么是有效感受野:
理论感受野中并不是所有像素点的贡献都是一样的,他们的贡献呈高斯分布,也就是说,感受野中间的像素对于输出会有更大的影响。之前大家一般考虑理论感受野,所以用小卷积核的堆叠代替大卷积核。但实际上用大卷积核的有效感受野要比小卷积核的堆叠的有效感受野更大。参考RepLKNet
影响感受野的因素:
- 卷积
- 反卷积
- 池化
- 残差连接
- concat
- 激活函数影响有效感受野(比如relu使得有效感受野变得稀疏)
2.6 归一化方法BN, LN, IN, GN
Batch Normalization (BN)、Layer Normalization (LN)、Instance Normalization (IN)、Group Normalization (GN)
从公式看它们都差不多:无非是减去均值,除以标准差,再施以线性映射:
2.6.1 BN
假设有B本书,书有C页,每页有H行,W列,这其实就是对应着特征图(B, C, H, W)
BN就是对(B, H, W)进行归一化,就是把B本书的第一页的H行W列一起进行归一化
此时:其中 是为了防止方差为0产生无效计算。
# coding;utf8
import torch
from torch import nn
# track_running_stats=False,求当前 batch 真实平均值和标准差,
# affine=False, 只做归一化,不乘以 gamma 加 beta(通过训练才能确定),默认是TRUE
# num_features 为 feature map 的 channel 数目
# eps 设为 0,让官方代码和我们自己的代码结果尽量接近
# eps就是\varepsilon,在分母添加的值,使得数值更加稳定
bn = nn.BatchNorm2d(num_features=3, eps=0, affine=False, track_running_stats=False)
x = torch.randn(10, 3, 5, 5)*10000 #x.shape:[10,3,5,5]
official_bn = bn(x)
# 把 channel 维度单独提出来,而把其它需要求均值和标准差的维度融合到一起
# view之前需要确保tensor存储是连续的contiguous
x1 = x.transpose(0,1).contiguous().view(3,-1)
# x1.mean(dim=1).shape: [3]
mu = x1.mean(dim=1).view(1, 3, 1, 1)
# x1.std(dim=1).shape: [3]
std = x1.std(dim=1, unbiased=False).view(1, 3, 1, 1)
my_bn = (x - mu)/std
diff = (official_bn - my_bn).sum()
print(my_bn)
print('diff={}'.format(diff))
2.6.2 LN
假设有B本书,书有C页,每页有H行,W列,这其实就是对应着特征图(B, C, H, W)
LN就是对(C, H, W)进行归一化,就是把1本书的C页H行W列一起进行归一化
BN的一个缺点就是较大的batchsize才能合理的估计数据集整体的均值和方差,这可能会导致内存不够用,特别是对于分割网络。LN的有事就是不需要批训练,在单条数据内部就能归一化。
# coding;utf8
import torch
from torch import nn
x = torch.randn(10, 3, 5, 5)*10000 #x.shape:[10,3,5,5]
# normalization_shape 相当于告诉程序这本书有多少页,每页多少行多少列
# eps=0 排除干扰,同BN
# elementwise_affine=False 不作映射
# 这里的映射和 BN 以及下文的 IN 有区别,它是 elementwise 的 affine,
# 即 gamma 和 beta 不是 channel 维的向量,而是维度等于 normalized_shape 的矩阵
ln = nn.LayerNorm(normalized_shape=[3,5,5], eps=0, elementwise_affine = False)
official_ln = ln(x)
# 把 channel 维度单独提出来,而把其它需要求均值和标准差的维度融合到一起
x1 = x.contiguous().view(10, -1)
# x1.mean(dim=1).shape: [10]
mu = x1.mean(dim=1).view(10, 1, 1, 1)
# x1.std(dim=1).shape: [3]
std = x1.std(dim=1, unbiased=False).view(10, 1, 1, 1)
my_ln = (x - mu)/std
diff = (official_ln - my_ln).sum()
print(my_ln)
print('diff={}'.format(diff))
2.6.3 IN
假设有B本书,书有C页,每页有H行,W列,这其实就是对应着特征图(B, C, H, W)
IN就是对(H, W)进行归一化,就是把1本书的1页的H行W列一起进行归一化
2.6.4 GN
Group Normalization (GN) 适用于占用显存比较大的任务,例如图像分割。对这类任务,可能 batchsize 只能是个位数,再大显存就不够用了。而当 batchsize 是个位数时,BN 的表现很差,因为没办法通过几个样本的数据量,来近似总体的均值和标准差。GN 也是独立于 batch 的,它是 LN 和 IN 的折中。
GN 计算均值和标准差时,把每一个样本 feature map 的 channel 分成 G 组,每组将有 C/G 个 channel,然后将这些 channel 中的元素求均值和标准差。各组 channel 用其对应的归一化参数独立地归一化。
2.6.5 BN详解
为什么要BN?
深度神经网络之所以如此难训练,其中一个重要原因就是网络中层与层之间存在高度的关联性与耦合性。随着训练的进行,网络的权重参数随着训练不断地更新,这意味着特征图在网络不同位置的数据分布也在改变,所以深层网络一直在适应分布持续变化的特征图,使得深层难以适应这些分布变化,训练收敛困难,这被称为ICS问题internal covariate shift。ICS产生的原因是由于参数更新带来的网络中每一层输入值分布的改变,并且随着网络层数的加深而变得更加严重,因此我们可以通过固定每一层网络输入值的分布来对减缓ICS问题。
ICS是什么问题?
上层网络需要不停调整来适应输入数据分布的变化,导致网络学习速度的降低
网络的训练过程容易陷入梯度饱和区,减缓网络收敛速度。比如假设网络为线性映射y=ax+b,当a很大时,y也很大,随后使用sigmoid或tanh激活函数之后的值无限接近于1,这样子计算的梯度就会接近于0,而随着网络层数的加深,梯度累计会越来越小,越接近于0,这样参数的更新就会很慢,收敛变慢
怎么解决ICS?
第一个,白化(whiten),PCA使得均值为0,方差为1,ZCA使得均值为0,方差相同(不一定为1)
第二个,BN。白化的计算成本太高,并且白化使得网络丢失了每层的数据的表达能力。所以BN就是在PCA记得基础上加一个线性映射
测试阶段如何使用BN?
训练中每一层计算的均值和方差都来源于当前batch的训练数据,可是测试阶段可能只有一个样本或很少的样本,此时用测试数据来计算均值一定得到的是有偏估计。
所以BN在训练过程中,我们保留了每组mini-batch训练数据在网络中每一层的 与 。此时我们使用整个样本的统计量来对Test数据进行归一化,
BN的有点:
- BN使得网络中每层输入数据的分布相对稳定,加速模型学习速度
BN通过规范化与线性变换使得每一层网络的输入数据的均值与方差都在一定范围内,使得后一层网络不必不断去适应底层网络中输入的变化,从而实现了网络中层与层之间的解耦,允许每一层进行独立学习,有利于提高整个神经网络的学习速度。 - BN使得模型对网络中的参数不那么敏感,简化调参过程,使得网络学习更加稳定
这主要说的是学习率,在过去如果学习率过大,会使得网络在局部最优点反复横跳,无法收敛。加了BN之后可以让最终的梯度处在一个比较稳定的值,参数收敛更加稳定 - BN允许网络使用饱和性激活函数(例如sigmoid,tanh等),缓解梯度消失问题
在不使用BN层的时候,由于网络的深度与复杂性,很容易使得底层网络变化累积到深层网络中,导致模型的训练很容易进入到激活函数的梯度饱和区;通过normalize操作可以让激活函数的输入数据落在梯度非饱和区,缓解梯度消失的问题;另外通过自适应学习 与 又让数据保留更多的原始信息。 - BN有一定的正则化效果
由于我们是用mini-batch中的均值和方差对整个训练样本进行估计,尽管每一个batch中的数据都是从总体样本中抽样得到,但不同mini-batch的均值与方差会有所不同,这就为网络的学习过程中增加了随机噪音,与Dropout通过关闭神经元给网络训练带来噪音类似,在一定程度上对模型起到了正则化的效果。2.7 梯度下降法GD gradient descent
梯度定义:梯度的本意是一个向量(矢量),表示函数在该店处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大。
梯度表示上升最快的方向
机器学习或深度学习模型都以凸函数(convex function)作为损失函数,到底什么是凸函数呢?
凸函数定义:凸函数指某一类函数,可以是二维的可能是多维。我的理解是用一个截平面与凸函数相交,相交的区间内有唯一的最小值。可以理解为凸函数是一种碗状的函数,碗口朝上,任何平面与之交叉都能在这个区间内找到唯一的最小值。
非凸函数:存在多个极小值
梯度下降法就是:利用目标函数计算出权重参数的梯度,再乘以一个步长获得下降的值,用这个值更新所有的参数,然后重复这个过程
步骤:
- 确认推理模型和损失函数
- 推理模型参数初始化,步长a
- 推理一遍,确定每个参数位置关于损失函数的梯度
- 用步长乘以损失函数的梯度,得到当前位置下降的距离
- 更新所有的参数
- 重复这个过程,直到算法不再收敛
缺点:
- 不能保证收敛到全局最优解
- 靠近极小值收敛速度变慢
- 可能会出现之字形下降
2.7.1 BGD
batch gradient descent批梯度下降,就是一次迭代训练所有样本,它得到的是一个全局最优解,缺点是一次性要将所有数据放入内存中,当数据集比较大时,计算资源吃不消,迭代速度极慢。
Batch gradient descent的优点是理想状态下经过足够多的迭代后可以达到全局最优。但是缺点也很明显,就是如果你的数据集非常的大(现在很常见),根本没法全部塞到内存(显存)里,所以BGD对于小样本还行,大数据集就没法娱乐了。而且因为每次迭代都要计算全部的样本,所以对于大数据量会非常的慢。
2.7.2 SGD
stochastic gradient descent随机梯度下降,SGD,每次只训练一个样本去更新参数,这样对计算资源的要求要小很多,缺点也很明显,就是每个样本的数据分布不一样,可能含有不同的噪音,导致训练的不稳定性很大,使得SGD并不是每次迭代都向着全局最优的方向下降,更可能是以一种之字型朝着局部最优点前进
2.7.3 mini-batch SGD
现在深度学习中,基本上都是用 mini-batch gradient descent,(在深度学习中,很多直接把mini-batch gradient descent(a.k.a stochastic mini-batch gradient descent)简称为SGD,所以当你看到深度学习中的SGD,一般指的就是mini-batch gradient descent)。
mini-batch梯度下降法的区别就是,选择一个batch大小的数据而不是一条数据进行计算,得到了B个关于参数的梯度,再对梯度进行加权平均,然后才进行一次参数的更新,所以没batch个数据更新一次参数,全部数据集一共更新M/B次。
优点:
- 节省内存:很好理解,一次只讲batch_size个数据放到显存中,不像BGD是所有数据
- 有利于逃离鞍点:因为mini-batch是随机选取的,每次选取的数据分布会不一样,噪声也会不同,所以不会让参数停留在鞍点上
2.7.4 动量优化法
动量优化方法是在梯度下降法的基础上进行的改变,具有加速梯度下降的作用。一般有标准动量优化方法Momentum、NAG(Nesterov accelerated gradient)动量优化方法。
Momentum优化表达式为:
其中,vtvt表示tt时刻积攒的加速度。αα表示动力的大小,一般取值为0.9(表示最大速度10倍于SGD)。ΔJ(Wt,X(is),Y(is))ΔJ(Wt,X(is),Y(is))含义见SGD算法。WtWt表示tt时刻模型参数。
- 动量主要解决SGD的两个问题:一是随机梯度的方法(引入的噪声);二是Hessian矩阵病态问题(可以理解为SGD在收敛过程中和正确梯度相比来回摆动比较大的问题)。
- 理解策略为:由于当前权值的改变会受到上一次权值改变的影响,类似于小球向下滚动的时候带上了惯性。这样可以加快小球向下滚动的速度。
- 记住这个:在更新时一定程度上保留之前的更新方向,一方面保留之前的梯度方向,另一方面计算新的梯度方向,如果两个方向相同则加速,相反则减速。总之动量可加速SGD收敛,抑制震荡。
2.8 优化器
SGD
SGD+momentum(一阶动量)
所以基本上动量SGD是沿着上一时刻的梯度在走
AdaGrad(二阶动量)
考虑了历史时刻所有的梯度,所以学习率是单调下降的,更新越频繁的参数,二阶动量越大,学习率越小,缺点就是学习率很快单调递减至0,训练提前结束。这里没有用一阶动量。
AdaDelta/RMSProp(窗口二阶动量)
不单纯的考虑过去所有时刻的梯度,考虑过去一段时间的梯度(或者说离现在时刻越远,考虑的越少)
这就避免了二阶动量持续积累,导致训练过程提前结束的问题了。
Adam(一阶动量+二阶动量,Adaptive momentum)
,这是一阶动量
,这二阶动量
adam是综合表现最好的优化器了
SGD和adam的优缺点:
SGD | 1. 内存要求小 1. 收敛速度加快(更新次数多) 1. 能够在线学习 |
更新值的方差较大,收敛过程会产生波动,可能落入极小值,选择合适的学习率比较困难 | 适用于需要在线更新的模型,适用于大规模训练样本情况 |
---|---|---|---|
Momentum | 能够在相关方向加速SGD,抑制振荡,从而加快收敛 | 需要人工设定学习率 | 适用于有可靠的初始化参数 |
Adam | 1. 对内存需求较小(不是历史所有梯度积累) 1. 为不同的参数计算不同的自适应学习率(更新次数多的学习率低) 1. 善于处理稀疏梯度 1. 适用性高,适用于大多非凸优化问题 |
需要快速收敛,训练复杂网络时;善于处理稀疏梯度和处理非平稳目标的优点,也适用于大多非凸优化 - 适用于大数据集和高维空间 |
2.9 激活函数
每个激活函数的输入都是一个数字,然后对其进行某种固定的数学操作。激活函数给神经元引入了非线性因素,如果不使用激活函数的话,无论神经网络有多少层,输出都是输入的线性组合。
直线是线性的,曲线是非线性的
Sigmoid
优点:平滑、易于求导
缺点:
- 激活函数计算量大
- 是饱和激活函数,在函数两侧的梯度接近于0,当多个接近于0的梯度相乘的时候很容易出现梯度消失的情况,从而无法完成深层网络的训练
- sigmoid函数的输出恒为正值,不是以零为中心的,这会导致权值更新时只能朝一个方向更新,从而影响收敛速度。
tanh双曲正切函数
它将实数值压缩到(-1,1)之间。
Tanh解决了Sigmoid的输出非0中心的问题,但仍然存在饱和问题。
为了防止饱和,现在主流的做法会在激活函数前多做一步Batch Normalization,尽可能保证每一层网络的输入呈均值较小、0中心的分布。
ReLU(线性整流函数(Linear rectification function))
- 仿生物学原理,只激活需要激活的神经元,而sigmoid在输入为0的时候已经为1/2,不符合实际生物学对模拟神经网络的期望
- 速度非常快
- 更有效率的梯度下降以及反向传播、避免了梯度爆炸和梯度消失问题
缺点:
- 杀死神经元,因为当输入为负时,梯度为0,神经元不再更新,就会一直输入0。可以通过合理的设置学习率降低神经元死掉的概率
Leaky ReLU
很明显,设置一个超参数,使得小于0的数字不再为0,并且也能计算梯度。注意等于0的时候梯度等于xita
PReLU
如果将xita作为一个深度学习的可学习参数,那么久变成PReLU,一个PRELU占一个参数量
ReLU6
当值大于等于6的时候等于6
当做嵌入式的时候,常常用到float16,float16,其中1个符号位,5个指数位,10个有效精度位。所以不看小数点的话,float16最大能表示2的5次方=32,那如果激活函数之后的值超过了32,float16无法表示,会存在训练和推理存在误差的情况,所以干脆在训练时就进行截断,为什么取6是因为实验发现6比较好
Softmax
只会被用在网络的最后一层,用来进行最后的分类和归一化
值得和为1,如果类别数为2,那么退化为sigmoid
2.10 深度可分离卷积、分组卷积、扩张卷积、转置卷积
深度可分离卷积
深度可分离卷积分为depth-wise卷积核point-wise卷积
depth-wise卷积空间上的卷积,只卷一层
point-wise卷积是点卷积,是通道上的卷积,垂直意义上的卷积,将不同通道的数据进行特征提取
深度可分离卷积在mobilenetv1中提出,使用深度可分离卷积可以使参数量减小
分组卷积
深度卷积是分组卷积的一种特殊情况,普通卷积的卷积核通道数为特征图的通道数,分组卷积,加入分为G组,则卷积核的通道数是C1/G,假如输出通道数为C2,那每组卷积核的数量就是C2/G,每组卷积核之间是共享参数的
空洞卷积
空洞卷积和普通卷积的区别就是多了一个扩张的概念,扩张率dilation rate,是一个超参数,扩张率为1的时候就是正常的卷积,为2及以上就位空洞卷积
空洞卷积可以用相同的参数获取更大的感受野,比如说一个3x3的卷积,扩张率为2,那他的实际感受野和5x5的卷积是一样的
虽然空洞卷积提出来能提升效果,但是工业上似乎不怎么喜欢,原因是空洞卷积的算子不好优化,一个很直观的解释就是推理的时候,空洞卷积需要把输入张量跳跃的提取,比如隔一个像素点提取一次,而不是连续的提取,这样的话其实读取效率就很低,空洞卷积的性价比就比较低。这个我是挺老师说的
转置卷积
上菜样的方法有很多,有最近邻插值法、线性插值法、双线性插值法
转置卷积有时候也被称为反卷积(deconvolution),这是有点误导的,因为转置卷积并不是正常卷积的直接取反或者转置,我们的卷积过程从信息论上来说是不可逆的。
然后具体实现方法的话是在输入图上加上padding,如果stride>1,还会给输入图像的像素之间加入空隙,再进行正常的普通卷积
2.11 手写代码损失函数
交叉熵
交叉熵是信息论里面的一个重要概念,主要用于度量两个概率分布之间的差异性。在深度学习里面就用来度量模型输出与标签之间的差异,也就是当做损失函数。
其中,表示类别数,是一个one-hot向量,元素只有和两种取值,如果该类别和样本的类别相同就取,否则取,至于表示预测样本属于的概率。
当类别数等于的时候,这个损失就是二元交叉熵,在Pytorch中提供了一个单独的实现。
nn.CrossEntropyLoss(input, target) # 多分类
nn.BCELoss(F.sigmoid(input), target) # 二分类
带权重交叉熵Loss
Nc表示GT类别为c的像素的个数。这样类别像素越多的loss就越小,类别像素越少的loss就越大
Dice loss
dice loss适用于样本极度不均衡的情况,一般情况下使用dice loss会对反向传播不利的影响,使得训练不稳定
Focal loss
Focal loss是何开明在retinanet中提出的,用于解决样本不平衡的问题。
带权重的CE是从样本分布比例的角度添加权重因子,Focal loss则是从样本分类难易度出发,是loss聚焦于难分样本
pt可以理解为预测的值,越接近于1,也就是越容易预测,测这个loss越小,而如果越接近于0,则loss越大,loss聚焦于困难样本
gamma被称作focusing parameter关注参数,是≥0的,=0的时候就退化为原始的交叉熵,经验来说gamma=2效果最好
IoU
def compute_iou(b1, b2):
x11, y11, x12, y12 = b1
x21, y21, x22, y22 = b2
#计算两个矩形的面积
s1 = (x12 - x11) * (y12 - y11)
s2 = (x22 - x21) * (y22 - y21)
# 计算交叉矩形
x_min = max(x11, x21)
y_min = max(y11, y21)
x_max = max(x12, x22)
y_max = max(y12, y22)
w = max(0, x_max - x_min)
h = max(0, y_max - y_min)
intersection = w * h
union = s1 + s2 - intersection
iou = intersection / union
return iou
IOU:
GIOU:
DIOU: ,其中\rho表示两点的欧几里得距离,b表示框的中心点位置,c表示最小外界矩形的对角线长度
CIOU:
a为权重系数。v这一项考虑了长宽比
3. 常见网络
3.1 AlexNet
alexnet是2012年imagenet竞赛比赛的冠军获得者所提出的网络,可以它打开了深度学习的大门,越来越多的人关注到了深度神经网络。它有非常高的历史意义,但是里面的技术都比较老了
- 首次使用GPU加速训练
- 使用relu激活函数而不是sigmoid或tanh
- 在全连接层前面使用了dropout技术
3.2 VGGNet
VGG是牛津大学2014年提出的的Visual Geometry Group的组提出的(大家应该能看出VGG名字的由来了)。该网络的特点就是深,突出了深度神经网络深的特点。作者证明了增加网络的深度能够在一定程度上提升网络最终的性能。VGG有VGG11,VGG16,VGG19,其实本质上没有什么区别,都是卷积和池化层的叠加,最后跟上3层全连接层,但是深度不一样(maxpool不包括在16里面)
改进点:
- 使用更小的33卷积核,和更深的网络。两个33卷积核的堆叠相对于55卷积核的视野,三个33卷积核的堆叠相当于77卷积核的视野。这样一方面可以减少参数(3个堆叠的33结构只有77结构参数数量的(333)/(77)=55%);另一方面拥有更多的非线性变换,增加了CNN对特征的学习能力。
- 引入了1*1的卷积核,在不影响输入输出维度的情况下,引入非线性变换,增加网络的表达能力,降低计算量
- 验证了加深网络能提升性能
缺点:
- 耗费更多的计算资源,其中绝大多数来自于第一个全连接层
说道感受野,我想到了前段时间看到一篇比较有意思的论文,他的名字叫RepLKNet,是CVPR2022的文章,他在论文开始现提出了一个有效感受野的概念,最早不是他提出的。就是两个3x3的感受野虽然等于一个5x5的卷积的感受野,但是这个是理论感受野,他们的实际感受野是不一样的,如果说把一个5x5的区域里面的像素做一个相关性比较,区域中间的相关性要更大一些,而越往边边角角相关性将越小。可以理解为中间为一个峰值,然后以正态分布四周降低。但是如果直接用大卷积核,他的实际感受野就要比小卷积核的堆叠要好。我们知道大卷积核的缺点是参数量大,作者用1x1卷积降维来抑制这个缺点,最终实现比较好的效果。
这个是RepLKNet,然后还要说道2020年的RepVGGNet,也是特别有意思的一篇文章。他的核心思想是参数重构化。VGG是简单的3x3卷积核2x2池化层的堆叠,作者为了提升网络的表达能力,作者在训练的时候使用了多分支策略,比如说一个卷积层,本来只有3x3一个卷积,它额外的增加了1x1卷积的和残差连接的支路。然后把3个支路的输出加过累加起来。这个是训练的过程,然后再推理的时候,将这3个支路合并为一条支路,这样子参数量和计算量就直线下降,还能拥有多支路的表达能力。具体怎么合并的呢,就是将其他支路重构成3x3的卷积核大小,比如说1x1卷积核,是可以编程3x3的,只要将1x1卷积核周围添加一圈参数为0的数字就变成了3x3,然后残差链接呢,输入输出是一样的,所以只要令这个3x3的卷积核在处理完输入之后,输出保持不变即可,残差链接相当于一个特殊权重的3x3卷积层。于是就可以吧3个3x3卷积核在参数上累加起来,就变成了一个分支。这个是RepVGGNet。
3.3 ResNet
在了解ResNet之前,我们需要知道目前CNN训练存在两大问题:
- 梯度消失与梯度爆炸:当网络很深,每层的梯度大于1或小于1时,根据链式法则累积下来梯度会趋近于0或者无穷大,就造成梯度消失和梯度爆炸。导致不能收敛或者训练失败。
然而梯度弥散/爆炸在很大程度上被合适的激活函数(ReLU)、流弊的网络初始化(Kaiming初始化、BN等Tricks)处理了。 - 退化:随着层数的增加,预测效果反而越来越差。
提出残差结构,人为地让神经网络某些层跳过下一层神经元的连接,隔层相连,弱化每层之间的强联系。
resnet18和34用basicblock,50和101用bottleneck
残差是什么意思?我理解是这样,输入为x,identity连接也是x,带有非线性映射的支路为f(x),那么最后的输出其实是y = f(x) + x,统计学里面残差的意思是观察值和估计值之间的差,这里观察值就是输入,估计值就是输出,那么他们的差就是f(x),正因为有了identity连接才有了残差这一单独的东西出来。
为什么resnet效果好:
- 他保证了至少不会网络越深效果越差。理论上,resnet提供了identity连接和残差连接,而普通的深度神经网络是只有残差连接部分的,所以理论上resnet可以比同等深度的神经网络效果好,也就是只走identity的部分,剩下的残差部分做一个恒等映射即可。
为什么resnet一开始用7x7的卷积核而不用basicblock?
原因: 7x7卷积实际上是用来直接对输入图片降采样(early downsampling), 注意像7x7这样的大卷积核一般只出现在input layer
目的是: 尽可能保留原始图像的信息, 而不需要增加channels数.
3.4 YOLO
双阶段网络:RCNN,FAST RCNN, FASTER RCNN
一阶段网络:YOLO, SSD
YOLOV1
yolov1其实是一种anchor-free的目标检测算法,他通过网格化的预测方式,预测出目标的中心店还有长和宽
其实他的网络设计非常简单,就是卷积、下采样,最后全连接层,然后reshape成一个7x7x30的特征图
缺点:
- YOLO对相互靠的很近的物体和很小的群体检测效果不好,这是因为一个网格中只预测了两个框,并且只属于一类;
- 同一类物体出现的新的不常见的长宽比和其他情况时,泛化能力偏弱;
- 由于损失函数的问题,定位误差是影响检测效果的主要原因。尤其是大小物体的处理上,还有待加强。
YOLOv3开始,网络结构发生了改变。利用了FPN的思想,网络输出了3个不同尺度的特征图,用于检测不同大小的物体,使得YOLO算法对小物体检测更加友好
YOLOV4和V5的话没有太大的创新,基本上就是新的basicblock,马赛克数据增强,新的FPN形势,还有就是使用CIOU loss
3.5 Swin Transformer
relative position
相对位置编码,其实我觉得应该叫相对距离编码,因为它是以像素为单位或者说以patch为单位,离你一个距离那距离就是1,无论是横坐标层面上还是众坐标层面
self.relative_position_bias_table=nn.Parameter(torch.zeros((2window_size[0] -1) (2window_size[1] -1), num_heads)) # 2Wh-1 2Ww-1, nH 创建一个1313=169的网格,只看行的话有6+6+1种不同的相对位置,左边6个右边6个自己(0,0)一个 torch.arange(7) torch.meshgrid([h, w]) torch.flatten(coord, 1) coords_flatten[:, :, None] -coords_flatten[:, None, :] relative_coords.permute(1, 2, 0).contiguous() relative_coords[:, :, 0] +=self.window_size[0] -1 relative_coords[:, :, 1] +=self.window_size[1] -1 relative_coords[:, :, 0] =2self.window_size[1] -1 relative_position_index=relative_coords.sum(-1) # WhWw, Wh*Ww self.register_buffer(“relative_position_index”, relative_position_index)
attention mask
原本swin transformer是在窗口内进行局部的自注意力计算,如果窗口一直不变的话的,一个窗口是没有办法和另一个窗口的内容发生关联的。所以作者提出了一个shift window的操作,就是将特征图进行一个roll的操作,同时要配合上attention mask。
现在我们想象一个44的特征图,然后窗口的大小是22,那就一共有四个窗口。现在我想让一个窗口的信息和其他窗口有交互,那做法就是把窗口往右下角移动一格。这个时候就会有一个问题,左边和上边的patch并没有被窗口所覆盖,很直观的做法就是也给他们创建一个窗口。可是这样子的话窗口的数量就翻了一倍,这样子会增加计算量而且代码得重新写,因为有一些窗口不是正方形了。
然后作者就想到了一个shift的做法,把特征图roll一下。我们把之前roll之前的窗口标上id,应该是有9个。经过roll之后呢,左边的窗口就到了右边,上边的窗口就到了下边,这样子的话我就又可以用正方形的窗口对他们进行划分了。但是现在还有最后一个问题
如果我重新划分窗口,会让原本窗口id不同的窗口进行自注意力计算,这个是不合理的,自注意力应该是相邻的区域进行计算而不是跨过一片区域和另一篇区域让左上角和右下角进行计算。然后作者就提出了一个很巧妙的做法,就是用attention mask,通过合理的设置attention mask,可以让不应该在一起计算的部分attention分数降到很低,具体实现是加上一个-100,然后通过softmax之后这部分区域的attention值就基本为0。刚才说的是为什么要attention mask。
我们现在给特征图上的每个window标上id,并且做一个shift也就是roll的动作。不难想象,原本属于左上角