机器学习之线性回归

回归问题,可以说是一个很有趣的问题——用已有的数据去预测未来的数据。

回归问题其实我们很早就学习过了,其本质就是找到一个方程去拟合一个数据的变换情况,假设方程是这个

y=X * w (x,w均为向量,y就是向量的内积)

那么如何去判断这个函数与实际是否贴切?那我们就要计算真实值与预测值之间的误差总值

error=sum{(y_true[i] - y_pre[i])^2}

error的值域为零到正无穷,当error等于0时,我们说这个方程就是这个数据的表现,当error很大时,我们的这个方程并不能体现这个数据的变化。因此,我们希望我们的error越小越好,这个减小error的过程就是机器学习的过程。

线性回归

这个线性回归是最简单的回归算法了,其方程我们就是认为是一次方程y=x * w,所以,该算法的最终目标就是解出w这个参数向量来。这里,x是特征向量矩阵,w是每个特征对应的权重矩阵;误差总值计算为sum{(y_true[i] - x[i].T * w)^2},因此,最终w的计算公式为:

1
w = (x.T * x)^-1 * x.T * y

代码计算实现

1
2
3
4
5
6
7
8
9
10
11
import numpy as np
from numpy import linalg as la


def standRegres(xArr, yArr):
xMat = np.mat(xArr); yMat = np.mat(yArr).T
xTx = xMat.T * xMat
if la.det(xTx) == 0.0:
return "该矩阵不能求逆"
ws = xTx * (xMat.T * yMat)
return ws

局部加权线性回归

线性回归的计算非常简单,但是简单也有简单的坏处——线性回归只能大概的描述数据的趋势,再细一些的数据变化,基本的线性回归是没办法解决的。比如这幅图:

machine-learning-5.1.png

如果只是用线性回归去预测,那么我们只能得到一条描述数据呈上升趋势的直线,其中转折点的数据情况则没办法,这个时候就有局部加权线性回归的方法。局部加权线性回归是为待预测点附近的点进行加权判断与待预测点是否属于一个线性回归模型中,这样,就在最开始的线性回归模型预测中进一步提取信息,在每一个子集中提取各自的线性回归模型,然后组合成为最后的线性回归模型。

1
2
3
w = (x.T * W * x)^-1 * x.T * W * y,(W是权重矩阵,w是回归系数矩阵)

W = exp{|x[i] - x| / (-2 * k^2)},(加权计算公式)

加权公式中,除了k是我们认为设置的之外,其余都是已知的代入求值。x[i] - x是用于求误差值,误差值越大,表明待预测点并不属于当前的线性回归模式,那么对应的加权就会更小,反之误差值越小,越属于当前的线性回归模式,对应的加权就会越大;而k,是局部加权线性回归中的局部子集的大小,如果k越大,那么就会变成最开始的线性回归模型,如果k越小,那么局部子集也就越小,线性回归模型就会过度拟合数据,无法完全描述出数据的变化趋势,因此,k存在最优质,这个就需要我们人工去调整,去尽可能的找到这个最优的k值。

代码实现局部加权线性回归

1
2
3
4
5
6
7
8
9
10
11
12
def lwlr(testPoint, xArr, yArr, k=1.0):
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):
diffMat = testPoint - xMat[j, :]
weights[j, j] = np.exp(diffMat * diffMat.T / (-2.0 * K ** 2))
xTx = xMat.T * (weights * xMat)
if np.linalg.det(xTx) == 0.0:
return
ws = xTx.T * (xMat.T * (weights * yMat))
return testPoint * ws

岭回归

回归参数w的计算公式中,x是特征值矩阵,在前面的计算中都有一步求矩阵的逆矩阵,如果特征数超过样本数,那么x矩阵就不是一个满秩矩阵,因此,逆矩阵也就无法求得了,这个时候,我们就需要判断:是否真的需要这么多特征参与回归计算?显然,我们可以猜到有一些特征是不需要参与回归计算的,那么我们就可以“缩减”特征矩阵。

1
w = (x.T * x + lambda * I)^-1 * x.T * y,其中,I是单位矩阵,lambda用于限制w的总和,可以理解为lambda * I为惩罚项,惩罚那些不必要的特征以达到缩减目的。

代码实现计算过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def ridgeRegress(xMat, yMat, lam=0.2):
xTx = xMat.T * xMat
denom = xTx + np.eye(np.shape(xMat)[1]) * lam
if np.linalg.det(denom) == 0.0:
return
ws = denom.I * (xMat.T * yMat)
return ws


def ridgeRegressTest(xArr, yArr):
xMat, yMat = np.mat(xArr), np.mat(yArr).T
yMean = np.mean(yMat, 0)
yMat = yMat - yMean
xMeans = np.mean(xMat, 0)
xVar = np.var(xMat, 0)
numTestPts = 30
wMat = np.zeros((numTestPts, np.shape(xMat)[1]))
for i in range(numTestPts):
ws = ridgeRegress(xMat, yMat, np.exp(i - 10))
wMat[i, :] = ws.T
return wMat

前向逐步回归

前向逐步回归可以说是岭回归的加强算法,前向逐步回归是一种贪心算法:对每一种特征进行调整来进行回归计算,然后判断该特征对于最终回归结果的影响,如果回归预测误差小于上一步得到的误差,那么就更新误差值,并且更新回归参数向量w。其最终目标就是缩减预测值与真实值之间的误差。·

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def rssError(yMat_true, yMat_test):
return np.sum(np.abs(yMat_test - yMat_true))


def regularize(xMat):
xMeans = np.mean(xMat, 0)
xVar = np.var(xMat, 0)
xMat = (xMat - xMeans) / xVar
return xMat


def stageWise(xArr, yArr, esp=0.01, numIt=100):
xMat = np.mat(xArr);
yMat = np.mat(yArr).T
yMean = np.mean(yMat, 0)
yMat -= yMean
xMat = regularize(xMat)
m, n = np.shape(xMat)
returnMat = np.zeros((numIt, n))
ws = np.zeros((n, 1));
wsTest = ws.copy();
wsMax = ws.copy()
for i in range(numIt):
print(ws.T)
lowestError = np.inf
for j in range(n):
for sign in [-1, 1]:
wsTest = ws.copy()
wsTest[j] += esp * sign
yTest = xMat * wsTest
rssE = rssError(yMat.A, yTest.A)
if rssE < lowestError:
lowestError = rssE
wsMax = wsTest
ws = wsMax.copy()
returnMat[i, :] = ws.T
return returnMat