本文主要是介绍预测算法-线性回归(鲍鱼年龄预测),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
预测算法-线性回归
面对可逆矩阵
线性回归(模型,策略,算法)
模型: h(x)=WTx+b h ( x ) = W T x + b
损失函数: J(θ)=∑i=1N(f(xi)−yi)2 J ( θ ) = ∑ i = 1 N ( f ( x i ) − y i ) 2
目标函数为: minJ(θ)=∑i=1N(f(xi)−yi)2 min J ( θ ) = ∑ i = 1 N ( f ( x i ) − y i ) 2
方法1:梯度下降法
θ=θ−α▽J(θ) θ = θ − α ▽ J ( θ )
α:学习速率 α : 学 习 速 率
▽J(θ):偏导数学习的方向 ▽ J ( θ ) : 偏 导 数 学 习 的 方 向
方法2:标准方程法
目标函数为: minJ(θ)=∑i=1N(f(xi)−yi)2 min J ( θ ) = ∑ i = 1 N ( f ( x i ) − y i ) 2
转化为: (yi−xTiw)T(yi−xTiw) ( y i − x i T w ) T ( y i − x i T w )
对w求导:w^=(xTx)−1xTy 对 w 求 导 : w ^ = ( x T x ) − 1 x T y
xTx x T x 的行列式不为0时,存在逆矩阵 那么条件成立, 才能计算 w^模型参数w w ^ 模 型 参 数 w
xTx x T x 的行列式为0时, 不存在逆矩阵
此时线性回归参数 w^=(xTx)−1xTy w ^ = ( x T x ) − 1 x T y
算法优劣:
- 优点:
求出的值是实际上的模型参数(梯度下降法求出的值只是近似于模型实际参数)
是对最小方差的无偏估计 - 缺点:
往往会发生欠拟合的情况
计算量大 将每个样本点都带入计算
import numpy as np
import matplotlib.pyplot as pltdef loadData(filename):fr = open(filename)featureNum = len(fr.readline().strip().split('\t')) - 1dataSet = list()labelMat = list()for lines in fr.readlines():lineArr = list()lines = lines.split('\t')for i in range(featureNum):lineArr.append(float(lines[i]))dataSet.append(lineArr)labelMat.append(float(lines[-1]))# print(dataSet)# print(labelMat)return dataSet, labelMatdef lr(dataSet, labelMat):"""标准方程法解线性回归:param dataSet: 样本数据集 特征向量 X:param labelMat: 样本数据集 输入变量 Y:returnw 模型系数"""xMat = np.mat(dataSet)# print(xMat)yMat = np.mat(labelMat).T# print(yMat)xMatx = xMat.T * xMatif np.linalg.det(xMatx) == 0:print('行列式为0 为不可逆矩阵')return Nonew = xMatx.I * xMat.T*yMat# print(w)"""[[3.00681047][1.69667188]]"""return wdef lr_stand(dataSet, labelMat):"""标准方程法解线性回归:param dataSet: 样本数据集 特征向量 X:param labelMat: 样本数据集 输入变量 Y:returnw 模型系数"""dataSet, labelMat = regularize(dataSet, labelMat)xMat = np.mat(dataSet)# print(xMat)yMat = np.mat(labelMat)# print(yMat)xMatx = xMat.T * xMatif np.linalg.det(xMatx) == 0:print('行列式为0 为不可逆矩阵')return Nonew = xMatx.I * xMat.T*yMat# print(w)"""[[3.00681047][1.69667188]]"""return wdef lr_plot(dataSet, labelMat, w):x = list()for i in dataSet:x.append(i[-1])y = labelMat"""失败尝试"""# w = np.mat(w).T# dataSet = np.mat(dataSet).T# print(np.shape(w))# print(np.shape(dataSet))# print(np.array(w * dataSet))# for i in (w * dataSet):# print('i:', i)# fit_y = [float(i) for i in np.array(w * dataSet)]fit_y = list()for i in dataSet:fit_y.append(float(np.array(i*w)))print(np.shape(fit_y))print('fit_y', fit_y)fig = plt.subplot(111)print(np.shape(x))print(np.shape(y))rSqure = calcR(y, fit_y)fig.scatter(x, y, s=12, c='b', alpha=0.5, edgecolors=None, marker='o')fig.plot(x, fit_y, c='r')plt.title(rSqure)plt.show()
衍生算法
局部加权线性回归
解决问题: 由于是对最小方差的无偏估计 往往得出的结果是欠拟合的
解决方法: 如果模型欠拟合将不能取得最好的预测效果 那么久在估计中引入一些偏差 从而降低预测的均方误差
一个方法就是局部加权线性回归(locally weighted linear regression–LWLR)
算法思想:
给预测点附近的每个点赋予一点的权重 然后与线性回归相似
在这个子集上基于最小均方误差来进行普通的回归
需要最小化的目标函数大致为: ∑iw(y(i)−y^(i))2 ∑ i w ( y ( i ) − y ^ ( i ) ) 2
目标函数中w为权重 而不是回归系数
该算法每次预测均需要事先选取出对应的数据子集
即: w^=(XTWX)−1XTWy w ^ = ( X T W X ) − 1 X T W y
其中W是一个矩阵,用来给每个预测数据点赋予权重,w^为回归系数 其 中 W 是 一 个 矩 阵 , 用 来 给 每 个 预 测 数 据 点 赋 予 权 重 , w ^ 为 回 归 系 数
LWLR使用’核’来对附近的点赋予更高的权重
核的类型可以自由选择, 最常见的核就是高斯核
w(i)=exp(xi−x)2−2k2 w ( i ) = exp ( x i − x ) 2 − 2 k 2
构建了一个只含有对角元素的权重矩阵 W W , 并且点
基本工作原理:
- 读入数据,将数据特征x、特征标签y存储在矩阵x、y中
- 利用高斯核构造一个权重矩阵 W,对预测点附近的点施加权重
- 验证 X^TWX 矩阵是否可逆
- 使用最小二乘法求得 回归系数 w 的最佳估计
使用回归,可以在给定输入的时候预测出一个数值,这是对分类方法的提升,因为这样可以预测连续型数据而不仅仅是离散的类别标签
def lwlr(weightPoint, dataSet, labelMat, k):"""局部加权线性回归 lwlr:param weightPoint: 预测数据点 给预测数据点赋予权重:param dataSet: 样本数据集 输入空间 X:param labelMat: 样本数据集 输出空间 Y:returnhat_w"""m, n = np.shape(dataSet)w = np.mat(np.eye(m))# 检查是否可逆xMat = np.mat(dataSet)yMat = np.mat(labelMat).Tfor i in range(m):diff = weightPoint - dataSet[i, :]w[i, i] = np.exp((diff*diff.T)/(-2*k**2))xTx = xMat.T * (w * xMat)if np.linalg.det(xTx) == 0:print('行列式为0 该矩阵不可逆')return None# print(np.shape(hat_w))ws = xTx.I * (xMat.T * (w * yMat))return weightPoint * wsdef lwlrTest(testSet, dataSet, labelMat, k):"""局部加权线性回归 返回fit_y 测试结果:param testSet: 预测数据集:param dataSet: 样本数据集 输入空间:param labelMat: 样本数据集 输出空间:param k: 权重计算 高斯核系数:returnfit_y"""dataSet = np.mat(dataSet)m, n = np.shape(dataSet)fit_y = np.zeros(m)# fit_y = list()for i in range(m):# print(dataSet[i, :])# fit_y[i] = copy_lwlr(testSet[i], dataSet, labelMat, k)fit_y[i] = lwlr(testSet[i], dataSet, labelMat, k)# print('局部线性加权回归-(fit_y):', fit_y)print('局部线性加权回归-(fit_y):', np.shape(fit_y))return fit_ydef lwlr_plot(dataSet, labelMat, fit_y):"""局部加权线性回归图像"""yHat = fit_yxMat = np.mat(dataSet)srtInd = xMat[:, 1].argsort(0) # argsort()函数是将x中的元素从小到大排列,提取其对应的index(索引),然后输出xSort = xMat[srtInd][:, 0, :]# print('xSort', xSort)fig = plt.figure()ax = fig.add_subplot(111)print(yHat[srtInd]) # 从小到大排序ax.plot(xSort[:, 1], yHat[srtInd])ax.scatter(xMat[:, 1].flatten().A[0], np.mat(labelMat).T.flatten().A[0], s=2, c='red')plt.show()
拓展
def calcR(y, fit_y):"""计算R的平方 R^2 = 回归平方和 - 总平方和总平方和 = \sum (y的实际值 - 平均值) ^ 2回归平方和 = 总平方和 - 残差平方和残差平方和 = \sum (y的估计值 - 实际值) ^ 2:param y: 实际值:param fit_y: 估计值:returnR^2 决定系数 表示回归关系可以解释应变量80%的变异"""y = np.mat(y)fit = np.mat(fit_y)yMean = np.mean(y)# print(yMean)# print(y - yMean)# print(np.sum(np.power((y-yMean), 2)))sumSqu = np.sum(np.power((y-yMean), 2))# print('总平方和', sumSqu)residual_squareSum = np.sum(np.power((fit_y - y), 2))# print('残差平方和', residual_squareSum)rSqure = (sumSqu - residual_squareSum) / sumSquprint(rSqure)print('R^2 %.2f %%' % (rSqure*100))return rSquredef rss_error(labelMat, fit_y):"""平方误差和: 实际值与预测值之差"""print('平方误差和:', np.sum(np.power((np.mat(labelMat) - fit_y), 2)))return np.sum(np.power((np.mat(labelMat) - fit_y), 2))
def regularize(dataSet, labelMat):"""按列标准化数据:param dataSet::param labelMat::return:"""dataSet = np.mat(dataSet)labelMat = np.mat(labelMat).TxMean = np.mean(dataSet, 0)yMean = np.mean(labelMat, 0)xVar = np.var(dataSet, 0)dataSet = (dataSet - xMean) / xVarlabelMat = labelMat - yMeanreturn dataSet, labelMat
面对不可逆矩阵(数据)
问题:使用标准方程法求解线性回归时, 需要满足条件 (XTX)−1,即(XTX)行列式不为0,为0时无法计算 ( X T X ) − 1 , 即 ( X T X ) 行 列 式 不 为 0 , 为 0 时 无 法 计 算
问题进一步推导: 如果当数据集中特征比样本点还多的时候,说明输入数据的矩阵x不是满秩矩阵,非满秩矩阵求逆会出问题 如 果 当 数 据 集 中 特 征 比 样 本 点 还 多 的 时 候 , 说 明 输 入 数 据 的 矩 阵 x 不 是 满 秩 矩 阵 , 非 满 秩 矩 阵 求 逆 会 出 问 题
解决方案: 引入岭回归 lasso法 前向逐步回归
岭回归
简述:岭回归就是在矩阵 XTX X T X 上加入一个 λI从而使得矩阵非奇异,进而能对XTX+λI求逆 λ I 从 而 使 得 矩 阵 非 奇 异 , 进 而 能 对 X T X + λ I 求 逆
其中矩阵I是一个n∗n(等于列数)单位矩阵对角线上元素全为1,其他元素全为0 其 中 矩 阵 I 是 一 个 n ∗ n ( 等 于 列 数 ) 单 位 矩 阵 对 角 线 上 元 素 全 为 1 , 其 他 元 素 全 为 0
此时回归系数的计算公式变为: w^=(XTX+λI)−1XTy w ^ = ( X T X + λ I ) − 1 X T y
岭回归最先用来处理特征数多于样本数的情况, 现在也用于在估计中加入偏差, 从而得到更好的估计.
这里通过引入 λ来限制所有w之和 λ 来 限 制 所 有 w 之 和 ,通过引入该惩罚项 能够减少不重要的参数 这种技术在统计学中叫做缩减
原理:
- (1)对于有些矩阵, 矩阵中某个元素的一个很小的变动,会引起最后计算结果误差很大,这种矩阵称为”病态矩阵”
有些时候不正确的计算方法也会使得一个正常的矩阵在运算中表现出病态, 对于高斯消去法来说,如果对角线上的元素很小,
计算时就会表现出病态的特征 - (2)回归分析中,最小二乘法是一种无偏估计, 在运算中往往是列满秩的运算
即: θx=y θ x = y
此时最小化损失函数为: ∑i(y−θx)2 ∑ i ( y − θ x ) 2 - (3)若x不是列满秩时或者某些列之间的线性相关关系比较大的时候, XTX X T X 接近于奇异 (XTX)−1 ( X T X ) − 1 误差变大
为了解决损失函数误差过大的情况,只要将不适当问题转化为适当问题(给损失函数加上一个正则化项):
||Xθ−y||2+||γθ||2 | | X θ − y | | 2 + | | γ θ | | 2 - (4)于是得出 θ(α)=(XTX+αI)−1XTy θ ( α ) = ( X T X + α I ) − 1 X T y
原理小结:
随着α的增大,θ(α)各元素θ(αi)的绝对值趋向于不断变小,相对于正确值θi的偏差也越来越大,α趋于无穷大时,θ(α)趋向0,其中,θ(α)随着α的改变而变化的轨迹,称为岭迹 随 着 α 的 增 大 , θ ( α ) 各 元 素 θ ( α i ) 的 绝 对 值 趋 向 于 不 断 变 小 , 相 对 于 正 确 值 θ i 的 偏 差 也 越 来 越 大 , α 趋 于 无 穷 大 时 , θ ( α ) 趋 向 0 , 其 中 , θ ( α ) 随 着 α 的 改 变 而 变 化 的 轨 迹 , 称 为 岭 迹
实质上: 岭回归是对最小二乘回归的一种补充,损失了无偏性,以换取高的数值稳定性,从而得到较高的计算精度
def ridge_regression(dataSet, labelMat, lamb=10):"""岭回归 求解模型参数优缺点: 损失了无偏性,得到较高的计算精度:param dataSet: 数据集 输入空间 X:param labelMat:数据集 输出空间 Y:param lamb: lambda 系数:returnw 模型参数"""xMat = np.mat(dataSet)yMat = np.mat(labelMat)yMat = np.mat(labelMat).Ti = np.eye(np.shape(dataSet)[1])demo = xMat.T * xMat + lamb * iif np.linalg.det(demo) == 0:print('行列式为0 无法计算逆矩阵')ws = demo.I * (xMat.T * yMat)# print('ws---', np.shape(ws))return wsdef ridge_test(dataSet, labelMat):"""转化数据集为均值为0,方差为1的数据集:param dataSet: 输入空间:param labelMat: 输出空间:param lamb: lambda 系数:returnw_list 模型参数集合"""# dataSet = np.mat(dataSet)# labelMat = np.mat(labelMat).T# # 计算均值# xMean = np.mean(dataSet, 0)# yMean = np.mean(labelMat, 0)## # print(xMean)# # print(yMean)# xVar = np.var(dataSet, 0)# dataSet = (dataSet - xMean) / xVar# labelMat = labelMat - yMeandataSet, labelMat = regularize(dataSet, labelMat)# print('dataSet', dataSet)# print('labelMat', labelMat)lamb = 30wMat = np.zeros((lamb, np.shape(dataSet)[1]))# print('wMat', np.shape(wMat))for i in range(lamb):ws = ridge_regression(dataSet, labelMat.T, lamb=np.exp(i-10))wMat[i, :] = ws.Treturn wMatdef ridge_regress_plot(wMat):fig = plt.figure()ax = fig.add_subplot(111)ax.plot(wMat)plt.show()
套索方法(lasso)
增加如下约束时, 普通的最小二乘法回归会得到与岭回归一样的公式
∑k=1nw2k≤λ ∑ k = 1 n w k 2 ≤ λ
上式限定了所有回归系数的平方和不能大于 λ λ , 使用普通的最小二乘法回归当在两个或更多的特征相关时,可能会得到一个很大的正系数和一个很大的负系数,使用岭回归可以避免这个问题
lasso 缩减
∑k=1n|wk|≤λ ∑ k = 1 n | w k | ≤ λ
实质上是与岭回归是差不多的,不同点在于某些系数会变为0
前向逐步回归 – 贪心算法
前向逐步回归可以得到与lasso差不多的效果,但是更加简单,属于贪心算法
算法简述: 首先, 所有权重都设置为0,然后每一步所做的决策是对某个权重增加或减少一个很小的值
伪代码如下:
数据标准化,使其分布满足 0 均值 和单位方差
在每轮迭代过程中:
设置当前最小误差 lowestError 为正无穷
对每个特征:增大或缩小:改变一个系数得到一个新的 w计算新 w 下的误差如果误差 Error 小于当前最小误差 lowestError: 设置 Wbest 等于当前的 W将 W 设置为新的 Wbest
def stepwise_regression(dataSet, labelMat, alpha, numCycle):"""前向逐步算法伪代码1.数据标准化 分布满足0均值 方差为12.对每次迭代过程3.设置当前最小误差 lowestError 为无穷大4.对每个特征5.增大或者缩小6. 改变一个系数得到一个新的w计算新w下的误差如果误差小于当前最小误差, 设置为wbest将w设置为新的wbest:param dataSet: 数据集 输入空间:param labelMat:数据集 输出空间:param alpha: 学习速率:param numCycle: 最大迭代次数:returnwMat 每次的wbest组成的列表"""dataSet = np.mat(dataSet)labelMat = np.mat(labelMat).TxMean = np.mean(dataSet, 0)yMean = np.mean(labelMat, 0)xVar = np.var(dataSet, 0)dataSet = (dataSet - xMean) / xVarlabelMat = labelMat - yMeanprint(np.shape(dataSet)) # (4176, 8)print(np.shape(labelMat)) # (4176, 1)feature_num = np.shape(dataSet)[1]print(feature_num) # 8w = np.zeros((1, feature_num))w_test = w.copy()wbest = NonewMat = np.zeros((numCycle, feature_num))print(np.shape(w)) # (1, 8)for cycle in range(numCycle):lowestError = np.inffor i in range(feature_num):for sign in [-1, 1]:w_test = w.copy()w_test[:, i] += alpha * signerror = np.sum(np.power((dataSet * w_test.T - labelMat), 2))if error < lowestError:lowestError = errorwbest = w_testw = wbest.copy()wMat[cycle, :] = wbest# print(wMat)return wMat
项目案例-鲍鱼年龄预测
def load_abalone_data(filename):fr = open(filename)data_Set = list()label_mat = list()featureNum = int(len(fr.readline().strip().split('\t'))-1)for lines in fr.readlines():line = lines.strip().split('\t')# print(line)lineArr = list()for index in range(featureNum):# print('index', index)lineArr.append(float(line[index]))data_Set.append(lineArr)label_mat.append(float(line[-1]))# print('xxxxx')# print(data_Set)return data_Set, label_matdef abalone_stepwise_regression():filename = 'abalone.txt'dataSet, labelMat = load_abalone_data(filename)result_stepwise = stepwise_regression(dataSet, labelMat, alpha=0.01, numCycle=200)print('result', result_stepwise[-1, :])# dataSet, labelMat = regularize(dataSet, labelMat)result_lr = lr_stand(dataSet, labelMat)print('result_lr:', result_lr.T)def abalone_predict_ridge_regress():filename = 'abalone.txt'dataSet, labelMat = load_abalone_data(filename)# w = ridge_regression(dataSet, labelMat)# print(w)# lr_plot(dataSet, labelMat, w)wMat = ridge_test(dataSet, labelMat)print('wMat,', wMat)ridge_regress_plot(wMat)def abalone_predict_project():filename = 'abalone.txt'# fr = open(filename)dataSet, labelMat = load_abalone_data("abalone.txt")# print('abX,', np.shape(dataSet), type(labelMat[0][0]))# print('abY,', np.shape(dataSet), type(labelMat[0]))"""abX, (4177, 8) <class 'float'>abY, (4177,) <class 'float'>"""# 使用不同的核进行预测fit_y01 = lwlrTest(dataSet[0:99], dataSet[0:99], labelMat[0:99], 0.1)fit_y1 = lwlrTest(dataSet[0:99], dataSet[0:99], labelMat[0:99], 1)fit_y10 = lwlrTest(dataSet[0:99], dataSet[0:99], labelMat[0:99], 10)# 打印出不同的核预测值与训练数据集上的真实值之间的误差大小error01 = rss_error(labelMat[0:99], fit_y01)error1 = rss_error(labelMat[0:99], fit_y1)error10 = rss_error(labelMat[0:99], fit_y10)# 打印出不同的核预测值与训练数据集上的r^2 拟合度r_square01 = calcR(labelMat[0:99], fit_y01)r_square1 = calcR(labelMat[0:99], fit_y1)r_square10 = calcR(labelMat[0:99], fit_y10)# 打印出 不同的核预测值 与 新数据集(测试数据集)上的真实值之间的误差大小new_fit_y01 = lwlrTest(dataSet[100:199], dataSet[0:99], labelMat[0:99], 0.1)new_fit_y1 = lwlrTest(dataSet[100:199], dataSet[0:99], labelMat[0:99], 1)new_fit_y10 = lwlrTest(dataSet[100:199], dataSet[0:99], labelMat[0:99], 10)new_error01 = rss_error(labelMat[100:199], new_fit_y01)new_error1 = rss_error(labelMat[100:199], new_fit_y1)new_error10 = rss_error(labelMat[100:199], new_fit_y10)def main():filename = 'data.txt'dataSet, labelMat = loadData(filename)# w = lr(dataSet, labelMat)# lr_plot(dataSet, labelMat, w)# fit_y = lwlrTest(dataSet, dataSet, labelMat, k=1)# fit_y = lwlrTest(dataSet, dataSet, labelMat, k=0.01)fit_y = lwlrTest(dataSet, dataSet, labelMat, k=0.03)lwlr_plot(dataSet, labelMat, fit_y)error = rss_error(labelMat, fit_y)# 查看平方误差和print('error', error) # 0.03 0.0678212359601 # 0.01 1.16751327518 # 1 1.3520374286r_square = calcR(labelMat, fit_y)# 查看r^2 相关系数print('r^2: %.2f %%' % (r_square*100)) # 0.03 99.45 % # 0.01 99.70 % # 1 97.30 %if __name__ == '__main__':# main() # 线性回归与局部线性回归# abalone_predict_project() # 局部线性回归 鲍鱼年龄预测项目# abalone_predict_ridge_regress() # 岭回归 鲍鱼年龄预测项目abalone_stepwise_regression() # 前向逐步算法 鲍鱼年龄预测项目
参考文献
《机器学习实战》
这篇关于预测算法-线性回归(鲍鱼年龄预测)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!