英文原文:https://www.analyticsvidhya.com/blog/2017/06/a-comprehensive-guide-for-linear-ridge-and-lasso-regression/
对原文的表达有部分改动
背景
考虑这样一个场景,你需要预测某一家连锁购物超市在某个节日的销售情况,你可以调用连锁超市的所有历史数据情况,包括这个商店,地理位置,商品类别,价格,尺寸等等。通过这些资料,预测出这家店在某个节日时的销售情况。请花上几分钟思考一下,可能有哪些因素?
数据集: The Big Mart Sales
一些简单的预测模型
刚开始,让我们使用一些简单的方法进行预测。
Mean sales(平均销售额)
如果您必须预测某件商品的销售额,即使没有任何机器学习的知识积累,你也可以将它过去几天(也可以是月或者年)的平均值作为预测。这是一个很好的开始,我们给出了一个自觉合理的模型。那么,这个模型有多好呢?
事实证明,我们可以通过多种方式来评估我们的模型有多好。最常见的方法是均方误差(Mean Squared Error)。让我们了解如何衡量它。
预测误差(Prediction error)
为了评估模型的好坏,我们需要了解误差预测的影响。如果我们预测销售额高于实际水平,商店将花费大量资金进行不必要的安排,从而导致库存过剩。另一方面,如果我预测得太低,我将失去销售的机会。
因此,计算误差的最简单方法是计算预测值和实际值的差异。但是,如果我们简单地将它们相加,它们可能会相互抵消,因此我们在相加之前对这些误差进行平方。我们还将它们除以样本的数量来计算平均误差,因为它不应该依赖于样本的数量。
这里 是实际值和预测值之间的差异。这就是均方误差(MSE)
下面通过代码求解训练数据和测试数据的 MSE:
test.csv
train.csv
'''
Predicting Mean
'''
# importing required libraries
import pandas as pd
from sklearn.metrics import mean_squared_error
# read the train and test dataset
train_data = pd.read_csv('train.csv')
test_data = pd.read_csv('test.csv')
print(train_data.head())
# shape of the dataset
print('\nShape of training data :',train_data.shape)
print('\nShape of testing data :',test_data.shape)
# Now, we need to predict the missing target variable in the test data using the mean only
# target variable - Item_Outlet_Sales
train_y = train_data['Item_Outlet_Sales']
test_y = test_data['Item_Outlet_Sales']
print('Mean of Target Variable : ',train_y.mean())
# predict the target on the test dataset
predict_train = [train_y.mean() for i in range(train_y.shape[0])]
# Root Mean Squared Error on training dataset
rmse_train = mean_squared_error(train_y,predict_train)**(0.5)
print('\nRMSE on train dataset : ', rmse_train)
# predict the target on the testing dataset
predict_test = [train_y.mean() for i in range(test_y.shape[0])]
# Root Mean Squared Error on testing dataset
rmse_test = mean_squared_error(test_y,predict_test)**(0.5)
print('\nRMSE on test dataset : ', rmse_test)
输出如下:
Shape of training data : (1364, 36)
Shape of testing data : (341, 36)
Mean of Target Variable : 2237.6478772727273
RMSE on train dataset : 1768.4984457265218
RMSE on test dataset : 1528.7079090483899
通过平均数的预测误差相当大,可见此方法在这里并不适用
线性回归(Linear Regression)
线性回归是在预测模型的最简单、使用最广泛的统计技术。它本质上为我们提供了一个方程,将数据集的特征作为自变量,因变量为销售额。
线性回归方程看起来像这样:
在这里,Y 作为我们的因变量(销售额),X 是自变量,w 是系数。系数根据特征的重要性作为权重分配为变量。例如,如果我们认为与店铺规模相比,某个商品的销售额对位置类型的依赖性更高,这意味着即使是较小的门店,1 级城市的销售额也会高于 3 级城市的销售额。因此,位置因素的系数将大于商店规模的系数。
所以,首先让我们尝试理解只有一个特征的线性回归,即只有一个自变量。因此我们的方程变为:
这个方程称为简单线性回归方程,它代表一条直线,其中 b 是截距,w 是直线的斜率。看看下面的销售和 MRP 之间的图表。
令人惊讶的是,我们可以看到产品的销售额随着其 MRP 的增加而增加。因此,红色虚线代表我们的预测的拟合线。但是如何找出这条线呢?
最佳拟合线
正如您在下面看到的,可以有很多根拟合线可用于根据其 MRP 估算销售额。那么你将如何选择最佳拟合线呢?
最佳拟合线的主要目的是我们的预测值应该更接近我们的实际值或观察值,因为预测如果远离真实值就没有意义了。换句话说,我们倾向于最小化我们预测的值与观察值之间的差异,这实际上称为误差(error)。误差的图形表示如下所示,误差由显示预测值和实际值之间差异的垂直线表示:
现在我们的主要目标是找出误差并将其最小化。但在此之前,我们先想一想如何处理第一部分,也就是计算误差。我们已经知道误差是我们预测的值与观测值之间的差值。让我们考虑三种计算误差的方法:
- 误差总和 ,它可能会抵消正负误差。
- 误差绝对值的总和 :绝对值会阻止消除误差
- 误差平方和 :这是实践中最常用的方法,因为与较小的误差值相比,这里我们对较高的误差值的惩罚要多得多,这使得区分和选择最佳拟合线变得容易。
我们最终选择通过误差平方和:
其中,
代价函数
由此可见,假设您增加了特定商店的规模,您预计销售额会更高。但如果规模扩大了,那家商店的销售额并没有增加多少。这时用于增加商店规模的成本给了您负面的结果。
所以,我们需要尽量减少这些成本。我们需要引入了一个代价函数,用于定义和衡量模型的误差:
如果你仔细看这个方程,它只是类似于 MSE,只是为了简化数学运算而乘以了。
为了改进我们的预测,我们需要最小化成本函数。为此,我们使用梯度下降算法。
梯度下降
让我们考虑一个例子,我们需要找到的最小值,在数学中,我们简单地取这个方程关于 的导数,并将它等于零。这给了我们这个方程最值的点(由于此处函数图像开口向上,所以为最小值),代入该值可以为我们提供该等式的最小值。
梯度下降以类似的方式工作。它迭代更新参数,以找到代价函数最小的点。如果你想深入研究梯度下降,我强烈建议你阅读这篇文章。
使用线性回归进行预测
现在让我们考虑使用线性回归来预测大型市场的销售额。
从前面的案例中,我们知道通过使用正确的特征可以提高我们的准确性。所以现在让我们使用两个特征,MRP 和开店年份来估算销售额。现在让我们在 Python 中构建一个仅考虑这两个特征的线性回归模型。
import numpy as np
import pandas as pd
from pandas import Series, DataFrame
from sklearn.model_selection import train_test_split
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
# importing linear regressionfrom sklearn
from sklearn.linear_model import LinearRegression
lreg = LinearRegression()
# splitting into training and cv for cross validation
X = train.loc[:,['Outlet_Establishment_Year','Item_MRP']]
x_train, x_cv, y_train, y_cv = train_test_split(X,train.Item_Outlet_Sales, test_size=0.25)
# training the model
lreg.fit(x_train,y_train)
# predicting on cv
pred = lreg.predict(x_cv)
# calculating mse
mse = np.mean((pred - y_cv)**2)
print(mse)
# get parameters
coeff = DataFrame(x_train.columns)
coeff['Coefficient Estimate'] = Series(lreg.coef_)
coeff
我们可以看到 MRP 具有较高的系数,这意味着价格较高的商品具有更好的销量。
评估模型 - R² 和调整后的 R²
你觉得这个模型有多准确呢?我们是否有任何评估指标,以便我们可以对此进行确认?
R²:它确定 Y(因变量)的总变化有多少是由 X(自变量)的变化所导致的。在数学上,它可以写成:
R-square 的值始终介于 0 和 1 之间,0 代表影响很小,1代表完全影响。
让我们查看一下两个参数的影响
lreg.score(x_cv,y_cv)
# 0.3896
在这种情况下,R² 为 32%,这意味着只有 32% 的销售额差异是由成立年份和 MRP 所导致的。换句话说,如果您知道成立年份和 MRP,您将有 32% 的把握来准确预测其销售额。
如果再引入一个特征会发生什么,我的模型是否会更接近于它的实际值预测值? R² 的值会增加吗?
具有更多变量的线性回归
通过使用两个变量而不是一个变量,我们提高了对商品销售做出准确预测的能力。让我们在案例中引入另一个特征 “重量”。 现在让我们构建一个具有这三个特征的回归模型。
X = train.loc[:,['Outlet_Establishment_Year','Item_MRP','Item_Weight']]
再次计算R² ,值为 0.4121,有所增大,MSE 也减少了,是否意味着增加 item weight 特征对我们的模型有用呢?
调整后的 R²
R² 的唯一缺点,如果将新的预测变量 (X) 添加到我们的模型中,R² 只会增加或保持不变,而不会减少。我们无法判断通过增加模型的复杂性,是否使其更准确?
调整后的 R²,已针对模型中的预测变量数量进行了调整。它结合了模型的自由度。调整后的 R 方仅在新增项提高模型精度时才会增加。
R²:普通 R² 计算结果。p:预测变量的个数。N:样本总数。
使用所有特征进行预测
现在让我们构建一个包含所有特征的模型。在构建回归模型时,我只使用连续特征。这是因为我们需要对非连续变量(分类变量)进行不同的处理,然后才能将它们用于线性回归模型。有不同的技术来处理它们,这里我使用了one hot 编码(将分类变量的每一类转换为特征)。
import numpy as np
import pandas as pd
from pandas import Series, DataFrame
import matplotlib.pyplot as plt
%matplotlib inline
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
# importing linear regression
from sklearn.linear_model import LinearRegression
lreg = LinearRegression()
# for cross validation
from sklearn.model_selection import train_test_split
X = train.drop('Item_Outlet_Sales',1)
x_train, x_cv, y_train, y_cv = train_test_split(X,train.Item_Outlet_Sales, test_size =0.3)
# training a linear regression model on train
lreg.fit(x_train,y_train)
# predicting on cv
pred_cv = lreg.predict(x_cv)
# calculating mse
mse = np.mean((pred_cv - y_cv)**2)
print(mse) # 1181354.3851238047
# evaluation using r-square
print(lreg.score(x_cv,y_cv)) # 0.5631540065576365
我们可以显著得看到 MSE 和 R² 都有很大的改进,这意味着我们的模型现在能够预测更接近实际值的值。
选择正确的特征
当我们有一个高维数据集时,使用所有变量将是非常低效的,因为其中一些变量可能会传递冗余的信息。我们需要选择正确的变量集,为我们提供准确的模型并能够很好的影响因变量。有多种方法可以为模型选择正确的变量集。其中首先是业务理解和领域知识。例如,在预测销售额时,我们知道营销工作应该对销售额产生积极影响,并且是您模型中的一个重要特征。我们还应该注意,我们选择的变量不应相互关联。
我们可以通过使用前向或后向选择来自动执行此过程。前向选择指从模型中最重要的预测变量开始,逐一添加变量。后向消除指从模型中的所有预测变量开始,并删除每个步骤中最不重要的变量,选择标准可以设置为 R-square、t-stat 等。
多项式回归
多项式回归是另一种回归的形式,其中自变量的最大幂值大于 1。在这种回归技术中,最佳拟合线是以曲线形式存在。
二次回归或二阶多项式回归由以下等式给出:
如下图所示:
显然,二次方程比简单的线性方程更拟合数据。在这种情况下,您认为二次回归的 R² 会比简单线性回归大吗?答案是肯定的。您也可以添加更高次的多项式。
下图显示了 6 次多项式方程的行为:
你认为使用高阶多项式来拟合数据集总是更好吗?可惜并不是。基本上,我们已经创建了一个非常拟合我们的训练数据的模型,但未能估计训练集之外的变量之间的真实关系。因此,我们的模型在测试数据上表现不佳。这个问题称为过拟合(over-fitting)。我们也说该模型具有高方差和低偏差。
同样,我们还有另一个问题称为欠拟合(underfitting),当我们的模型既不拟合训练数据也不能预测新数据时,就会发生这种情况。
回归模型中的偏差和方差
这种偏差和方差实际上意味着什么?让我们通过一个射箭的例子来理解一下:
假设我们有一个非常准确的模型,因此误差会很低,这意味着低偏差和低方差,正如第一张图所示。所有数据点都在靶心范围内。类似地,我们可以说,如果方差增加,我们的数据点的传播就会增加,从而导致预测不太准确。随着偏差的增加,我们的预测值和观察值之间的误差也会增加。
现在如何平衡这种偏差和方差以获得完美的模型?看看下面的图片并尝试理解:
随着我们向模型中添加越来越多的参数,其复杂性会增加,从而导致方差增加、偏差减少,即过拟合。所以我们需要在我们的模型中找出一个最佳点,即偏差的减少等于方差的增加。在实践中,没有分析方法可以找到这一点。那么如何处理高方差或高偏差呢?
为了克服欠拟合,我们基本上可以向我们的模型添加新参数,从而增加模型复杂度,从而减少高偏差。
我们如何克服回归模型的过度拟合?基本上有两种方法可以克服过度拟合,
- 降低模型复杂度
- 正则化
在这里,我们将详细讨论正则化以及如何使用它来使您的模型更具有一般性。
正则化
你已经准备好了模型,并且已经预测了模型的输出。那为什么还需要研究正则化呢?这有必要吗?
在正则化中,我们所做的通常是保持相同数量的特征,但减少系数 j 的大小。降低系数对我们有什么帮助?
让我们来看看上述回归模型中的特征系数。
# checking the magnitude of coefficients
predictors = x_train.columns
coef = Series(lreg.coef_,predictors).sort_values()
coef.plot(kind='bar', title='Modal Coefficients')
我们可以看到,与其余系数相比,outlet_size_hight 和 Outlet_Type_Supermarket_Type3 的系数要高得多。由此可得,一个项目的总销售额将更多地受到这两个特征的驱动。
我们如何减少模型中系数的大小?为此,我们使用正则化来克服这个问题。
Ridge Regression
让我们首先针对上面的问题来实现,并检查我们的结果,它是否比我们的线性回归模型表现更好。
from sklearn.linear_model import Ridge
## training the model
ridgeReg = Ridge(alpha=0.05, normalize=True)
ridgeReg.fit(x_train,y_train)
pred = ridgeReg.predict(x_cv)
## calculating mse
mse = np.mean((pred_cv - y_cv)**2)
mse # 1348171.96 ## calculating score ridgeReg.score(x_cv,y_cv) 0.5691
所以,我们可以看到我们的模型有轻微的改进,因为 的值增加了。
注意 alpha 的值,它是 Ridge 的超参数,这意味着它们不是由模型自动学习的,而是必须手动设置。
这里我们考虑 alpha = 0.05。但是让我们考虑不同的 alpha 值并绘制每种情况的系数。
您可以看到,随着我们增加 alpha 的值,系数的大小会减小,其中值达到零但不是绝对零。
但是,如果您为每个 alpha 计算 ,我们将看到 的值将在 alpha=0.05 处达到最大值。因此,我们必须通过迭代一系列值并使用给我们最低误差的值来明智地选择它。
所以,现在您知道如何实现它了,但让我们也看看数学方面。到目前为止,我们的想法基本上是最小化成本函数,使得预测值更接近期望的结果。
ridge 的 cost function:
这里我们会遇到一个额外的术语,惩罚项。这里给出的 λ,实际上是由 Ridge 中的 alpha 参数表示的。所以通过改变 alpha 的值,我们基本上是在控制惩罚项。 alpha 值越高,惩罚越大,因此系数的大小会降低。
核心要点如下:
- 它缩小了参数,主要用于防止多重共线性。
- 它通过系数收缩降低了模型的复杂性。
- 它使用 L2 正则化技术
Lasso regression
LASSO(Least Absolute Shrinkage Selector Operator)与 Ridge 非常相似,让我们通过在我们的大型集市问题中实现它来理解它们的区别吧。
from sklearn.linear_model import Lasso
lassoReg = Lasso(alpha=0.3, normalize=True)
lassoReg.fit(x_train,y_train)
pred = lassoReg.predict(x_cv)
# calculating mse
mse = np.mean((pred_cv - y_cv)**2)
print(mse) # 1346205.82
print(lassoReg.score(x_cv,y_cv)) # 0.5720
正如我们所看到的,仿佛 Lasso 模型比线性模型和 Ridge 型预测得更好。
再次让我们改变 alpha 的值,看看它如何影响系数。
因此,我们可以看到,即使 alpha 值很小,系数也降低了很多。通过查看这些图,您能找出 Ridge 和 Lasso 之间的区别吗?
我们可以看到,当我们增加 alpha 的值时,系数接近于零,但是如果你看到在 Lasso 的情况下,即使在较小的 alpha 下,我们的系数也减少到绝对零。因此,Lasso 仅选择某些特征,而将其他特征的系数减少为零。此属性称为特征选择,在 Ridge 的情况下不存在。
Lasso 回归背后的数学与 Ridge 回归的数学表达相似,区别在于我们将添加 w的绝对值,而不是添加 w 的平方。
Lassio 的要点:
- 它使用 L1 正则化技术
- 当我们有更多的特征时,通常会使用它,因为它会自动进行特征选择。
现在您已经对 Ridge 回归和 Lasso 回归有了基本的了解,让我们考虑一个例子,我们有一个大型数据集,假设它有 10,000 个特征。并且我们知道一些特征与其他特征相关。你会使用哪个回归,Ridge 还是 Lasso?
让我们一一讨论。如果我们对其应用 Ridge 回归,它将保留所有特征,但会缩小系数。但问题是模型仍然会很复杂,因为有 10,000 个特征,这可能导致模型性能不佳。
Lasso 回归的主要问题是当我们有相关变量时,它只保留一个变量并将其他相关变量设置为零。这可能会导致一些信息丢失,从而导致我们模型的准确性降低。
那么这个问题的解决方案是什么?实际上我们有另一种回归,称为弹性网络(Elastic Net)回归,它基本上是 Ridge 回归和 Lasso 回归的混合。
Elastic Net Regression
from sklearn.linear_model import ElasticNet
ENreg = ElasticNet(alpha=1, l1_ratio=0.5, normalize=False)
ENreg.fit(x_train,y_train)
pred_cv = ENreg.predict(x_cv)
#calculating mse
mse = np.mean((pred_cv - y_cv)**2)
print(mse)
# #calculating R square
score = ENreg.score(x_cv,y_cv)
print(score)
我们得到了 的值,它远小于 Ridge 和 Lasso。这种失败背后的原因是我们没有大量的特征。当我们有一个大数据集时,Elastic Net Regression通常效果很好。
注意,这里我们有两个参数 alpha
和 l1_ratio
。首先让我们讨论一下,Elastic Net 中发生了什么,以及它与Ridge 和 Lasso 有何不同。
Elastic Net是 L1 和 L2 正则化的组合。因此,你可以通过调整参数来实现 Ridge 和 Lasso。它同时使用了 L1 和 L2 的惩罚项,因此它的方程如下所示:
那么我们如何调整 alpha 以控制 L1 和 L2 惩罚项?让我们通过一个例子来理解。你正试图从池塘里钓一条鱼。而你只有一张网,你会怎么做?你会随意撒网吗?不,你实际上会等到你看到一条鱼游来游去,然后你就朝那个方向撒网,基本上能把整群鱼都收了。因此,即使它们是相关的,我们仍然希望查看它们的所有组合。
Elastic Net Regression的工作方式类似。假设我们在一个数据集中有一堆相关的自变量,那么弹性网络将简单地形成一个由这些相关变量组成的组。现在,如果该组中的任何一个变量是强预测变量(意味着与因变量有很强的关系),那么我们将在模型构建中包括整个组,因为省略其他变量(如我们在 lasso 中所做的)可能导致在解释能力方面丢失一些信息,导致模型性能不佳。
所以,如果你看上面的代码,我们需要在定义模型的同时定义 alpha 和 l1_ratio。如果您希望分别控制 L1 和 L2 惩罚,Alpha 和 l1_ratio 是您可以相应设置的参数。其实我们有
其中,a 和 b 权重分别分配给 L1 和 L2 项。所以当我们改变 alpha 和 l1_ratio 的值时,a 和 b 被相应地设置,使得它们控制 L1 和 L2 之间的权衡。