回归分析

概要

分类的目标是标称型数据,而回归将会对连续型的数据做出预测。依然参考《机器学习实战》第 \(8\) 章,总结一下总有收获。

其中有谈到方差和偏差的概念,腾讯 2018 春招笔试第一大题就是关于这个的。Here we go!
 

 


线性回归

基本公式

 
线性回归最该要掌握的应该就是如何求解线性回归方程了,而且必须能瞬间写出求解公式。

假设 \(X\) 是数据集,其对应标签为 \(y\),回归系数 \(w\). 我们的目的就是求出一个 \(w\) 使得平方误差
\begin{align}
\sum_{i=1}m(y_i-x_iTw)^2 = (y-Xw)^T(y-Xw)
\end{align}
最小。如果对 \(w\) 求导,得到 \(X^T(y-Xw)\),令其等于零,解出
\begin{align}
\hat{w} = (XTX)X^Ty
\end{align}
这个解需要对矩阵 \(X^TX\) 求逆,如果矩阵的逆不存在怎么办?那就不能用直接用这个方法了呗。

下图是使用线性回归的一个例子:

图 1:线性回归最佳拟合直线

 

模型好坏

 
几乎任一数据集都可以用线性回归方法建立模型,那么如何判断这些模型的好坏呢?看下图 \(2\)

图 2:具有相同回归系数的两组数据。上图的相关系数是 0.58,而下图的相关系数是 0.99

所以相关系数就可以计算预测序列与真实序列的匹配程度。其值越大(越接近于 \(1\)),匹配得越好。如果相关系数太小,比如图 2 的上图,或许换个拟合方式更好。

 

线性回归优缺点

 

  • 优点:结果易于理解,计算上不复杂
  • 缺点:对非线性数据拟合不好

 

局部加权线性回归

基本原理

 
线性回归和一个问题是有可能出现欠拟合现象。如果模型欠拟合将不能取得最好的预测结果,所以有些方法允许在估计中引入一些偏差,从而降低预测的均方误差。其中一个方法就是局部加权线性回归(Locally Weighted Linear Regression,LWLR).

在该算法中,我们给待预测点附近的每个点赋予一定的权重;然后再在这个子集上基于最小均方差来进行普通的回归,即使得 \(\sum_{i=1}^m W(i,i)(y_i-x_i^T w)^2\) 最小。该算法解出回归系数 \(w\) 的形式如下:
\begin{align}
\hat{w} = (XTWX)X^TWy
\end{align}
其中 \(W\) 是一个对角矩阵,用来给每个数据点赋予权重。

LWLR 与支持向量机一样,通过引入“核”来对附近的点赋予更高的权重(该模型认为样本点距离更近,越可能符合同一个线性模型)。最常用的核是高斯核,高斯核对应的权重如下:
\begin{align}
W(i,i) = \mathrm{exp}\left( \frac{\lVert x^{(i)}-x \lVert_2 2}{-2k2} \right)
\end{align}
这样就构建了一个只含对角元素的权重矩阵 \(W\),并且点 \(x\)\(x(i)\) 越近,\(w(i,i)\) 将会越大。上述公式包含一个超参数 \(k\),它决定了对附近的点赋予多大的权重。下面我们画图看看参数 \(k\) 与权重的关系。

图 3:每个点的权重图(假定我们正预测的点是 $x=0.5$),最上面的图是原始数据集,第二个图显示了当 $k=0.5$ 时,大部分的数据都用于训练回归模型;而最下面的图显示当 $k=0.01$ 时,仅有很少的局部点被用于训练回归模型

下图看模型的效果:

图 4:使用 $3$ 种不同平滑值绘出的局部加权线性回归结果

从上图可以看到: \(k=1.0\) 时的模型效果与最小二乘法差不多,\(k=0.01\) 时该模型可以挖出数据的潜在规律,而 \(k=0.003\) 时则考虑了太多的噪声,进而导致了过拟合现象。

局部加权线性回归也存在一个问题,即增加了计算量,因为它对每个点做预测时都必须使用整个数据集。从图 4 可以看出, \(k=0.01\) 时可以得到很好的估计,但是同时看一下图 3 中 \(k=0.01\) 的情况,就会发现大多数数据点的权重都接近于零。如果避免这些计算将可以减少程序运行时间,从而缓解因计算量增加带来的问题。

如果数据的特征比样本点还多,这时肯定不能使用线性回归的,因为无法求逆,为了解决这个问题,下面介绍通过缩减系数的方法来理解数据。

本部分的主要代码:

# __*__ coding: UTF-8 __*__

import numpy as np
import matplotlib.pyplot as plt

def loadDataSet(fileName):
	numFeat = len(open(fileName).readline().split('\t')) -1
	dataMat = []
	labelMat = []
	fr = open(fileName)
	for line in fr.readlines():
		lineArr = []
		curLine = line.strip().split('\t')
		for i in range(numFeat):
			lineArr.append(float(curLine[i]))
		dataMat.append(lineArr)
		labelMat.append(float(curLine[-1]))
	return dataMat, labelMat

def standRegres(xArr, yArr):
	xMat = np.mat(xArr)
	yMat = np.mat(yArr).T
	xTx = xMat.T*xMat
	if np.linalg.det(xTx) == 0.0:
		print ("This matrix is singular, cannot do inverse")
		return
	ws = xTx.I * (xMat.T*yMat)
	return ws	

# 核函数
def Gauss(x, xi, k):
    return np.exp(((x-xi)*(x-xi).T) / (-2.*k**2))
    
def lwlr(testPoint, xArr, yArr, k=1.0):
    testPoint = np.mat(testPoint)
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    
    m = np.shape(xMat)[0]
    weights = np.mat(np.eye((m)))
    for j in range(m):
       weights[j,j] = Gauss(testPoint, xMat[j,:], k)
 
    xTx = xMat.T * weights * xMat
    if np.linalg.det(xTx) == 0.0:
       print ("This matrix is singular, cannot do inverse")
       return 
    ws = xTx.I * xMat.T * weights * yMat
    return testPoint * ws

def lwlrTest(testArr, xArr, yArr, k=1.0):
	m = np.shape(testArr)[0]
	yHat = np.zeros(m)
	for i in range(m):
		yHat[i] = lwlr(testArr[i], xArr, yArr, k)
	return yHat
					
def plotLwlrTest():
    xArr, yArr = loadDataSet('ex0.txt')
    xMat = np.mat(xArr)
    yHat = lwlrTest(xArr, xArr, yArr, 1.)
    srtInd = xMat[:,1].argsort(0)
    xSort = xMat[srtInd][:,0,:]
    
    yHat2 = lwlrTest(xArr, xArr, yArr, 0.01)
    srtInd2 = xMat[:,1].argsort(0)
    xSort2 = xMat[srtInd2][:,0,:]
    
    yHat3 = lwlrTest(xArr, xArr, yArr, 0.003)
    srtInd3 = xMat[:,1].argsort(0)
    xSort3 = xMat[srtInd3][:,0,:]
    
    fig = plt.figure()
    
    ax = fig.add_subplot(311)
    ax.plot(xSort[:,1], yHat[srtInd])
    ax.text(0.1, 4.0, r'$k=1$')
    ax.scatter(xMat[:,1].flatten().A[0], np.mat(yArr).T.flatten().A[0], s=2, c='red')
    
    ax2 = fig.add_subplot(312)
    ax2.plot(xSort2[:,1], yHat2[srtInd])
    ax2.text(0.1, 4.0, r'$k=0.01$')
    ax2.scatter(xMat[:,1].flatten().A[0], np.mat(yArr).T.flatten().A[0], s=2, c='red')
    
    ax3 = fig.add_subplot(313)
    ax3.plot(xSort3[:,1], yHat3[srtInd])
    ax3.text(0.1, 4.0, r'$k=0.003$')
    ax3.scatter(xMat[:,1].flatten().A[0], np.mat(yArr).T.flatten().A[0], s=2, c='red')
    
    plt.show()
# 画图 4
plotLwlrTest()

 


岭回归

简单来说,岭回归就是在矩阵 \(X^TX\) 上加一个 \(\lambda I\) 从而使得矩阵非奇异,进而能对 \(X^TX+\lambda I\) 求逆,其中矩阵 \(I\) 是一个 \(m\times m\) 的单位矩阵,而 \(\lambda\) 是一个用户定义的数值。在这种情况下,回归系数的计算公式将变成
\begin{align}
\hat{w} = (X^TX+\lambda I){-1}XTy
\end{align}

岭回归另一种说法是在平方误差的基础上增加 \(L_2\)正则项:
\begin{align}
\sum_{i=1}m(y_i-x_iTw)^2+\lambda \sum_{j} w_j^2
\end{align}
通过确定 \(\lambda\) 的值可以使得在方差和偏差之间达到平衡:随着 \(\lambda\) 的增大,模型方差减小而偏差增大。上式对 \(w\) 求导结果为 \(2X^T(Y-XW)-2\lambda W\),令其为零就得 \(\hat{w} = (X^TX+\lambda I)^{-1}X^Ty\).

岭回归最先用来处理特征数多于样本数的情况,现在也用于在估计中加入偏差,从而得到更好的估计。这里通过引入 \(\lambda\) 来限制了所有 \(w\) 之和,通过引入该惩罚项,能够减少不重要的参数,这个技术在统计学中叫做缩减。缩减方法可以去掉不重要的参数,因此能更好的理解数据。此外,与简单的线性回归相比,缩减方法能取得更好的预测效果。

为了确定合适的参数 \(\lambda\),可以取不同的 \(\lambda\) 进行实验观察结果,下图是取得 \(30\) 个不同的 \(\lambda\) 值,其中 \(x\) 轴是 \(\log(\lambda)\)\(y\) 轴是回归系数。

图 5:岭回归的回归系数变化图。$\lambda$ 非常小时,系数与普通回归地样。而 $\lambda$ 非常大时,所有回归系数缩减为 $0$. 可以在中间某处找到使得预测结果最好的 $\lambda$ 值。

上边根据 \(\lambda\) 画的回归系数变化曲线称为岭迹,观察岭迹图确定参数 \(\lambda\) 的过程称为岭迹分析。根据岭迹图选择岭参数 \(\lambda\) 的一般原则有:

  • 各回归系数的岭估计基本稳定
  • 用最小二乘估计时符号不合理的回归系数,其岭估计的符号变得合理
  • 回归系数没有不合乎经济意义的绝对值
  • 残差平方和增大不太多

岭回归选择变量的原则:

  • 在岭回归中设计矩阵 \(X\) 已经中心化和标准化了,这样可以直接比较标准化岭回归系数的大小。可以剔除掉标准化岭回归系数比较稳定且绝对值很小的自变量
  • 随着 \(k\) 的增加,回归系数不稳定,震动趋于零的自变量也可以剔除
  • 如果依照上述去掉变量的原则,有若干个回归系数不稳定,究竟去掉几个,去掉哪几个,这并无一般原则可循,这需根据去掉某个变量后重新进行岭回归分析的效果来确定

 


lasso

岭回归相当于加了约束 \(\sum_{k=1}^n w_k^2 \leqslant \lambda\),而lasso 是另一种缩减方法,与岭回归不同的是,它是在平方误差后面加的是 \(L1\) 惩罚项,即 \(\sum_{k=1}^n \lvert w_k\rvert \leqslant \lambda\).

虽然约束只是稍作变化,结果却大不一样:在 \(\lambda\) 足够小的时候,一些系数会因此被迫缩减到 \(0\),这个特性可以帮助我们更好地理解数据。下面贴个图看看 lasso 和岭回归的几何意义:

红色的椭圆和蓝色的区域的切点就是目标函数的最优解,我们可以看到,如果是圆,则很容易切到圆周的任意一点,但是很难切到坐标轴上,则在该维度上取值不为 \(0\),因此没有稀疏;但是如果是菱形或者多边形,则很容易切到坐标轴上,使得部分维度特征权重为 \(0\),因此很容易产生稀疏的结果。所以有结论:\(L1\) 惩罚项有稀疏的效果,\(L2\) 则没有

这两个约束条件在公式上看起来相差无几,但细微的变化却极大地增加了计算复杂度(为了在这个新的约束条件下解出回归系数,需要使用二次规划算法)。下面将介绍一个更为简单的方法来得到结果,该方法叫做前向逐步回归

 


前向逐步回归

前向逐步回归算法可以得到与 lasso 差不多的结果,但更加简单。它属于一种贪心算法,即每一步都尽可能减少误差。一开始,所有的权重都设为 \(1\),然后每一步所做的决策是对某个权重增加或减少一个很小的值。该算法的伪代码如下所示:

数据标准化,使其公布满足 0 均值和单位方差
在每轮迭代过程中:
        设置当前最小误差 lowestError 为正无穷
        对每个特征:
                增大或减小:
                        改变一个系数得到一个新的 W
                        计算新 W 下的误差
                        如果误差 Error 小于当前最小误差 lowestError:设置 Wbest 等于当前的 W
                将 W 设置为新的 Wbest     

逐步线性回归算法主要的优点在于它可以帮助我们理解现有的模型并做出改进。当构建了一个模型后,可以运行该算法找出重要的特征,这样就有可能及时停止对那些不重要特征的收集。最后要说的是,当应用缩减方法(如逐步线性回归或岭回归)时,模型也增加了偏差,与此同时却减小了模型的方差
 


方差与偏差

下图给出了训练误差和测试误差的曲线图,上面的曲线就是测试误差,下面的曲线是训练误差。

上图描述了偏差方差折中与测试误差的关系,可以看到:模型复杂度较低时,预测误差处于低方差高偏差的状态,当模型复杂度较高时,处于高方差低偏差的状态。为了做出最好的预测,我们应该调整模型复杂度来达到测试误差的最小值。

一般认为,上述两种误差由三个部分组成:偏差、测量误差和随机噪声。在局部加权回归中,我们引入了三个越来越小的核来不断增大模型的方差,而在缩减法中,可以将一些系数缩减成很小的值或直接缩减为 \(0\),减少了模型的复杂度,这是一个增大模型偏差的例子。
因此可以说:

  • 高偏差属于欠拟合的情况,对应的模型复杂度较低
  • 高方差属于过拟合的情况,对应的模型复杂度较高

 

 

posted @ 2018-04-06 20:57  小鱼吻水  阅读(540)  评论(0编辑  收藏  举报