---
- oeasy Python 0812
- 这是 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`
---- R²=0.9997 已经很厉害了!
=============================================================
伽利略斜面实验 双模型评估报告 (纯角度版 | 分层随机拆分)
====================================================
【角度线性回归】训练集 R²=0.9856 | RMSE=615.3 | 验证集 R²=0.9690 | RMSE=928.3
【角度二次回归】训练集 R²=0.9997 | RMSE=91.0 | 验证集 R²=0.9993 | RMSE=135.0
=============================================
📌 训练集角度范围: 0 ° ~ 86 °
📌 验证集角度范围: 3 ° ~ 89 °
=================================================
- 还可以有更好的拟合方式吗?🤔
- 古希腊
- 托勒密(公元 2 世纪)
- 在《天文学大成》中编制了弦表
- 用圆的弦长与直径的比值
- 描述角度与线段的关系
- 这是正弦的雏形
- 阿拉伯时期
- 数学家阿尔・巴塔尼(9–10 世纪)
- 将弦长转化为半弦长
- 即现代正弦的定义:sinθ=斜边对边
- 并编制了更精确的三角函数表
- 欧洲文艺复兴
- 15–16 世纪 三角学被欧洲学者系统翻译和推广
- 正弦、余弦的几何比值
- 成为天文观测、几何计算的常用工具
- 比如哥白尼研究行星轨道时就大量使用
- 伽利略自己也研究这个
- 从0到89度
- 仰角对应的边长
- 对比三种拟合方式
- 线性
- 二次
- 带三角函数特征工程的 拟合
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import r2_score, mean_squared_error
from sklearn.model_selection import train_test_split
# ✅ 核心精简:一步到位 直接定义完整数据集all_data (无需拼接,最简洁)
all_data = pd.DataFrame({
'Serial': list(range(1, 39)),
'Angle': [0,1,2,3,4,5,6,7,8,9,10,20,30,40,50,60,70,80,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89],
'Distance': [0,225,450,675,900,1125,1350,1575,1800,2025,2270,4455,6520,8360,9940,11275,12200,12810,12200,12278,12351,12419,12482,12540,12593,12641,12684,12722,12810,12844,12874,12900,12922,12940,12955,12966,12975,12980]
})
# 提取自变量和因变量
X = all_data[['Angle']]
y = all_data['Distance']
# ✅ 保留你指定的【分层随机拆分】训练集70% + 验证集30% (最优划分,无重叠无过拟合)
X_train, X_val, y_train, y_val = train_test_split(
X, y,
test_size=0.3, # 验证集占比30%
random_state=42, # 随机种子,结果固定可复现
stratify=pd.cut(all_data['Angle'], bins=3) # 分层抽样,角度分布均匀
)
# ✅ 不拼接特征矩阵!各自独立生成特征(你要求的核心,代码最清爽)
# ① 角度二次特征 (给二次回归用)
poly = PolynomialFeatures(degree=2, include_bias=False)
X_train_poly = poly.fit_transform(X_train)
X_val_poly = poly.transform(X_val)
# ② 角度正弦特征 (给正弦回归用,修正报错:加.values转numpy数组)
X_train_sin = np.sin(np.radians(X_train.values)).reshape(-1, 1)
X_val_sin = np.sin(np.radians(X_val.values)).reshape(-1, 1)
# 为绘图准备的全角度数据
angle_range = pd.DataFrame({'Angle': np.linspace(0, 90, 200)})
angle_range_poly = poly.transform(angle_range)
angle_range_sin = np.sin(np.radians(angle_range.values)).reshape(-1, 1)
# 2. 三模型独立训练(无任何特征拼接,完全独立,逻辑清晰)
## 角度线性回归 (纯Angle)
model_linear = LinearRegression().fit(X_train, y_train)
y_pred_linear = model_linear.predict(X_train)
y_val_pred_linear = model_linear.predict(X_val)
## 角度二次回归 (Angle + Angle²)
model_quad = LinearRegression().fit(X_train_poly, y_train)
y_pred_quad = model_quad.predict(X_train_poly)
y_val_pred_quad = model_quad.predict(X_val_poly)
## 正弦角度回归 (sin(Angle)) 【物理最优】
model_sin = LinearRegression().fit(X_train_sin, y_train)
y_pred_sin = model_sin.predict(X_train_sin)
y_val_pred_sin = model_sin.predict(X_val_sin)
# 3. 计算评估指标 R² + RMSE
def calc_metrics(y_true, y_pred):
return r2_score(y_true, y_pred), np.sqrt(mean_squared_error(y_true, y_pred))
# 训练集指标
r2_l, rmse_l = calc_metrics(y_train, y_pred_linear)
r2_q, rmse_q = calc_metrics(y_train, y_pred_quad)
r2_s, rmse_s = calc_metrics(y_train, y_pred_sin)
# 验证集指标
r2_l_val, rmse_l_val = calc_metrics(y_val, y_val_pred_linear)
r2_q_val, rmse_q_val = calc_metrics(y_val, y_val_pred_quad)
r2_s_val, rmse_s_val = calc_metrics(y_val, y_val_pred_sin)
# 4. 精简评估报告 (无任何多余表格,只有核心指标,干净整洁)
print("="*90)
print("伽利略斜面实验 三模型评估报告 (纯角度+二次+正弦 | 随机分层拆分)")
print("="*90)
k_l, b_l = model_linear.coef_[0], model_linear.intercept_
print(f"【角度线性】公式: y={k_l:.2f}×θ{b_l:+.2f} | 训练 R²={r2_l:.4f} RMSE={rmse_l:.1f} | 验证 R²={r2_l_val:.4f} RMSE={rmse_l_val:.1f}")
k2, k1, b_q = model_quad.coef_[1], model_quad.coef_[0], model_quad.intercept_
print(f"【角度二次】公式: y={k2:.2f}×θ²{k1:+.2f}×θ{b_q:+.2f} | 训练 R²={r2_q:.4f} RMSE={rmse_q:.1f} | 验证 R²={r2_q_val:.4f} RMSE={rmse_q_val:.1f}")
k_s, b_s = model_sin.coef_[0], model_sin.intercept_
print(f"【正弦角度】公式: y={k_s:.2f}×sinθ{b_s:+.2f} | 训练 R²={r2_s:.4f} RMSE={rmse_s:.1f} | 验证 R²={r2_s_val:.4f} RMSE={rmse_s_val:.1f}")
print("="*90)
# 5. 可视化:三模型拟合曲线+训练/验证散点 (完整保留,中文正常显示)
plt.rcParams['font.sans-serif']=['WenQuanYi Zen Hei']
plt.rcParams['axes.unicode_minus']=False
plt.figure(figsize=(12,7),dpi=120)
# 绘制训练/验证散点
plt.scatter(X_train, y_train, c='darkred', s=150, label='训练集(随机70%)', edgecolors='white', lw=2, zorder=5)
plt.scatter(X_val, y_val, c='navy', s=120, label='验证集(随机30%)', edgecolors='white', lw=2, zorder=5, alpha=0.8)
# 绘制三条拟合曲线
plt.plot(angle_range, model_linear.predict(angle_range), c='darkblue', lw=3, label=f'角度线性 R²={r2_l:.4f}', zorder=3)
plt.plot(angle_range, model_quad.predict(angle_range_poly), c='orange', lw=3, ls='--', label=f'角度二次 R²={r2_q:.4f}', zorder=4)
plt.plot(angle_range, model_sin.predict(angle_range_sin), c='forestgreen', lw=3, ls=':', label=f'正弦角度 R²={r2_s:.4f}【物理最优】', zorder=4)
# 图表样式配置
plt.xlabel('斜面倾角 (°)', fontsize=12)
plt.ylabel('小球滚动距离 (刻度)', fontsize=12)
plt.title('伽利略斜面实验 - 角度线性 vs 角度二次 vs 正弦角度回归', fontsize=14)
plt.legend(loc='upper left', fontsize=11)
plt.grid(alpha=0.3, linestyle='--')
plt.savefig('/home/project/伽利略_三模型_final.png', dpi=120, bbox_inches='tight')- 模型验证
【角度线性】公式: y=152.96×θ+727.62 | 训练 R²=0.9856 RMSE=615.3 | 验证 R²=0.9690 RMSE=928.3
【角度二次】公式: y=-1.28×θ²+264.06×θ-153.19 | 训练 R²=0.9997 RMSE=91.0 | 验证 R²=0.9993 RMSE=135.0
【正弦角度】公式: y=12988.80×sinθ+1.46 | 训练 R²=1.0000 RMSE=11.8 | 验证 R²=1.0000 RMSE=12.1
- 正弦函数曲线 比 二阶曲线
- 明显拟合得更好
-
$R^2$ 提升至≈0.9997,RMSE 大幅降低(从线性600+到90+)- 拟合曲线能勉强跟上真实数据
- 核心本质:二次项系数为负数
- 这个负的二次项刚好抵消了角度线性的高估偏差
- 是纯数学的曲线拟合,没有任何物理含义
- 致命缺陷:过拟合风险极高
- 这个二次模型只在 10°~80° 区间拟合好
- 一旦外推到80°以上(比如85°)
- 预测值会快速下降
- 违背物理规律
-
$R^2$ 达到1- RMSE 最小(从二阶线性的90+到11.8)
- 拟合精度碾压另外两个模型
- 残差特征
- 残差值极小
- 且完全随机分布在0附近,无任何趋势
- 这是回归模型的「黄金残差特征」
- 说明模型完美捕捉了数据的核心规律
- 物理层面
-
$\sin\theta$ 的系数≈$13100$ - 完美贴合伽利略的实验规律
- 系数有明确物理意义
-
- 虽然那时候
- 还没有人知道重力的物理意义
- 甚至没有物理学科
- 用正弦值($\sin\theta$)代替角度($\theta$)作为自变量的操作
- 不属于一种新的回归模型
- 还是线性回归
- 而是回归建模中特征工程
- 对自变量(角度)进行三角函数映射
- 生成新的特征(正弦值)
- 再将新特征代入回归模型(比如线性回归)
- 基于三角函数特征变换的线性回归
-
$\sin\theta$ 在$0^\circ\sim90^\circ$ 区间内是单调递增函数- 因此用
$\sin\theta$ 替换$\theta$ - 属于单调函数变换的一种特例
- 因此用
- 单调变换 不会改变 自变量和因变量的相关性趋势
- 只会将非线性的“角度-位移”关系
- 转化为线性的“$\sin\theta$-位移”关系
- 同时保留数据的核心规律
- 同类例子
- 对数变换($x\rightarrow\ln x$)
- 指数变换($x\rightarrow e^x$)
-
从物理规律出发
- 替换掉数学形式的自变量
- 用更贴合本质的物理特征建模
-
二阶回归 也是 特征工程 吗?
- 二阶回归 ≈ 【特征工程 + 线性回归】
- 其中 构造二次特征的环节,100% 是特征工程 ✔️
✔️ 原始状态(无特征工程)
你的原始输入特征:
X_train = [θ]只有1个特征 斜面倾角θ 此时跑线性回归 拟合公式:$\boldsymbol{y = w_1 \times θ + b}$ (角度线性回归)
- 「角度二阶回归」
- 本质 = 【多项式特征工程】 + 【普通线性回归】
- 「构造
$θ^2$ 这个特征」的行为- 纯纯的特征工程
- 对
$θ、θ^2$ 这两个特征做拟合
poly = PolynomialFeatures(degree=2, include_bias=False)特征工程后(二次特征构造)
poly.fit_transform(X_train)干的唯一一件事把 「1个原始特征 θ」
加工成 「2个新特征:$θ、θ^2$」
此时你的输入特征变成:
X_train_poly = [θ, θ²]
- 你后续跑的还是
LinearRegression()- 对加工后的2个特征做拟合
X_train_poly = poly.fit_transform(X_train) # X_train 是原始特征 [Angle]
拟合公式:$\boldsymbol{y = w_1 \times θ + w_2 \times θ^2 + b}$
(角度二阶回归)
- 二次回归 本质还是「线性回归」
- ❌ 误区:很多人以为「二次回归」是「非线性模型」
- ✅ 真相:模型是线性的,特征是非线性加工的
PolynomialFeatures(degree=2, include_bias=False) 特征工程 → 生成 θ、θ² 两列特征
+
LinearRegression() 线性回归模型
=
二元线性回归
俗称「角度二阶回归」
- 到底啥是线性?
- 线性回归的「线性」
- 指的是 对模型的参数(
$w_1,w_2,b$ )是线性的 - 而不是对「特征」线性
- 指的是 对模型的参数(
- 只要拟合公式是
$y = w_1x_1 + w_2x_2 + ... + w_nx_n + b$ 就是线性回归
-
$y = w_1θ + w_2θ² + b$ →$x_1=θ,x_2=θ²$ - 完全符合上面的公式!
-
这里面有两个自变量
$θ、θ^2$
-
$θ^2$ 在这里- 只是一个普通的「第二列特征」
- 和第一列特征$θ$平起平坐
- 模型会给它分配一个独立的权重系数$w_2$
- 和$w_1$没有任何关系
只要是两列特征 >> 就是二元线性回归
只要是一列特征 >> 就是一元线性回归
- 疑问:$θ^2$ 明明是「非线性的平方项」
- 为什么模型还是「线性回归」?
-
线性回归的「线性」
- 指的是【对模型的参数(系数w)线性】
- 不是对「特征x」线性!
-
机器学习里最核心的易混点
- 「线性」的对象是 系数
$w_1、w_2$ - 你的二元线性回归公式里
-
$w_1、w_2$ 都是一次方 - 没有出现
$w_1^2、w_1×w_2$ 这种形式 → - 满足「线性」定义
- 特征本身可以是 任意形式
- 特征可以是
$θ、θ^2、sinθ、θ^3、logθ$ 等等 - 哪怕是再复杂的非线性形式
- 只要特征是固定的数值列
- 模型对系数是线性的
- 就叫「线性回归」。
- 特征可以是
- 「线性」的对象是 系数
-
大白话总结:
你把
$θ^2$ 看作一个新的、独立的特征变量
比如令
$x_1=θ,x_2=θ^2$
那么你的公式就变成了:$\boldsymbol{y = w_1x_1 + w_2x_2 + b}$
这就是标准到不能再标准的二元线性回归公式!!!
- 一元线性回归算是特征工程吗?
- 一元线性回归 ≠ 特征工程
- 它本身是「机器学习模型」
- 但是分两种情况:
- 「用原始θ特征的一元线性回归」(Angle列直接拟合)
- 全程无任何特征工程 ✔️
- 「用sinθ特征的一元线性回归」(θ转正弦值后拟合)
- 有特征工程 ✔️
- 最关键的判定原则
特征工程的唯一判定标准:看「原始自变量X」是否被做过【加工/转换/改造】
✅ 有加工 → 有特征工程
✅ 无加工 → 无特征工程
✅ 和「是一元/二元/多元回归」无关、和「是线性/其他模型」无关!
- 这个原则是所有知识点的总纲
- 完美适配你代码里的所有场景
- 没有任何例外!
- 原始倾角
$\boldsymbol{θ}$ - 一元线性回归
- 无特征工程
# 特征:直接用 data[['Angle']] 原始值,θ就是θ,无任何改动
model_linear = LinearRegression().fit(X_train, y_train)- ✅ 核心属性:
- 特征输入:原始特征
$\boldsymbol{θ}$ (1列) - 回归类型:一元线性回归 (特征列数=1)
- 特征工程:❌ 无任何特征工程
- 拟合公式:$y = w_1 \times θ + b$
- 核心本质:把原始数据直接喂给模型
- 无任何数据加工
- 是最基础的模型调用方式
- 特征输入:原始特征
- 原始倾角 $\boldsymbol{θ} \stackrel{多项式衍生}{\rightarrow}
- [θ、θ^2]$ → 二元线性回归
- 【有特征工程 ✅ 单特征衍生多特征】
# 特征:θ 经过 PolynomialFeatures 加工成 θ、θ² 两个特征,特征被改造+数量增加
poly = PolynomialFeatures(degree=2, include_bias=False)
X_train_poly = poly.fit_transform(X_train)
model_quad = LinearRegression().fit(X_train_poly, y_train)- 核心属性
- 特征输入:加工特征
$\boldsymbol{θ、θ^2}$ (2列) - 回归类型:二元线性回归 (特征列数=2,列数增加)
- 特征工程:✅ 有特征工程
- 类型:【单特征多项式衍生】 (1列→2列,特征被改造+数量增加)
- 拟合公式:$y = w_1 \times θ + w_2 \times θ^2 + b$
- 核心本质:
-
PolynomialFeatures就是做这件事的专属工具,它的作用就是「把1个特征,衍生出多个多项式特征 - 这个场景里的「二次回归」,就是 二元线性回归 的「通俗叫法」
- 特征工程的核心是「把θ变成θ²」,特征被改造是核心,列数增加是副产品
-
- 特征输入:加工特征
- 原始倾角
$\boldsymbol{θ} \stackrel{函数变换}{\rightarrow} sinθ$ - 一元线性回归 【有特征工程 ✅ 单特征改造】
# 特征:θ 经过 np.sin(np.radians(θ)) 加工成 sinθ,θ≠sinθ,特征被改造
data['sin_theta'] = np.sin(np.radians(data['Angle']))
model_sin = LinearRegression().fit(X_train_sin, y_train)- 核心属性:
- 特征输入:加工特征
$\boldsymbol{sinθ}$ (1列) - 回归类型:一元线性回归 (特征列数=1,列数没变)
- 特征工程:✅ 有特征工程
- 类型:【单特征函数变换】 (1列→1列,特征被改造,数量不变)
- 拟合公式:$y = w_1 \times sinθ + b$
- 核心本质:特征被改造,但列数不变
- 用「物理规律驱动的特征工程」注入了真理
- 是最高效的特征工程之一
- 特征输入:加工特征
- 线性回归 是 机器学习模型
| 序号 | 特征处理方式 | 特征输入 | 特征列数 | 回归类型 | 是否有特征工程 | 特征工程类型 | 拟合公式 |
|---|---|---|---|---|---|---|---|
| 1 | 原始特征直接用 | 1列 | 一元线性 | ❌ 无 | 无 | ||
| 2 | 1列 | 一元线性 | ✅ 有 | 单特征函数变换 | |||
| 3 | 2列 | 二元线性 | ✅ 有 | 单特征多项式衍生 |
特征列数不变,只是把原始特征的数值用函数做变换 → 你的场景2($\theta \rightarrow sinθ$)是代表案例 其他例子:$\theta \rightarrow logθ$、$\theta \rightarrow \sqrtθ$、$\theta \rightarrow θ+1$ 等,都是特征工程。 ✅ 核心:改数值,不改列数
特征列数增加,从1个原始特征,生出多个相关的新特征 → 你的场景3($\theta \rightarrow [θ、θ^2]$)是代表案例 其他例子:$\theta \rightarrow [θ、θ^2、θ^3]$(3次多项式),都是特征工程。 ✅ 核心:既改数值,又增列数,
PolynomialFeatures是做这件事的「官方专用工具」
- 一元/二元/多元线性回归
- 都是模型
- 不是特征工程
- 判定「几元」只看特征列数
- 特征工程的判定
- 只看原始特征是否被加工
- 和列数无关
- 「二阶回归」= 二阶多项式特征工程 + 二元线性回归
- 本质是二元线性回归
- 核心是特征工程
- 你的3个模型
- 用的是同一个LinearRegression()模型
- **全部来自特征工程的好坏**
- 这就是特征工程的终极价值
- **数据决定上限,模型只是逼近上限**
- 特征工程相关术语中英对照表
| 中文术语 | 英文术语 | 核心说明(贴合伽利略斜面实验场景) |
|---|---|---|
| 特征工程 | Feature Engineering | 涵盖特征的构造、变换、选择等全流程,是提升模型效果的核心步骤 |
| 三角函数特征变换 | Trigonometric Feature Transformation | 将角度 |
| 单调函数特征变换 | Monotonic Function Feature Transformation |
|
| 物理意义驱动的特征重构 | Physical Meaning-Driven Feature Reconstruction | 基于 |
| 线性回归 | Linear Regression | 对变换后的 |
| 特征映射 | Feature Mapping | 从原始特征(角度 |
| 非线性关系线性化 | Nonlinear Relationship Linearization | 通过 |
| 特征选择 | Feature Selection | 选择 |
- 我们想要回到最初
- 回到伽利略 最初的数据集
- 我们下次再说👋
- 本文来自 oeasy Python 系统教程。
- 想完整、扎实学 Python,
- 搜索 oeasy 即可。





