当前位置: 首页 > news >正文

从机器学习的角度实现 excel 中趋势线:揭秘梯度下降过程

1. 引言:Excel 的“一键魔法”背后藏着什么智慧?

在 Excel 中,我们只需右键 → 添加趋势线,一条完美的直线就出现了。它快得像魔法,但魔法背后,是数学的严谨。

今天,我们不关心 Excel 内部用了什么算法可能是解析法),而是要问:

如果机器一开始‘什么都不知道’,它该怎么一步步‘学会’画出这条线?


2. 我们的任务:预测一个简单的线性关系

假设我们有一组数据,它们大致遵循 y = 2x + 1 的规律,但有一些随机噪声(模拟真实世界的测量误差)。

我们的目标:仅凭这10个数据点,让机器“学会”这个规律,找到最佳的 a(斜率)和 b(截距)


3. 准备数据:10 个“玩具”样本

我们用 Python 生成这10个数据点:

import numpy as np
import matplotlib.pyplot as plt# 设置随机种子,保证结果可复现
np.random.seed(42)# 生成 10 个 x 值,从 1 到 10
x = np.arange(1, 11)  # [1, 2, 3, ..., 10]# 生成 y 值:y = 2x + 1 + 噪声
true_a, true_b = 2, 1
noise = np.random.normal(0, 0.5, size=x.shape)  # 添加标准差为 0.5 的高斯噪声
y = true_a * x + true_b + noise# 查看数据
print("x:", x)
print("y:", np.round(y, 2))  # 保留两位小数

输出

x: [ 1  2  3  4  5  6  7  8  9 10]
y: [ 3.25  4.93  7.32  9.76 10.88 12.88 15.79 17.38 18.77 21.27]

关键点:真实规律是 y = 2x + 1,但数据有噪声,所以 y 值不完全精确。


4. 可视化:我们的目标是什么?

让我们画出这些数据点,并标出真实的线(y=2x+1):

plt.figure(figsize=(8, 6))
plt.scatter(x, y, color='blue', label='真实数据点', s=50)
# 画出真实规律的线
x_line = np.linspace(0, 11, 100)
y_line = true_a * x_line + true_b
plt.plot(x_line, y_line, 'g--', label=f'真实规律 y={true_a}x+{true_b}', linewidth=2)plt.xlabel('x')
plt.ylabel('y')
plt.title('我们的“学习”任务:根据10个数据点,找到最佳拟合线')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()


我们的任务就是:仅从这些蓝色散点,让机器找到一条最接近绿色虚线的直线


5. 模型定义:y_pred = a * x + b

我们假设模型的形式是:

y_pred = a * x + b
  • a:斜率(slope)
  • b:截距(intercept)
  • 初始时,我们不知道 a 和 b 是多少,可以先猜一个值,比如 a=0, b=0

6. 损失函数:我们“错”了多少?

我们需要一个“尺子”来衡量预测的好坏。用均方误差 (MSE)

MSE = (1/n) * Σ(y - y_pred)²

代码实现:

def compute_mse(y_true, y_pred):return np.mean((y_true - y_pred) ** 2)# 初始猜测:a=0, b=0
a, b = 0.0, 0.0
y_pred_initial = a * x + b
initial_loss = compute_mse(y, y_pred_initial)
print(f"初始损失 (a=0, b=0): {initial_loss:.3f}")
初始损失 (a=0, b=0): 182.431

解释:损失很大,因为 y_pred 全是 0,和真实 y 差很远。

7. 核心:梯度下降——如何“学习”?

现在,我们教模型如何“学习”:

  • 比喻:你在浓雾中的山坡上,想找到谷底(损失最小的地方)。你看不见路,但能感觉到脚下的坡度(梯度)。你每次都向坡度最陡的下坡方向走一步(更新参数),一步步接近谷底。

  • 数学计算(对 ab 求偏导):

    • ∂MSE/∂a = (2/n) * Σ((a*x + b - y) * x)
    • ∂MSE/∂b = (2/n) * Σ(a*x + b - y)
  • 更新规则

    • a = a - learning_rate * ∂MSE/∂a
    • b = b - learning_rate * ∂MSE/∂b

8. 动手实现:手动训练模型
# 超参数
learning_rate = 0.01  # 学习率,步长
epochs = 10000           # 训练轮数# 初始化参数
a, b = 0.0, 0.0# 记录历史,用于画图
loss_history = []
a_history = []
b_history = []for i in range(epochs):# 前向:计算预测值y_pred = a * x + b# 计算损失loss = compute_mse(y, y_pred)loss_history.append(loss)a_history.append(a)b_history.append(b)# 计算梯度n = len(x)da = (2 / n) * np.sum((y_pred - y) * x)  # 注意:y_pred - ydb = (2 / n) * np.sum(y_pred - y)# 更新参数a = a - learning_rate * dab = b - learning_rate * db# 打印进度if (i+1) % 1000 == 0:print(f"第 {i+1} 轮: a={a:.3f}, b={b:.3f}, 损失={loss:.3f}")print(f"\n训练完成!")
print(f"我们的模型找到: a ≈ {a:.3f}, b ≈ {b:.3f}")
print(f"真实规律是: a = {true_a}, b = {true_b}")

输出

第 100 轮: a=2.054, b=0.840, 损失=0.153
第 200 轮: a=2.021, b=1.070, 损失=0.124
第 300 轮: a=2.007, b=1.168, 损失=0.119
第 400 轮: a=2.001, b=1.211, 损失=0.118
第 500 轮: a=1.999, b=1.229, 损失=0.118
第 600 轮: a=1.997, b=1.237, 损失=0.118
第 700 轮: a=1.997, b=1.240, 损失=0.118
第 800 轮: a=1.997, b=1.242, 损失=0.118
第 900 轮: a=1.997, b=1.243, 损失=0.118
第 1000 轮: a=1.997, b=1.243, 损失=0.118训练完成!
我们的模型找到: a ≈ 1.997, b ≈ 1.243
真实规律是: a = 2, b = 1

看! 模型通过1000次迭代,a 从 0 逐渐接近 2,b 接近 1,损失不断下降。


9. 可视化学习过程
  • 损失下降曲线
plt.plot(loss_history)
plt.xlabel('训练轮数 (Epoch)')
plt.ylabel('损失 (MSE)')
plt.title('损失随训练过程下降')
plt.grid(True, alpha=0.3)
plt.show()

  • 参数变化过程
plt.plot(a_history, label='a (斜率)')
plt.plot(b_history, label='b (截距)')
plt.axhline(y=true_a, color='g', linestyle='--', label='真实 a')
plt.axhline(y=true_b, color='r', linestyle='--', label='真实 b')
plt.xlabel('训练轮数')
plt.ylabel('参数值')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

  • 最终拟合结果
plt.figure(figsize=(8, 6))
plt.scatter(x, y, color='blue', label='真实数据点', s=50)
plt.plot(x_line, y_line, 'g--', label=f'真实规律 y={true_a}x+{true_b}', linewidth=2)
plt.plot(x_line, a * x_line + b, 'r-', label=f'拟合线 y={a:.2f}x+{b:.2f}', linewidth=2)
plt.xlabel('x')
plt.ylabel('y')
plt.title('梯度下降拟合结果')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

  • excel拟合结果
10. 对比专业工具:Scikit-learn

让我们看看专业的机器学习库 scikit-learn 是怎么做的:

from sklearn.linear_model import LinearRegression# 注意:sklearn 需要 2D 输入
X = x.reshape(-1, 1)model = LinearRegression()
model.fit(X, y)print(f"Scikit-learn 结果: 斜率 a = {model.coef_[0]:.3f}, 截距 b = {model.intercept_:.3f}")

输出

Scikit-learn 结果: 斜率 a = 1.997, 截距 b = 1.243

完全一致! 我们手动实现的梯度下降,和 sklearn 的结果一样。


附. 深入理解:梯度下降 vs 解析法——殊途同归的两种智慧

你可能会问:“我们手动实现的梯度下降,和 scikit-learnLinearRegression,是同一种方法吗?”

答案是:不是。 它们在求解方式、数学基础和适用场景上有本质区别。但最终结果一致,是因为它们都在寻找同一个“最优解”。

下面,我们来揭开这个“黑箱”。


一、核心原理:迭代逼近 vs 数学解析
方法核心思想求解方式
手动梯度下降逐步“试错”,像盲人下山,一步步逼近最优解。迭代法:重复“计算梯度 → 更新参数”直到收敛。
scikit-learn LinearRegression直接用数学公式算出理论最优解,一步到位。解析法(闭式解):通过公式 w = (XᵀX)⁻¹Xᵀy 直接计算。
1. 手动梯度下降(迭代法)
  • 数学逻辑

    • 定义损失函数(MSE):L(a,b) = \frac{1}{n} \sum_{i=1}^{n} (y_i - (ax_i + b))^2

    • 计算梯度(偏导数):\frac{\partial L}{\partial a} = \frac{2}{n} \sum_{i=1}^{n} (ax_i + b - y_i) x_i\frac{\partial L}{\partial b} = \frac{2}{n} \sum_{i=1}^{n} (ax_i + b - y_i)

    • 更新参数:a = a - \eta \cdot \frac{\partial L}{\partial a}, \quad b = b - \eta \cdot \frac{\partial L}{\partial b}     (η 为学习率)

  • 特点

    • 近似解:需要足够迭代才能接近最优。

    • 适用于大数据:可分批处理(如随机梯度下降)。

    • 需调参:学习率、迭代次数等。

2. scikit-learn LinearRegression(解析法)
  • 数学逻辑

    • 将问题表示为矩阵形式:\mathbf{y} = \mathbf{X}\mathbf{w} + \boldsymbol{\epsilon}

    • 最优参数 ww 的闭式解为:\mathbf{w} = (\mathbf{X}^T \mathbf{X})^{-1} \mathbf{X}^T \mathbf{y}
      这个公式是通过对损失函数求导并令其为 0 推导出的理论最优解

  • 特点

    • 精确解:无需迭代,一步到位。

    • 计算复杂度高:矩阵求逆的复杂度为 O(n3)O(n3),特征数多时很慢。

    • 无超参数:直接计算,无需调学习率。


二、关键差异对比
维度梯度下降(迭代法)LinearRegression(解析法)
求解方式逐步迭代逼近直接公式计算
结果性质近似解(可无限接近)理论最优解
计算效率大数据更高效特征多时慢
超参数依赖需调学习率、迭代次数无超参数
适用场景大数据、高维(如深度学习)小数据、低维(如本例)
数值稳定性受学习率影响受多重共线性影响

三、为什么结果能一致?—— 殊途同归

尽管方法不同,但我们的手动实现和 sklearn 结果几乎一致,原因在于:

梯度下降的迭代过程,本质上是在数学上“收敛于”解析法的最优解。

就像用牛顿法求方程的根:虽然是迭代过程,但最终会无限接近理论上的精确解。

在本例中,由于数据量小、特征少,解析法高效且精确。而梯度下降通过300次迭代,也成功逼近了这个最优解。


四、通俗类比
  • 梯度下降:像盲人下山,通过感受坡度,一步步向下走,最终到达谷底,适用于复杂、大规模问题,是深度学习的基石。
  • 解析法:像使用 GPS,直接定位谷底坐标,一步到位,适用于简单、小规模问题,高效精确。

两种方法最终会到达同一个“山底”,只是路径和效率不同。


文章转载自:

http://kAM8ilFq.rsmtx.cn
http://TvRIShbI.rsmtx.cn
http://XUdSf5bA.rsmtx.cn
http://z1JlUF4D.rsmtx.cn
http://CMZsoEZX.rsmtx.cn
http://GGIVZEpS.rsmtx.cn
http://Q0tynJvv.rsmtx.cn
http://5cc79Gg7.rsmtx.cn
http://U1S46Ul6.rsmtx.cn
http://HzgwZ8kJ.rsmtx.cn
http://mzswBX7l.rsmtx.cn
http://cNGfcWCW.rsmtx.cn
http://6aA742Du.rsmtx.cn
http://lWDnZ4BP.rsmtx.cn
http://axpw2MAj.rsmtx.cn
http://MKjpBcYC.rsmtx.cn
http://VJbjTJhY.rsmtx.cn
http://WNqlN3u8.rsmtx.cn
http://XhLSra8C.rsmtx.cn
http://6fLJeX2c.rsmtx.cn
http://RdqtSg66.rsmtx.cn
http://0dMhQDfA.rsmtx.cn
http://zZPfpClZ.rsmtx.cn
http://dI1SMynO.rsmtx.cn
http://WtMK7dXx.rsmtx.cn
http://zruGa8BB.rsmtx.cn
http://h6fposN9.rsmtx.cn
http://EQu57lhX.rsmtx.cn
http://9GeNJ6JH.rsmtx.cn
http://6P5sR136.rsmtx.cn
http://www.dtcms.com/a/369853.html

相关文章:

  • Docker部署搜索引擎SearXNG
  • Hadoop(八)
  • 【Python自动化】 21.2 Pandas 读取 Excel 时的 dtype 参数完全指南
  • 【面板数据】各省制造业出口技术复杂度数据集(2010-2023年)
  • 使用 YAML 自动化 Azure DevOps 管道
  • 【数据库相关】TxSQL新增数据库节点步骤
  • 理想汽车智驾方案介绍 4 World model + 强化学习重建自动驾驶交互环境
  • 大语言模型预训练数据采集与清洗技术实践:从语料到知识库的全流程优化
  • 腾讯混元翻译模型Hunyuan-MT-7B开源,先前拿了30个冠军
  • MiniDrive:面向自动驾驶的更高效的视觉语言模型
  • 2025年渗透测试面试题总结-54(题目+回答)
  • 《Kubernetes 构建 MySQL MGR 集群实战教程》
  • 创建阿里云ECS实例操作(免费试用版)
  • 【数学建模】质量消光系数在烟幕遮蔽效能建模中的核心作用
  • 小孔成像原理
  • 操作系统基本概念.1
  • Jupyter Notebook与cpolar:构建跨地域数据科学协作平台
  • 山西移动九联UNT413HS-海思MV320-2+8G-原机全量备份包
  • AI热点周报(8.31~9.6): Qwen3‑Max‑Preview上线、GLM-4.5提供一键迁移、Gemini for Home,AI风向何在?
  • 【C++】C++11的可变参数模板、emplace接口、类的新功能
  • [特殊字符] 从零到一:打造你的VSCode圈复杂度分析插件
  • JVM如何排查OOM
  • Miniconda安装与VSCode搭建远程Python、Jupyter开发环境
  • 智能客户服务支持智能体
  • Gutenberg块编辑器:WordPress 2025高效内容开发指南
  • JUC、JVM八股补充
  • windows找不到gpedit.msc(本地组策略编辑器)
  • 【洛谷】队列相关经典算法题详解:模板队列、机器翻译、海港
  • 激光频率梳 3D 轮廓测量 - 油路板的凹槽深度和平面度测量
  • 24.线程概念和控制(一)