- Published on
机器学习 | 正则化以及线性回归调参侠工具包
- Authors

- Name
- Jiaqi Wang(Ramis)
[ 最后更新 于 2024-06-30]
今天有一个炫酷的标题! 哎 只是觉得 《谈谈正则化》这样的标题好普通啊… 其实也是不想只谈正则化,还想记录本人关于调参这件事的一些小想法。
过度拟合陷阱
刚开始接触回归统计的时候,很容易有一个误区,就是一直死盯着R方看。事实上,R方是一个很具有迷惑性的东西,会误导你拼命往你的模型里加很多变量。甚至有时候,我们或许会因为变量太多开始罔顾逻辑地为所欲为,陷入一个过拟合(overfitting)的陷阱——做出一个只能解释训练集本身的一个模型。比较极端的一个例子就是用5个变量(or高次项)去拟合只有五个数据的训练集,方程甚至有个解(再加一个就有多个解了喂…)。

所以,今天就来说说面对一大堆数据时避免过度拟合的一般思路:
减少自变量(Subset Selection)
既然过拟合是自变量过多引起的,那么一个很直观的想法就是从中挑选一些。
1)首先,我们在不过脑子的情况下,考虑朴素的排列组合:n个变量从中分别取1,2,3...n个,复杂度是。别说人工筛不出来,在变量超过30个的情况下,计算机筛选也需要大半天了。因此,这个想法在时间和硬件成本有限的情况下,大概率会被直接pass。
2) 穷举不成,直接贪心(一看就是老骗分侠啦)。简单来说就是从空模型开始,依次加入能让模型性能提升(或者说cost最显著下降)的变量,直到没有变量可以使得显著提升了。上述的这种思路又可以被称为正向选择法(Forward Selection)。当然,我们也可以反过来想,先把所有模型全放进去,然后依次删除无关紧要的变量,直到若再删一个变量模型性能就显著下降了(Backward Selection)。或者将二者结合,在每一步里既可以添加又可以减少。但是,既然是贪心,我们不能保证以上这些步骤就一定能找到全局最优的变量组合,因为我们不能断言模型的性能不一定是遵循这样每一步都最优的递进关系的。
虽然看起来不是很科学,但是不得不说直接删一些变量是最方便实用的一个减少过拟合的方法之一了。
设置验证集/交叉验证(Validation Set/Cross Validation)
除了变量删除,交叉验证这个方法也是很常见的避免过度拟合的手段之一。道理很简单,若对训练集过度拟合,对验证集的预测表现往往就不会很好。所以,我们通常把手中的数据采用80%-20%的比例来划分成训练集和验证集——训练集用于模型的训练,而验证集用于验证模型的可靠性(哇,废话文学浓度百分百)。而这个训练集的划分也是有讲究的,需要保证验证集和测试集具有相似的分布特征。而为了确保验证集具有代表性,除了随机取样,常常需要多次取样交叉验证。比如常用的K-ford法:
将数据集分成k个等大小的子集,每次使用k−1个子集进行训练,用剩下的一个子集进行验证。重复这一过程 k次以便得到更稳定的模型评估结果。比如说,我们的数据集有1000个样本,选择5折交叉验证,则每个子集有200个样本。模型会被训练5次,每次使用800个样本进行训练,200个样本进行验证。
当然,交叉验证的具体操作并非今天之重点,或许之后会在详细写一篇。因为下面我们终于要开始介绍正则化了。
正则化 Regularization
之前对这个概念及翻译很懵,网上查处的一众资料也没有交代实行正则化的前因后果。直到最近正紧看了书才知道,哦,这个其实不就是可以看作自动档调参吗(如果说上面的变量删除是手动挡的话)。
所谓L1正则化(Lasso)和L2正则化(Ridge):
二者都是在原有的cost function基础上加了一个所谓的惩罚项(Penalty Term)。只不过前者是绝对值项,后者是平方项。这里的选取很重要,一般需要在保持变量的解释性和避免过拟合之间进行平衡(可以结合k-ford cross validation的方法选出最佳的)。
下面我们借着之前逻辑回归时预测录取概率的三个变量,开始一边介绍L1和L2正则化的实现方法和异同。
首先,让我们先造出多个高阶变量使得这个模型变得有了一些过度拟合的倾向(虽然不知道到底有没有)。我们在w1,w2,w3的基础上变出二次项w4,w5,w6以及三次项w7,w8和w9(numpy里是自带这些函数功能的。):
squared_columns = np.square(X_input)
cubic_columns = np.power(X_input, 3)
X_train_poly = np.concatenate((X_input, squared_columns,cubic_columns), axis=1)
于是我们就造出了我们新的有九个变量的数据集,
X_train_poly=z_score(X_train_poly)
此时,我们还需要改写cost function的函数以及梯度递降的梯度值函数(此处以L1函数为例,对应的L2实现只需要把惩罚项的绝对值改成对应的平方项即可)
def compute_cost_reg_L1(X,y,w,b,lambda_=1):
m, n = X.shape
cost_without_reg = compute_cost(X, y, w, b)
reg_cost = np.sum(np.abs(w)) #L1正则化以绝对值作为惩罚项,L2此处用w[j]的平方参与求和
reg_cost = (lambda_ / m) * reg_cost
total_cost = cost_without_reg + reg_cost
return total_cost
def compute_gradient_reg_L1(X, y, w, b, lambda_ = 1):
m, n = X.shape
dj_dw,dj_db = compute_gradient(X, y, w, b)
for j in range(0,n):
dj_dw_reg=lambda_/m*np.sign(w[j]) #L1正则化梯度的更新加入sign函数项(正数为1,负数为-1,绝对值函数求导所得),L2正则化此处则为简单的一次项w[j]本身
dj_dw[j]=dj_dw_reg+dj_dw[j]
return dj_dw, dj_db
接着,我们选取一系列的(从0.001,0.01到1000)分别用L1和L2正则化进行梯度递降来查看对9个自变量的影响结果:
我们可以看到,L1正则化更倾向于先把某些不重要的变量的系数调为0(将矩阵变得稀疏)。在图表里我们可以看到,在时,几条浅蓝色/绿色的线已经率先降到0,从模型里消失了。而此时,cost曲线并没有下降很多,说明它们之前的存在很可能就是过度拟合了。这其实和手动的删变量的结果或许类似,只不过L1帮你自动化了这个删除的过程。不过,当较大(达到100)时,多个变量都被迫将为0,模型也失去了估计的能力。因而,对于的取值,还是要十分谨慎的。
再看看L2正则化,我们不难看出所有的变量在时候也没有任何一个参数完全消失,而是所有参数几乎以同等比例被缩放的。这样同样在一定程度上降低过度拟合的风险,虽然我们依然拥有九个解释变量。那么为什么L1和L2的调节结果这么不同呢?起因还是惩罚项的形式:
如果我们以二元为例,将cost function的前后两项想象成图形,前者是梯度递降时的等高线一样的椭圆,后者在L1中则是一个菱形,在L2中的平方项变成了一个圆形/椭圆。
我们此时希望求的最小值,
这也可以理解为,在的约束条件下,求原cost function的最小值。此处的s为我们假定的一个用在惩罚项上的预算值(当然这个预算值是受到取值影响的,越大所能承受的预算相应就越小,且求极值的整个过程是一个动态的过程,但其菱形的本质形状不会随着放大或缩小而改变)。那么该题看作在该菱形内所有点中,寻找对于cost function的取值最小点,也就是菱形和cost function层层椭圆中最内环者相接处——往往在角点。而L2正则化的相接位置,则变为圆形/椭圆的相切点。这就造成了二者对于系数放缩的结果不同。

而在实际的使用中,到底是使用L1正则化还是L2正则化则需要依据情况来判断:假如多个解释变量的解释能力差异不大,难分主次的话,可以使用L2减少来进行相似比例的放缩而减少过拟合。相反,如果各个解释变量之间主次分明,回归后的w差异过大,需要取舍(例如 一次项二次项及其他高次项同时存在而需要做取舍),那么通过L1来减少部分边缘的解释变量是合适的。
结语:
本文简要介绍了一些最近接触使用的减少过拟合的方法,在看的过程中还看到了很多相关概念,比如说关于多重共线性、高维回归,这些话题虽然和过拟合相关,但暂时没有被放进来(因为我还没有开始深入看完呀…),有待之后继续研究讨论。
总而言之,过拟合是盲目追求模型的精确性(prediction accuracy)的一个产物(尤其是指过分追求其对于虚训练集的准确性)。而我们进行数据分析和回归,除了准确性以外,模型的可解释性(interpretability)以及其预测的泛化性(Generalizability)也是回归分析所需要考虑的因素。这也是很多其他机器学习算法大行其道的今天,我们依然需要简单线性回归的一个原因,因为——它好解释 讲道理呀。有的时候能用一条直线就说明白的关系,为什么要用曲线呢,就因为显卡现在不要钱了是吧(滑稽脸)?

这周把线性回归所涉及到的大部分概念补全。神经网络的内容还没有完全看完,估计要下周才能动笔了。
学得好慢呀。
-鹅仔 2024/06/30 18:30