回归是一种应用广泛的预测建模技术,这种技术的核心在于预测的结果是连续型变量。决策树,随机森林,支持向量机的分类器等分类算法的预测标签是分类变量,多以{0,1}来表示,而无监督学习算法比如PCA,KMeans并不求解标签,注意加以区别。回归算法源于统计学理论,它可能是机器学习算法中产生最早的算法之一,其在现实中的应用非常广泛,包括使用其他经济指标预测股票市场指数,根据喷射流的特征预测区域内的降水量,根据公司的广告花费预测总销售额,或者根据有机物质中残留的碳-14的量来估计化石的年龄等等,只要一切基于特征预测连续型变量的需求,我们都使用回归技术。
回归类算法的数学相对简单,相信在经历了逻辑回归,主成分分析与奇异值分解,支持向量机这三个章节之后,大家不会感觉到线性回归中的数学有多么困难。通常,理解线性回归可以有两种角度:矩阵的角度和代数的角度。几乎所有机器学习的教材都是从代数的角度来理解线性回归的,类似于我们在逻辑回归和支持向量机中做的那样,将求解参数的问题转化为一个带条件的最优化问题,然后使用三维图像让大家理解求极值的过程。如果大家掌握了逻辑回归和支持向量机,这个过程可以说是相对简单的,因此我们在本章节中就不进行赘述了。相对的,在我们的课程中一直都缺乏比较系统地使用矩阵来解读算法的角度,因此在本堂课中,我将全程使用矩阵方式(线性代数的方式)为大家展现回归大家族的面貌。
sklearn中的线性模型模块是linear_model,我们曾经在学习逻辑回归的时候提到过这个模块。linear_model包含了多种多样的类和函数,其中逻辑回归相关的类和函数在这里就不给大家列举了。今天的课中我将会为大家来讲解:普通线性回归,多项式回归,岭回归,LASSO,以及弹性网。
现在问题转换成了求解让RSS最小化的参数向量 ,这种通过最小化真实值和预测值之间的RSS来求解参数的方法叫做最小二乘法。求解极值的第一步往往是求解一阶导数并让一阶导数等于0,最小二乘法也不能免俗。因此,我们现在残差平方和RSS上对参数向量 求导。
除了多元线性回归的推导之外,这里还需要提到一些在上面的推导过程中不曾被体现出来的问题。在统计学中,使用最小二乘法来求解线性回归的方法是一种”无偏估计“的方法,这种无偏估计要求因变量,也就是标签的分布必须服从正态分布。这是说,我们的y必须经由正太化处理(比如说取对数,或者使用在第三章《数据预处理与特征工程》中提到的类QuantileTransformer或者PowerTransformer)。在机器学习中,我们会先考虑模型的效果,如果模型效果不好,那我们可能考虑改变因变量的分布。
sklearn.linear_model.LinearRegression (fit_intercept=True, normalize=False, copy_X=True, n_jobs=None)
线性回归的类可能是我们目前为止学到的最简单的类,仅有四个参数就可以完成一个完整的算法。并且看得出,这些参数中并没有一个是必填的,更没有对我们的模型有不可替代作用的参数。这说明,线性回归的性能,往往取决于数据本身,而并非是我们的调参能力,线性回归也因此对数据有着很高的要求。幸运的是,现实中大部分连续型变量之间,都存在着或多或少的线性联系。所以线性回归虽然简单,却很强大。
顺便一提,sklearn中的线性回归可以处理多标签问题,只需要在fit的时候输入多维度标签就可以了。
回归类算法的模型评估一直都是回归算法中的一个难点,但不像我们曾经讲过的无监督学习算法中的轮廓系数等等评估指标,回归类与分类型算法的模型评估其实是相似的法则——找真实标签和预测值的差异。只不过在分类型算法中,这个差异只有一种角度来评判,那就是是否预测到了正确的分类,而在我们的回归类算法中,我们有两种不同的角度来看待回归的效果:
第一,我们是否预测到了正确的数值。
第二,我们是否拟合到了足够的信息。
这两种角度,分别对应着不同的模型评估指标。
对于回归类算法而言,只探索数据预测是否准确是不足够的。除了数据本身的数值大小之外,我们还希望我们的模型能够捕捉到数据的”规律“,比如数据的分布规律,单调性等等,而是否捕获了这些信息并无法使用MSE来衡量。
我们观察到,我们在加利福尼亚房屋价值数据集上的MSE其实不是一个很大的数(0.5),但我们的 不高,这证明我们的模型比较好地拟合了一部分数据的数值,却没有能正确拟合数据的分布。让我们与绘图来看看,究竟是不是这样一回事。我们可以绘制一张图上的两条曲线,一条曲线是我们的真实标签Ytest,另一条曲线是我们的预测结果yhat,两条曲线的交叠越多,我们的模型拟合就越好。
可见,虽然我们的大部分数据被拟合得比较好,但是图像的开头和结尾处却又着较大的拟合误差。如果我们在图像右侧分布着更多的数据,我们的模型就会越来越偏离我们真正的标签。这种结果类似于我们前面提到的,虽然在有限的数据集上将数值预测正确了,但却没有正确拟合数据的分布,如果有更多的数据进入我们的模型,那数据标签被预测错误的可能性是非常大的。
在第二节中我们曾推导了多元线性回归使用最小二乘法的求解原理,我们对多元线性回归的损失函数求导,并得出求解系数 的式子和过程:
多重共线性与相关性
多重共线性如果存在,则线性回归就无法使用最小二乘法来进行求解,或者求解就会出现偏差。幸运的是,不能存在多重共线性,不代表不能存在相关性——机器学习不要求特征之间必须独立,必须不相关,只要不是高度相关或者精确相关就好。
多重共线性 Multicollinearity 与 相关性 Correlation
多重共线性是一种统计现象,是指线性模型中的特征(解释变量)之间由于存在精确相关关系或高度相关关系,多重共线性的存在会使模型无法建立,或者估计失真。多重共线性使用指标方差膨胀因子(variance inflationfactor,VIF)来进行衡量(from statsmodels.stats.outliers_influence import variance_inflation_factor),通常当我们提到“共线性”,都特指多重共线性。相关性是衡量两个或多个变量一起波动的程度的指标,它可以是正的,负的或者0。当我们说变量之间具有相关性,通常是指线性相关性,线性相关一般由皮尔逊相关系数进行衡量,非线性相关可以使用斯皮尔曼相关系数或者互信息法进行衡量。
然而多重共线性就不是这样一回事了,它的存在会造成模型极大地偏移,无法模拟数据的全貌,因此这是必须解决的问题。为了保留线性模型计算快速,理解容易的优点,我们并不希望更换成非线性模型,这促使统计学家和机器学习研究者们钻研出了多种能够处理多重共线性的方法,其中有三种比较常见的:
在线性模型之中,除了线性回归之外,最知名的就是岭回归与Lasso了。这两个算法非常神秘,他们的原理和应用都不像其他算法那样高调,学习资料也很少。这可能是因为这两个算法不是为了提升模型表现,而是为了修复漏洞而设计的(实际上,我们使用岭回归或者Lasso,模型的效果往往会下降一些,因为我们删除了一小部分信息),因此在结果为上的机器学习领域颇有些被冷落的意味。这一节我们就来了解一下岭回归。
岭回归,又称为吉洪诺夫正则化(Tikhonov regularization)。通常来说,大部分的机器学习教材会使用代数的形式来展现岭回归的原理,这个原理和逻辑回归及支持向量机非常相似,都是将求解 的过程转化为一个带条件的最优化问题,然后用最小二乘法求解。然而,岭回归可以做到的事其实可以用矩阵非常简单地表达出来。
在sklearn中,岭回归由线性模型库中的Ridge类来调用:
class sklearn.linear_model.Ridge (alpha=1.0, fit_intercept=True, normalize=False, copy_X=True, max_iter=None,
tol=0.001, solver=’auto’, random_state=None)
和线性回归相比,岭回归的参数稍微多了那么一点点,但是真正核心的参数就是我们的正则项的系数 ,其他的参数是当我们希望使用最小二乘法之外的求解方法求解岭回归的时候才需要的,通常我们完全不会去触碰这些参数。所以大家只需要了解 的用法就可以了。之前我们在加利佛尼亚房屋价值数据集上使用线性回归,得出的结果大概是训练集上的拟合程度是60%,测试集上的拟合程度也是60%左右,那这个很低的拟合程度是不是由多重共线性造成的呢?在统计学中,我们会通过VIF或者各种检验来判断数据是否存在共线性,然而在机器学习中,我们可以使用模型来判断——如果一个数据集在岭回归中使用各种正则化参数取值下模型表现没有明显上升(比如出现持平或者下降),则说明数据没有多重共线性,顶多是特征之间有一些相关性。反之,如果一个数据集在岭回归的各种正则化参数取值下表现出明显的上升趋势,则说明数据存在多重共线性。
接下来,我们就在加利佛尼亚房屋价值数据集上来验证一下这个说法:
另外,在正则化参数逐渐增大的过程中,我们可以观察一下模型的方差如何变化:
但是泛化能力毕竟没有直接衡量的指标,因此我们往往只能够通过观察模型的准确性指标和方差来大致评判模型的泛化能力是否提高。来看看多重共线性更为明显一些的情况:
from sklearn.datasets import load_boston
from sklearn.model_selection import cross_val_score
X = load_boston().data
y = load_boston().target
Xtrain,Xtest,Ytrain,Ytest = TTS(X,y,test_size=0.3,random_state=420) #先查看方差的变化
alpharange = np.arange(1,1001,100)
ridge, lr = [], []
for alpha in alpharange:
reg = Ridge(alpha=alpha)
linear = LinearRegression()
varR = cross_val_score(reg,X,y,cv=5,scoring="r2").var()
varLR = cross_val_score(linear,X,y,cv=5,scoring="r2").var()
ridge.append(varR)
lr.append(varLR)
plt.plot(alpharange,ridge,color="red",label="Ridge")
plt.plot(alpharange,lr,color="orange",label="LR")
plt.title("Variance")
plt.legend()
plt.show()
#查看R2的变化
alpharange = np.arange(1,1001,100)
ridge, lr = [], []
for alpha in alpharange:
reg = Ridge(alpha=alpha)
linear = LinearRegression()
regs = cross_val_score(reg,X,y,cv=5,scoring = "r2").mean()
linears = cross_val_score(linear,X,y,cv=5,scoring = "r2").mean()
ridge.append(regs)
lr.append(linears)
plt.plot(alpharange,ridge,color="red",label="Ridge")
plt.plot(alpharange,lr,color="orange",label="LR")
plt.title("Mean")
plt.legend()
plt.show()
#细化学习曲线
alpharange = np.arange(100,300,10)
ridge, lr = [], []
for alpha in alpharange:
reg = Ridge(alpha=alpha)
regs = cross_val_score(reg,X,y,cv=5,scoring = "r2").mean()
ridge.append(regs)
lr.append(linears)
plt.plot(alpharange,ridge,color="red",label="Ridge")
plt.title("Mean")
plt.legend()
plt.show()
难得一提的机器学习发展历史:过时的岭迹图
其实,岭迹图会有这样的问题不难理解。岭回归最初始由Hoerl和Kennard在1970提出来用来改进多重共线性问题的模型,在这片1970年的论文中,两位作者提出了岭迹图并且向广大学者推荐这种方法,然而遭到了许多人的批评和反抗。大家接受了岭回归,却鲜少接受岭迹图,这使得岭回归被发明了50年之后,市面上关于岭迹图的教材依然只有1970年的论文中写的那几句话。1974年,Stone M发表论文,表示应当在统计学和机器学习中使用交叉验证。1980年代,机器学习技术迎来第一次全面爆发(1979年ID3决策树被发明出来,1980年之后CART树,adaboost,带软间隔的支持向量,梯度提升树逐渐诞生),从那之后,除了统计学家们,几乎没有人再使用岭迹图了。在2000年以后,岭迹图只是教学中会被略微提到的一个知识点(还会被强调是过时的技术),在现实中,真正应用来选择正则化系数的技术是交叉验证,并且选择的标准非常明确——我们选择让交叉验证下的均方误差最小的正则化系数 。
4.3.2 Lasso的核心作用:特征选择
class sklearn.linear_model.Lasso (alpha=1.0, fit_intercept=True, normalize=False, precompute=False,
copy_X=True, max_iter=1000, tol=0.0001, warm_start=False, positive=False, random_state=None, selection=’cyclic’)
可以看到,岭回归没有报出错误,但Lasso就不一样了,虽然依然对系数进行了计算,但是报出了整整三个红条:
这三条分别是这样的内容:
看到这三条内容,大家可能会比较懵——怎么出现了坐标下降?这是由于sklearn中的Lasso类不是使用最小二乘法来进行求解,而是使用坐标下降。考虑一下,Lasso既然不能够从根本解决多重共线性引起的最小二乘法无法使用的问题,那我们为什么要坚持最小二乘法呢?明明有其他更快更好的求解方法,比如坐标下降就很好呀。下面两篇论文解释了了scikit-learn坐标下降求解器中使用的迭代方式,以及用于收敛控制的对偶间隙计算方式,感兴趣的大家可以进行阅读。