精华内容
下载资源
问答
  • 机器学习中正则化项L1和L2的直观理解

    万次阅读 多人点赞 2019-03-02 11:15:39
    机器学习中几乎都可以看到损失函数后面会添加一个额外项,常用的额外项一般有两种,一般英文称作ℓ1ℓ1\ell_1-norm和ℓ2ℓ2\ell_2-norm,中文称作L1正则化和L2正则化,或者L1范数和L2范数。 L1正则化和L2正则化可以...

    正则化(Regularization)

    机器学习中几乎都可以看到损失函数后面会添加一个额外项,常用的额外项一般有两种,一般英文称作 1\ell_1-norm2\ell_2-norm,中文称作 L1正则化L2正则化,或者 L1范数L2范数

    L1正则化和L2正则化可以看做是损失函数的惩罚项。所谓『惩罚』是指对损失函数中的某些参数做一些限制。对于线性回归模型,使用L1正则化的模型建叫做Lasso回归,使用L2正则化的模型叫做Ridge回归(岭回归)。下图是Python中Lasso回归的损失函数,式中加号后面一项αw1\alpha||w||_1即为L1正则化项。

    lasso regression

    下图是Python中Ridge回归的损失函数,式中加号后面一项αw22\alpha||w||_2^2即为L2正则化项。

    ridge regression

    一般回归分析中ww表示特征的系数,从上式可以看到正则化项是对系数做了处理(限制)。L1正则化和L2正则化的说明如下:

    • L1正则化是指权值向量ww中各个元素的绝对值之和,通常表示为w1||w||_1
    • L2正则化是指权值向量ww中各个元素的平方和然后再求平方根(可以看到Ridge回归的L2正则化项有平方符号),通常表示为w2||w||_2

    一般都会在正则化项之前添加一个系数,Python的机器学习包sklearn中用α\alpha表示,一些文章也用λ\lambda表示。这个系数需要用户指定。

    那添加L1和L2正则化有什么用?下面是L1正则化和L2正则化的作用,这些表述可以在很多文章中找到。

    • L1正则化可以产生稀疏权值矩阵,即产生一个稀疏模型,可以用于特征选择
    • L2正则化可以防止模型过拟合(overfitting);一定程度上,L1也可以防止过拟合

    稀疏模型与特征选择的关系

    上面提到L1正则化有助于生成一个稀疏权值矩阵,进而可以用于特征选择。为什么要生成一个稀疏矩阵?

    稀疏矩阵指的是很多元素为0,只有少数元素是非零值的矩阵,即得到的线性回归模型的大部分系数都是0. 通常机器学习中特征数量很多,例如文本处理时,如果将一个词组(term)作为一个特征,那么特征数量会达到上万个(bigram)。在预测或分类时,那么多特征显然难以选择,但是如果代入这些特征得到的模型是一个稀疏模型,表示只有少数特征对这个模型有贡献,绝大部分特征是没有贡献的,或者贡献微小(因为它们前面的系数是0或者是很小的值,即使去掉对模型也没有什么影响),此时我们就可以只关注系数是非零值的特征。这就是稀疏模型与特征选择的关系。

    L1和L2正则化的直观理解

    这部分内容将解释为什么L1正则化可以产生稀疏模型(L1是怎么让系数等于零的),以及为什么L2正则化可以防止过拟合

    正则化和特征选择的关系

    假设有如下带L1正则化的损失函数:
    J=J0+αww(1)J = J_0 + \alpha \sum_w{|w|} \tag{1}
    其中J0J_0是原始的损失函数,加号后面的一项是L1正则化项,α\alpha是正则化系数。注意到L1正则化是权值的绝对值之和JJ是带有绝对值符号的函数,因此JJ是不完全可微的。机器学习的任务就是要通过一些方法(比如梯度下降)求出损失函数的最小值。当我们在原始损失函数J0J_0后添加L1正则化项时,相当于对J0J_0做了一个约束。令L=αwwL = \alpha \sum_w{|w|},则J=J0+LJ = J_0 + L,此时我们的任务变成LL约束下求出J0J_0取最小值的解考虑二维的情况,即只有两个权值w1w^1w2w^2,此时L=w1+w2L = |w^1|+|w^2|。对于梯度下降法,求解J0J_0的过程可以画出等值线,同时L1正则化的函数LL也可以在w1w2w^1w^2的二维平面上画出来。如下图:

    @图1 L1正则化
    图1 L1正则化

    图中等值线是J0J_0的等值线,黑色方形是LL函数的图形。L=w1+w2L = |w^1|+|w^2|,这个函数画出来就是一个方框(可以自己动手画一下)。

    在图中,当J0J_0等值线与LL图形首次相交的地方就是最优解。上图中J0J_0LLLL的一个顶点处相交,这个顶点就是最优解。注意到这个顶点的值是(w1,w2)=(0,w)(w^1, w^2) = (0, w)。可以直观想象,因为LL函数有很多『突出的角』(二维情况下四个,多维情况下更多),J0J_0与这些角接触的机率会远大于与LL其它部位接触的机率(这是很直觉的想象,突出的角比直线的边离等值线更近写),而在这些角上,会有很多权值等于0(因为角就在坐标轴上),这就是为什么L1正则化可以产生稀疏模型,进而可以用于特征选择。

    而正则化前面的系数α\alpha,可以控制LL图形的大小。α\alpha越小,LL的图形越大(上图中的黑色方框);α\alpha越大,LL的图形就越小,可以小到黑色方框只超出原点范围一点点,这是最优点的值(w1,w2)=(0,w)(w1,w2)=(0,w)中的ww可以取到很小的值。

    类似地,假设有如下带L2正则化的损失函数:

    J=J0+αww2(2)J = J_0 + \alpha \sum_w{w^2} \tag{2}

    同样可以画出他们在二维平面上的图形,如下:

    @图2 L2正则化
    图2 L2正则化

    二维平面下L2正则化的函数图形是个圆(绝对值的平方和,是个圆),与方形相比,被磨去了棱角。因此J0J_0LL相交时使得w1w^1w2w^2等于零的机率小了许多(这个也是一个很直观的想象),这就是为什么L2正则化不具有稀疏性的原因,因为不太可能出现多数ww都为0的情况。

    为什么梯度下降的等值线与正则化函数第一次交点是最优解?

    评论中有人问到过这个问题,这是带约束的最优化问题。这应该是在大一的高等数学就学到知识点,因为这里要用到拉格朗日乘子。如果有这样的问题,就需要复习一下高等数学了。这里有一个比较详细的数学讲解,可以参考:带约束的最优化问题

    L2正则化和过拟合的关系

    拟合过程中通常都倾向于让权值尽可能小,最后构造一个所有参数都比较小的模型。因为一般认为参数值小的模型比较简单,能适应不同的数据集,也在一定程度上避免了过拟合现象。可以设想一下对于一个线性回归方程,若参数很大,那么只要数据偏移一点点,就会对结果造成很大的影响;但如果参数足够小,数据偏移得多一点也不会对结果造成什么影响,专业一点的说法是『抗扰动能力强』。

    那为什么L2正则化可以获得值很小的参数?

    以线性回归中的梯度下降法为例,使用Andrew Ng机器学习的参数表示方法。假设要求解的参数为θ\thetahθ(x)h_\theta(x)是我们的假设函数。线性回归一般使用平方差损失函数。单个样本的平方差是(hθ(x)y)2(h_\theta(x) - y)^2,如果考虑所有样本,损失函数是对每个样本的平方差求和,假设有mm个样本,线性回归的代价函数如下,为了后续处理方便,乘以一个常数12m\frac{1}{2m}

    J(θ)=12mi=1m(hθ(x(i))y(i))2(3)J(\theta) = \frac{1}{2m}\sum_{i=1}^{m}(h_\theta(x^{(i)}) - y^{(i)})^2 \tag{3}

    在梯度下降算法中,需要先对参数求导,得到梯度。梯度本身是上升最快的方向,为了让损失尽可能小,沿梯度的负方向更新参数即可。

    对于单个样本,先对某个参数θj\theta_j求导:

    θjJ(θ)=1m(hθ(x)y)θjhθ(x)(3.1) \frac{\partial}{\partial \theta_j} J(\theta) = \frac{1}{m} (h_\theta(x) - y) \frac{\partial}{\partial \theta_j} h_\theta(x) \tag{3.1}

    注意到hθ(x)h_\theta(x)的表达式是hθ(x)=θ0x0+θ1x1++θnxnh_\theta(x)=\theta_0 x_0 + \theta_1 x_1 + \dots + \theta_n x_n. 单个样本对某个参数θj\theta_j求导,θjhθ(x)=xj\frac{\partial}{\partial \theta_j} h_\theta(x) = x_j. 最终(3.1)式结果如下:

    θjJ(θ)=1m(hθ(x)y)xj(3.2) \frac{\partial}{\partial \theta_j} J(\theta) = \frac{1}{m} (h_\theta(x) - y) x_j \tag{3.2}

    在考虑所有样本的情况,将每个样本对θj\theta_j的导数求和即可,得到下式:

    θjJ(θ)=1mi=1m(hθ(x(i))y(i))xj(i)(3.3) \frac{\partial}{\partial \theta_j} J(\theta) = \frac{1}{m} \sum_{i=1}^m (h_\theta(x^{(i)}) - y^{(i)}) x_j^{(i)} \tag{3.3}

    梯度下降算法中,为了尽快收敛,会沿梯度的负方向更新参数,因此在(3.3)式前添加一个负号,并乘以一个系数α\alpha(即学习率),得到最终用于迭代计算参数θj\theta_j的形式:

    θj:=θjα1mi=1m(hθ(x(i))y(i))xj(i)(4)\theta_j := \theta_j - \alpha \frac{1}{m}\sum_{i=1}^{m}(h_\theta(x^{(i)}) - y^{(i)})x_j^{(i)} \tag{4}

    其中α\alpha是学习率(learning rate)。 上式是没有添加L2正则化项的迭代公式,如果在原始代价函数之后添加L2正则化,则迭代公式会变成下面的样子:
    θj:=θj(1αλm)α1mi=1m(hθ(x(i))y(i))xj(i)(5)\theta_j := \theta_j(1-\alpha \frac{\lambda}{m}) - \alpha \frac{1}{m}\sum_{i=1}^{m}(h_\theta(x^{(i)}) - y^{(i)})x_j^{(i)} \tag{5}

    其中λ\lambda就是正则化参数。从上式可以看到,与未添加L2正则化的迭代公式相比,每一次迭代,θj\theta_j都要先乘以一个小于1的因子(即(1αλm)(1-\alpha \frac{\lambda}{m})),从而使得θj\theta_j不断减小,因此总的来看,θ\theta是不断减小的。

    最开始也提到L1正则化一定程度上也可以防止过拟合。之前做了解释,当L1的正则化系数很小时,得到的最优解会很小,可以达到和L2正则化类似的效果。

    正则化参数的选择

    L1正则化参数

    通常越大的λ\lambda可以让代价函数在参数为0时取到最小值。因为正则化系数越大,正则化的函数图形(上文图中的方形或圆形)会向坐标轴原点收缩得越厉害,这个现象称为shrinkage,过程可以称为shrink to zero. 下面是一个简单的例子,这个例子来自Quora上的问答。为了方便叙述,一些符号跟这篇帖子的符号保持一致。

    假设有如下带L1正则化项的代价函数:

    F(x)=f(x)+λx1F(x) = f(x) + \lambda ||x||_1

    其中xx是要估计的参数,相当于上文中提到的ww以及θ\theta. 这个例子中的正则化函数LL就是L=λxL=\lambda |x|。注意到L1正则化在某些位置是不可导的,当λ\lambda足够大时可以使得F(x)F(x)x=0x = 0时取到最小值。如下图:

    @图3 L1正则化参数的选择
    图3 L1正则化参数的选择

    作为一个直观的例子,这个图的示例中,取了f(x)=(x1)2f(x) = (x-1)^2作为损失函数,其实可以取更复杂的,但不好画图,不过原理是一样的,因为损失函数都是凸函数,很多性质是一样的。

    正则化分别取λ=0.5\lambda = 0.5λ=2\lambda = 2,可以看到越大的λ\lambda越容易使F(x)F(x)x=0x=0时取到最小值。

    此外也可以自己计算一下,当损失函数f(x)f(x)和正则化函数L=xL=|x|在定义域内第一次相交的地方,就是整个代价函数F(x)F(x)的最优解。

    L2正则化参数

    从公式5可以看到,λ\lambda越大,θj\theta_j衰减得越快。另一个理解可以参考图2,λ\lambda越大,L2圆的半径越小,最后求得代价函数最值时各参数也会变得很小,同样是一个shrink to zero的过程,原理与L1正则化类似。

    Reference

    过拟合的解释:
    https://hit-scir.gitbooks.io/neural-networks-and-deep-learning-zh_cn/content/chap3/c3s5ss2.html

    正则化的解释:
    https://hit-scir.gitbooks.io/neural-networks-and-deep-learning-zh_cn/content/chap3/c3s5ss1.html

    正则化的解释:
    http://blog.csdn.net/u012162613/article/details/44261657

    正则化的数学解释(一些图来源于这里):
    http://blog.csdn.net/zouxy09/article/details/24971995

    展开全文
  • 机器学习中的范数规则化之(一)L0、L1与L2范数

    万次阅读 多人点赞 2014-05-04 12:32:20
    机器学习中的范数规则化之(一)L0、L1与L2范数zouxy09@qq.comhttp://blog.csdn.net/zouxy09 今天我们聊聊机器学习中出现的非常频繁的问题:过拟合与规则化。我们先简单的来理解下常用的L0、L1、L2和核范数规则化。...

    机器学习中的范数规则化之(一)L0、L1与L2范数

    zouxy09@qq.com

    http://blog.csdn.net/zouxy09

     

           今天我们聊聊机器学习中出现的非常频繁的问题:过拟合与规则化。我们先简单的来理解下常用的L0、L1、L2和核范数规则化。最后聊下规则化项参数的选择问题。这里因为篇幅比较庞大,为了不吓到大家,我将这个五个部分分成两篇博文。知识有限,以下都是我一些浅显的看法,如果理解存在错误,希望大家不吝指正。谢谢。

     

           监督机器学习问题无非就是“minimizeyour error while regularizing your parameters”,也就是在规则化参数的同时最小化误差。最小化误差是为了让我们的模型拟合我们的训练数据,而规则化参数是防止我们的模型过分拟合我们的训练数据。多么简约的哲学啊!因为参数太多,会导致我们的模型复杂度上升,容易过拟合,也就是我们的训练误差会很小。但训练误差小并不是我们的最终目标,我们的目标是希望模型的测试误差小,也就是能准确的预测新的样本。所以,我们需要保证模型“简单”的基础上最小化训练误差,这样得到的参数才具有好的泛化性能(也就是测试误差也小),而模型“简单”就是通过规则函数来实现的。另外,规则项的使用还可以约束我们的模型的特性。这样就可以将人对这个模型的先验知识融入到模型的学习当中,强行地让学习到的模型具有人想要的特性,例如稀疏、低秩、平滑等等。要知道,有时候人的先验是非常重要的。前人的经验会让你少走很多弯路,这就是为什么我们平时学习最好找个大牛带带的原因。一句点拨可以为我们拨开眼前乌云,还我们一片晴空万里,醍醐灌顶。对机器学习也是一样,如果被我们人稍微点拨一下,它肯定能更快的学习相应的任务。只是由于人和机器的交流目前还没有那么直接的方法,目前这个媒介只能由规则项来担当了。

           还有几种角度来看待规则化的。规则化符合奥卡姆剃刀(Occam's razor)原理。这名字好霸气,razor!不过它的思想很平易近人:在所有可能选择的模型中,我们应该选择能够很好地解释已知数据并且十分简单的模型。从贝叶斯估计的角度来看,规则化项对应于模型的先验概率。民间还有个说法就是,规则化是结构风险最小化策略的实现,是在经验风险上加一个正则化项(regularizer)或惩罚项(penalty term)。

           一般来说,监督学习可以看做最小化下面的目标函数:

           其中,第一项L(yi,f(xi;w)) 衡量我们的模型(分类或者回归)对第i个样本的预测值f(xi;w)和真实的标签yi之前的误差。因为我们的模型是要拟合我们的训练样本的嘛,所以我们要求这一项最小,也就是要求我们的模型尽量的拟合我们的训练数据。但正如上面说言,我们不仅要保证训练误差最小,我们更希望我们的模型测试误差小,所以我们需要加上第二项,也就是对参数w的规则化函数Ω(w)去约束我们的模型尽量的简单。

            OK,到这里,如果你在机器学习浴血奋战多年,你会发现,哎哟哟,机器学习的大部分带参模型都和这个不但形似,而且神似。是的,其实大部分无非就是变换这两项而已。对于第一项Loss函数,如果是Square loss,那就是最小二乘了;如果是Hinge Loss,那就是著名的SVM了;如果是exp-Loss,那就是牛逼的 Boosting了;如果是log-Loss,那就是Logistic Regression了;还有等等。不同的loss函数,具有不同的拟合特性,这个也得就具体问题具体分析的。但这里,我们先不究loss函数的问题,我们把目光转向“规则项Ω(w)”。

           规则化函数Ω(w)也有很多种选择,一般是模型复杂度的单调递增函数,模型越复杂,规则化值就越大。比如,规则化项可以是模型参数向量的范数。然而,不同的选择对参数w的约束不同,取得的效果也不同,但我们在论文中常见的都聚集在:零范数、一范数、二范数、迹范数、Frobenius范数和核范数等等。这么多范数,到底它们表达啥意思?具有啥能力?什么时候才能用?什么时候需要用呢?不急不急,下面我们挑几个常见的娓娓道来。

     

    一、L0范数与L1范数

           L0范数是指向量中非0的元素的个数。如果我们用L0范数来规则化一个参数矩阵W的话,就是希望W的大部分元素都是0。这太直观了,太露骨了吧,换句话说,让参数W是稀疏的。OK,看到了“稀疏”二字,大家都应该从当下风风火火的“压缩感知”和“稀疏编码”中醒悟过来,原来用的漫山遍野的“稀疏”就是通过这玩意来实现的。但你又开始怀疑了,是这样吗?看到的papers世界中,稀疏不是都通过L1范数来实现吗?脑海里是不是到处都是||W||1影子呀!几乎是抬头不见低头见。没错,这就是这节的题目把L0和L1放在一起的原因,因为他们有着某种不寻常的关系。那我们再来看看L1范数是什么?它为什么可以实现稀疏?为什么大家都用L1范数去实现稀疏,而不是L0范数呢?

           L1范数是指向量中各个元素绝对值之和,也有个美称叫“稀疏规则算子”(Lasso regularization)。现在我们来分析下这个价值一个亿的问题:为什么L1范数会使权值稀疏?有人可能会这样给你回答“它是L0范数的最优凸近似”。实际上,还存在一个更美的回答:任何的规则化算子,如果他在Wi=0的地方不可微,并且可以分解为一个“求和”的形式,那么这个规则化算子就可以实现稀疏。这说是这么说,W的L1范数是绝对值,|w|在w=0处是不可微,但这还是不够直观。这里因为我们需要和L2范数进行对比分析。所以关于L1范数的直观理解,请待会看看第二节。

           对了,上面还有一个问题:既然L0可以实现稀疏,为什么不用L0,而要用L1呢?个人理解一是因为L0范数很难优化求解(NP难问题),二是L1范数是L0范数的最优凸近似,而且它比L0范数要容易优化求解。所以大家才把目光和万千宠爱转于L1范数。

           OK,来个一句话总结:L1范数和L0范数可以实现稀疏,L1因具有比L0更好的优化求解特性而被广泛应用。

           好,到这里,我们大概知道了L1可以实现稀疏,但我们会想呀,为什么要稀疏?让我们的参数稀疏有什么好处呢?这里扯两点:

    1)特征选择(Feature Selection):

           大家对稀疏规则化趋之若鹜的一个关键原因在于它能实现特征的自动选择。一般来说,xi的大部分元素(也就是特征)都是和最终的输出yi没有关系或者不提供任何信息的,在最小化目标函数的时候考虑xi这些额外的特征,虽然可以获得更小的训练误差,但在预测新的样本时,这些没用的信息反而会被考虑,从而干扰了对正确yi的预测。稀疏规则化算子的引入就是为了完成特征自动选择的光荣使命,它会学习地去掉这些没有信息的特征,也就是把这些特征对应的权重置为0。

    2)可解释性(Interpretability):

           另一个青睐于稀疏的理由是,模型更容易解释。例如患某种病的概率是y,然后我们收集到的数据x是1000维的,也就是我们需要寻找这1000种因素到底是怎么影响患上这种病的概率的。假设我们这个是个回归模型:y=w1*x1+w2*x2+…+w1000*x1000+b(当然了,为了让y限定在[0,1]的范围,一般还得加个Logistic函数)。通过学习,如果最后学习到的w*就只有很少的非零元素,例如只有5个非零的wi,那么我们就有理由相信,这些对应的特征在患病分析上面提供的信息是巨大的,决策性的。也就是说,患不患这种病只和这5个因素有关,那医生就好分析多了。但如果1000个wi都非0,医生面对这1000种因素,累觉不爱。

     

    二、L2范数

           除了L1范数,还有一种更受宠幸的规则化范数是L2范数: ||W||2。它也不逊于L1范数,它有两个美称,在回归里面,有人把有它的回归叫“岭回归”(Ridge Regression),有人也叫它“权值衰减weight decay”。这用的很多吧,因为它的强大功效是改善机器学习里面一个非常重要的问题:过拟合。至于过拟合是什么,上面也解释了,就是模型训练时候的误差很小,但在测试的时候误差很大,也就是我们的模型复杂到可以拟合到我们的所有训练样本了,但在实际预测新的样本的时候,糟糕的一塌糊涂。通俗的讲就是应试能力很强,实际应用能力很差。擅长背诵知识,却不懂得灵活利用知识。例如下图所示(来自Ng的course):

           上面的图是线性回归,下面的图是Logistic回归,也可以说是分类的情况。从左到右分别是欠拟合(underfitting,也称High-bias)、合适的拟合和过拟合(overfitting,也称High variance)三种情况。可以看到,如果模型复杂(可以拟合任意的复杂函数),它可以让我们的模型拟合所有的数据点,也就是基本上没有误差。对于回归来说,就是我们的函数曲线通过了所有的数据点,如上图右。对分类来说,就是我们的函数曲线要把所有的数据点都分类正确,如下图右。这两种情况很明显过拟合了。

           OK,那现在到我们非常关键的问题了,为什么L2范数可以防止过拟合?回答这个问题之前,我们得先看看L2范数是个什么东西。

           L2范数是指向量各元素的平方和然后求平方根。我们让L2范数的规则项||W||2最小,可以使得W的每个元素都很小,都接近于0,但与L1范数不同,它不会让它等于0,而是接近于0,这里是有很大的区别的哦。而越小的参数说明模型越简单,越简单的模型则越不容易产生过拟合现象。为什么越小的参数说明模型越简单?我也不懂,我的理解是:限制了参数很小,实际上就限制了多项式某些分量的影响很小(看上面线性回归的模型的那个拟合的图),这样就相当于减少参数个数。其实我也不太懂,希望大家可以指点下。

           这里也一句话总结下:通过L2范数,我们可以实现了对模型空间的限制,从而在一定程度上避免了过拟合。

           L2范数的好处是什么呢?这里也扯上两点:

    1)学习理论的角度:

           从学习理论的角度来说,L2范数可以防止过拟合,提升模型的泛化能力。

    2)优化计算的角度:

           从优化或者数值计算的角度来说,L2范数有助于处理 condition number不好的情况下矩阵求逆很困难的问题。哎,等等,这condition number是啥?我先google一下哈。

           这里我们也故作高雅的来聊聊优化问题。优化有两大难题,一是:局部最小值,二是:ill-condition病态问题。前者俺就不说了,大家都懂吧,我们要找的是全局最小值,如果局部最小值太多,那我们的优化算法就很容易陷入局部最小而不能自拔,这很明显不是观众愿意看到的剧情。那下面我们来聊聊ill-condition。ill-condition对应的是well-condition。那他们分别代表什么?假设我们有个方程组AX=b,我们需要求解X。如果A或者b稍微的改变,会使得X的解发生很大的改变,那么这个方程组系统就是ill-condition的,反之就是well-condition的。我们具体举个例子吧:

           咱们先看左边的那个。第一行假设是我们的AX=b,第二行我们稍微改变下b,得到的x和没改变前的差别很大,看到吧。第三行我们稍微改变下系数矩阵A,可以看到结果的变化也很大。换句话来说,这个系统的解对系数矩阵A或者b太敏感了。又因为一般我们的系数矩阵A和b是从实验数据里面估计得到的,所以它是存在误差的,如果我们的系统对这个误差是可以容忍的就还好,但系统对这个误差太敏感了,以至于我们的解的误差更大,那这个解就太不靠谱了。所以这个方程组系统就是ill-conditioned病态的,不正常的,不稳定的,有问题的,哈哈。这清楚了吧。右边那个就叫well-condition的系统了。

           还是再啰嗦一下吧,对于一个ill-condition的系统,我的输入稍微改变下,输出就发生很大的改变,这不好啊,这表明我们的系统不能实用啊。你想想看,例如对于一个回归问题y=f(x),我们是用训练样本x去训练模型f,使得y尽量输出我们期待的值,例如0。那假如我们遇到一个样本x’,这个样本和训练样本x差别很小,面对他,系统本应该输出和上面的y差不多的值的,例如0.00001,最后却给我输出了一个0.9999,这很明显不对呀。就好像,你很熟悉的一个人脸上长了个青春痘,你就不认识他了,那你大脑就太差劲了,哈哈。所以如果一个系统是ill-conditioned病态的,我们就会对它的结果产生怀疑。那到底要相信它多少呢?我们得找个标准来衡量吧,因为有些系统的病没那么重,它的结果还是可以相信的,不能一刀切吧。终于回来了,上面的condition number就是拿来衡量ill-condition系统的可信度的。condition number衡量的是输入发生微小变化的时候,输出会发生多大的变化。也就是系统对微小变化的敏感度。condition number值小的就是well-conditioned的,大的就是ill-conditioned的。

           如果方阵A是非奇异的,那么A的conditionnumber定义为:

           也就是矩阵A的norm乘以它的逆的norm。所以具体的值是多少,就要看你选择的norm是什么了。如果方阵A是奇异的,那么A的condition number就是正无穷大了。实际上,每一个可逆方阵都存在一个condition number。但如果要计算它,我们需要先知道这个方阵的norm(范数)和Machine Epsilon(机器的精度)。为什么要范数?范数就相当于衡量一个矩阵的大小,我们知道矩阵是没有大小的,当上面不是要衡量一个矩阵A或者向量b变化的时候,我们的解x变化的大小吗?所以肯定得要有一个东西来度量矩阵和向量的大小吧?对了,他就是范数,表示矩阵大小或者向量长度。OK,经过比较简单的证明,对于AX=b,我们可以得到以下的结论:

           也就是我们的解x的相对变化和A或者b的相对变化是有像上面那样的关系的,其中k(A)的值就相当于倍率,看到了吗?相当于x变化的界。

           对condition number来个一句话总结:conditionnumber是一个矩阵(或者它所描述的线性系统)的稳定性或者敏感度的度量,如果一个矩阵的condition number在1附近,那么它就是well-conditioned的,如果远大于1,那么它就是ill-conditioned的,如果一个系统是ill-conditioned的,它的输出结果就不要太相信了。

           好了,对这么一个东西,已经说了好多了。对了,我们为什么聊到这个的了?回到第一句话:从优化或者数值计算的角度来说,L2范数有助于处理 condition number不好的情况下矩阵求逆很困难的问题。因为目标函数如果是二次的,对于线性回归来说,那实际上是有解析解的,求导并令导数等于零即可得到最优解为:

           然而,如果当我们的样本X的数目比每个样本的维度还要小的时候,矩阵XTX将会不是满秩的,也就是XTX会变得不可逆,所以w*就没办法直接计算出来了。或者更确切地说,将会有无穷多个解(因为我们方程组的个数小于未知数的个数)。也就是说,我们的数据不足以确定一个解,如果我们从所有可行解里随机选一个的话,很可能并不是真正好的解,总而言之,我们过拟合了。

           但如果加上L2规则项,就变成了下面这种情况,就可以直接求逆了:

           这里面,专业点的描述是:要得到这个解,我们通常并不直接求矩阵的逆,而是通过解线性方程组的方式(例如高斯消元法)来计算。考虑没有规则项的时候,也就是λ=0的情况,如果矩阵XTX的 condition number 很大的话,解线性方程组就会在数值上相当不稳定,而这个规则项的引入则可以改善condition number。

           另外,如果使用迭代优化的算法,condition number 太大仍然会导致问题:它会拖慢迭代的收敛速度,而规则项从优化的角度来看,实际上是将目标函数变成λ-strongly convex(λ强凸)的了。哎哟哟,这里又出现个λ强凸,啥叫λ强凸呢?

           当f满足:

           时,我们称f为λ-stronglyconvex函数,其中参数λ>0。当λ=0时退回到普通convex 函数的定义。

           在直观的说明强凸之前,我们先看看普通的凸是怎样的。假设我们让f在x的地方做一阶泰勒近似(一阶泰勒展开忘了吗?f(x)=f(a)+f'(a)(x-a)+o(||x-a||).):

           直观来讲,convex 性质是指函数曲线位于该点处的切线,也就是线性近似之上,而 strongly convex 则进一步要求位于该处的一个二次函数上方,也就是说要求函数不要太“平坦”而是可以保证有一定的“向上弯曲”的趋势。专业点说,就是convex 可以保证函数在任意一点都处于它的一阶泰勒函数之上,而strongly convex可以保证函数在任意一点都存在一个非常漂亮的二次下界quadratic lower bound。当然这是一个很强的假设,但是同时也是非常重要的假设。可能还不好理解,那我们画个图来形象的理解下。

           大家一看到上面这个图就全明白了吧。不用我啰嗦了吧。还是啰嗦一下吧。我们取我们的最优解w*的地方。如果我们的函数f(w),见左图,也就是红色那个函数,都会位于蓝色虚线的那根二次函数之上,这样就算wt和w*离的比较近的时候,f(wt)和f(w*)的值差别还是挺大的,也就是会保证在我们的最优解w*附近的时候,还存在较大的梯度值,这样我们才可以在比较少的迭代次数内达到w*。但对于右图,红色的函数f(w)只约束在一个线性的蓝色虚线之上,假设是如右图的很不幸的情况(非常平坦),那在wt还离我们的最优点w*很远的时候,我们的近似梯度(f(wt)-f(w*))/(wt-w*)就已经非常小了,在wt处的近似梯度∂f/∂w就更小了,这样通过梯度下降wt+1=wt-α*(∂f/∂w),我们得到的结果就是w的变化非常缓慢,像蜗牛一样,非常缓慢的向我们的最优点w*爬动,那在有限的迭代时间内,它离我们的最优点还是很远。

           所以仅仅靠convex 性质并不能保证在梯度下降和有限的迭代次数的情况下得到的点w会是一个比较好的全局最小点w*的近似点(插个话,有地方说,实际上让迭代在接近最优的地方停止,也是一种规则化或者提高泛化性能的方法)。正如上面分析的那样,如果f(w)在全局最小点w*周围是非常平坦的情况的话,我们有可能会找到一个很远的点。但如果我们有“强凸”的话,就能对情况做一些控制,我们就可以得到一个更好的近似解。至于有多好嘛,这里面有一个bound,这个 bound 的好坏也要取决于strongly convex性质中的常数α的大小。看到这里,不知道大家学聪明了没有。如果要获得strongly convex怎么做?最简单的就是往里面加入一项(α/2)*||w||2

           呃,讲个strongly convex花了那么多的篇幅。实际上,在梯度下降中,目标函数收敛速率的上界实际上是和矩阵XTX的 condition number有关,XTX的 condition number 越小,上界就越小,也就是收敛速度会越快。

    这一个优化说了那么多的东西。还是来个一句话总结吧:L2范数不但可以防止过拟合,还可以让我们的优化求解变得稳定和快速。

           好了,这里兑现上面的承诺,来直观的聊聊L1和L2的差别,为什么一个让绝对值最小,一个让平方最小,会有那么大的差别呢?我看到的有两种几何上直观的解析:

    1)下降速度:

           我们知道,L1和L2都是规则化的方式,我们将权值参数以L1或者L2的方式放到代价函数里面去。然后模型就会尝试去最小化这些权值参数。而这个最小化就像一个下坡的过程,L1和L2的差别就在于这个“坡”不同,如下图:L1就是按绝对值函数的“坡”下降的,而L2是按二次函数的“坡”下降。所以实际上在0附近,L1的下降速度比L2的下降速度要快。所以会非常快得降到0。不过我觉得这里解释的不太中肯,当然了也不知道是不是自己理解的问题。

           L1在江湖上人称Lasso,L2人称Ridge。不过这两个名字还挺让人迷糊的,看上面的图片,Lasso的图看起来就像ridge,而ridge的图看起来就像lasso。

    2)模型空间的限制:

           实际上,对于L1和L2规则化的代价函数来说,我们可以写成以下形式:

           也就是说,我们将模型空间限制在w的一个L1-ball 中。为了便于可视化,我们考虑两维的情况,在(w1, w2)平面上可以画出目标函数的等高线,而约束条件则成为平面上半径为C的一个 norm ball 。等高线与 norm ball 首次相交的地方就是最优解:

           可以看到,L1-ball 与L2-ball 的不同就在于L1在和每个坐标轴相交的地方都有“角”出现,而目标函数的测地线除非位置摆得非常好,大部分时候都会在角的地方相交。注意到在角的位置就会产生稀疏性,例如图中的相交点就有w1=0,而更高维的时候(想象一下三维的L1-ball 是什么样的?)除了角点以外,还有很多边的轮廓也是既有很大的概率成为第一次相交的地方,又会产生稀疏性。

           相比之下,L2-ball 就没有这样的性质,因为没有角,所以第一次相交的地方出现在具有稀疏性的位置的概率就变得非常小了。这就从直观上来解释了为什么L1-regularization 能产生稀疏性,而L2-regularization 不行的原因了。

           因此,一句话总结就是:L1会趋向于产生少量的特征,而其他的特征都是0,而L2会选择更多的特征,这些特征都会接近于0。Lasso在特征选择时候非常有用,而Ridge就只是一种规则化而已。


           OK,就聊到这里。下一篇博文我们聊聊核范数和规则化项参数选择的问题。全篇的参考资料也请见下一篇博文,这里不重复列出。谢谢。


    展开全文
  • pytorch实现L2和L1正则化regularization的方法

    万次阅读 多人点赞 2019-03-14 16:44:27
    pytorch实现L2和L1正则化的方法 目录 目录 pytorch实现L2和L1正则化的方法 1.torch.optim优化器实现L2正则化 2. 如何判断正则化作用了模型? 2.1 未加入正则化loss和Accuracy 2.1 加入正则化loss和Accuracy ...

    pytorch实现L2和L1正则化的方法

    目录

    目录

    pytorch实现L2和L1正则化的方法

    1.torch.optim优化器实现L2正则化

    2. 如何判断正则化作用了模型?

    2.1 未加入正则化loss和Accuracy

    2.1 加入正则化loss和Accuracy 

    2.3 正则化说明

    3.自定义正则化的方法

    3.1 自定义正则化Regularization类

    3.2 Regularization使用方法

    4. Github项目源码下载


    1.torch.optim优化器实现L2正则化

    torch.optim集成了很多优化器,如SGD,Adadelta,Adam,Adagrad,RMSprop等,这些优化器自带的一个参数weight_decay,用于指定权值衰减率,相当于L2正则化中的λ参数,注意torch.optim集成的优化器只有L2正则化方法,你可以查看注释,参数weight_decay 的解析是:

            weight_decay (float, optional): weight decay (L2 penalty) (default: 0)

     使用torch.optim的优化器,可如下设置L2正则化

        optimizer = optim.Adam(model.parameters(),lr=learning_rate,weight_decay=0.01)
    

    但是这种方法存在几个问题,

    (1)一般正则化,只是对模型的权重W参数进行惩罚,而偏置参数b是不进行惩罚的,而torch.optim的优化器weight_decay参数指定的权值衰减是对网络中的所有参数,包括权值w偏置b同时进行惩罚。很多时候如果对b 进行L2正则化将会导致严重的欠拟合,因此这个时候一般只需要对权值w进行正则即可。(PS:这个我真不确定,源码解析是 weight decay (L2 penalty) ,但有些网友说这种方法会对参数偏置b也进行惩罚,可解惑的网友给个明确的答复

    (2)缺点:torch.optim的优化器固定实现L2正则化,不能实现L1正则化。如果需要L1正则化,可如下实现:

    (3)根据正则化的公式,加入正则化后,loss会变原来大,比如weight_decay=1的loss为10,那么weight_decay=100时,loss输出应该也提高100倍左右。而采用torch.optim的优化器的方法,如果你依然采用loss_fun= nn.CrossEntropyLoss()进行计算loss,你会发现,不管你怎么改变weight_decay的大小,loss会跟之前没有加正则化的大小差不多。这是因为你的loss_fun损失函数没有把权重W的损失加上。

    (4)采用torch.optim的优化器实现正则化的方法,是没问题的!只不过很容易让人产生误解,对鄙人而言,我更喜欢TensorFlow的正则化实现方法,只需要tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES),实现过程几乎跟正则化的公式对应的上。

    (5)Github项目源码:https://github.com/PanJinquan/pytorch-learning-tutorials/blob/master/image_classification/train_resNet.py麻烦给个“Star”

    为了,解决这些问题,我特定自定义正则化的方法,类似于TensorFlow正则化实现方法。


    2. 如何判断正则化作用了模型?

    一般来说,正则化的主要作用是避免模型产生过拟合,当然啦,过拟合问题,有时候是难以判断的。但是,要判断正则化是否作用了模型,还是很容易的。下面我给出两组训练时产生的loss和Accuracy的log信息,一组是未加入正则化的,一组是加入正则化:

    2.1 未加入正则化loss和Accuracy

    优化器采用Adam,并且设置参数weight_decay=0.0,即无正则化的方法

        optimizer = optim.Adam(model.parameters(),lr=learning_rate,weight_decay=0.0)

    训练时输出的 loss和Accuracy信息

    step/epoch:0/0,Train Loss: 2.418065, Acc: [0.15625]
    step/epoch:10/0,Train Loss: 5.194936, Acc: [0.34375]
    step/epoch:20/0,Train Loss: 0.973226, Acc: [0.8125]
    step/epoch:30/0,Train Loss: 1.215165, Acc: [0.65625]
    step/epoch:40/0,Train Loss: 1.808068, Acc: [0.65625]
    step/epoch:50/0,Train Loss: 1.661446, Acc: [0.625]
    step/epoch:60/0,Train Loss: 1.552345, Acc: [0.6875]
    step/epoch:70/0,Train Loss: 1.052912, Acc: [0.71875]
    step/epoch:80/0,Train Loss: 0.910738, Acc: [0.75]
    step/epoch:90/0,Train Loss: 1.142454, Acc: [0.6875]
    step/epoch:100/0,Train Loss: 0.546968, Acc: [0.84375]
    step/epoch:110/0,Train Loss: 0.415631, Acc: [0.9375]
    step/epoch:120/0,Train Loss: 0.533164, Acc: [0.78125]
    step/epoch:130/0,Train Loss: 0.956079, Acc: [0.6875]
    step/epoch:140/0,Train Loss: 0.711397, Acc: [0.8125]

    2.1 加入正则化loss和Accuracy 

    优化器采用Adam,并且设置参数weight_decay=10.0,即正则化的权重lambda =10.0

        optimizer = optim.Adam(model.parameters(),lr=learning_rate,weight_decay=10.0)
    

    这时,训练时输出的 loss和Accuracy信息:

    step/epoch:0/0,Train Loss: 2.467985, Acc: [0.09375]
    step/epoch:10/0,Train Loss: 5.435320, Acc: [0.40625]
    step/epoch:20/0,Train Loss: 1.395482, Acc: [0.625]
    step/epoch:30/0,Train Loss: 1.128281, Acc: [0.6875]
    step/epoch:40/0,Train Loss: 1.135289, Acc: [0.6875]
    step/epoch:50/0,Train Loss: 1.455040, Acc: [0.5625]
    step/epoch:60/0,Train Loss: 1.023273, Acc: [0.65625]
    step/epoch:70/0,Train Loss: 0.855008, Acc: [0.65625]
    step/epoch:80/0,Train Loss: 1.006449, Acc: [0.71875]
    step/epoch:90/0,Train Loss: 0.939148, Acc: [0.625]
    step/epoch:100/0,Train Loss: 0.851593, Acc: [0.6875]
    step/epoch:110/0,Train Loss: 1.093970, Acc: [0.59375]
    step/epoch:120/0,Train Loss: 1.699520, Acc: [0.625]
    step/epoch:130/0,Train Loss: 0.861444, Acc: [0.75]
    step/epoch:140/0,Train Loss: 0.927656, Acc: [0.625]
    

    当weight_decay=10000.0

    step/epoch:0/0,Train Loss: 2.337354, Acc: [0.15625]
    step/epoch:10/0,Train Loss: 2.222203, Acc: [0.125]
    step/epoch:20/0,Train Loss: 2.184257, Acc: [0.3125]
    step/epoch:30/0,Train Loss: 2.116977, Acc: [0.5]
    step/epoch:40/0,Train Loss: 2.168895, Acc: [0.375]
    step/epoch:50/0,Train Loss: 2.221143, Acc: [0.1875]
    step/epoch:60/0,Train Loss: 2.189801, Acc: [0.25]
    step/epoch:70/0,Train Loss: 2.209837, Acc: [0.125]
    step/epoch:80/0,Train Loss: 2.202038, Acc: [0.34375]
    step/epoch:90/0,Train Loss: 2.192546, Acc: [0.25]
    step/epoch:100/0,Train Loss: 2.215488, Acc: [0.25]
    step/epoch:110/0,Train Loss: 2.169323, Acc: [0.15625]
    step/epoch:120/0,Train Loss: 2.166457, Acc: [0.3125]
    step/epoch:130/0,Train Loss: 2.144773, Acc: [0.40625]
    step/epoch:140/0,Train Loss: 2.173397, Acc: [0.28125]

    2.3 正则化说明

    就整体而言,对比加入正则化和未加入正则化的模型,训练输出的loss和Accuracy信息,我们可以发现,加入正则化后,loss下降的速度会变慢,准确率Accuracy的上升速度会变慢,并且未加入正则化模型的loss和Accuracy的浮动比较大(或者方差比较大),而加入正则化的模型训练loss和Accuracy,表现的比较平滑。并且随着正则化的权重lambda越大,表现的更加平滑。这其实就是正则化的对模型的惩罚作用,通过正则化可以使得模型表现的更加平滑,即通过正则化可以有效解决模型过拟合的问题。


    3.自定义正则化的方法

    为了解决torch.optim优化器只能实现L2正则化以及惩罚网络中的所有参数的缺陷,这里实现类似于TensorFlow正则化的方法。

    3.1 自定义正则化Regularization类

    这里封装成一个实现正则化的Regularization类,各个方法都给出了注释,自己慢慢看吧,有问题再留言吧

    # 检查GPU是否可用
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    # device='cuda'
    print("-----device:{}".format(device))
    print("-----Pytorch version:{}".format(torch.__version__))
    
    
    class Regularization(torch.nn.Module):
        def __init__(self,model,weight_decay,p=2):
            '''
            :param model 模型
            :param weight_decay:正则化参数
            :param p: 范数计算中的幂指数值,默认求2范数,
                      当p=0为L2正则化,p=1为L1正则化
            '''
            super(Regularization, self).__init__()
            if weight_decay <= 0:
                print("param weight_decay can not <=0")
                exit(0)
            self.model=model
            self.weight_decay=weight_decay
            self.p=p
            self.weight_list=self.get_weight(model)
            self.weight_info(self.weight_list)
    
        def to(self,device):
            '''
            指定运行模式
            :param device: cude or cpu
            :return:
            '''
            self.device=device
            super().to(device)
            return self
    
        def forward(self, model):
            self.weight_list=self.get_weight(model)#获得最新的权重
            reg_loss = self.regularization_loss(self.weight_list, self.weight_decay, p=self.p)
            return reg_loss
    
        def get_weight(self,model):
            '''
            获得模型的权重列表
            :param model:
            :return:
            '''
            weight_list = []
            for name, param in model.named_parameters():
                if 'weight' in name:
                    weight = (name, param)
                    weight_list.append(weight)
            return weight_list
    
        def regularization_loss(self,weight_list, weight_decay, p=2):
            '''
            计算张量范数
            :param weight_list:
            :param p: 范数计算中的幂指数值,默认求2范数
            :param weight_decay:
            :return:
            '''
            # weight_decay=Variable(torch.FloatTensor([weight_decay]).to(self.device),requires_grad=True)
            # reg_loss=Variable(torch.FloatTensor([0.]).to(self.device),requires_grad=True)
            # weight_decay=torch.FloatTensor([weight_decay]).to(self.device)
            # reg_loss=torch.FloatTensor([0.]).to(self.device)
            reg_loss=0
            for name, w in weight_list:
                l2_reg = torch.norm(w, p=p)
                reg_loss = reg_loss + l2_reg
    
            reg_loss=weight_decay*reg_loss
            return reg_loss
    
        def weight_info(self,weight_list):
            '''
            打印权重列表信息
            :param weight_list:
            :return:
            '''
            print("---------------regularization weight---------------")
            for name ,w in weight_list:
                print(name)
            print("---------------------------------------------------")
    

    3.2 Regularization使用方法

    使用方法很简单,就当一个普通Pytorch模块来使用:例如

    # 检查GPU是否可用
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    
    print("-----device:{}".format(device))
    print("-----Pytorch version:{}".format(torch.__version__))
    
    weight_decay=100.0 # 正则化参数
    
    model = my_net().to(device)
    # 初始化正则化
    if weight_decay>0:
       reg_loss=Regularization(model, weight_decay, p=2).to(device)
    else:
       print("no regularization")
    
    
    criterion= nn.CrossEntropyLoss().to(device) # CrossEntropyLoss=softmax+cross entropy
    optimizer = optim.Adam(model.parameters(),lr=learning_rate)#不需要指定参数weight_decay
    
    # train
    batch_train_data=...
    batch_train_label=...
    
    out = model(batch_train_data)
    
    # loss and regularization
    loss = criterion(input=out, target=batch_train_label)
    if weight_decay > 0:
       loss = loss + reg_loss(model)
    total_loss = loss.item()
    
    # backprop
    optimizer.zero_grad()#清除当前所有的累积梯度
    total_loss.backward()
    optimizer.step()

    训练时输出的 loss和Accuracy信息:

    (1)当weight_decay=0.0时,未使用正则化

    step/epoch:0/0,Train Loss: 2.379627, Acc: [0.09375]
    step/epoch:10/0,Train Loss: 1.473092, Acc: [0.6875]
    step/epoch:20/0,Train Loss: 0.931847, Acc: [0.8125]
    step/epoch:30/0,Train Loss: 0.625494, Acc: [0.875]
    step/epoch:40/0,Train Loss: 2.241885, Acc: [0.53125]
    step/epoch:50/0,Train Loss: 1.132131, Acc: [0.6875]
    step/epoch:60/0,Train Loss: 0.493038, Acc: [0.8125]
    step/epoch:70/0,Train Loss: 0.819410, Acc: [0.78125]
    step/epoch:80/0,Train Loss: 0.996497, Acc: [0.71875]
    step/epoch:90/0,Train Loss: 0.474205, Acc: [0.8125]
    step/epoch:100/0,Train Loss: 0.744587, Acc: [0.8125]
    step/epoch:110/0,Train Loss: 0.502217, Acc: [0.78125]
    step/epoch:120/0,Train Loss: 0.531865, Acc: [0.8125]
    step/epoch:130/0,Train Loss: 1.016807, Acc: [0.875]
    step/epoch:140/0,Train Loss: 0.411701, Acc: [0.84375]
    

    (2)当weight_decay=10.0时,使用正则化

    ---------------------------------------------------
    step/epoch:0/0,Train Loss: 1563.402832, Acc: [0.09375]
    step/epoch:10/0,Train Loss: 1530.002686, Acc: [0.53125]
    step/epoch:20/0,Train Loss: 1495.115234, Acc: [0.71875]
    step/epoch:30/0,Train Loss: 1461.114136, Acc: [0.78125]
    step/epoch:40/0,Train Loss: 1427.868164, Acc: [0.6875]
    step/epoch:50/0,Train Loss: 1395.430054, Acc: [0.6875]
    step/epoch:60/0,Train Loss: 1363.358154, Acc: [0.5625]
    step/epoch:70/0,Train Loss: 1331.439697, Acc: [0.75]
    step/epoch:80/0,Train Loss: 1301.334106, Acc: [0.625]
    step/epoch:90/0,Train Loss: 1271.505005, Acc: [0.6875]
    step/epoch:100/0,Train Loss: 1242.488647, Acc: [0.75]
    step/epoch:110/0,Train Loss: 1214.184204, Acc: [0.59375]
    step/epoch:120/0,Train Loss: 1186.174561, Acc: [0.71875]
    step/epoch:130/0,Train Loss: 1159.148438, Acc: [0.78125]
    step/epoch:140/0,Train Loss: 1133.020020, Acc: [0.65625]

    (3)当weight_decay=10000.0时,使用正则化

    step/epoch:0/0,Train Loss: 1570211.500000, Acc: [0.09375]
    step/epoch:10/0,Train Loss: 1522952.125000, Acc: [0.3125]
    step/epoch:20/0,Train Loss: 1486256.125000, Acc: [0.125]
    step/epoch:30/0,Train Loss: 1451671.500000, Acc: [0.25]
    step/epoch:40/0,Train Loss: 1418959.750000, Acc: [0.15625]
    step/epoch:50/0,Train Loss: 1387154.000000, Acc: [0.125]
    step/epoch:60/0,Train Loss: 1355917.500000, Acc: [0.125]
    step/epoch:70/0,Train Loss: 1325379.500000, Acc: [0.125]
    step/epoch:80/0,Train Loss: 1295454.125000, Acc: [0.3125]
    step/epoch:90/0,Train Loss: 1266115.375000, Acc: [0.15625]
    step/epoch:100/0,Train Loss: 1237341.000000, Acc: [0.0625]
    step/epoch:110/0,Train Loss: 1209186.500000, Acc: [0.125]
    step/epoch:120/0,Train Loss: 1181584.250000, Acc: [0.125]
    step/epoch:130/0,Train Loss: 1154600.125000, Acc: [0.1875]
    step/epoch:140/0,Train Loss: 1128239.875000, Acc: [0.125]
    

    对比torch.optim优化器的实现L2正则化方法,这种Regularization类的方法也同样达到正则化的效果,并且与TensorFlow类似,loss把正则化的损失也计算了。

    此外更改参数p,如当p=0表示L2正则化,p=1表示L1正则化。

    4. Github项目源码下载

    《Github项目源码》https://github.com/PanJinquan/pytorch-learning-tutorials/blob/master/image_classification/train_resNet.py

    麻烦给个“Star”:


    如果你觉得该帖子帮到你,还望贵人多多支持,鄙人会再接再厉,继续努力的~

    展开全文
  • V4L2框架-v4l2 device

    千次阅读 2018-06-23 11:22:00
    本文对 V4L2 中比较容易理解的骨干结构进行介绍,涉及两个核心结构体:v4l2_device, v4l2_subdev。文章围绕这两个结构体以 Linux-4.4 内核的 omap3isp 代码为例进行相关的介绍,所谓介绍还是起到辅助作用,真真儿的...

    阅读原文

    本文对 V4L2 中比较容易理解的骨干结构进行介绍,涉及两个核心结构体:v4l2_devicev4l2_subdev。文章围绕这两个结构体以 Linux-4.4 内核的 omap3isp 代码为例进行相关的介绍,所谓介绍还是起到辅助作用,真真儿的还是要靠 RTFSC、WTFSC。

    00 - V4L2框架概述

    下面「该例程」均指的是 omap3isp 这个例程。

    V4L2 框架补充

    首先看图:
    V4L2 子模块划分

    V4L2 子模块划分

    这并不是官方的划分,是我自己根据自己感觉划分出来的,也就是那么回事儿吧。那么这篇文章里面秀的主题就是子设备系统这一块,就是上面图中的第三个「subdev」。由图也可知,本文主要是讲如何管理众多的输入设备。注意:是所有的设备,并不是运行时的数据流通路上面的设备。

    主设备

    主设备使用 v4l2_device 进行抽象化表示。该例程使用了设备树来进行设备解析,使用平台驱动进行相应的驱动 probe。在文件 drivers/media/platform/omap3isp/isp.c 中有 isp_probe 函数,该函数的第一个参数定义即是 isp_device,回忆前面的文章,里面有提到大多数情况下,需要将 v4l2_device 结构体嵌入到一个更大的结构体里面使用,此为驱动自定义的结构体,那就是它了,那么它的定义如下(删去了与分析无关的部分):

    struct isp_device {
    	struct v4l2_device v4l2_dev;
    	struct v4l2_async_notifier notifier;
    	struct media_device media_dev;
    
    	... ...
    }
    

    其中与本节有关的就属于是 struct v4l2_device v4l2_dev 这个了,它将作为串联管理整个 omap3isp 的管理者的存在。

    同时它的用法契合前面讲的「嵌入式」,struct isp_device 是驱动自定义的结构体,这个结构体可以看作是整个 omap3isp 的设备抽象化结构体,也就是说:它就代表了 omap3isp 这个大的设备。下面将看到它的用法。

    注册 v4l2_device

    在 isp.c 的 isp_probe 函数中有调用 isp_register_entities 函数,里面开头的内容大致如下:

    ... ...
    	isp->v4l2_dev.mdev = &isp->media_dev;
    	ret = v4l2_device_register(isp->dev, &isp->v4l2_dev);
    	if (ret < 0) {
    		dev_err(isp->dev, "%s: V4L2 device registration failed (%d)\n",
    			__func__, ret);
    		goto done;
    	}
    ... ...
    

    其实可以看到注册函数里面并没有把高设备加入到一个链表里面什么的,而是初始化结构体成员,比如子设备链表头初始化,增加 dev 的引用等。在一切子设备被串联起来之前首先要初始化注册 v4l2_device 这个总的设备。

    v4l2_subdev

    该结构体是抽象化的子设备,用于子设备管理之用。它有「初始化」、「注册子设备」、「注册子设备节点」等几个操作。通常情况下「初始化」的函数实例被定义在一个个的子设备驱动模块内部,「注册子设备」与「注册子设备节点」这两个函数的实体被定义在父设备模块内部,比如: isp.c 中。

    上代码(isp_probe 函数中):

    ret = isp_initialize_modules(isp);
    if (ret < 0)
    	goto error_iommu;
    ret = isp_register_entities(isp);
    if (ret < 0)
    	goto error_modules;
    

    下面首先看下 isp_initialize_modules 函数中的实现:

    static int isp_initialize_modules(struct isp_device *isp)
    {
    	int ret;
    
    	ret = omap3isp_csiphy_init(isp);
    
    	ret = omap3isp_csi2_init(isp);
    
    ... ...
    
    	ret = omap3isp_h3a_aewb_init(isp);
    
    	ret = omap3isp_h3a_af_init(isp);
    ... ...
    	return 0;
    ... ...
    

    再进去 omap3isp_xxxx_init 函数里面就可以看到有类似 v4l2_subdev_init 的函数调用了,文章尽量少贴代码,多讲实现、使用机理,相信你一定能自己找到相关代码的。

    所以结构大体上是这样子的:

    1. 你有一个输入设备 omap3isp,它的管理者列在一个单独的代码文件里面名为 isp.c
    2. 定义一个自定义的抽象化结构体代表 omap3isp 这个设备,名为 isp_device,并把 v4l2_device 嵌入内部作为子设备管理工具;
    3. 把子设备-类似 csi、preview、3a 等抽象化为一个个子设备,每个子设备一个代码文件,名为 ispxxx.c,分别有自己的抽象化结构体,名为 isp_xxx_dev,内部嵌入了 v4l2_subdev 作为子设备的抽象工具使用。同时实现自己的设备初始化函数,名为 xxx_init_eneities
    4. 在管理者 isp.c 的 probe 函数里面调用子设备的 xxx_init_entities,子设备初始化函数里面会做好 v4l2_subdev 的初始化工作;
    5. 管理者的 probe 函数里面注册 v4l2_device,注册子设备,必要时注册子设备节点在用户空间生成 /dev/nodeX
    6. 大功告成,此时你就可以通过 v4l2_device 来管理所有的子设备了,框架本身提供了很好用的管理方式与相关的回调函数、结构体成员等等。

    设备的管理

    设备的管理必然需要主设备与子设备之间能够互联互通,否则的话谈何去管理,本节就介绍如何实现主设备与子设备之间的数据互联互通。

    主设备子设备互通

    通过上面的步骤建立了连接之后怎么从主设备找到子设备呢?如何从子设备找到主设备?如何从 v4l2_device 到自定义的主设备抽象结构体?如何从 v4l2_subdev 到子设备自定义的结构体?

    • 如何从主设备找到子设备
      首先需要获取 v4l2_device 结构体,然后可以使用 list_for_each_entry 来对子设备进行遍历,其中子设备的结构体内部有一个 name 成员,长度为32个字节,这个字段要求是整个 v4l2_device 下属唯一的,所以要想找到某一个指定的子设备完全可以在遍历的时候对比子设备的 name 字段看是不是自己想要找的。
    • 如何从子设备找到主设备
      v4l2_subdev 的结构体里面有一个 v4l2_dev 的指针成员,该成员会在子设备被注册的时候指向 v4l2_device 成员,注册函数为 v4l2_device_register_subdev。在该步骤执行完毕之后就可以通过获取子设备结构体内部的 v4l2_dev 成员来获得主设备结构体。
    • 如何从主设备到主设备实例化结构体
      可以看到 v4l2_device 内部并没有什么私有指针之类的东西,那怎么去找到主设备的实例化结构体呢,此时可以通过另一种偏门方法获取,比如在定义结构体的时候把 v4l2_device 放在结构体成员的第一个,之后通过 v4l2_subdev 获取到 v4l2_device 之后就可以把其地址强制转换为主设备自定义的实例化结构体来实现访问。
    • 如何从子设备到子设备实例化结构体
      子设备内部有两个私有的指针:dev_privhost_priv。前一个好理解也很好使用,使用的时候就调用 v4l2_set_subdevdata 函数将 dev_priv 指向子设备实例化结构体即可,然后就可以用 v4l2_get_subdevdata 来从 v4l2_subdev 获取到子设备结构体实例化的结构体数据了。后一个不是很好理解其用处,但是也可以通过 v4l2_set_subdev_hostdata/v4l2_get_subdev_hostdata 来进行设置/获取,host 也即主控端,比如一个 camera sensor 的 SOC 端的控制器就可以作为主控端,再比如使用 I2C 进行通信的 camera sensor 的 SOC 端的 I2C 控制器就可以作为 host_priv,必要时通过 I2C 来控制子设备的行为。或者干脆把主设备实例化的结构体作为 host data 也可以。

    主子设备信息交流

    本节使用多个实际的用例来深入解释下各种信息交流方式与情景。比如:如何控制访问指定类型的子设备?子设备如何向主设备回返通知?

    • 访问所有的 sensor 设备并关闭其数据流
    1. 子设备注册的时候应该要提供了相关的操作函数,那就是 v4l2_subdev_ops 这个结构体了,在此例中我们就仅仅设置其 video 成员的 s_stream 成员。
    2. 提供子设备组 id,也就是 v4l2_subdevgrp_id 成员,此处我们设置为一个我自己假定的枚举类型(你只要保证这个枚举类型是整个 v4l2_device 下属唯一的就行),我假定为 OMAP3ISP_CAMSENSOR
    3. 初始化并注册子设备,就不再多说了,初始化以及注册的方式前面都有提到过了。
    4. 执行 v4l2_device_call_all(v4l2_device, OMAP3ISP_CAMSENSOR, video, s_stream, 0);,此时会遍历挂在 v4l2_device 名下的所有的 OMAP3ISP_CAMSENSOR 组的子设备,调用其 s_stream 的模块函数进行数据流的关闭。
    • 子设备数据流关闭后向主设备回返通知
    1. 需要提供主设备的 notify 成员操作函数。
    2. 定义好 notification 的格式,比如我自己的定义,高8位表示哪个子设备,次8位表示哪种类型的操作(此处是 video 类型的 ops),再次8位表示具体的操作函数(s_stream),低8位表示操作值(0关闭)。
    3. 子设备调用 v4l2_subdev_notify 函数进行正式通知的发送,此时也可以带一些参数,只需要传递其地址就可以了,主子设备端商定好数据的格式即可。
    4. 主设备收到通知之后进行相关的操作。

    交通枢纽 video_device

    该结构体整合了数据流管理的终端模块功能,负责提供从内核空间到用户空间的数据交流,属于非常重要的一个功能了,必不可少的那种。

    通常情况下所有的子设备都可以注册一个 video_device 结构体来在用户空间生成一个设备节点以供用户进行操作,但是区别在于只有负责真正传递视频数据的那个模块用得着注册 video 类型的设备节点名称(比如内核输入设备数据链的 DMA 数据终端),其它的使用 v4l-subdev 类型的就可以了。

    video_device 只与 v4l2_device 进行绑定关联,通过后者这层关系可以访问到整个子设备网络的资源。

    如何注册 video 类型节点

    使用 video_register_device 配合 VFL_TYPE_GRABBER 参数进行注册,此时该函数执行完毕并返回的时候就可以在用户空间看到形如 /dev/videoX 的设备节点了。

    注意需要提供其操作函数,类似下面的:

    static struct v4l2_file_operations isp_video_fops = {
    	.owner = THIS_MODULE,
    	.unlocked_ioctl = video_ioctl2,
    	.open = isp_video_open,
    	.release = isp_video_release,
    	.poll = isp_video_poll,
    	.mmap = isp_video_mmap,
    };
    

    关于其成员如何实现本节不详细介绍,在后面的 videobuf2 一文中会进行详细介绍。

    如何注册其它类型节点

    对于 v4l2 输入设备来说,使用 v4l2_device_register_subdev_nodes 来进行批量的设备节点注册,它内部依然会调用 video_register_device 函数,只不过会使用 VFL_TYPE_SUBDEV 类型来代替上面 VFL_TYPE_GRABBER,那么在用户空间生成的设备节点名称就是 v4l-subdevX 了。

    这种情况下注册的设备节点的操作函数是在 v4l2-device.c 里面定义好的默认操作函数,它的定义如下:

    const struct v4l2_file_operations v4l2_subdev_fops = {
    	.owner = THIS_MODULE,
    	.open = subdev_open,
    	.unlocked_ioctl = subdev_ioctl,
    #ifdef CONFIG_COMPAT
    	.compat_ioctl32 = subdev_compat_ioctl32,
    #endif
    	.release = subdev_close,
    	.poll = subdev_poll,
    };
    

    结束语

    到这里为止,基本上关于设备这块的基础操作已经介绍完毕,至于还有一些比较高级的操作就放在后续的文章里面进行介绍了。预告一下,下一篇文章是讲 media framework 的,循序渐进。该篇文章读完并实践之后就可以在用户空间看到有 /dev/video 设备节点了,可以写一个小的测试用例,当打开设备节点的时候遍历一遍子设备把子设备的名字打出来,也可以增加更详细的信息,总之,本文的目的是实现一个 v4l2_device 管理框架下的设备拓扑,你可以把这个拓扑结构打印出来就算是完美完成任务了。

    以防万一有滴同学不知道:
    「RTFSC」:Read The Fxxking Source Code
    「WTFSC」:Dao Li Tong Shang


    想做的事情就去做吧
    展开全文
  • 欧式距离,l2范数,l2-loss,l2正则化1.欧氏距离L2范数范数计算公式L1范数L2范数在机器学习方面的区别为什么L2范数可以防止过拟合?3. L2-Loss4. L2正则化正则化L2正则化参考文献 1.欧氏距离 距离度量(Distance)用于...
  • V4L2框架概述

    万次阅读 多人点赞 2018-06-17 15:25:07
    本文开启 linux 内核 V4L2 框架部分的学习之旅,本文仅先对 V4L2 的框架做一个综述性的概括介绍,然后接下来的文章中会对 V4L2 框架的各个子模块进行一个全面的介绍,包括每一部分的实现原理,如何使用,用在什么...
  • V4L2

    千次阅读 2017-08-31 10:15:16
    v4L2是针对uvc免驱usb设备的编程框架 ,主要用于采集usb摄像头等 HIGHGUI ERROR: V4L2: Pixel format of incoming image is unsupported by OpenCV Unable to stop the stream.: Bad file ...
  • l2范数

    2020-06-05 15:21:04
    xi2,l2范数公式\|x\|_2 = \sqrt{\sum_{i=1}^n \ x^2_i\quad} \quad \text {,l2范数公式} ∥x∥2​=i=1∑n​ xi2​​,l2范数公式 也就是欧几里得范数,即距离 机器学习 正则化 机器学习的正则化是什么意思? ...
  • L2TP

    2019-01-08 05:43:45
    gt;&gt; ...L2TP ...L2TP是一种工业标准的Internet隧道协议,功能大致和PPTP协议类似,比如同样可以对网络数据流进行加密。不过也有不同之处,比如PPTP要求网络为IP网络,L2TP要求面向数据包...L2TP提供包头压缩...
  • Win10 L2TP连接不上的解决办法

    万次阅读 2018-07-23 22:32:18
    八步搞定Win10 L2TP 连接不上 1. 单击“开始”,单击“运行”,键入“regedit”,然后单击“确定” 2. 找到注册表的几项 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RasMan\Parameters中的 AllowL...
  • PPTP、L2F、L2TP协议

    2020-06-30 14:44:10
    PPTP\L2F\L2TP协议学习笔记
  • L2 normalization和L1,L2 regulation

    千次阅读 2019-09-18 11:48:56
    L2 Normalization 第二种Normalization对于每个样本缩放到单位范数(每个样本的范数为1),主要有L1-normalization(L1范数)、L2-normalization(L2范数)等 Normalization主要思想是对每个样本计算其p-范数,然后...
  • L2TP基本原理

    万次阅读 多人点赞 2019-08-31 20:40:20
    L2TP VPN简介 L2TP基本概念: L2TP(Layer 2 Tunneling Protocol) VPN是一种用于承载PPP报文的隧道技术,该技术主要应用在远程办公场景中为出差员工远程访问企业内网资源提供接入服务。 目的: L2TP VPN技术出现...
  • v4l2框架v4l2-device API分析

    千次阅读 2017-05-19 15:23:32
    涉及到的结构体: struct v4l2_device在v4l2框架中充当所有v4l2_subdev的父设备...struct v4l2_device { /* dev->driver_data points to this struct. Note: dev might be NULL if there is no parent device as
  • L2范数

    千次阅读 2019-01-11 16:23:49
    L2范数: 有人把有它的回归叫“岭回归”(Ridge Regression),有人也叫它“权值衰减weight decay”,这用的很多吧,因为它的强大功效是改善机器学习里面一个非常重要的问题:过拟合。至于过拟合是什么,上面也解释了...
  • 在练习机器学习时,可能会选择决定是使用L1范数还是L2范数进行正则化,还是作为损失函数等。 L1范数也称为最小绝对偏差(LAD),最小绝对误差(LAE)。它基本上是最小化目标值(Yi)和估计值(f(xi))之间的绝对...
  • L1范数与L2范数的区别

    万次阅读 多人点赞 2019-05-17 11:51:59
    把答案放在前面 L0范数是指向量中非0的元素的个数。...L2范数可以防止过拟合,提升模型的泛化能力,有助于处理 condition number不好下的矩阵(数据变化很小矩阵求解后结果变化很大) (核心:L2对大数,对outlier离...
  • L2TP简介

    万次阅读 2018-08-13 11:30:23
    一、L2TP介绍 1、基础介绍 L2TP(Layer 2 Tunneling Protocol,二层隧道协议)通过在公共网络(如Internet)上建立点到点的L2TP隧道,将PPP(Point-to-Point Protocol,点对点协议)数据帧封装后通过L2TP隧道传输...
  • L2 normalization

    千次阅读 2018-04-14 17:25:37
    https://blog.csdn.net/zqjackking/article/details/69938901(SSD中有L2的实现)...
  • gstreamer v4l2src和v4l2sink用代码实现

    千次阅读 2020-01-19 17:39:01
    gstreamer 中有很多插件,对于V4L2框架中的视频输入设备和视频输出设备分别由v4l2src和v4l2sink插件来对应。 其实v4l2src和v4l2sink也都是调用标准的v4l2 api来实现的,只不过将其整入到了gstreamer框架中,加入了...
  • v4l2

    2018-07-20 22:22:48
    以vivi.c为例,分析v4l2流程: a.分配video_device结构体 b.设置 c.注册: video_register_device(); 1.分配: vfd = video_device_alloc(); if (!vfd) goto unreg_dev; 2.设置: /**********************...
  • L2TP客户端

    热门讨论 2012-02-20 10:07:37
    用于WINDOWS平台下的L2TP客户端工具,支持思科,华为等主要网络设备的L2TP技术,可支持PPTP。。
  • L2TP协议

    千次阅读 2018-05-05 15:29:20
    L2TP协议 L2TP(Layer 2 Tunneling Protocol) 第二层隧道协议。该协议是工业标准的Internet隧道协议。L2TP实现的两种方式LAC (L2TP Access Concentrator L2TP访问集中器)是附属在交换机网络中具有PPP端系统和L2TP...
  • L1和L2正则

    千次阅读 2019-09-27 21:31:34
    l1、l2 l2 总述l1、l2 各种范式
  • L1、L2正则VS L1、L2 loss

    千次阅读 2018-05-14 14:44:52
    1.L1、L2正则——参数空间 L1范数表达式为:,L2范数表达式:L1正则(上图左),使得某些特征量变为0,因此具有稀疏性,可用于特征选择;L2正则(上图右),整体压缩特征向量,使用较广。2. L1、L2损失——loss函数...
  • 视频应用可以通过两种方式从V4L2驱动申请buffer 1. USERPTR, 顾名思义是用户空间指针的意思,应用层负责分配需要的内存空间,然后以指针的形式传递给V4L2驱动层,V4L2驱动会把capture的内容保存到指针所指的空间 ...
  • aa = tf.nn.l2_normalize(a) # a中每个元素除以(平方和开2次根号), 标准的l2范数, 得到是一个shape与输入a相同的tensor # 第二种正则化方法 a_l2_loss = tf.nn.l2_loss(a) # a中每个元素平方和除以2, 是一个...
  • L2tp协议

    千次阅读 2018-05-07 09:27:46
    LAC(L2TP Access Concentrator L2TP访问集中器): 是附属在交换网络上的具有PPP端系统和L2TP协议处理能力的设备。LAC一般是一个网络接入服务器NAS,主要用于通过PSTN/ISDN网络为用户提供接入服务。 LNS(L2TP ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 47,251
精华内容 18,900
关键字:

l2