梯度下降能求一元二次函数极值,因其损失函数可导、光滑且凸(a>0)或凹(a

梯度下降为什么能求一元二次函数极值
因为一元二次函数 f(x) = ax² + bx + c(a ≠ 0)是凸(a > 0)或凹(a )的光滑函数,其导数 <code>f'(x) = 2ax + b 连续且单调,梯度下降沿负梯度方向迭代必然收敛到唯一极值点(顶点 x* = -b/(2a))。但注意:它不保证一步到位,也不自动判断收敛精度——这些全得你手写控制。
核心迭代逻辑怎么写(C++基础实现)
关键就三步:计算当前梯度 → 更新参数 → 判断是否停。别套模板,直接用 double 和裸循环最清晰:
double x = 0.0; // 初始猜测(可任意,比如 0、1、-5) double lr = 0.01; // 学习率,太大会震荡,太小收敛慢 double eps = 1e-6; // 收敛阈值(梯度绝对值 for (int i = 0; i a x + b; // f'(x),必须手算或用数值微分(不推荐) if (std::abs(grad)
-
a、b、c是函数系数,需提前定义为const double或传入 - 学习率
lr对结果影响极大:a=1, b=-4时,lr=0.5可能发散;lr=0.001要跑上千步 - 别用
while (abs(grad) > eps)——没迭代上限容易死循环
如何避免常见数值陷阱
手写意味着所有边界都要自己兜底,这几个坑高频又隐蔽:
- 学习率选错:先试
lr = 1.0 / (2 * std::abs(a))(理论最大稳定步长),再按需下调 - 初始值离极值太远 + 大学习率 →
x溢出(变成inf或nan),加一句if (!std::isfinite(x)) break; - 用
float代替double→ 低精度下grad长期卡在1e-7级别,永远不满足eps,直接卡死 - 忘记
#include <cmath></cmath>导致std::abs、std::isfinite编译失败
和解析解对比验证是否写对了
手写梯度下降不是为了替代公式,而是理解过程。每次跑完,务必和解析解比对:
double x_analytic = -b / (2 * a); double error = std::abs(x - x_analytic); std::cout <p>若 <code>error > 1e-4</code>,优先检查:学习率是否过大、是否漏了负号(<code>x = x - lr * grad</code> 不是 <code>+</code>)、<code>a</code> 是否为 0(退化成线性函数,无极值)。</p><p>真正麻烦的从来不是写几行迭代,而是当 <code>a</code> 接近 0、或者你把函数误写成 <code>ax³ + bx² + c</code> 时,梯度下降还在“认真”收敛到一个根本不存在的极值点——这时候,解析验证就是唯一的刹车片。</p>
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











