Skip to content

Latest commit

 

History

History
549 lines (419 loc) · 20.8 KB

File metadata and controls

549 lines (419 loc) · 20.8 KB
Error in user YAML: (<unknown>): could not find expected ':' while scanning a simple key at line 3 column 1
---
- 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度
    • 仰角对应的边长

尝试编写代码

  • 对比三种拟合方式
    1. 线性
    2. 二次
    3. 带三角函数特征工程的 拟合
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
  • 正弦函数曲线 比 二阶曲线
    • 明显拟合得更好

图片描述

三种回归方式评估

✅ 结论1:角度二次回归 是「数学上的补救」,拟合效果提升但无物理意义

  • $R^2$ 提升至≈0.9997,RMSE 大幅降低(从线性600+到90+)
    • 拟合曲线能勉强跟上真实数据
  • 核心本质:二次项系数为负数
    • 这个负的二次项刚好抵消了角度线性的高估偏差
    • 纯数学的曲线拟合,没有任何物理含义
  • 致命缺陷:过拟合风险极高
    • 这个二次模型只在 10°~80° 区间拟合好
    • 一旦外推到80°以上(比如85°)
    • 预测值会快速下降
    • 违背物理规律

✅ 结论2:$\boldsymbol{\sin\theta}$ 线性回归 是「绝对最优解」,大角度下封神

  • $R^2$ 达到1
    • RMSE 最小(从二阶线性的90+到11.8)
    • 拟合精度碾压另外两个模型
  • 残差特征
    • 残差值极小
    • 完全随机分布在0附近,无任何趋势
    • 这是回归模型的「黄金残差特征」
    • 说明模型完美捕捉了数据的核心规律
  • 物理层面
    • $\sin\theta$ 的系数≈$13100$
    • 完美贴合伽利略的实验规律
    • 系数有明确物理意义
  • 虽然那时候
    • 还没有人知道重力的物理意义
    • 甚至没有物理学科

特征工程

  • 正弦值($\sin\theta$)代替角度($\theta$)作为自变量的操作
    • 不属于一种新的回归模型
    • 还是线性回归
    • 而是回归建模中特征工程

1. 三角函数特征变换

  • 对自变量(角度)进行三角函数映射
    • 生成新的特征(正弦值)
    • 再将新特征代入回归模型(比如线性回归)
    • 基于三角函数特征变换的线性回归

2. 单调函数特征变换(更广义)

  • $\sin\theta$$0^\circ\sim90^\circ$ 区间内是单调递增函数
    • 因此用 $\sin\theta$ 替换 $\theta$
    • 属于单调函数变换的一种特例
  • 单调变换 不会改变 自变量和因变量的相关性趋势
    • 只会将非线性的“角度-位移”关系
    • 转化为线性的“$\sin\theta$-位移”关系
    • 同时保留数据的核心规律
  • 同类例子
    • 对数变换($x\rightarrow\ln x$)
    • 指数变换($x\rightarrow e^x$)

3. 针对性的术语:物理意义驱动的特征重构

  • 从物理规律出发

    • 替换掉数学形式的自变量
    • 用更贴合本质的物理特征建模
  • 二阶回归 也是 特征工程 吗?

二阶回归

  • 二阶回归 ≈ 【特征工程 + 线性回归】
    • 其中 构造二次特征的环节,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」线性!
  • 机器学习里最核心的易混点

    1. 「线性」的对象是 系数 $w_1、w_2$
      • 你的二元线性回归公式里
      • $w_1、w_2$ 都是一次方
      • 没有出现 $w_1^2、w_1×w_2$ 这种形式 →
      • 满足「线性」定义
    2. 特征本身可以是 任意形式
      • 特征可以是 $θ、θ^2、sinθ、θ^3、logθ$ 等等
      • 哪怕是再复杂的非线性形式
      • 只要特征是固定的数值列
      • 模型对系数是线性的
      • 就叫「线性回归」。
  • 大白话总结:

你把 $θ^2$ 看作一个新的、独立的特征变量

比如令 $x_1=θ,x_2=θ^2$

那么你的公式就变成了:$\boldsymbol{y = w_1x_1 + w_2x_2 + b}$

这就是标准到不能再标准的二元线性回归公式!!!

  • 一元线性回归算是特征工程吗?

分别

  • 一元线性回归 ≠ 特征工程
    • 它本身是「机器学习模型」
    • 但是分两种情况:
    1. 「用原始θ特征的一元线性回归」(Angle列直接拟合)
      • 全程无任何特征工程 ✔️
    2. 「用sinθ特征的一元线性回归」(θ转正弦值后拟合)
      • 有特征工程 ✔️
  • 最关键的判定原则

特征工程的唯一判定标准:看「原始自变量X」是否被做过【加工/转换/改造】

✅ 有加工 → 有特征工程

✅ 无加工 → 无特征工程

✅ 和「是一元/二元/多元回归」无关、和「是线性/其他模型」无关!

  • 这个原则是所有知识点的总纲
    • 完美适配你代码里的所有场景
    • 没有任何例外!

✔️ 场景 1

  • 原始倾角 $\boldsymbol{θ}$
    • 一元线性回归
    • 无特征工程
# 特征:直接用 data[['Angle']] 原始值,θ就是θ,无任何改动
model_linear = LinearRegression().fit(X_train, y_train)
  • ✅ 核心属性:
    • 特征输入:原始特征 $\boldsymbol{θ}$ (1列)
    • 回归类型:一元线性回归 (特征列数=1)
    • 特征工程:❌ 无任何特征工程
    • 拟合公式:$y = w_1 \times θ + b$
    • 核心本质:把原始数据直接喂给模型
      • 无任何数据加工
      • 是最基础的模型调用方式

✔️ 场景 2

  • 原始倾角 $\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$
    • 核心本质:
      1. PolynomialFeatures 就是做这件事的专属工具,它的作用就是「把1个特征,衍生出多个多项式特征
      2. 这个场景里的「二次回归」,就是 二元线性回归 的「通俗叫法」
      3. 特征工程的核心是「把θ变成θ²」,特征被改造是核心,列数增加是副产品

✔️ 场景 3

  • 原始倾角 $\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 原始特征直接用 $\theta$ 1列 一元线性 ❌ 无 $y = w_1θ + b$
2 $\theta \to sinθ$ $sin\theta$ 1列 一元线性 ✅ 有 单特征函数变换 $y = w_1sinθ + b$
3 $\theta \to [θ,θ^2]$ $\theta、θ^2$ 2列 二元线性 ✅ 有 单特征多项式衍生 $y = w_1θ + w_2θ² + b$

✔️ 类型 A:单特征改造(1列 → 1列)

特征列数不变,只是把原始特征的数值用函数做变换 → 你的场景2($\theta \rightarrow sinθ$)是代表案例 其他例子:$\theta \rightarrow logθ$、$\theta \rightarrow \sqrtθ$、$\theta \rightarrow θ+1$ 等,都是特征工程。 ✅ 核心:改数值,不改列数

✔️ 类型 B:单特征衍生(1列 → N列)

特征列数增加,从1个原始特征,生出多个相关的新特征 → 你的场景3($\theta \rightarrow [θ、θ^2]$)是代表案例 其他例子:$\theta \rightarrow [θ、θ^2、θ^3]$(3次多项式),都是特征工程。 ✅ 核心:既改数值,又增列数PolynomialFeatures 是做这件事的「官方专用工具」

4个「结论」

✔️ 结论 1

  • 一元/二元/多元线性回归
    • 都是模型
    • 不是特征工程
    • 判定「几元」只看特征列数

✔️ 结论 2

  • 特征工程的判定
    • 只看原始特征是否被加工
    • 和列数无关

✔️ 结论 3

  • 「二阶回归」= 二阶多项式特征工程 + 二元线性回归
    • 本质是二元线性回归
    • 核心是特征工程

✔️ 结论 4

  • 你的3个模型
    • 用的是同一个LinearRegression()模型

所有效果差异

- **全部来自特征工程的好坏** 
- 这就是特征工程的终极价值
	- **数据决定上限,模型只是逼近上限**

总结

  • 特征工程相关术语中英对照表
中文术语 英文术语 核心说明(贴合伽利略斜面实验场景)
特征工程 Feature Engineering 涵盖特征的构造、变换、选择等全流程,是提升模型效果的核心步骤
三角函数特征变换 Trigonometric Feature Transformation 将角度 $\theta$ 转换为 $\sin\theta$ 等三角函数值作为新特征,是本场景的核心变换手段
单调函数特征变换 Monotonic Function Feature Transformation $\sin\theta$$0^\circ\sim90^\circ$ 内单调递增,该变换属于此类,可保留变量相关性趋势
物理意义驱动的特征重构 Physical Meaning-Driven Feature Reconstruction 基于 $a\propto\sin\theta$ 的物理规律,用 $\sin\theta$ 替换角度 $\theta$,让特征更贴合本质
线性回归 Linear Regression 对变换后的 $\sin\theta$ 特征与位移做拟合,是本场景的最终回归模型
特征映射 Feature Mapping 从原始特征(角度 $\theta$)到新特征($\sin\theta$)的映射过程的统称
非线性关系线性化 Nonlinear Relationship Linearization 通过 $\sin\theta$ 变换,将大角度下 $\theta$ 与位移的非线性关系转为线性关系
特征选择 Feature Selection 选择 $\sin\theta$ 而非 $\theta$ 作为自变量,属于基于物理意义的特征选择
  • 我们想要回到最初
    • 回到伽利略 最初的数据集
  • 我们下次再说👋

  • 本文来自 oeasy Python 系统教程。
  • 想完整、扎实学 Python,
  • 搜索 oeasy 即可。