目录
feature scaling 特征缩放、归一化、标准化、中心化
归一化、正则化、BN、IN、LN、GN、BN原理
参考了很多博客,转载的
feature scaling 特征缩放、归一化、标准化、中心化
什么是特征缩放
特征缩放的目标就是数据规范化,使得特征的范围具有可比性。它是数据处理的预处理处理,对后面的使用数据具有关键作用。
机器算法为什么要特征缩放
主要因为两种情况:
-
特征间的单位(尺度)可能不同。比如预测一个值需要很多个特征,这些特征的尺度不同,尺度大的特征会起决定性作用,而尺度小的特征其作用可能会被忽略,为了消除特征间单位和尺度差异的影响,以对每维特征同等看待,需要对特征进行归一化。如:身高和体重,比如摄氏度和华氏度,一个特征的变化范围是[1000,10000],另一个特征的变化范围是[−0.1,0.2],距离有关的计算时,单位不同会导致计算结果不同,因此:有些模型在各个维度进行不均匀伸缩后,最优解与原来不等价,例如SVM。对于这样的模型,除非本来各维数据的分布范围就比较接近,否则必须进行标准化,以免模型参数被分布范围较大或较小的数据dominate。
-
原始特征下,因尺度差异,其损失函数的等高线图可能是椭圆形,梯度方向垂直于等高线,下降会走zigzag z字型路线,而不是指向local minimum。通过对特征进行zero-mean and unit-variance变换后,其损失函数的等高线图更接近圆形,梯度下降的方向震荡更小,收敛更快,如下图所示,图片来自Andrew Ng。
因此有些模型在各个维度进行不均匀伸缩后,最优解与原来等价,例如logistic regression。对于这样的模型,是否标准化理论上不会改变最优解。但是,由于实际求解往往使用迭代算法,如果目标函数的形状太“扁”,迭代算法可能收敛得很慢甚至不收敛。所以对于具有伸缩不变性的模型,最好也进行数据标准化。
总之:1)特征缩放还可以使机器学习算法工作的更好。比如在K近邻算法中,分类器主要是计算两点之间的欧几里得距离,如果一个特征比其它的特征有更大的范围值,那么距离将会被这个特征值所主导。因此每个特征应该被归一化,比如将取值范围处理为0到1之间。2)第二个原因则是,特征缩放也可以让其损失函数的等高线图更接近圆形,加快梯度收敛的速度。
feature scaling的方法可以分成2类,逐行进行和逐列进行。逐行是对每一维特征操作,逐列是对每个样本操作
标准化(Standardization)-常用feature scaling方法
特点:数据的标准化是将数据按比例缩放,使之落入一个小的特定区间。
主要算法:
-
z-score标准化又叫中心化,即零-均值标准化(常用方法)y=(x-μ)/σ 是一种统计的处理,基于正态分布的假设,将数据变换为均值为0、标准差为1的标准正态分布。但即使数据不服从正态分布,也可以用此法。特别适用于数据的最大值和最小值未知,或存在孤立点。原始数据(近似)最好高斯分布。否则归一化后的效果可能会很差。
-
非线性归一化 如小数定标标准化 y=x/10^j (j确保max(|y|)<1)通过移动x的小数位置进行标准化、对数Logistic模式 y=1/(1+e^(-x))
中心化 -PCA示例
在做PCA的时候,我们需要找出矩阵的特征向量,也就是主成分(PC)。比如说找到的第一个特征向量是a = [1, 2],a在坐标平面上就是从原点出发到点 (1,2)的一个向量。去掉量纲,将不同量纲的数据拉伸至同一水平。公平比较。
如果没有对数据做中心化,那算出来的第一主成分的方向可能就不是一个可以“描述”(或者说“概括”)数据的方向(看图)。黑色线就是第一主成分的方向。只有中心化数据之后,计算得到的方向才能比较好的“概括”原来的数据(图2)
从线性变换的本质来说,PCA就是在线性空间做一个旋转(数据矩阵右乘协方差矩阵的特征向量矩阵),然后取低维子空间(实际上就是前n_components个特征向量张成的子空间)上的投影点来代替原本的点,以达到降维的目的,注意我说的,只做了旋转,没有平移,所以首先你要保证原本空间里的点是以原点为中心分布的,这样做的好处是能够使得求解协方差矩阵变得更容易。
具体来说,中心化之后原特征矩阵其实就是 这样,X样本的协方差矩阵为:
(协方差计算方法为: ) 而x/y都中心化了,μ就是0.
可以很好得简化协方差的计算。因此:PCA的第一步就是要去均值化。
归一化(Normalization)-常用feature scaling方法
特点:是标准化的特殊形式。1)把数据变为(0,1)之间的小数。主要是为了方便数据处理,因为将数据映射到0~1范围之内,可以使处理过程更加便捷、快速。2)把有量纲表达式变换为无量纲表达式,成为纯量。经过归一化处理的数据,处于同一数量级,可以消除指标之间的量纲和量纲单位的影响,提高不同数据指标之间的可比性。
主要算法:
-
线性归一化,即min-max归一化(常用方法)y=(x-min)/(max-min) 注意:实际使用时,不同样本集得到的max/min可能不同,造成归一化结果不稳定,从而使模型后续使用也不稳定。可以经验值来代替max/min,比如人的年龄,max=100,min=0。避免不同样本集max/min的不同造成的模型偏差。
-
非线性归一化(Nonlinear Scaling)包括对数log,指数e,正切等。如:对数函数转换 y=log10(x)、反余切函数转换 y=atan(x)*2/PI 应用场景:数据分化比较大,有些很大,有些很小,可能用此方法将数值映射到一个比较小的范围进行处理。
缩放到单位长度(Scaling to unit length)-常用feature scaling方法
x=x / ||x|| 就是除以向量的欧拉长度( the Euclidean length of the vector),二维范数。
方法的选择
1、对于需要求距离的分类、聚类、相似度、协方差等,数据符合或者近似符合高斯正态分布时,PCA降维时,常用0均值标准化,可以得到较好的效果。
2、对于其他情况,如果数据分化不是很大,可以用线性归一化处理。在不涉及距离度量、协方差计算、数据不符合正太分布的时候,可以使用第一种方法或其他归一化方法。比如图像处理中,将RGB图像转换为灰度图像后将其值限定在[0 255]的范围。
3、如果数据分化很大,可以用非线性归一化处理。
什么时候不需要Feature Scaling?
-
与距离计算无关的概率模型,不需要feature scaling,比如Naive Bayes;
-
与距离计算无关的基于树的模型,不需要feature scaling,比如决策树、随机森林等,树中节点的选择只关注当前特征在哪里切分对分类更好,即只在意特征内部的相对大小,而与特征间的相对大小无关。
批量归一化 BN、IN、Ln、GN、BN
为什么要BN
随机梯度下降法(SGD)对于训练深度网络简单高效,但是它有个毛病,就是需要我们人为的去选择参数,比如学习率、参数初始化、权重衰减系数、Drop out比例等。这些参数的选择对训练结果至关重要,以至于我们很多时间都浪费在这些的调参上。那么使用BN(详见论文《Batch Normalization_ Accelerating Deep Network Training by Reducing Internal Covariate Shift》)之后,你可以不需要那么刻意的慢慢调整参数。
神经网络一旦训练起来,那么参数就要发生更新,除了输入层的数据外(因为输入层数据,我们已经人为的为每个样本归一化),后面网络每一层的输入数据分布是一直在发生变化的,因为在训练的时候,前面层训练参数的更新将导致后面层输入数据分布的变化。 数据分布的改变称之为:“Internal Covariate Shift”。Paper所提出的算法,就是要解决在训练过程中,中间层数据分布发生改变的情况。与激活函数层、卷积层、全连接层、池化层一样,BN(Batch Normalization)也属于网络的一层,但是不像之前的归一化方法,它是一个可学习、有参数(γ、β)的网络层。BN往往用在深度神经网络中激活层之前。其作用可以加快模型训练时的收敛速度,使得模型训练过程更加稳定,避免梯度爆炸或者梯度消失。并且起到一定的正则化作用,几乎代替了Dropout。
BN
如果是仅仅使用归一化公式,对网络某一层A的输出数据做归一化,然后送入网络下一层B,这样是会影响到本层网络A所学习到的特征的。比如我网络中间某一层学习到特征数据本身就分布在S型激活函数的两侧,你强制把它给我归一化处理、标准差也限制在了1,把数据变换成分布于s函数的中间部分,这样就相当于我这一层网络所学习到的特征分布被你搞坏了,这可怎么办?
BN的正向传播
在网络的每一层输入的时候,d维输入 ,又插入了一个归一化层,也就是先做一个均值归一化处理(归一化至:均值0、方差为1),归一化每一维度
,然后
其中γ、β是可学习的,在一定程度上保留原数据的分布。
训练时,均值、方差分别是该批次内数据相应维度的均值与方差;
推理时,均值、方差是基于所有批次的期望计算所得,公式如下:
其中
表示 x 的期望。
sample_mean = np.mean(x,axis=0)#可以看作滑动平均,相对于gamma和beta,要注意时对样本求均值和方差,不要直接用mean和var公式而不加axis参数。
sample_var = np.var(x,axis=0)
x_normalized = (x - sample_mean) / np.sqrt(sample_var + eps)
out = gamma * x_normalized + beta
cache = (x_normalized, gamma, beta, sample_mean, sample_var, x, eps)
BN的反向传播
可以把BN按照下图进行每一步提取。反向传播就比较明确了。通过图,我们可以发现D是特征维度,也就是C;N是batch-size,X是(N,X,Y,D),正则化之后shape不变。g和b是(D,)
yi=γxi^+β dgamma=xi^dout dbata=dout
def batchnorm_backward(dout, cache):
"""
Backward pass for batch normalization.
For this implementation, you should write out a computation graph for
batch normalization on paper and propagate gradients backward through
intermediate nodes.
Inputs:
- dout: Upstream derivatives, of shape (N, D)
- cache: Variable of intermediates from batchnorm_forward.
Returns a tuple of:
- dx: Gradient with respect to inputs x, of shape (N, D)
- dgamma: Gradient with respect to scale parameter gamma, of shape (D,)
- dbeta: Gradient with respect to shift parameter beta, of shape (D,)
"""
x_normalized, gamma, beta, sample_mean, sample_var, x, eps = cache
N, D = x.shape #D->D,x,y
dx, dgamma, dbeta = None, None, None
dbeta = np.sum(dout,axis=0).reshape(D,)
dgamma = np.sum(dout*(x_normalized),axis=0).reshape(D,)
dx_normalized = gamma*dout
x_denominator = 1/np.sqrt(sample_var+eps)
x_numerator = x - sample_mean
#print("dbeta:",dbeta)
#print("dgamma:",dgamma)
dx_norm_numerator = x_denominator*dx_normalized
dx_norm_denominator = np.sum(x_numerator*dx_normalized,axis=0)
#print("denominator:",dx_norm_denominator)
dx_var = -(1/2)*(1/np.sqrt(sample_var + eps))*(1/(sample_var + eps))*dx_norm_denominator
#print("var:",dx_var)
dx_minus_flow_2 = 2*(x-sample_mean)*(1/N)*np.ones((N,D))*dx_var
dx_minus_flow_1 = dx_norm_numerator
#print("flow_x2:",dx_minus_flow_2)
#print("flow_x1,numerator:",dx_minus_flow_1)
dx_flow_1 = dx_minus_flow_2+dx_minus_flow_1
dx_flow_2 = -(1/N)*np.ones((N,D))*np.sum(dx_flow_1,axis=0)
dx = dx_flow_1 + dx_flow_2
return dx, dgamma, dbeta
可以参考:https://zhuanlan.zhihu.com/p/26138673
为什么BN的使用可以不使用dropout?
你再也不用去理会过拟合中drop out、L2正则项参数的选择问题,采用BN算法后,你可以移除这两项了参数,或者可以选择更小的L2正则约束参数了,因为BN具有提高网络泛化能力的特性;因为BN基于一个mini batch的数据计算均值和方差,而不是基于整个Training set来做,相当于进行梯度计算式引入噪声,相当于数据增强了。因此不容易过拟合。
BN的作用:
1)改善流经网络的梯度
2)允许更大的学习率,大幅提高训练速度:
你可以选择比较大的初始学习率,让你的训练速度飙涨。以前还需要慢慢调整学习率,甚至在网络训练到一半的时候,还需要想着学习率进一步调小的比例选择多少比较合适,现在我们可以采用初始很大的学习率,然后学习率的衰减速度也很大,因为这个算法收敛很快。当然这个算法即使你选择了较小的学习率,也比以前的收敛速度快,因为它具有快速训练收敛的特性;
3)减少对初始化的强烈依赖
4)改善正则化策略:作为正则化的一种形式,轻微减少了对dropout的需求
你再也不用去理会过拟合中drop out、L2正则项参数的选择问题,采用BN算法后,你可以移除这两项了参数,或者可以选择更小的L2正则约束参数了,因为BN具有提高网络泛化能力的特性;
5)再也不需要使用使用局部响应归一化层了(局部响应归一化是Alexnet网络用到的方法,搞视觉的估计比较熟悉),因为BN本身就是一个归一化网络层;
6)可以把训练数据彻底打乱(防止每批训练的时候,某一个样本都经常被挑选到,文献说这个可以提高1%的精度)。
注:以上为学习过程,在测试时,均值和方差(mean/std)不基于小批量进行计算, 可取训练过程中的激活值的均值。
Batch Size大小对训练过程、bn的影响
batch-size大小对训练的影响:不考虑bn的情况下,batch size的大小决定了深度学习训练过程中的完成每个epoch所需的时间和每次迭代(iteration)之间梯度的平滑程度。由于目前主流深度学习框架处理mini-batch的反向传播时,默认都是先将每个mini-batch中每个instance得到的loss平均化之后再反求梯度,也就是说每次反向传播的梯度是对mini-batch中每个instance的梯度平均之后的结果,所以b的大小决定了相邻迭代之间的梯度平滑程度,b太小,相邻mini-batch间的差异相对过大,那么相邻两次迭代的梯度震荡情况会比较严重,不利于收敛;b越大,相邻mini-batch间的差异相对越小,虽然梯度震荡情况会比较小,一定程度上利于模型收敛,但如果b极端大,相邻mini-batch间的差异过小,相邻两个mini-batch的梯度没有区别了,整个训练过程就是沿着一个方向蹭蹭蹭往下走,很容易陷入到局部最小值出不来。
总结下来:batch size过小,花费时间多,同时梯度震荡严重,不利于收敛;batch size过大,不同batch的梯度方向没有任何变化,容易陷入局部极小值。
batch size越小,相邻iter之间的loss震荡就越厉害,相应的,反传回去的梯度的变化也就越大,也就越不利于收敛。同时很有意思的一个现象,batch size为1的时候,loss到后期会发生爆炸,这主要是lr=0.02设置太大,所以某个异常值的出现会严重扰动到训练过程。这也是为什么对于较小的batchsize,要设置小lr的原因之一,避免异常值对结果造成的扰巨大扰动。而对于较大的batchsize,要设置大一点的lr的原因则是大batch每次迭代的梯度方向相对固定,大lr可以加速其收敛过程。
Batch Size大小对BN的影响
BN在小batch size条件下很不稳定,不如不用,或者选择group normalization方法.BN基于一个mini batch的数据计算均值和方差,而不是基于整个Training set来做,相当于进行梯度计算式引入噪声,所以BN不适合类似于在线学习这样mini-batch为1/很小的情况。
https://www.aiuai.cn/aifarm1206.html Multi-GPU BN
BN、LN、IN、GN、SN
我们将输入的 feature map shape 记为[N, C, H, W],其中N表示batch size,即N个样本;C表示通道数;H、W分别表示特征图的高度、宽度。这几个方法主要的区别就是在:1. BN是在batch上,对N、H、W做归一化,而保留通道 C 的维度。BN对较小的batch size效果不好。BN适用于固定深度的前向神经网络,如CNN,不适用于RNN;对不同特征进行统计,之后让每一个特征内部进行相同的操作。按照N×H×W都加起来,之后除以N×H×W,得到C。之后每一个N×H×W都减去Cmean除以Cvar。
2. LN在通道方向上,对C、H、W归一化,而保留N的维度,主要对RNN效果明显;对不同的样例进行统计,每一个样例内部进行相同操作。让不同样本之前没有差异,所以适合RNN同时进来很多个样例,让样例之前分布归一化,之后看样例之前的特征差异和相似。
3. IN在图像像素上,对H、W做归一化,而保留(N,c)的维度,用在风格化迁移;
4. GN将channel分组,然后再做归一化。
每个子图表示一个特征图,其中N为批量,C为通道,(H,W)为特征图的高度和宽度。 通过蓝色部分的值来计算均值和方差,从而进行归一化。
另外,还需要注意它们的映射参数γ和β,对于 BN,IN,GN, 其γ和β都是维度等于通道数 C 的向量。而对于 LN,其γ和β都是维度等于 normalized_shape 的矩阵。
BN 和 IN 可以设置参数:momentum和track_running_stats来获得在整体数据上更准确的均值和标准差。LN 和 GN 只能计算当前 batch 内数据的真实均值和标准差。
BN Batch Normalization
使数据在进入激活函数之前,沿着通道计算每个batch的均值、方差,‘强迫’数据保持均值为0,方差为1的正态分布,避免发生梯度消失。加入缩放和平移变量的原因是:保证每一次数据经过归一化后还保留原有学习来的特征,同时又能完成归一化操作,加速训练。 BN的作用(1)允许较大的学习率;(2)减弱对初始化的强依赖性(3)保持隐藏层中数值的均值、方差不变,让数值更稳定,为后面网络提供坚实的基础;(4)有轻微的正则化作用(相当于给隐藏层加入噪声,类似Dropout)
BN存在的问题:(1)每次是在一个batch上计算均值、方差,如果batch size太小,则计算的均值、方差不足以代表整个数据分布。(2)batch size太大:会超过内存容量;需要跑更多的epoch,导致总训练时间变长;会直接固定梯度下降的方向,导致很难更新。
import torch
from torch import nn
import numpy as np
random_data = np.random.rand(100, 30, 64, 64) * 1000
data1 = random_data.transpose((1, 0, 2, 3)).reshape(30, -1)
mu1 = data1.mean(axis=1).reshape(1, 30, 1, 1)
std1 = data1.std(axis=1).reshape(1, 30, 1, 1)
numpy_bn = (random_data - mu1) / (std1 + 1e-8)
bn_ = nn.BatchNorm2d(num_features=30, eps=1e-8, affine=False, track_running_stats=False)
x2 = torch.from_numpy(random_data)
offical_bn = bn_(x2)
print('diff:{}'.format((offical_bn.numpy() - numpy_bn).sum()))
# diff:-7.560503308773859e-08 差距非常小
LN Layer Normalization
针对BN不适用于深度不固定的网络(sequence长度不一致,如RNN),LN对深度网络的某一层的所有神经元的输入按以下公式进行normalization操作。 LN中同层神经元的输入拥有相同的均值和方差,不同的输入样本有不同的均值和方差。Layer Normalization (LN) 的一个优势是不需要批训练,在单条数据内部就能归一化。LN不依赖于batch size和输入sequence的长度,因此可以用于batch size为1和RNN中。LN用于RNN效果比较明显,但是在CNN上,效果不如BN。
import torch
from torch import nn
import numpy as np
random_data = np.random.rand(100, 30, 64, 64) * 1000
data1 = random_data.reshape(100, -1)
mu1 = data1.mean(axis=1).reshape(100, 1, 1, 1)
std1 = data1.std(axis=1).reshape(100, 1, 1, 1)
numpy_ln = (random_data - mu1) / std1
ln = nn.LayerNorm(normalized_shape=[30, 64, 64], eps=0, elementwise_affine=False)
offical_ln = ln(torch.from_numpy(random_data))
print('diff:{}'.format((numpy_ln - offical_ln.numpy()).sum()))
# diff = 2.4112720174197834e-09
IN Instance Normalization
最初用于图像的风格化迁移。在图像风格化中,生成结果主要依赖于某个图像实例,feature map 的各个 channel 的均值和方差会影响到最终生成图像的风格。所以对整个batch归一化不适合图像风格化中,因而对H、W做归一化。可以加速模型收敛,并且保持每个图像实例之间的独立。
import torch
from torch import nn
import numpy as np
random_data = np.random.rand(100, 30, 64, 64) * 1000
data1 = random_data.reshape(100 * 30, -1)
mu1 = data1.mean(axis=1).reshape(100, 30, 1, 1)
std1 = data1.std(axis=1).reshape(100, 30, 1, 1)
numpy_in = (random_data - mu1) / std1
ln = nn.InstanceNorm2d(num_features=30, eps=0, affine=False, track_running_stats=False)
offical_in = ln(torch.from_numpy(random_data))
print('diff:{}'.format((numpy_in - offical_in.numpy()).sum()))
#diff= 1.9767101974942072e-10
GN Group Normalization
GN是为了解决BN对较小的mini-batch size效果差的问题。GN适用于占用显存比较大的任务,例如图像分割。对这类任务,可能 batch size 只能是个位数,再大显存就不够用了。而当 batch size 是个位数时,BN 的表现很差,因为没办法通过几个样本的数据量,来近似总体的均值和标准差。GN 也是独立于 batch 的,它是 LN 和 IN 的折中。
GN的主要思想:在 channel 方向 group,然后每个 group 内做 LN,计算(C/G)*H*W的均值和方差,这样就与batch size无关,不受其约束。各组 channel 用其对应的归一化参数独立地归一化。
import torch
from torch import nn
import numpy as np
random_data = np.random.rand(100, 30, 64, 64) * 10000
data1 = random_data.reshape(100, 10, -1)
mu1 = data1.mean(axis=-1).reshape(100, 10, -1)
std1 = data1.std(axis=-1).reshape(100, 10, -1)
numpy_gn = (data1 - mu1) / std1
numpy_gn = numpy_gn.reshape(100, 30, 64, 64)
gn_ = nn.GroupNorm(num_groups=10, num_channels=30, eps=0, affine=False)
offical_gn = gn_(torch.from_numpy(random_data))
#diff:-6.625669787246119e-10
SN Switchable Normalization
第一,归一化虽然提高模型泛化能力,然而归一化层的操作是人工设计的。在实际应用中,解决不同的问题原则上需要设计不同的归一化操作,并没有一个通用的归一化方法能够解决所有应用问题;
第二,一个深度神经网络往往包含几十个归一化层,通常这些归一化层都使用同样的归一化操作,因为手工为每一个归一化层设计操作需要进行大量的实验。
因此作者提出自适配归一化方法——Switchable Normalization(SN)来解决上述问题。与强化学习不同,SN使用可微分学习,相当于bn、in、ln都用,之后为一个深度网络中的每一个归一化层确定合适的归一化操作。
def SwitchableNorm(x, gamma, beta, w_mean, w_var):
# x_shape:[B, C, H, W]
results = 0.
eps = 1e-5
mean_in = np.mean(x, axis=(2, 3), keepdims=True)
var_in = np.var(x, axis=(2, 3), keepdims=True)
mean_ln = np.mean(x, axis=(1, 2, 3), keepdims=True)
var_ln = np.var(x, axis=(1, 2, 3), keepdims=True)
mean_bn = np.mean(x, axis=(0, 2, 3), keepdims=True)
var_bn = np.var(x, axis=(0, 2, 3), keepdims=True)
mean = w_mean[0] * mean_in + w_mean[1] * mean_ln + w_mean[2] * mean_bn
var = w_var[0] * var_in + w_var[1] * var_ln + w_var[2] * var_bn
x_normalized = (x - mean) / np.sqrt(var + eps)
results = gamma * x_normalized + beta
return results
拟合问题、正则化、dropout
拟合问题
过拟合与欠拟合
1、我们先对该数据做线性回归,也就是左边第一张图。如果这么做,我们可以获得拟合数据的这样一条直线,但是,实际上这并不是一个很好的模型。我们看看这些数据,很明显,随着房子面积增大,住房价格的变化趋于稳定或者说越往右越平缓。因此线性回归并没有很好拟合训练数据。我们把此类情况称为欠拟合(underfitting),或者叫作叫做高偏差(bias)。
2、第二幅图,我们在中间加入一个二次项,也就是说对于这幅数据我们用二次函数去拟合。自然,可以拟合出一条曲线,事实也证明这个拟合效果很好。
3、另一个极端情况是,如果在第三幅图中对于该数据集用一个四次多项式来拟合。因此在这里我们有五个参数θ0到θ4,这样我们同样可以拟合一条曲线,通过我们的五个训练样本,我们可以得到如右图的一条曲线。一方面,我们似乎对训练数据做了一个很好的拟合,因为这条曲线通过了所有的训练实例。但是,这实际上是一条很扭曲的曲线,它不停上下波动。因此,事实上我们并不认为它是一个预测房价的好模型。所以,我们把这类情况叫做过拟合(overfitting),也叫高方差(variance)。
过拟合
过度拟合的问题通常发生在过多的变量(特征),同时只有非常少的训练数据。
这种情况下训练出的方程总是能很好的拟合训练数据,也就是说,我们的代价函数可能非常接近于 0 或者就为 0。但是,这样的曲线千方百计的去拟合训练数据,这样会导致它无法泛化到新的数据样本中,以至于无法预测新样本价格。在这里,术语"泛化"指的是一个假设模型能够应用到新样本的能力。新样本数据是指没有出现在训练集中的数据。
发生了过拟合问题,我们应该如何处理?
1)方法一:尽量减少选取变量的数量 2)方法二:正则化:保留所有的特征变量,但是会减小特征变量的数量级
当我们有很多特征变量时,其中每一个变量都能对预测产生一点影响。正如我们在房价预测的例子中看到的那样,我们可以有很多特征变量,其中每一个变量都是有用的,因此我们不希望把它们删掉,只不过有点变量对结果影响大有的影响小。这就导致了正则化概念的发生。
机器学习中防止过拟合的方法主要有三种:1. 增加数据集 2. 正则化方法 3. Dropout
数据集增强:小幅旋转,平移,放大,缩小甚至给图片加上波动,GAN
正则化
在线性代数理论中,不适定问题通常是由一组线性代数方程定义的,而且这组方程组通常来源于有着很大的条件数的不适定反问题。大条件数意味着舍入误差或其它误差会严重地影响问题的结果。而因为大条件数的存在,如果我们想要结果没问题,这些条件就不要有误差,可是这么多条件都没有误差就很可能产生过拟合问题。
而正则化 通俗可以理解为是 对某一问题加以先验的限制或约束以达到某种特定目的的一种手段或操作。 使用正则化的目的是防止出现过拟合。
LP范数
范数简单可以理解为用来表征向量空间中的距离,而距离的定义很抽象,只要满足非负、自反、三角不等式就可以称之为距离。范数是一种强化了的距离概念,它在定义上比距离多了一条数乘的运算法则。有时候为了便于理解,我们可以把范数当作距离来理解。
L-P范数不是一个范数,而是一组范数,其定义如下:
上图表示了p从无穷到0变化时,三维空间中到原点的距离(范数)为1的点构成的图形的变化情况。以常见的L-2范数(p=2)为例,此时的范数也即欧氏距离,空间中到原点的欧氏距离为1的点构成了一个球面。
L0范数 p=0
表示向量x中非零元素的个数。对于L0范数,其优化问题为:min||x||0 在实际应用中,由于L0范数本身不容易有一个好的数学表示形式,给出上面问题的形式化表示是一个很难的问题,故被人认为是一个NP难问题。所以在实际情况中,L0的最优问题会被放宽到L1或L2下的最优化。
L1范数 p=1
表示向量x中非零元素的绝对值之和。L1范数有很多的名字,例如我们熟悉的曼哈顿距离、最小绝对误差等。使用L1范数可以度量两个向量间的差异,如绝对误差和(Sum of Absolute Difference):SAD(x1,x2)=∑i|x1i−x2i| 对于L1范数,它的优化问题如下:min||x||1 由于L1范数的天然性质,对L1优化的解是一个稀疏解,因此L1范数也被叫做稀疏规则算子。通过L1可以实现特征的稀疏,去掉一些没有信息的特征,例如在对用户的电影爱好做分类的时候,用户有100个特征,可能只有十几个特征是对分类有用的,大部分特征如身高体重等可能都是无用的,利用L1范数就可以过滤掉。
L2范数 p=2
表示向量元素的平方和再开平方定义如下: 。像L1范数一样,L2也可以度量两个向量间的差异,如距离欧氏距离,平方差和:
对于L2范数,它的优化问题如下:min||x|| L2范数通常会被用来做优化目标函数的正则化项,防止模型为了迎合训练集而过于复杂造成过拟合的情况,从而提高模型的泛化能力。
L-∞范数
主要被用来度量向量元素的最大值。用上面的L-P定义可以得到的L∞的定义。与L0一样,在通常情况下,大家都用的是:||x||∞=max(|xi|)∞
与数据拟合的关系,为什么loss函数用L1/L2范数
数据拟合中,通常说通过Lp范数最小化求解,是指yi的p次方和作为损失函数
损失函数最小,以求解表达式中的参数。通常取 p>=1,实际在小数据、少量参数情况下,通过优化算法,可以实现0<p<1情况的数据拟合。要理解Lp范数对数据拟合的意义,我们先考虑如下情况:
p= 0 时,a 的结果为数列X的众数,x里与a的差值里非0元素个数要小,如果a是x集合里的众数,那么差值非0的自然就最小
p= 1 时,a 的结果为数列X的中位数。想要x里的数与a的差值的和最小,那么a是x里的中位数,自然最小。
p= 2 时,a 的结果为数列X的平均数。想要x里的数与a的距离的和最小,那么a是x里的平均数,自然最小。
p=+∞时,a 的结果为数列X的中程数(即最大数与最小数的平均值),要x里的数与的差值里最大的数最小。那么如果a是最小值,那么和最大值的差也很大。因此a为x里最大值和最小值的平均值,得到的最大的差值是最小的。
以上结论可以完全适用在Lp范数最小化的数据拟合上(偏差=真实数据-拟合函数预测值)
在对异常值敏感度上,某数据在偏差Lp范数总和(损失函数)中占比越大,则对结果影响越大。如p=2,相当于我们结果越倾向平均值越好,而异常大/小/不在正常范围内的值对平均值都会有影响,而异常值对应的偏差 y - f(a ,b...) (通常较大) 经过平方之后,在损失函数值中占比更大对结果的影响也更大,因此,相比L1范数求解方法,L2范数对异常值更敏感。总结,Lp范数最小化进行数据拟合时,有如下意义:
L0范数为众数回归,对异常值无感,因为有异常值也不会是众数,但是L0找不到最小值,
L1范数为中位数回归,对异常值不敏感,因为有正偏差和负偏差异常值相等的情况,抵消
L2范数为平均数回归,对异常值较敏感,平方之后更敏感了,L3开始也敏感不好优化min
L+∞范数为中程数回归,对异常值很高的敏感,但最大正偏差和最小负偏差绝对值相等也可抵消,但是这种异常太不常见了,而如果有一个很高的异常值对结果的影响很大。
深度学习中的L1/L2正则化
1、为什么正则化可以防止过拟合?
过拟合的时候,拟合函数的系数往往非常大,为什么?如下图所示,过拟合,就是拟合函数需要顾忌每一个点,最终形成的拟合函数波动很大。在某些很小的区间里,函数值的变化很剧烈。这就意味着函数在某些小区间里的导数值(绝对值)非常大,由于自变量值可大可小,所以只有系数足够大,才能保证导数值很大。而正则化是通过约束参数的范数使其不要太大,所以可以在一定程度上减少过拟合情况
以L2为例,在不使用L2正则化时,求导结果中w前系数为1,现在w前面系数为 1−ηλ/n ,因为η、λ、n都是正的,所以 1−ηλ/n小于1,它的效果是减小w,这也就是权重衰减(weight decay)的由来。当然考虑到后面的导数项,w最终的值可能增大也可能减小。我们知道w都太大其实就相当于大常数项很多,很可能会过拟合。而让w尽量小,可以防止过拟合。
2、L1/L2正则化的区别
正则化是一种为了减小测试误差的行为(有时候会增加训练误差)。
在目标函数后面添加一个系数的“惩罚项”是正则化的常用方式,为了防止系数过大从而让模型变得复杂。在加了正则化项之后的目标函数为:
式中, 是一个常数,
为样本个数,
是一个超参数,用于控制正则化程度。
L1 : L2 :
L1正则化通过让原目标函数加上了所有特征系数绝对值的和来实现正则化,而 L2 正则化通过让原目标函数加上了所有特征系数的平方和来实现正则化。
两者都是通过加上一个和项来限制参数大小,却有不同的效果: L1 正则化更适用于特征选择,而 L2 正则化更适用于防止模型过拟合。
具体来说:假设数据只有两个特征即 考虑L1正则化的目标函数
在每次更新 时:
=
+/-
若 为正数,则每次更新会减去一个常数;若
为负数,则每次更新会加上一个常数,首先他并没有让结果衰减,其次这样直接加减变小的方法,很容易产生特征的系数为 0 的情况,特征系数为 0 表示该特征不会对结果有任何影响,因此正则化会让特征变得稀疏,起到特征选择的作用。
现考虑L2正则化的目标函数: 在每次更新
时:
.从上式可以看出每次更新时,会对特征系数进行一个比例的缩放而不是像L1正则化减去一个固定值,这会让系数趋向变小而不会变为 0,因此L2正则化会让模型变得更简单,防止过拟合,而不会起到特征选择的作用。
左图是当不使用正则化,发生过拟合时,右图使用 正则化,正常拟合在图中可以有比较直观的感受是,过拟合时,分类边界的起伏会更大,也就是在部分点斜率更大,而正常拟合时,分类边界更加平缓。这也是为什么在目标函数中加入“惩罚项”可以达到正则化的效果,“惩罚项”可以使每个参数趋向更小,在求导时斜率也会更小,等于变相的让模型更加简单了,更加简单的模型当然更加不容易过拟合。
注意:!要区分L2正则化和损失函数使用L2。
dropout
他的基本步骤是在每一次的迭代中,随机删除一部分节点,只训练剩下的节点。每次迭代都会随机删除,每次迭代删除的节点也都不一样,相当于每次迭代训练的都是不一样的网络,通过这样的方式降低节点之间的关联性以及模型的复杂度,从而达到正则化的效果。这点上有点类似 bagging,但是远比 bagging 来的简单。
说dropout简单是因为你只需要设置一个超参数 keep_prob,这个参数的意义是每层节点随机保留的比例,比如将 keep_prob 设置为 0.7,那么就会随机30%的节点消失,消失这个动作其实很简单,只是将这一层的参数矩阵与根据 keep_prob 生成的 {0, 1} 矩阵做 逐点乘积,当然前向传播与反向传播都需要做以上的操作。
dropout的缺点在于,需要将训练集分为不同子集输入每一次的迭代,这就需要较大的训练集,所以在训练集较小的情况下,dropout的效果可能并不好。我们上面也说过,增大数据集是最好的正则化方式,所以在增大数据集的情况下,使用 dropout 需要使用的计算代价可能会比他带来正则化效果更高,这需要我们在实际使用场景中做取舍。
梯度检查 earlystopping
提前终止可能是最简单的正则化方式,他适用于模型的表达能力很强的时候。这种情况下,一般训练误差会随着训练次数的增多逐渐下降,而测试误差则会先下降而后再次上升。我们需要做的就是在测试误差最低的点停止训练即可。例如:看出随着训练次数的增加,训练误差会一直下降,而训练2500次的测试误差要高于训练1500次的测试误差,所以我们在1500次的时候停止会更好。
总结:
需要归一化的模型有:神经网络,标准差归一化、支持向量机,标准差归一化、线性回归,可以用梯度下降法求解,需要标准差归一化、PCA、LDA、聚类算法基本都需要、K近邻,线性归一化,归一到[0,1]区间内、逻辑回归
不需要归一化的模型:决策树: 每次筛选都只考虑一个变量,不考虑变量之间的相关性,所以不需要归一化。随机森林:不需要归一化,mtry为变量个数的均方根。朴素贝叶斯
需要正则化的模型:Lasso、Elastic Net