梯度下降法——从下山直觉到线性回归实战(原理图解与手写实现)

张开发
2026/4/17 3:13:19 15 分钟阅读

分享文章

梯度下降法——从下山直觉到线性回归实战(原理图解与手写实现)
1. 从下山直觉理解梯度下降想象你正在一座浓雾弥漫的山上能见度只有脚下两三米。此刻你的目标是找到最快下山的路但视线受限看不到全局地形。这时候你会怎么做大多数人会本能地选择这样一个策略用脚试探周围的地面找到当前最陡的下坡方向然后迈出一小步。重复这个过程直到到达山脚。这个朴素的策略恰恰就是梯度下降法Gradient Descent的核心思想。在数学优化领域我们面临的正是类似的场景需要找到复杂函数的最低点最小值但无法直接看到全局信息。梯度下降就像那个在雾中谨慎下山的登山者通过局部信息不断调整前进方向。让我们把这个比喻转化为数学语言。假设山体的形状对应函数f(x)x²的图像import numpy as np import matplotlib.pyplot as plt x np.linspace(-5, 5, 100) y x**2 plt.plot(x, y) plt.xlabel(x) plt.ylabel(f(x)) plt.title(函数f(x)x²的图像) plt.grid() plt.show()在这个简单的例子中最低点显然是x0处。梯度下降的工作方式可以这样描述随机选择一个起点比如x-4计算当前点的梯度导数f(x)2x在x-4时为-8沿着负梯度方向即下山方向移动一步新位置当前位置-学习率×梯度重复直到收敛2. 梯度下降的数学原理2.1 梯度的本质含义梯度在数学上是一个向量指向函数值增长最快的方向。对于单变量函数f(x)梯度就是导数f(x)对于多变量函数f(x₁,x₂,...)梯度则是各个偏导数组成的向量∇f(∂f/∂x₁, ∂f/∂x₂,...)。关键性质梯度方向函数在该点上升最快的方向梯度大小函数在该方向的斜率变化率负梯度方向函数下降最快的方向举个例子对于函数f(x,y)x²y²from mpl_toolkits.mplot3d import Axes3D x np.linspace(-5, 5, 100) y np.linspace(-5, 5, 100) X, Y np.meshgrid(x, y) Z X**2 Y**2 fig plt.figure() ax fig.add_subplot(111, projection3d) ax.plot_surface(X, Y, Z) plt.title(函数f(x,y)x²y²的图像) plt.show()这个碗状函数的梯度∇f(2x,2y)在点(1,1)处梯度为(2,2)表示从该点出发沿(1,1)方向函数值增长最快。2.2 梯度下降的迭代公式基于上述理解梯度下降的更新规则可以表示为 θᵢ θᵢ₋₁ - α·∇f(θᵢ₋₁)其中θᵢ第i次迭代的参数值α学习率步长∇f(θᵢ₋₁)在θᵢ₋₁处的梯度对于线性回归问题我们的损失函数通常是均方误差MSE J(θ) 1/(2m) Σ(hθ(xⁱ)-yⁱ)² 其中hθ(x)θᵀx是预测函数。3. 线性回归中的梯度下降实现3.1 损失函数与梯度计算线性回归的目标是找到一组参数θ使得预测值hθ(x)与实际值y的均方误差最小。我们需要计算损失函数J(θ)对各个θⱼ的偏导数∂J(θ)/∂θⱼ 1/m Σ(hθ(xⁱ)-yⁱ)xⱼⁱ这个推导过程看似复杂但其实只需要基本的微积分链式法则。实际计算时我们通常使用向量化实现以提高效率 ∇J(θ) 1/m Xᵀ(Xθ - y)3.2 批量梯度下降的实现下面是一个完整的Python实现def compute_cost(X, y, theta): 计算损失函数值 m len(y) predictions X.dot(theta) cost (1/(2*m)) * np.sum(np.square(predictions-y)) return cost def gradient_descent(X, y, theta, alpha, iterations): 执行梯度下降 m len(y) cost_history np.zeros(iterations) for i in range(iterations): gradient (1/m) * X.T.dot(X.dot(theta) - y) theta theta - alpha * gradient cost_history[i] compute_cost(X, y, theta) return theta, cost_history使用示例# 生成示例数据 np.random.seed(42) X 2 * np.random.rand(100, 1) y 4 3 * X np.random.randn(100, 1) # 添加偏置项 X_b np.c_[np.ones((100, 1)), X] # 初始化参数 theta np.random.randn(2, 1) # 运行梯度下降 theta_final, cost_history gradient_descent(X_b, y, theta, alpha0.1, iterations1000) print(最终参数:, theta_final.ravel())4. 梯度下降的实战技巧4.1 学习率的选择学习率α是梯度下降最重要的超参数之一它决定了每一步更新的大小。选择不当会导致两种问题学习率太小收敛速度过慢需要大量迭代学习率太大可能越过最低点甚至发散实践中可以通过绘制损失函数值随迭代次数的变化曲线来判断学习率是否合适plt.plot(range(1000), cost_history) plt.xlabel(Iterations) plt.ylabel(Cost) plt.title(Cost Function over Iterations) plt.show()理想情况下曲线应该平稳下降并逐渐趋于平缓。如果出现震荡或上升说明学习率可能过大。4.2 特征缩放的重要性当不同特征的取值范围差异很大时如房屋面积和房间数量梯度下降会收敛得很慢。这是因为梯度在参数空间的不同方向上变化率差异很大。解决方法是对特征进行标准化from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_scaled scaler.fit_transform(X)4.3 收敛判断与停止条件在实际实现中我们通常不会固定迭代次数而是设置收敛条件梯度范数小于阈值损失函数变化小于阈值验证集性能不再提升改进后的梯度下降实现def gradient_descent_improved(X, y, theta, alpha, max_iter1000, tolerance1e-6): m len(y) prev_cost compute_cost(X, y, theta) for i in range(max_iter): gradient (1/m) * X.T.dot(X.dot(theta) - y) theta theta - alpha * gradient cost compute_cost(X, y, theta) if abs(prev_cost - cost) tolerance: break prev_cost cost return theta, i15. 梯度下降的变体与比较5.1 随机梯度下降(SGD)批量梯度下降每次迭代都要计算所有样本的梯度计算量大。随机梯度下降每次随机选择一个样本计算梯度def stochastic_gradient_descent(X, y, theta, alpha, epochs): m len(y) cost_history np.zeros(epochs) for epoch in range(epochs): for i in range(m): random_index np.random.randint(m) xi X[random_index:random_index1] yi y[random_index:random_index1] gradient xi.T.dot(xi.dot(theta) - yi) theta theta - alpha * gradient cost_history[epoch] compute_cost(X, y, theta) return theta, cost_history特点计算更快适合大数据集参数更新有噪声可能跳出局部极小值学习率需要逐渐减小以保证收敛5.2 小批量梯度下降折中方案每次使用一小批(batch)样本计算梯度def mini_batch_gradient_descent(X, y, theta, alpha, epochs, batch_size32): m len(y) cost_history np.zeros(epochs) for epoch in range(epochs): shuffled_indices np.random.permutation(m) X_shuffled X[shuffled_indices] y_shuffled y[shuffled_indices] for i in range(0, m, batch_size): xi X_shuffled[i:ibatch_size] yi y_shuffled[i:ibatch_size] gradient (1/batch_size) * xi.T.dot(xi.dot(theta) - yi) theta theta - alpha * gradient cost_history[epoch] compute_cost(X, y, theta) return theta, cost_history5.3 三种方法的比较方法每次迭代计算量收敛稳定性适用场景批量GD大全部样本稳定小数据集随机GD小单个样本不稳定大数据集在线学习小批量GD中小批量较稳定一般场景深度学习在实际项目中小批量梯度下降通常是最佳选择特别是在深度学习领域。批量大小(batch size)是另一个重要超参数常见值有32、64、128等。6. 梯度下降的常见问题与解决方案6.1 局部极小值与鞍点在高维空间中梯度下降可能被困在局部极小值某点附近都是更高点鞍点某些方向是极小值另一些方向是极大值解决方案使用带动量的优化器如Momentum、Adam多次随机初始化添加噪声如随机梯度下降6.2 梯度消失与爆炸在深层网络中梯度可能变得极小或极大梯度消失参数更新几乎停止梯度爆炸参数剧烈震荡解决方法合适的权重初始化如Xavier、He初始化梯度裁剪限制梯度最大值使用Batch Normalization6.3 学习率衰减策略固定学习率可能不是最优选择常见衰减策略阶梯衰减每N轮乘以衰减系数指数衰减α α₀·e⁻ᵏᵗ1/t衰减α α₀/(1kt)Python实现示例def learning_rate_schedule(t, initial_alpha0.1): # 指数衰减 return initial_alpha * np.exp(-0.1*t) # 在梯度下降循环中使用 alpha learning_rate_schedule(iteration)7. 进阶话题优化算法比较7.1 Momentum加速模拟物理中的动量概念积累之前的梯度方向 vₜ γvₜ₋₁ α∇J(θ) θ θ - vₜ其中γ通常取0.9左右。7.2 Adam优化器结合了Momentum和RMSProp的思想是当前最流行的优化器之一from tensorflow.keras.optimizers import Adam optimizer Adam(learning_rate0.001)特点自适应学习率对每个参数单独调整适合非凸优化问题7.3 优化器选择指南优化器优点缺点适用场景SGD简单理论保证收敛慢需调学习率凸优化Momentum加速收敛需调两个参数一般神经网络Adam自适应效果好内存占用稍大深度学习首选在实际项目中Adam通常是默认选择除非有特殊需求。对于凸优化问题如线性回归普通SGD或Momentum可能更合适。8. 完整项目实战房价预测让我们用一个完整的波士顿房价预测项目来实践梯度下降import pandas as pd from sklearn.datasets import load_boston from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 加载数据 boston load_boston() X boston.data y boston.target.reshape(-1,1) # 数据预处理 scaler StandardScaler() X_scaled scaler.fit_transform(X) X_b np.c_[np.ones((len(X),1)), X_scaled] # 划分训练测试集 X_train, X_test, y_train, y_test train_test_split(X_b, y, test_size0.2, random_state42) # 梯度下降训练 theta np.random.randn(X_train.shape[1],1) theta, cost_history gradient_descent(X_train, y_train, theta, alpha0.01, iterations1000) # 评估 train_pred X_train.dot(theta) test_pred X_test.dot(theta) def r2_score(y_true, y_pred): ss_res np.sum((y_true - y_pred)**2) ss_tot np.sum((y_true - np.mean(y_true))**2) return 1 - (ss_res/ss_tot) print(f训练集R²: {r2_score(y_train, train_pred):.3f}) print(f测试集R²: {r2_score(y_test, test_pred):.3f}) # 绘制学习曲线 plt.plot(cost_history) plt.xlabel(Iterations) plt.ylabel(MSE) plt.title(Learning Curve) plt.show()这个项目展示了完整的机器学习流程数据加载、预处理、模型训练、评估和可视化。通过调整学习率和迭代次数你可以观察模型性能的变化。

更多文章