目录
学习完机器学习实战的线性回归,简单的做个笔记。文中部分描述属于个人消化后的理解,仅供参考。
本篇综合了先前的文章,如有不理解,可参考:
所有代码和数据可以访问 我的 github
如果这篇文章对你有一点小小的帮助,请给个关注喔~我会非常开心的~
0. 前言
线性回归(Linear Regression)是通过线性函数,拟合数据,达到预测的目的。
- 优点:结果易于理解
- 缺点:对非线性的数据拟合不好
- 适用数据类型:数值型和标称型数据(标称型数据将被转换为二值型)
本篇主要介绍一下几种回归:
- 标准线性回归
- 局部加权线性回归
- 岭回归
- lasso
- 前向逐步线性回归
在本篇中,参数可表示为 或者 ,是一致的。
1. 假设函数(Hypothesis)
假设函数定义如下:
通常取 ,表示 为一个截距。
2. 标准线性回归
2.1. 代价函数(Cost Function)
每一次预测的误差为真实值减去预测值,总体的误差可采用误差平方和(error sum of squares)表示:
代价函数可根据误差平方和,采用MSE(mean-square error,均方误差)表示:
其中,乘一个系数并不影响结果。
2.2. 梯度下降(Gradient Descent)
寻找最佳的参数 ,可通过最小化MSE达到。
每一次迭代都将参数往梯度下降方向移动即可达到全局最小值(要求函数为凸函数),梯度下降定义为:
2.3. 特征缩放(Feature Scaling)
如果 的不同维度的取值范围相差甚远,会造成梯度下降速度缓慢,可采用均值归一化:
其中, 为均值, 为最大值减去最小值。
2.4. 正规方程(Normal Equation)
除了通过梯度下降方法求解参数外,还可通过正规方程直接求解:
当存在矩阵不可逆的情况时(多发生在 时),可采用:
- 删除冗余的特征,如其中两个变量间存在线性关系
- 对于太多的特征 ,删除部分特征,或采用正则化(regularization)
注:矩阵逆运算的时间复杂度通常为 ,所以当 较大时,建议使用梯度下降。
3. 局部加权线性回归(LWLR)
局部加权线性回归(Locally Weighted Linear Regression)给待测点附近的每个点赋予一定的权值,采用高斯核函数:
其中,权值矩阵 只含对角线元素, 为下降到 的速率,与 越近,权值越大。
对正规方程进行修改,定义为:
4. 岭回归
前面提到,当 时,很有可能 不是满秩矩阵,不能进行逆运算,此时可采用岭回归:
其中, 为 的单位矩阵, 为常量。可通过初试设定 训练参数 ,不断测试不同的 来得到。
因为单位矩阵 是一个主对角线为 ,其他为 的矩阵, 就像“岭”一样,这就是岭回归名称的由来。
注:岭回归通过引入 来限制所有 之和,引入了惩罚项,能够减少不重要的参数,这在统计学中叫做缩减(shrinkage)。
5. lasso
在增加约束的情况下,普通的回归和岭回归都会推导得出以下公式:
当两个特征相关时,可能会出现一个很大的正系数和一个很大的负系数,可采用 lasso 缩减方法:
6. 前向逐步线性回归
前向逐步线性回归是一种贪心算法,一开始所有权重都设定为 ,每一次迭代都对一个特征增加或减少很小的值,判断当前误差是否减小。
注:前向逐步线性回归的优点在于可以帮助人们理解模型的改善在哪里,发现哪些参数是对模型有效的,可以尽早停止对无用特征的训练。
7. 正则化(Regularization)
在统计学的缩减中,引入了惩罚项,减少了不重要的参数,同时还可采用正则化(regularization)减少不重要的参数。
对代价函数作如下修改:
正则项的目的是使得 减小,正则化使得假设函数更加简单,减小发生过拟合概率。
8. 实战案例
8.1. 普通线性回归
# coding:utf-8
from numpy import *
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 = mat(xArr)
yMat = mat(yArr).T
xTx = xMat.T * xMat
if linalg.det(xTx) == 0.0:
print("This matrix is singular, cannot do inverse")
return
ws = xTx.I * (xMat.T * yMat)
return ws
# 画数据点和回归直线的图
def drawDataSetAndReg(xMat, yMat, ws):
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xMat[:, 1].flatten().A[0], yMat.T[:, 0].flatten().A[0])
xCopy = xMat.copy()
xCopy.sort(0)
yHat = xCopy * ws
ax.plot(xCopy[:, 1], yHat)
plt.show()
# 计算预测值和真实值的相关矩阵
def computeCorrcoef(yHat, yMat):
print(corrcoef(yHat.T, yMat))
if __name__ == '__main__':
xArr, yArr = loadDataSet('ex0.txt')
ws = standRegres(xArr, yArr)
print(ws)
drawDataSetAndReg(mat(xArr), mat(yArr), ws)
computeCorrcoef(mat(xArr) * ws, mat(yArr))
8.2. 局部加权线性回归
# coding:utf-8
from numpy import *
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 lwlr(testPoint, xArr, yArr, k=1.0):
xMat = mat(xArr)
yMat = mat(yArr).T
m = shape(xMat)[0]
weights = mat(eye((m)))
for j in range(m):
diffMat = testPoint - xMat[j, :]
weights[j, j] = exp(diffMat * diffMat.T / (-2.0 * k ** 2))
xTx = xMat.T * (weights * xMat)
if 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 = shape(testArr)[0]
yHat = zeros(m)
for i in range(m):
yHat[i] = lwlr(testArr[i], xArr, yArr, k)
return yHat
# 画数据点和回归直线的图
def drawDataSetAndReg(xMat, yMat, yHat):
srtInd = xMat[:, 1].argsort(0)
xSort = xMat[srtInd][:, 0, :]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(xSort[:, 1], yHat[srtInd])
ax.scatter(xMat[:, 1].flatten().A[0], yMat.T[:, 0].flatten().A[0], s=2, c='red')
plt.show()
if __name__ == '__main__':
xArr, yArr = loadDataSet('ex0.txt')
yHat = lwlrTest(xArr, xArr, yArr, 0.1)
drawDataSetAndReg(mat(xArr), mat(yArr), yHat)
yHat = lwlrTest(xArr, xArr, yArr, 0.01)
drawDataSetAndReg(mat(xArr), mat(yArr), yHat)
yHat = lwlrTest(xArr, xArr, yArr, 0.003)
drawDataSetAndReg(mat(xArr), mat(yArr), yHat)
8.3. 岭回归
# coding:utf-8
from numpy import *
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 ridgeRegres(xMat, yMat, lam=0.2):
xTx = xMat.T * xMat
denom = xTx + eye(shape(xMat)[1]) * lam
if linalg.det(denom) == 0.0:
print("This matrix is singular, cannot do inverse")
return
ws = denom.I * (xMat.T * yMat)
return ws
# 岭回归测试函数
def ridgeTest(xArr, yArr):
xMat = mat(xArr)
yMat = mat(yArr).T
# y进行均值化
yMean = mean(yMat, 0)
yMat = yMat - yMean
# x进行均值归一化
xMeans = mean(xMat, 0)
xVar = var(xMat, 0)
xMat = (xMat - xMeans) / xVar
# 测试lambda的次数
numTestPts = 30
wMat = zeros((numTestPts, shape(xMat)[1]))
for i in range(numTestPts):
ws = ridgeRegres(xMat, yMat, exp(i - 10))
wMat[i, :] = ws.T
return wMat
# 画lambda
# ax.plot(X) X为m*n时
# 横坐标为 m,[0, m-1]
# 纵坐标 [:, 1] 为第一条直线, [:, 2]为第二条直线....总共n条直线
def drawLambda(ridgeWeights):
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(ridgeWeights)
plt.show()
if __name__ == '__main__':
abX, abY = loadDataSet('abalone.txt')
ridgeWeights = ridgeTest(abX, abY)
print(ridgeWeights)
drawLambda(ridgeWeights)
8.4. 前向逐步线性回归
# coding:utf-8
from numpy import *
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 regularize(xMat):
inMat = xMat.copy()
inMeans = mean(inMat, 0)
inVar = var(inMat, 0)
inMat = (inMat - inMeans) / inVar
return inMat
# 计算平方误差
def rssError(yArr, yHatArr):
return ((yArr - yHatArr) ** 2).sum()
# 前向逐步回归
def stageWise(xArr, yArr, eps=0.01, numIt=100):
xMat = mat(xArr)
yMat = mat(yArr).T
# y均值化
yMean = mean(yMat, 0)
yMat = yMat - yMean
# x均值归一化
xMat = regularize(xMat)
m, n = shape(xMat)
returnMat = zeros((numIt, n))
ws = zeros((n, 1))
wsTest = ws.copy()
wsMax = ws.copy()
for i in range(numIt):
print(ws.T)
lowestError = inf
for j in range(n):
for sign in [-1, 1]:
wsTest = ws.copy()
# eps为学习率,步长
wsTest[j] += eps * 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
if __name__ == '__main__':
xArr, yArr = loadDataSet('abalone.txt')
wsMat = stageWise(xArr, yArr, 0.01, 200)
print(wsMat)
如果这篇文章对你有一点小小的帮助,请给个关注喔~我会非常开心的~