优点:结果易于理解,计算上不复杂
缺点:对非线性的数据拟合不好
线性回归
用于数值预测,回归方程为
(large y = x_{1}w_{1} + x_{2}w_{2} + ... + x_{n}w_{n} + b)
写成矩阵形式
(large y = XW + b)
为方便计算,添加 (large x_{0}=1), 取 (large w_{0} = b), 将式子改写为
(large y = XW)
采用平方差做损失函数
(large L = sum_{i=1}^{n}(y_{i} -X_{i}W)^2)
用矩阵表示为
(large L = (Y - XW)^{T}(Y - XW))
其中 X 是 (m,n) 矩阵,W 是 (n,1) 矩阵,Y 是 (m,1) 矩阵
对 (small W) 求导
(large frac{partial L}{partial W} = frac{partial (Y - XW)^{T}(Y - XW)}{partial W})
(large = frac{partial (Y^{T} - W^{T}X^{T})(Y - XW)}{partial W})
(large = frac{partial (Y^{T}Y - Y^{T}XW - W^{T}X^{T}Y + W^{T}X^{T}XW)}{partial W})
(large = frac{partial Y^{T}Y}{partial W} - frac{partial Y^{T}XW}{partial W} - frac{partial W^{T}X^{T}Y}{partial W} + frac{partial W^{T}X^{T}XW}{partial W})
(large = 0 - X^{T}Y - X^{T}Y + (X^{T}X + (X^{T}X)^{T})W)
(large = - 2X^{T}Y + (2X^{T}X)W)
(large = - 2X^{T}(Y - XW))
另导数为 0 得到最优的 (small W)
(large - 2X^{T}(Y - XW) = 0)
(large X^{T}Y = X^{T}XW)
(large (X^{T}X)^{-1}X^{T}Y = (X^{T}X)^{-1}X^{T}XW)
(large W = (X^{T}X)^{-1}X^{T}Y)
代码
def standRegres(xArr, yArr):
"""
xArr - 样本特征,是 (m,n) 矩阵,每行的第一个值既 X0 固定为 1
yArr - 样本标签,是 (1,m)
"""
xMat = np.mat(xArr)
yMat = np.mat(yArr).T
xTx = xMat.T * xMat
if np.linalg.det(xTx) == 0.0:
# 如果行列式等于 0,意味着没有逆矩阵
return None
# 也可以用 ws = np.linalg.solve(xTx, xMat.T*yMat)
w = xTx.I * xMat.T * yMat
# ws 是 (n,1) 矩阵
return w
通过相关系统判断效果好坏
# 结果越接近 1 效果越好
np.corrcoef(yEstimate, yActual)
局部加权线性回归 (Locally Weighted Linear Regression,LWLR)
线性回归的一个问题是有可能出现欠拟合现象,就是偏离拟合曲线的点比较多
与此对应的有过拟合问题,就是拟合曲线过于迎合数据点,这意味着曲线被噪点干扰比较大
解决欠拟合问题的一个方法是局部加权线性回归
给待预测点附近的每个点赋予权重,然后在这个子集上基于最小均方差进行普通的回归
注意:与 kNN 一样,这种算法每次预测均需要事先选取出对应的数据子集
LWLR 使用核函数来对附近的点赋予权重,核的类型可以自由选择,常用的是高斯核
(large Weight(i,i) = exp(frac{|x_{i}-x|}{-2k^{2}}))
只含对角元素的矩阵,x 是测试点,xi 与 x 越近权重越大,k 越大权重越大,权值必然小于 1
k 越大越容易欠拟合,越小越容易过拟合
算法如下
(large W = (X^{T}Weight X)^{-1}X{^T}Weight Y)
代码
def lwlr(testPoint, xArr, yArr, k=1.0):
"""
testPoint - 待预测的点 (1,n)
xArr - 样本特征 (m,n),每个样本的第一个值既 X0 固定为 1
yArr - 样本标签 (1,m)
"""
xMat = np.mat(xArr)
yMat = np.mat(yArr).T
m = np.shape(xMat)[0]
# eye 是单位矩阵,对角线是 1,其余是 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:
# 如果行列式等于 0,意味着没有逆矩阵
return
# 得出回归系数 (n,1)
w = xTx.I * (xMat.T * (weights * yMat))
return testPoint * w
缩减法
如果数据的特征比样本点还多,则不能再使用前面的方法,因为在计算逆矩阵时会出错
岭回归
岭回归最先用来处理特征数多于样本数的情况
现在也用于在估计中加入偏差,从而得到更好的估计
为 (small X^TX) 加上 (small r cdot I) 使得矩阵可逆,其中 r 是用户定义的数值,I 是单位矩阵
回归系数
(large W = (X^{T}X + rI)^{-1}X^TY)
代码
def ridgeRegres(xMat, yMat, lam=0.2):
"""
xMat - 样本特征 (m,n),每个样本的第一个值既 X0 固定为 1
yMat - 样本标签 (1,m)
"""
xTx = xMat.T * xMat
# 加上 r*I 使得矩阵可逆
denom = xTx + np.eye(np.shape(xMat)[1]) * lam
if np.linalg.det(denom) == 0.0:
return
w = denom.I * (xMat.T * yMat)
return w
def ridgeTest(xArr, yArr):
"""
xArr - 样本特征 (m,n),每个样本的第一个值既 X0 固定为 1
yArr - 样本标签 (1,m)
"""
xMat = np.mat(xArr)
yMat = np.mat(yArr).T
# Y 数据标准化,减去均值
yMean = np.mean(yMat, 0)
yMat = yMat - yMean
# X 数据标准化,减去均值,除以方差
xMeans = np.mean(xMat, 0)
xVar = np.var(xMat, 0)
xMat = (xMat - xMeans) / xVar
numTestPts = 30
# 初始化回归系数矩阵,每行是一次迭代产生的回归系数
wMat = np.zeros((numTestPts, np.shape(xMat)[1]))
for i in range(numTestPts):
# 迭代,尝试不同的 r 参数
ws = ridgeRegres(xMat, yMat, np.exp(i - 10))
wMat[i, :] = ws.T
# 返回所有回归系数,为了定量地找到最佳参数值,还需要进行交叉验证
# 一般讲,r 很小时就和普通回归系数一样,r 很大时回归系数趋向于 0
return wMat
lasso 方法
限制所有回归系数的绝对值的和必须小于某个值
(large sum_{k=1}^{n}w_{k}^{2} leqslant lambda)
前向逐步回归
属于一种贪心算法,每步尽可能减少误差
一开始所有的权重都设为 1,然后每步所做的决策是对某个权重增加或减少一个很小的值
前向逐步回归算法可以得到与 lasso 差不多的效果,但更加简单
代码
def stageWise(xArr, yArr, step=0.01, numIt=100):
"""
xArr - 样本特征 (m,n),每个样本的第一个值既 X0 固定为 1
yArr - 样本标签 (1,m)
step - 步长
numIt - 迭代次数
"""
xMat = np.mat(xArr)
yMat = np.mat(yArr).T
# Y 数据标准化,减去均值
yMean = np.mean(yMat, 0)
yMat = yMat - yMean
# X 数据标准化,减去均值,除以方差
xMeans = np.mean(xMat, 0)
xVar = np.var(xMat, 0)
xMat = (xMat - xMeans) / xVar
m, n = np.shape(xMat)
# 初始化回归系数矩阵,每行是一次迭代产生的回归系数
returnMat = np.zeros((numIt, n))
ws = np.zeros((n, 1))
wsMax = ws.copy()
# 迭代
for i in range(numIt):
lowestError = np.inf
# 每个系数
for j in range(n):
# 每个方向
for sign in [-1, 1]:
wsTest = ws.copy()
# 在上一次迭代产生的系数向量的基础上,按指定的步长、指定的方向,调整指定的系数
wsTest[j] += step * sign
# 预测结果
yTest = xMat * wsTest
# 计算方差
rssE = ((yMat.A - yTest.A) ** 2).sum()
# 效果更好则保存该系数
if rssE < lowestError:
lowestError = rssE
wsMax = wsTest
# 得到本次迭代的最佳系数
ws = wsMax.copy()
# 保存该最佳系数
returnMat[i, :] = ws.T
# 返回结果
return returnMat