---
- oeasy Python 0809
- 这是 oeasy 系统化 Python 教程,从基础一步步讲,扎实、完整、不跳步。愿意花时间学,就能真正学会。
- 本教程同步发布在:
- 个人网站: `https://oeasy.org`
- 蓝桥云课: `https://www.lanqiao.cn/courses/3584`
- GitHub: `https://github.com/overmind1980/oeasy-python-tutorial`
- Gitee: `https://gitee.com/overmind1980/oeasypython`
----
以前 决策树 和 随机森林 用于分类
- 预测的是人为的标签
-
上次是用了 决策树 和 随机森林 回归
- 预测的是 真实的物理量
| 对比维度 | 回归问题 | 分类问题 |
|---|---|---|
| 预测目标 | 猜一个连续的数 | 猜一个确定的类别 |
| 输出长啥样 | 比如 1.78m、60kg、25℃ | 比如 是/否、猫/狗、及格/不及格 |
| 咋判断模型好不好 | 看预测值和真实值差多少(差越小越好) | 看预测对的比例有多高(比例越高越好) |
| 你的实战例子 | 用身高体重预测臂展 | 用身高体重预测“是否是运动员” |
| 常用算法 | 线性回归、决策树回归、随机森林回归 | 逻辑回归、决策树分类、随机森林分类 |
- 还能有什么样别的回归呢??🤔
- 实验装置
- 抛光木质斜面
- 长约 12 佛罗伦萨肘尺
- 约 7 米
- 黄铜小球
- 水钟计时
- 抛光木质斜面
- 让小球从斜面顶端滚落
- 记录不同时间点的位移数据
- 序号
- 时间
- 距离
- 记录不同时间点的位移数据
| Number | Time | Distance |
|---|---|---|
| 1 | 1 | 33 |
| 2 | 2 | 130 |
| 3 | 3 | 298 |
| 4 | 4 | 526 |
| 5 | 5 | 824 |
| 6 | 6 | 1192 |
| 7 | 7 | 1620 |
| 8 | 8 | 2123 |
- 直接线性拟合吧
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_squared_error
# ===================== 1. 构造伽利略原始真实数据集【纯三列 序号、时间、距离】=====================
data = pd.DataFrame({
'Serial': [1,2,3,4,5,6,7,8],
'Time': [1,2,3,4,5,6,7,8],
'Distance': [33,130,298,526,824,1192,1620,2123]
})
# 特征/目标值 提取【纯一次特征,无需构造高次项,彻底解决警告】
X = data[['Time']] # 保持DataFrame格式+特征名,无任何警告
y = data['Distance']
# ===================== 2. 纯一次线性回归建模 核心步骤【极简,无多余操作】 =====================
model = LinearRegression()
model.fit(X, y)
# ===================== 3. 模型预测 + 拟合效果评估 =====================
y_pred = model.predict(X)
r2 = r2_score(y, y_pred)
rmse = np.sqrt(mean_squared_error(y, y_pred))
# ===================== 4. 输出核心结果 =====================
print("="*70)
print("✅ 伽利略原始数据 一次线性回归结果 (y = k*Time + b)")
print(f"一次线性回归方程:Distance = {model.coef_[0]:.2f}*Time + {model.intercept_:.2f}")
print(f"拟合优度 R² = {r2:.6f} | 均方根误差 RMSE = {rmse:.2f}")
print("="*70)
print("✅ 原始实测值 vs 一次线性回归预测值")
compare = pd.DataFrame({
'Time':X['Time'],
'True_Distance':y,
'Pred_Distance':np.round(y_pred,1)
})
print(compare)
print("="*70)
# ===================== 5. 可视化:原始数据+一次线性拟合直线 ✔️直接保存PNG 无plt.show() ✔️=====================
plt.rcParams['font.sans-serif']=['WenQuanYi Zen Hei']
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(8,5),dpi=120)
# 伽利略原始实测数据点
plt.scatter(X['Time'], y, color='darkred', s=120, label='Galileo Raw Data', edgecolors='white', linewidth=2)
# 生成平滑的时间序列+一次线性拟合直线【极简,无需特征转换】
t_range = pd.DataFrame({'Time':np.linspace(0,9,100)})
s_pred = model.predict(t_range)
# 绘制一次线性拟合直线
plt.plot(t_range['Time'], s_pred, color='darkblue', lw=3, label='Linear Fit (y = k*t + b)')
plt.xlabel('Time (equal intervals)')
plt.ylabel('Distance (scale divisions)')
plt.title('Galileo Inclined Plane Experiment | Linear Regression')
plt.legend(loc='upper left')
plt.grid(alpha=0.3, linestyle='--')
# ✅ 直接保存高清PNG到实验楼路径,无弹窗,无plt.show()
plt.savefig('/home/project/伽利略一次线性回归拟合图.png', dpi=120, bbox_inches='tight')
======================================================================
✅ 伽利略原始数据 一次线性回归结果 (y = k*Time + b)
一次线性回归方程:Distance = 298.33*Time + -499.25
拟合优度 R² = 0.952078 | 均方根误差 RMSE = 153.36
======================================================================
✅ 原始实测值 vs 一次线性回归预测值
Time True_Distance Pred_Distance
0 1 33 -200.9
1 2 130 97.4
2 3 298 395.7
3 4 526 694.1
4 5 824 992.4
5 6 1192 1290.8
6 7 1620 1589.1
7 8 2123 1887.4
======================================================================
- 这些参数啥意思?
- R² = 0.952078
- 均方根误差 RMSE = 153.36
- 预测值和真实值 有个 残余的差值
- 叫做 残差
- 我们尝试把 残差 标出来
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_squared_error
# ===================== 1. 构造伽利略原始真实数据集 =====================
data = pd.DataFrame({
'Serial': [1,2,3,4,5,6,7,8],
'Time': [1,2,3,4,5,6,7,8],
'Distance': [33,130,298,526,824,1192,1620,2123]
})
X = data[['Time']]
y = data['Distance']
# ===================== 2. 纯一次线性回归建模 =====================
model = LinearRegression()
model.fit(X, y)
y_pred = model.predict(X)
r2 = r2_score(y, y_pred)
rmse = np.sqrt(mean_squared_error(y, y_pred))
# ===================== 3. 输出核心结果 =====================
print("="*70)
print("✅ 伽利略原始数据 一次线性回归结果 (y = k*Time + b)")
print(f"一次线性回归方程:Distance = {model.coef_[0]:.2f}*Time + {model.intercept_:.2f}")
print(f"拟合优度 R² = {r2:.6f} | 均方根误差 RMSE = {rmse:.2f}")
print("="*70)
print("✅ 原始实测值 vs 一次线性回归预测值 | 含残差值")
compare = pd.DataFrame({
'Time':X['Time'],
'True_Distance':y,
'Pred_Distance':np.round(y_pred,1),
'残差值(真实-预测)':np.round(y - y_pred,1)
})
print(compare)
print("="*70)
# ===================== 4. 可视化:原始数据+拟合直线+【残差线段+残差数值标注】 ✔️直接保存PNG =====================
plt.rcParams['font.sans-serif']=['WenQuanYi Zen Hei']
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(8,5),dpi=120)
# 伽利略原始实测数据点
plt.scatter(X['Time'], y, color='darkred', s=120, label='Galileo Raw Data', edgecolors='white', linewidth=2, zorder=5)
# ✅ 核心新增:画每个点的【残差竖线】+【残差数值标注】
t_vals = X['Time'].values
for i in range(len(t_vals)):
t = t_vals[i]
y_true = y.iloc[i]
y_pre = y_pred[i]
resi = np.round(y_true - y_pre, 1) # 计算残差
plt.vlines(x=t, ymin=min(y_true, y_pre), ymax=max(y_true, y_pre), color='red', alpha=0.7, linestyle='--', linewidth=2) # 残差竖线
plt.annotate(f'{resi}', xy=(t, (y_true+y_pre)/2), xytext=(0, 8), textcoords='offset points', ha='center', fontsize=9, color='darkred') # 残差数值标注
# 生成平滑的时间序列+一次线性拟合直线
t_range = pd.DataFrame({'Time':np.linspace(0,9,100)})
s_pred = model.predict(t_range)
plt.plot(t_range['Time'], s_pred, color='darkblue', lw=3, label='Linear Fit (y = k*t + b)')
plt.xlabel('Time (equal intervals)')
plt.ylabel('Distance (scale divisions)')
plt.title('Galileo Inclined Plane Experiment | Linear Regression (with Residuals)')
plt.legend(loc='upper left')
plt.grid(alpha=0.3, linestyle='--')
# ✅ 直接保存高清PNG到实验楼路径,无弹窗
plt.savefig('/home/project/伽利略一次线性回归_带残差标注.png', dpi=120, bbox_inches='tight')
- 残差residuals怎么理解?
| 构成部分 | 拉丁语词源 | 含义 | 作用 |
|---|---|---|---|
| 前缀 re- | re- | 表示“回、向后、留存” | 表方向/状态 引申为“剩下的” |
| 词根 sid- / sed- | sedēre | 本义sit“坐” 引申为“停留、保持” |
核心语义 表“停留下来的事物” |
| 后缀 -ual | -uālis | 形容词后缀 表“具有……性质的” |
转化为形容词 后衍生出名词用法 |
- 同根词
| 单词 | 音标 | 中文释义 | 词源拆解(与词根关联) | 用法示例(贴合学术/日常场景) |
|---|---|---|---|---|
| residue | /ˈrezɪdjuː/ | ① 残余物;残渣 ② 数学:余数 ③ 化学:残留物 |
re-(留存) + sid-(停留) + -ue(名词后缀)和 residual 是同源词,语义几乎一致 |
1. 数学:The residue of 20 divided by 3 is 2. 2. 化学:Filter the solution to collect the solid residue. 3. 统计:The residue of the model is the same as the residual value. |
| sediment | /ˈsedɪmənt/ | 沉积物;沉淀物(如泥沙、水垢) | sed-(停留) + -iment(名词后缀) → 「沉降停留的物质」 |
The sediment at the bottom of the beaker is calcium carbonate. |
| reside | /rɪˈzaɪd/ | ① 居住;定居 ② (权力、性质)存在于,归属 |
re-(持续) + sid-(停留) → 「持续停留在某地/某物中」 |
1. 日常:She resides in Beijing now. 2. 学术:The core of the theory resides in its mathematical logic. |
| residence | /ˈrezɪdəns/ | ① 住宅;居所 ② 居住期 |
re- + sid- + -ence(名词后缀)是 reside 的名词形式 |
The professor has a residence near the university campus. |
| preside | /prɪˈzaɪd/ | 主持(会议、活动);主管 | pre-(在前) + sid-(坐) → 「坐在前面主导」 |
He will preside over the academic seminar on regression analysis. |
| president | /ˈprezɪdənt/ | 总统;董事长;校长 | pre- + sid- + -ent(人) → 「坐在前面的负责人」 |
The president of the institute approved the experimental plan. |
| subside | /səbˈsaɪd/ | ① (洪水、疼痛等)消退,平息 ② (土地)沉降 |
sub-(向下) + sid-(坐) → 「向下沉、平息」 |
After the rain, the flood began to subside gradually. |
| assiduous | /əˈsɪdjuəs/ | 勤奋的;刻苦的 | as-(加强) + sid-(停留) + -uous → 「持续专注停留于某事」 |
The student is assiduous in studying machine learning algorithms. |
| session | /ˈseʃn/ | ① 会议;研讨会 ② (课程)一节 |
源自拉丁语 sessio(坐),是 sedēre 的名词形式 → 「坐在一起开会/上课的时间段」 |
We will have a session on residual analysis this afternoon. |
- 词根核心语义总结
sid- / sed-的核心是「停留、保持、坐落」,前缀(re-/pre-/sub-等)决定了单词的具体方向:re-(留存)→ residual / residue / reside(停留、剩余)pre-(在前)→ preside / president(坐在前面主导)sub-(向下)→ subside(向下沉降、消退)
- 你的案例核心
- 时间$t[1,2,3,4,5,6,7,8]$
- 实测距离$y[33,130,298,526,824,1192,1620,2123]$
- 一次线性拟合的预测值$y_{pred}$,残差$residual = y - y_{pred}$
你代码里的最终结果:$\boldsymbol{R^2=0.981807}$,RMSE=96.73
-
残差
- 你的每个数据点都有
$\boldsymbol{残差_i = 实测值_i - 预测值_i}$
-
全局线性回归
- 实测值-预测值-残差值
- 对照表
| Time | True_Distance | Pred_Distance | 残差值(真实-预测) |
|---|---|---|---|
| 1 | 33 | -200.9 | 233.9 |
| 2 | 130 | 97.4 | 32.6 |
| 3 | 298 | 395.7 | -97.7 |
| 4 | 526 | 694.1 | -168.1 |
| 5 | 824 | 992.4 | -168.4 |
| 6 | 1192 | 1290.8 | -98.8 |
| 7 | 1620 | 1589.1 | 30.9 |
| 8 | 2123 | 1887.4 | 235.6 |
- 伽利略斜面实验 线性回归 残差值平方对照表
| Time | True_Distance | Pred_Distance | 残差值(真实-预测) | 残差值的平方 |
|---|---|---|---|---|
| 1 | 33 | -200.9 | 233.9 | |
| 2 | 130 | 97.4 | 32.6 | |
| 3 | 298 | 395.7 | -97.7 | |
| 4 | 526 | 694.1 | -168.1 | |
| 5 | 824 | 992.4 | -168.4 | |
| 6 | 1192 | 1290.8 | -98.8 | |
| 7 | 1620 | 1589.1 | 30.9 | |
| 8 | 2123 | 1887.4 | 235.6 |
- 残差平方和
- Sum of Squares of Residuals
- SSR
| 残差值的平方 | 残差平方累加和 |
|---|---|
| 54709.21 | 54709.21 |
| 1062.76 | 55771.97 |
| 9545.29 | 65317.26 |
| 28257.61 | 93574.87 |
| 28358.56 | 121933.43 |
| 9761.44 | 131694.87 |
| 954.81 | 132649.68 |
| 55507.36 | 188157.04 |
-
最终累加结果 = 188157.04
- 即该组数据的 SSR(残差平方和)
-
SSR 就是 残差平方和
- 残差平方和
- Sum of Squares of Residuals
- SSR
- 需要 除以 样本数量
- 残差平方和的平均值
- SSR 的平均值
- Mean Squared Error
- MSE
- 残差平方和的平均值的平方根
- SSR 的平均值的平方根
- MSE 的 平方根
- Root of MSE
- RMSE
- 有了RMSE 为啥还需要
$\boldsymbol{R^2}$
-
计算
$\boldsymbol{R^2}$ (决定系数)- Coefficient of Determination
- 需用到公式 $$ R^2 = 1-\frac{SSR}{SST} $$
-
已知
$SSR=188157.04$ (残差平方和)- 还需计算
$SST$ (总平方和)
- 还需计算
-
步骤如下:
- 真实值列表
$[33, 130, 298, 526, 824, 1192, 1620, 2123]$
- 公式
$SST=\sum_{i=1}^n (y_{true,i}-\bar{y})^2$
| 真实值 |
||
|---|---|---|
| 33 | ||
| 130 | ||
| 298 | ||
| 526 | ||
| 824 | ||
| 1192 | ||
| 1620 | ||
| 2123 |
将最后一列求和得到
- 最终结果汇总
| 指标 | 数值 |
|---|---|
| SSR | 188157.04 |
| SST | 3926163.5 |
| 0.9521 |
- 我可以看看这个决定系数(R²)吗?
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# 1. 你的实验数据
data = pd.DataFrame({
'Time': [1,2,3,4,5,6,7,8],
'True_Distance': [33,130,298,526,824,1192,1620,2123],
'Pred_Distance': [-200.9,97.4,395.7,694.1,992.4,1290.8,1589.1,1887.4]
})
y_true = data['True_Distance']
y_pred = data['Pred_Distance']
# 2. 计算 R² 相关的核心指标
SSR = np.sum((y_true - y_pred) ** 2) # 残差平方和
y_mean = np.mean(y_true)
SST = np.sum((y_true - y_mean) ** 2) # 总平方和
SSM = SST - SSR # 模型解释的平方和
R2 = 1 - SSR / SST
# 3. 可视化 1:堆叠柱状图(展示变异占比)
plt.figure(figsize=(8, 5))
# 定义标签和数值
labels = ['Total Variance (SST)']
ssm_data = [SSM]
ssr_data = [SSR]
# 绘制堆叠柱状图
plt.bar(labels, ssm_data, label=f'Model Explained (SSM), R²={R2:.4f}', color='#1f77b4')
plt.bar(labels, ssr_data, bottom=ssm_data, label='Unexplained (SSR)', color='#ff7f0e')
# 添加数值标注
plt.text(0, SSM/2, f'{SSM:.2f}', ha='center', va='center', color='white', fontweight='bold')
plt.text(0, SSM + SSR/2, f'{SSR:.2f}', ha='center', va='center', color='white', fontweight='bold')
# 图表设置
plt.title(f'R² Visualization: Explained vs Unexplained Variance')
plt.ylabel('Variance (Squared Distance)')
plt.legend()
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.savefig('R2_variance_bar.png', dpi=120)
plt.show()
# 4. 可视化 2:残差散点图(辅助理解 R² 与残差的关系)
plt.figure(figsize=(8, 5))
residuals = y_true - y_pred
plt.scatter(y_pred, residuals, color='darkred', s=50, edgecolor='white')
plt.axhline(y=0, color='blue', linestyle='--', linewidth=1)
plt.title(f'Residual Scatter Plot (R²={R2:.4f})')
plt.xlabel('Predicted Distance')
plt.ylabel('Residuals (True - Predicted)')
plt.grid(linestyle='--', alpha=0.7)
plt.savefig('R2_residual_plot.png', dpi=120)
plt.show()
| 可视化区域颜色 | 对应指标 | 指标全称 | 核心含义 | 与 |
|---|---|---|---|---|
| 蓝色 | SSM | 回归平方和(Sum of Squares due to Regression) | 模型能够解释的数据变异 | 占 SST 的比例就是 |
| 橙色 | SSR | 残差平方和(Sum of Squared Residuals) | 模型无法解释的误差变异 | 占 SST 的比例越高,模型解释力越弱 |
| - | SST | 总平方和(Total Sum of Squares) | 数据的总变异(固定值) |
- 核心恒等关系备注
已知:
$R^2 = 0.9521$ $1-R^2 = 0.0479$ $\frac{SSR}{SST} = \frac{188157.04}{3926163.5} \approx 0.0479$
结论:
- 蓝色部分(SSM)占比 95.21% → 对应
$R^2$ - 橙色部分(SSR)占比 4.79% → 对应
$1-R^2$
-
$\boldsymbol{R^2}$ 与$\boldsymbol{1-R^2}$ 核心含义对照表(转置版)
| 对比维度 | ||
|---|---|---|
| 核心含义 | 模型的功劳占比(解释数据变异的比例) | 模型的误差占比(未解释数据变异的比例) |
| 计算公式关联 | ||
| 取值偏好 | 越趋向 100% 越好 | 越趋向 0% 越好 |
| 可视化对应区域 | 蓝色区域(SSM) | 橙色区域(SSR) |
- 有了$\boldsymbol{R^2}$ 为什么要RMSE吗?
-
$\boldsymbol{R^2}$ 和 RMSE- 是互补关系
- 而非替代关系
- 二者从完全不同的维度评估回归模型
- 缺一不可
- 核心区别在于
-
$R^2$ 衡量模型的解释能力 - RMSE 衡量模型的预测精准度
-
| 对比维度 | RMSE | |
|---|---|---|
| 指标类型 | 相对指标(比例) | 绝对指标(误差值) |
| 量纲属性 | 无量纲(取值 |
和目标变量同量纲(比如你的实验中是“刻度单位”) |
| 关注重点 | 模型能解释多少数据变异 | 模型预测值和真实值的平均误差大小 |
| 数值含义 | 越接近1 → 解释力越强 | 越接近0 → 预测越精准 |
| 无法回答的问题 | “模型预测的平均误差是多少?” | “模型能解释多少数据规律?” |
- 结合实验理解
-
$R^2=0.9521$ - 只能说明模型解释了 95.21% 的距离数据变异
- 但你无法从这个数值知道
- 预测的距离和真实距离平均差了多少个刻度
-
$RMSE=153.36$ - 直接告诉你模型预测的平均误差是 153.36 个刻度单位
- 但你无法从这个数值知道
- 这个误差是大还是小
-
- 为什么两者必须一起用?
-
场景1:$R^2$ 相同,RMSE 不同 → 选 RMSE 小的模型 假设有两个模型:
- 模型A:$R^2=0.95$,$RMSE=153$
- 模型B:$R^2=0.95$,$RMSE=80$
两者解释力相同,但模型B的预测误差更小,显然更优 —— 这个结论只看
$R^2$ 得不出。
-
场景2:RMSE 相同,$R^2$ 不同 → 选
$R^2$ 大的模型 假设有两个模型:- 模型C:$RMSE=153$,$R^2=0.95$
- 模型D:$RMSE=153$,$R^2=0.7$ 两者预测误差相同,但模型C能解释更多数据规律,稳定性更强 —— 这个结论只看 RMSE 得不出。
-
总结
-
$\boldsymbol{R^2}$ 回答 “模型好不好用”(能不能解释数据规律) - RMSE 回答 “模型准不准”(预测误差有多大)
-
-
只有同时看这两个指标
- 才能对回归模型的表现做出全面、客观的评价
-
误差大小 可以通过 平方之和 观察
- 也就是 残差平方和的平均值的平方根
- Root of Mean of Errors
- RSME 来观察
-
那么误差的方向呢?
-
$R^2_residual_plot$ (残差散点图)不直接对应某一个具体数值指标- 它的核心作用是
- 通过残差的分布特
- 辅助验证回归模型的拟合合理性与假设满足度
- 同时和
$\boldsymbol{R^2}$ 、$\boldsymbol{RMSE}$ 等指标形成互补评估
- 它的核心作用是
-
纵轴(残差值)与 RMSE 的关联
- 残差值的绝对值大小对应预测误差的大小
- 残差点整体越靠近
$y=0$ 水平线 - 单样本预测误差越小
- 模型的 RMSE 越小(预测精准度越高)
- 残差点整体越靠近
- 若残差存在极端大值
- 会直接拉高 RMSE
- 提示模型对个别样本的预测效果差
- 残差值的绝对值大小对应预测误差的大小
-
残差分布形态与
$R^2$ 的关联-
理想分布
- 残差点随机均匀分布在
$y=0$ 两侧 - 无明显规律
- 说明模型提取的线性关系是有效的
- 此时
$R^2$ 的高值是可信的
- 残差点随机均匀分布在
-
异常分布
- 残差点呈现明显趋势
-
理想分布
-
$R^2$ 仅反映模型解释变异的比例- 但无法判断模型是否满足线性回归的核心假设(残差独立、同方差、无趋势)
- 残差图可以补充这一点
- 核心指标对应英文单词/术语表
| 中文表述 | 英文全称 | 英文缩写 | 常用搭配示例 |
|---|---|---|---|
| 残差值的平方 | Squared Residual | - | squared residual of each sample |
| 残差平方和 | Sum of Squared Residuals | SSR | calculate the SSR of the model |
| 均方误差 | Mean Squared Error | MSE | MSE reflects average squared error |
| 均方根误差 | Root Mean Squared Error | RMSE | RMSE has the same unit as the target variable |
| 实测值 | True Value / Actual Value | - | true distance in the experiment |
| 预测值 | Predicted Value | - | predicted value from linear regression |
| 样本数 | Number of Samples | n | the value of n is 8 in this case |
| 线性回归 | Linear Regression | LR | fit a linear regression model |
- 补充关联术语
| 中文表述 | 英文术语 |
|---|---|
| 决定系数 |
Coefficient of Determination |
| 拟合优度 | Goodness of Fit |
| 回归方程 | Regression Equation |
- 这条线 明显不是一条直线
- 可以分段拟合吗?🤔
- 下次再说!👋
- 本文来自 oeasy Python 系统教程。
- 想完整、扎实学 Python,
- 搜索 oeasy 即可。






