大数据-206 用 NumPy 矩阵乘法手写多元线性回归:正规方程、SSE/MSE/RMSE 与 R²

TL;DR

  • 场景:不依赖 sklearn,用 pandas+NumPy 通过矩阵运算实现线性回归并评估拟合效果
  • 结论:用正规方程 w=(X^TX)^{-1}X^Ty 可快速得到权重;奇异矩阵需处理;R²可直观判断拟合度
  • 产出:可运行的回归求解函数 + SSE 计算函数 + R² 计算函数 + 可视化拟合曲线

多元线性回归实现

利用矩阵乘法编写回归算法

多元线性回归的执行函数编写并不复杂,主要设计大量的矩阵计算,需要借助 Numpy 中的矩阵数据格式来完成。 首先执行标准导入:

python 复制代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

执行结果如下图所示,如果报错可能是缺少了库,需要安装: 编写线性回归函数,同理,我们假设输入数据集为 DataFrame,且最后一列为标签值:

python 复制代码
def standRegres(dataSet):
    xMat = np.mat(dataSet.iloc[:,:-1].values) # 提取特征
    yMat = np.mat(dataSet.iloc[:,-1].values).T # 提取标签
    xTx = xMat.T*xMat
    if np.linalg.det(xTx) ==0:
        print('This matrix is singular,cannot do inverse') #行列式为0,则该矩阵为奇异
        矩阵,无法求解逆矩阵
        return
    ws = xTx.I * (xMat.T*yMat)
    return ws

这里需要判断 XTX 是否为满秩矩阵,若不满秩,则无法对其进行求逆矩阵的操作。定义函数后,即可测试运行效果,此处我们建立线性随机数进行多元线性回归方程拟合。这里需要注意的是,当使用矩阵分解来求解多元线性回归方程时,必须添加一列全为 1 的列,用于表征线性方程截距W0。

python 复制代码
rng = np.random.RandomState(1) # 设置随机种子
x = 5*rng.rand(100) # 100个[0,5)的随机数
y = 2*x-5+rng.randn(100) # 真实规律的标签取值
X = pd.DataFrame(x)
Y = pd.DataFrame(y)
ex = pd.DataFrame(np.ones([100,1])) #添加一列权威1的列,表示截距
data = pd.concat([ex,X,Y],axis=1)

数据满足基本的建模要求,然后执行函数运算:

python 复制代码
ws = standRegres(data)
ws

执行结果如下图所示: 返回结果即为各列特征权重,其中数据集第一列值均为 1,因此返回结果的第一个分量表示截距,然后可以用可视化图形展示模型拟合效果:

python 复制代码
yhat = data.iloc[:,:-1].values*ws # 预测标签值
plt.plot(data.iloc[:,1],data.iloc[:,2],'o') #原始数据点
plt.plot(data.iloc[:,1],yhat) # 拟合直线

执行结果如下图所示:

算法评估指标

残差平方和 SSE,其计算公式如下: 其中的 m 为数据集记录书,yi 为实际标签值,y^为预测值,则可利用下面表达式进行计算:

python 复制代码
y = data.iloc[:,-1].values
yhat = yhat.flatten()
SSE = np.power(yhat-y,2).sum()
print(SSE)

执行结果如下图所示: 当然也可以编写成一个完整的函数,为提高服用性,设置输入参数为数据集和回归方法:

python 复制代码
def sseCal(dataSet,regres):
    n = dataSet.shape[0]
    y = dataSet.iloc[:,-1].values
    ws = regres(dataSet)
    yhat = dataSet.iloc[:,:-1].values*ws
    yhat = yhat.flatten()
    SSE = np.power(yhat-y,2).sum()
    return SSE

测试运行:

python 复制代码
sseCal(data,standRegres)

执行结果如下: 同时,在回归算法中,为了消除数据集规模对残差平方和的影响,往往我们会计算平均残差 MSE:

其中 m 为数据集样例个数,以及 RMSE 误差的均方根,为 MSE 开平方后所得结果。当然除此之外最常用的还是 R - square 判定系数,判定系数的计算需要使用之前介绍的组间误差平方和与离差平方和的概念。 在回归分析中,SSR 表示聚类中类似的组间平方和概念,译为:Sum of squares of the regression,由预测数据与标签均值之间差值的平方和构成。

而 SST (Total sum of squares)则是实际值和均值之间的差值的平方和:

对比之前介绍的聚类分析,此处可对比理解为以点聚点。同样,和轮廓系数类似,最终的判断系数表也同时结合了"组内误差"和"组件误差"两个指标。

判定系数 R方 测度了回归直线对观测数据的拟合程度:

  • 若所有观测点都落在了直线上,残差平方和为 SSE = 0,则 R 方 = 1,拟合是完全的
  • 如果 y 的变化与 x 无关,完全无助于解释 y 的变差,R 方 = 0。

可见 R 方的取值范围是 [0,1]:

  • R 方越接近 1,表明回归平方和占总平方和的比例越大,回归直线与各个观测点越接近,用 x 的变化来解释 y 值变差(取值的波动称为变差)的部分越多,回归直线的拟合程度就越好。
  • 反之,R 方越接近 0,回归直线的拟合程度就越差。

接下来,尝试计算上述拟合结果的判定系数:

python 复制代码
sse = sseCal(data,standRegres)
y = data.iloc[:,-1].values
sst = np.power(y-y.mean(),2).sum()
1-sse/sst

执行结果如下所示: 结果为 0.91,能够看出最终拟合效果非常好,当然,我们也可以编写函数封装计算判断系数的相关操作,同样留一个调整回归函数的接口。

python 复制代码
def rSquare(dataSet,regres):
sse = sseCal(dataSet,regres)
y = dataSet.iloc[:,-1].values
sst = np.power(y-y.mean(),2).sum()
return 1-sse/sst

然后进行测试

python 复制代码
rSquare(data,standRegres)

执行结果如下图所示:

错误速查

症状 根因定位 修复方法
直接报 SyntaxError(回归函数里出现中文"矩阵,无法求解逆矩阵") 非注释中文文本混入函数体 standRegres()print(...) 后一行 删除该行或改为注释 # ...
IndentationError / NameError(R²函数无法运行) rSquare 函数体未缩进 def rSquare(dataSet,regres): 下面多行将 sse=.../y=.../sst=.../return ... 缩进到函数体内
打印 "This matrix is singular,cannot do inverse" 并返回空 X^TX 奇异(共线/样本不足/特征重复/截距列处理不当) np.linalg.det(xTx)==0 分支用 np.linalg.pinv(xTx) 或直接 np.linalg.lstsq(X,y);或移除共线特征/正则化
ValueError: shapes ... not aligned X 与 ws 维度不匹配(列数不一致、标签列位置不对) yhat = data.iloc[:,:-1].values*ws 确保最后一列是标签;特征列数与 ws 行数一致;截距列是否已加入要一致
结果权重明显不合理(截距/系数偏离预期) 截距列未加、特征未标准化、数据列顺序混乱 构造数据 ex/X/Yconcat 后列顺序明确列顺序:[1, x1, x2, ..., y];必要时对特征做标准化并保持同一处理流程
TypeError/UFuncTypeError(矩阵乘法失败) DataFrame 含字符串/类别型/NaN np.mat(dataSet.iloc[:,:-1].values) 训练前 astype(float)、缺失值处理(删行/填充)、类别特征先编码
R² 为负或远低于预期 模型欠拟合、数据噪声大、特征无关;或训练/评估列错位 1 - sse/sst 输出校验 y 与 yhat 是否一一对应;检查标签列是否最后一列;增加有效特征或改用更合适模型
SSE 数值很大但图像看似还行 SSE 对样本规模与量纲敏感 SSE = ...sum() 同时输出 MSE/RMSE;对比同量纲指标;必要时对 y 做尺度变换

其他系列

🚀 AI篇持续更新中(长期更新)

AI炼丹日志-29 - 字节跳动 DeerFlow 深度研究框斜体样式架 私有部署 测试上手 架构研究 ,持续打造实用AI工具指南! AI研究-132 Java 生态前沿 2025:Spring、Quarkus、GraalVM、CRaC 与云原生落地

💻 Java篇持续更新中(长期更新)

Java-218 RocketMQ Java API 实战:同步/异步 Producer 与 Pull/Push Consumer MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务已完结,Dubbo已完结,MySQL已完结,MongoDB已完结,Neo4j已完结,FastDFS 已完结,OSS已完结,GuavaCache已完结,EVCache已完结,RabbitMQ已完结,RocketMQ正在更新... 深入浅出助你打牢基础!

📊 大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈! 大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解

相关推荐
源代码•宸1 分钟前
Golang基础语法(go语言指针、go语言方法、go语言接口、go语言断言)
开发语言·经验分享·后端·golang·接口·指针·方法
Bony-1 分钟前
Golang 常用工具
开发语言·后端·golang
pyniu3 分钟前
Spring Boot车辆管理系统实战开发
java·spring boot·后端
love_summer3 分钟前
深入理解Python控制流:从if-else到结构模式匹配,写出更优雅的条件判断逻辑
后端
牛奔5 分钟前
GVM:Go 版本管理器安装与使用指南
开发语言·后端·golang
武子康5 分钟前
大数据-207 如何应对多重共线性:使用线性回归中的最小二乘法时常见问题与解决方案
大数据·后端·机器学习
颜酱7 分钟前
用填充表格法-继续吃透完全背包及其变形
前端·后端·算法
pathfinder同学7 分钟前
Node.js 框架的 10 个写法痛点,以及更优雅的解决方案
后端
gelald11 分钟前
AQS 解析:从原理到实战
java·后端
天远云服11 分钟前
拒绝性能瓶颈:使用Go协程高效清洗天远多头借贷行业风险数据
大数据·api