恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

从零手写线性回归:梯度下降原理与Python实现详解

  • 首页
  • 资讯中心
  • /
  • 从零手写线性回归:梯度下降原理与Python实现详解

相关资讯

华为数通HCIA ENSP实验三:VLAN 2026/10/19 2:33:09
【无线抓包实验1】使用WiFiGrab抓包获取隐藏WiFi名称 2026/10/19 2:33:09
C++命令行酒店点菜系统:无数据库轻量级业务闭环实现 2026/10/19 2:28:08

最新资讯

让Code Review自动化:基于CI的增量代码审查流水线实践
YOLO26涨点优化:RepViTBlock与C3k2二次创新融合方案
双狐地质建模软件:构造曲面缝合与T0时深转换实战指南
用Groovy脚本搞定Tomcat压测:从动态参数到JMX监控实战
开源代码审查工具 open-code-review:原理、部署与工程化实践
SELinux 不再关:从拒绝日志到策略配置的完整排错实战

今日推荐

OpenCowork 实测:支持本地文件、飞书机器人的 Windows AI 助手(只需配置 Token)
GPT-6 Sol 与 Luna 怎么选?Chat、Work、Codex 版本差异与 TaoToken 接入实测
openclaw.net 集成 ElBruno.MempalaceNet 记忆系统:把 MCP 配置改到 TaoToken 的完整实践

本周热门

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

从零手写线性回归:梯度下降原理与Python实现详解

发布时间:2026/10/19 2:33:09
从零手写线性回归:梯度下降原理与Python实现详解 1. 回归问题的底层逻辑与项目定位1.1 从房价预测说起线性回归到底在做什么线性回归训练从零开始这个标题听起来简单真正动手把每一步梯度算明白、把参数迭代到收敛才是理解整个机器学习流程的关键。我用一个最常见的场景切入假设你手里有一批二手房的成交记录每套房子只有一个特征——面积你要根据面积预测价格。数据长得很朴素横轴是面积纵轴是价格点状分布肉眼能看出一个大概向上的趋势。线性回归做的事情就是在这堆点里画一条直线让这条直线尽量贴合所有点然后新来的房子只要告诉我面积我就能在这条直线上读出预测价格。这条直线写成数学形式就是 y wx b。w是斜率决定面积每增加一平米价格涨多少b是截距代表面积为0时的基础价格。训练的目标就是找出一组能让预测误差尽量小的w和b。你可能觉得这太简单了中学就学过最小二乘法。但真正把它当作一个机器学习项目来做涉及数据准备、特征处理、损失函数设计、梯度下降迭代、收敛判断、结果可视化这一整条链路每一步都会踩到不同的坑。这篇博文就按这个流程完整走一遍不调现成的机器学习库手写训练过程。1.2 为什么非要“从零开始”现在用现成框架训练一个线性模型基本就是三行代码的事加载数据实例化模型调用fit。很多初学者这么跑完一遍模型是训练出来了但心里是懵的参数是怎么从随机值一步步变成最终值的学习率调大调小分别会发生什么为什么有时候loss变成NaN这些问题的答案框架不会告诉你只有亲手写过一遍梯度下降的人才会有体感。“从零开始”的意思是用纯Python加numpy把训练循环自己写出来。包括前向计算、损失计算、反向求梯度、参数更新每一个环节都亲手控制。这样做的价值不在于效率而在于把黑盒拆开。我见过不少转行做算法的朋友简历上写着熟悉多种模型被问到“梯度下降里梯度是怎么算出来的”就卡壳。手写一遍线性回归就是花一下午时间把这个卡壳点补上。它适合刚入门机器学习的同学也适合那些调框架调了很久但想补基础的人。整个项目敲下来你对“训练”这个词的理解会完全不一样。1.3 项目整体目标与效果预期这个项目的目标很明确构造一组带噪声的线性数据用自己写的梯度下降代码拟合出回归直线最终让训练得到的参数尽可能接近生成数据时用的真实参数。为了能直观看到效果我会用二维平面上的散点和拟合直线来展示结果同时画出损失函数随训练轮数下降的曲线。整个流程跑通后你会得到三条核心经验。第一理解了梯度下降中“梯度”到底是什么——它是损失函数对每个参数的偏导数是参数更新的方向和步幅依据。第二理解了学习率这个超参数对训练过程的实际影响同样的代码只改一个数字结果可能天差地别。第三掌握了排查训练异常的基本思路比如loss变成NaN、模型输出全零、收敛极慢这类典型问题知道该从哪里下手。这些能力是框架给不了你的也是从零训练一个模型的真正收获。2. 损失函数与梯度下降先把数学搞明白2.1 损失函数选型为什么是均方误差训练的本质是把“预测得有多差”量化成一个数字然后想办法让这个数字变小。这个量化的函数叫损失函数线性回归最常用的是均方误差简称MSE。公式长这样对每一个样本计算预测值y_pred与真实值y的差平方一下把所有样本的结果加起来求平均。MSE (1/m) * Σ(y_pred - y)^2m是样本数量。为什么选均方误差而不是别的方式两个原因一是数学性质好平方函数是凸函数整个损失函数对参数来说只有一个全局最低点梯度下降只要不迷失方向最终一定能走到这个最低点附近。二是它对大误差非常敏感预测偏差两倍的样本平方之后损失是偏差一倍的样本的四倍这会让模型更集中精力去修正那些差得很离谱的预测。这个特性既是优点也是缺点如果数据里有极端异常点MSE会把大量注意力花在拟合这个异常点上拖累整体效果。实际业务里如果发现模型被少数样本带偏就要考虑改用平均绝对误差或者Huber损失但在入门阶段MSE是最合适的起点梯度形式也最简洁。从数学上还有一个天然的连接点在误差服从正态分布的假设下对线性回归做极大似然估计推导出来的目标函数刚好就是MSE加上一个常数项。这意味着用MSE做损失函数本质上就是在做“误差符合正态分布”条件下的最优参数估计。理解了这一层你就明白为什么线性回归和最小二乘总是绑定在一起出现。2.2 梯度下降沿着最陡的方向下山损失函数L(w, b)是一个关于两个参数的曲面我们的目标是在这个曲面上找到最低点。梯度下降的思路可以类比成在大雾天走山路下山你只能感知脚下地面朝哪个方向倾斜最厉害就朝那个方向迈一步重复这个过程慢慢就下到山脚。这里的“倾斜最厉害的方向”就是梯度方向“迈一步的大小”就是学习率。具体到公式推导。损失函数对w求偏导用链式法则层层展开dL/dw (1/m) * Σ 2 * (y_pred - y) * x。这个式子的含义很直白每个样本都贡献了一个方向信号——预测值大于真实值时这个信号是正的告诉我们要减小w预测值小于真实值时信号是负的告诉我们要增大w。所有样本的信号平均之后就是w该移动的方向。b的梯度更简单把上式里的x去掉dL/db (1/m) * Σ 2 * (y_pred - y)。参数更新就是沿着梯度的反方向走w w - 学习率 * dL/dw。注意是反方向因为梯度指向损失增加最快的方向我们要去的是损失最小的地方。这里有一个初学者最容易绕晕的点为什么是减而不是加。想清楚“爬坡最快方向”和“下坡方向”的关系就不会错了。我训练时习惯把公式写在代码上面每写一步更新都对照一遍。2.3 学习率步子迈多大是个学问学习率可以说是这个项目里最敏感的一个数字。它决定参数每次更新的步长学习率太小梯度下降走得很慢几百轮迭代下来参数还在原地附近打转学习率太大每一步都迈过最低点损失不但不降反而可能越跳越高甚至直接变成NaN。我在调参时经常做一个对比实验同一份数据同样的初始化只改学习率。设成0.01时loss曲线是一条很缓的下降线1000轮还没完全收敛设成0.5时loss前几轮剧烈跳动然后直接爆掉变成inf。肉眼看到这种对比比背一万遍“学习率要适中”都有用。具体多少算合适没有固定答案和数据的数值范围、损失函数的形态都有关。实践中一个非常实用的策略是先尝试一組数量级1、0.1、0.01、0.001观察哪一个数量级下loss能稳定下降然后在这个数量级内部微调。3. 动手实现线性回归训练完整代码与运行过程3.1 数据准备生成一份带噪声的线性数据集真实世界的数据总是有噪声的没有人能用一条直线完全拟合所有观测点。训练之前先造一份模拟数据这样好处是心里有标准答案知道训练结果对不对。我用numpy的随机数生成器来造设定真实参数w_true 3.2b_true 5.5在0到10之间均匀取20个点作为面积特征然后每个点加上一个服从正态分布的随机误差噪声标准差设为1.5。import numpy as np # 固定随机种子保证结果可复现 np.random.seed(42) # 生成特征和真实值 x np.linspace(0, 10, 20) true_w 3.2 true_b 5.5 y_true true_w * x true_b # 添加高斯噪声模拟真实场景中的测量误差 noise np.random.normal(0, 1.5, sizex.shape) y y_true noise为什么要加噪声因为如果没有噪声数据点完全落在同一条直线上任何方法都能精准恢复参数训练过程的很多问题都不会暴露。加了噪声后模型的目标不是记住每个点而是学到一个能描述整体趋势的规律。这对理解“泛化”这个概念非常重要。3.2 特征归一化一个很多人跳过的重要步骤直接拿原始x去做训练不是不行但会遇到一个很实际的问题x的范围是0到10y的范围大概是5到40w和b的梯度尺度差异很大导致某些学习率下w更新正常、b更新过猛或者反过来。最好的做法是对特征做标准化让x的均值为0标准差为1。这样损失函数的曲面更接近圆形梯度各个方向的尺度也就更均衡。# 特征标准化 x_mean x.mean() x_std x.std() x_norm (x - x_mean) / x_std标准化之后x_norm的取值范围大致在-1.7到1.7之间和参数初始化在同一数量级。记住这个变换后面还需要把训练得到的参数还原回原始尺度不然你得到的是“标准化空间里”的w和b没法直接用来说“面积每增加一平米价格涨多少”。还原公式也不难原始y w_std * ((x - x_mean) / x_std) b_std展开整理后得到原始斜率w_orig w_std / x_std原始截距b_orig b_std - w_orig * x_mean。代码里实现这个还原逻辑时我习惯单独封装一个函数避免后面拿着归一化后的参数去做解释。3.3 训练循环手写梯度下降的核心代码初始化参数w和b为0然后进入训练循环。循环内部的每一步都对应我刚才讲的梯度下降流程前向计算预测值计算损失计算梯度更新参数。这里有两个细节值得留意一是梯度用的是整个训练集算出来的平均梯度这叫批量梯度下降数据量小的时候效果最稳二是损失计算把每个epoch的数值记录下来训练完画成曲线就能直观看到收敛过程。# 初始化参数 w 0.0 b 0.0 learning_rate 0.1 epochs 1000 # 记录训练过程 loss_history [] w_history [] b_history [] # 训练主循环 for epoch in range(epochs): # 前向计算 y_pred w * x_norm b # 计算损失 loss np.mean((y_pred - y) ** 2) loss_history.append(loss) w_history.append(w) b_history.append(b) # 计算梯度 dw 2 * np.mean((y_pred - y) * x_norm) db 2 * np.mean(y_pred - y) # 更新参数 w - learning_rate * dw b - learning_rate * db # 每100轮输出一次进度 if epoch % 100 0: print(fEpoch {epoch}, Loss {loss:.4f}, w {w:.4f}, b {b:.4f})我建议每100个epoch打印一次w和b而不是只打印loss。因为你很快就会在终端里看到参数如何一步步从0逼近真实值那种“眼见着数字自己长成正确答案”的过程是理解机器学习训练的第一个直观经验。我在本地跑这个循环时前100轮loss从一千多快速降到几十后面下降明显变慢这正是梯度下降的典型节奏离最低点远的时候梯度大、步子大靠近最低点的时候梯度变小、步子自动变小。3.4 结果验证与可视化训练到底学对了什么训练结束后先看loss是否已经降低到接近噪声本身的标准差水平。我们的噪声标准差是1.5理论上完美模型的MSE应该在2.25附近噪声方差因为哪怕参数完全正确预测值和真实值之间仍然会差一个随机噪声。如果最终loss降到接近这个水平说明模型已经把能做到的部分都做到了。接着把标准化空间里的参数还原成原始尺度拿还原后的w和b去和生成数据时用的真实参数对比。# 还原参数到原始尺度 w_orig w / x_std b_orig b - w_orig * x_mean print(f真实参数: w {true_w}, b {true_b}) print(f训练参数: w {w_orig:.4f}, b {b_orig:.4f})我自己跑的结果还原后的w大概是3.15b大概是5.4左右和真实值非常接近差异主要来自噪声干扰。这个结果说明整个训练链路是通的。为了更直观把原始数据散点、学到的回归直线、真实数据生成线画在一个图里能清楚看到训练线和真实线的重合程度。如果你也看到两条线几乎贴在一起那恭喜你的第一个手写神经网络已经训练成功了。4. 训练中的常见问题与排查技巧实录4.1 损失爆炸loss变成NaN怎么查第一次手写梯度下降的人十有八九会碰到loss变成NaN的情况。我印象最深的一次是我把学习率从0.1改成0.5输出前几轮loss在跳等到第五十轮直接打印出inf再往下就成了NaN。NaN一旦出现参数更新全部变成无效值整个训练就废了。排查思路按顺序来第一条检查学习率。这是最常见的原因学习率太大导致参数更新幅度过大跨越了损失函数的最低点甚至跑到函数值非常大的区域。解决办法很直接把学习率除以10再试。第二条检查特征是否有异常值或NaN。如果数据里有缺失值没有处理前向计算得到的预测值也包含NaN梯度自然就是NaN。第三条检查梯度计算有没有除以样本数。如果忘记除以m梯度数值会放大几十倍表现起来和学习率太大一模一样。我在排查这类问题时会先单独打印几个epoch的dw和db确认梯度的数量级是否合理。梯度是千分之一级别的时候学习率设0.1完全没问题如果梯度本身是几百那问题就出在梯度的计算而不是学习率上。这个先后顺序能省很多时间。4.2 收敛太慢训练半天loss还在高位晃悠和爆炸相反的另一个极端是收敛极其缓慢。loss每次只下降一点点跑了一万轮还没到理想水平。最直接的原因是学习率太小虽然训练过程很稳定但每一步都跟蜗牛爬一样。排查方法很简单同样打印梯度看一眼如果每个epoch的梯度已经很接近0但loss还很大说明模型卡在了高原区这时可以适当加大学习率让参数更快冲过平缓地带。还有一个容易被忽略的原因特征没有归一化。如果x的取值范围是0到10000y的取值范围是0到1那么w的梯度会被x放大很多倍而b的梯度只取决于误差本身两个参数的更新步长差距巨大。结果就是模型在某个方向上过度调整在另一个方向上几乎不动整体收敛速度非常慢。这就是为什么我在第3节专门做了标准化处理——不是为了炫技而是让梯度在各个方向上尽量均衡训练才能又快又稳。4.3 如何判断训练已经收敛别只看loss很多新手判断训练完成的标准是loss降到了某一个值以下然后就直接收工。这个做法不够严谨。我一般会同时看三个信号。第一是loss曲线的尾部是否基本水平如果随着epoch增加loss还在明显下降说明训练不充分如果曲线已经平了很长时间继续训练也不会带来多大提升。第二是参数是否还在显著变化打印最后几个epoch的w和b如果两位有效数字都不再变化说明参数已经稳定在最优解附近。第三是验证集上的表现如果训练loss很低但验证集loss很高可能是过拟合这时候需要回头检查数据划分和模型复杂度不过在线性回归这个场景里过拟合通常不太严重更多是欠拟合或者数据本身噪声太大。我训练时习惯把w_history和b_history也画出来三个子图并排放一排loss、w、b随epoch的变化一目了然。有一次我看到w还在一路飙升虽然loss下降得很平稳但这其实是非常危险的信号——说明w正在朝着某个极端值移动只是当前阶段恰好loss也在下降等越过某个临界点就会突然爆炸。提前看到这个趋势就能及时调整学习率而不是等到loss变成负数才反应过来。4.4 常见问题速查表现象可能原因排查方向处理办法loss变成NaN学习率过大参数爆炸发散打印前几步的loss和梯度降低学习率检查特征是否有缺失值loss下降极慢学习率过小或特征未归一化观察梯度数量级和loss斜率调大学习率对特征做标准化loss先降后升学习率偏大或数据含异常值对比训练loss与真实噪声方差调整学习率检查是否需要对异常值做处理w或b更新异常剧烈梯度计算错误未对样本数求平均手动验证前几个样本的梯度公式核对导数公式中的平均操作训练结果与真实值偏差大数据噪声过大或训练轮数不足对比loss是否降到噪声方差附近适当增加epoch收集更多数据5. 单特征通往多元回归这是你的下一步5.1 从标量到向量多特征的通用形式单特征线性回归理解透彻之后多元回归其实就是换个写法。当输入不再是单一的面积而是面积、楼层、房龄、周边配套评分等多个特征时参数w也变成了一组权重x变成了一个特征向量。如果再想在多个特征外保留截距b有一个很优雅的处理方式给每个样本的特征向量前面拼接一个1这样b就变成了这个固定特征对应的权重模型统一写成y_pred X W一个矩阵乘法搞定全部样本的前向计算。用矩阵形式写梯度更新会更简洁。假设X的形状是(m, n1)第0列全是1W是长度为n1的权重向量那么前向计算是pred X W误差是err pred - y梯度是dw (2/m) * X.T err。这一行矩阵运算就完成了之前循环里所有样本的梯度累加。我在实际使用中强烈建议一开始就按矩阵形式写代码因为当特征数量从1变成100甚至1000时逐样本循环的性能劣势会变得非常明显而向量化的写法基本不用改就能直接跑大特征规模的数据。5.2 扩展方向正则化、特征工程与模型家族多特征版本的损失函数可以更进一步比如加入L2正则项变成岭回归。正则项的作用是惩罚过大的权重防止模型把某些特征的权重学得很大、过度依赖单一信息这在特征多而样本少的时候非常重要。梯度更新时参数w的计算会额外减去一个lambda * w的项理解和实现起来都不难。此外还可以做多项式特征扩展把x^2、xy这类交叉项加进去让线性模型能够拟合非线性关系本质上是通过特征工程扩展了模型的表达能力。线性回归还是更复杂模型的基石。逻辑回归只是在线性输出外面套了一个sigmoid函数把它变成分类模型神经网络的第一层本质上也是线性变换加非线性激活。把这个从零训练的过程走一遍再去看一层的神经网络你会觉得非常熟悉因为前向计算、损失、反向传播、参数更新这些组件全都是一样的。这也是为什么我一直建议入门机器学习的人先别急着上深度学习框架花一个下午手写线性回归之后学任何模型都会更有底气。我在实际手写训练过程中最大的体会是那些框架帮你自动完成的细节恰恰是理解整个机器学习流程的钥匙。比如我最早手写这版代码时总是在梯度方向符号上犯迷糊后来养成一个习惯先手动算几步链式法则在草稿纸确认符号对了再敲到代码里这个习惯后来救过我很多次。最后分享一个实用的小技巧训练过程中每50个epoch把w、b、loss打一行你盯着参数一点点挪向正确答案的过程比直接调用fit然后看结果要有意思得多也更能在直觉上建立“训练到底在干什么”的真实体感。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号