查看全课大纲(第 27 / 93 节)
  1. 1.计算机视觉概述
  2. 2.计算机视觉应用场景
  3. 3.困难与挑战
  4. 4.开源库介绍
  5. 5.图像显示与存储
  6. 6.CLAHE
  7. 7.腐蚀膨胀与开闭运算
  8. 8.滤波与边缘填充
  9. 9.各种滤波
  10. 10.图像金字塔
  11. 11.傅里叶变换
  12. 12.短时傅里叶变换与小波变换
  13. 13.颜色特征与颜色直方图
  14. 14.几何特征_边缘
  15. 15.几何特征-角点与斑点
  16. 16.局部特征-SIFT特征描述子
  17. 17.局部特征-SURF特征描述子
  18. 18.局部特征-ORB特征描述子
  19. 19.其他特征描述子
  20. 20.传统图像分割技术
  21. 21.GraphCut
  22. 22.GrabCut
  23. 23.人脸检测的传统方法
  24. 24.行人检测的传统方法HOG+SVM
  25. 25.行人检测DPM
  26. 26.人工神经网络的架构
  27. 27.目标函数与梯度下降
  28. 28.误差反向传播
  29. 29.可手算的神经网络
  30. 30.改进目标函数与克服梯度消散
  31. 31.神经网络之传统神经网络与深度网络的差异
  32. 32.各种梯度下降算法
  33. 33.如何选择合适的梯度下降算法
  34. 34.避免过适应
  35. 35.CNN的架构
  36. 36.CNN的误差反向传播_卷积层
  37. 37.CNN的误差反向传播_池化层
  38. 38.分类所用的数据集
  39. 39.Alexnet详解
  40. 40.VGG详解
  41. 41.GoogLeNet_v1
  42. 42.BatchNormalization
  43. 43.GoogLeNet_v3v4
  44. 44.ResNet
  45. 45.ResNeXt
  46. 46.CNN设计准则
  47. 47.图像检索与推土机距离
  48. 48.基于颜色相似性
  49. 49.基于形状相似性
  50. 50.基于纹理相似性
  51. 51.基于局部特征相似性
  52. 52.DL的二值哈希编码
  53. 53.高维空间中检索KDTree
  54. 54.高维空间中检索LSH
  55. 55.目标识别任务综述
  56. 56.RCNN
  57. 57.SPP-Net
  58. 58.Fast R-CNN
  59. 59.Faster R-CNN
  60. 60.R-CNN系列模型回顾
  61. 61.R-FCN的设计思路
  62. 62.R-FCN的实现
  63. 63.YOLOv1
  64. 64.YOLOv2_9000
  65. 65.YOLOv3
  66. 66.图像分割应用场景与数据集
  67. 67.上采样的实现方式
  68. 68.FCN网络架构的由来
  69. 69.FCN构建细节
  70. 70.DeepLabv1
  71. 71.CRF
  72. 72.DeepLabv2
  73. 73.DeepLabv3
  74. 74.特定的医学影像分割任务
  75. 75.U-Net
  76. 76.3D U-Net
  77. 77.V-Net
  78. 78.DenseNet
  79. 79.病理切片分析任务及数据集
  80. 80.病理切片分析
  81. 81.图说任务及其传统处理方法
  82. 82.深度语言模型与RNN
  83. 83.LSTM及其变种
  84. 84.NIC模型
  85. 85.SAT模型
  86. 86.图说的性能指标
  87. 87.判别模型与生成模型
  88. 88.KL散度与JS散度
  89. 89.VAE
  90. 90.GAN的基本原理
  91. 91.DCGAN与CGAN
  92. 92.Wasserstein GAN
  93. 93.SRGAN

目标函数与梯度下降

小象实战讲义 · 基于深度学习的计算机视觉

在上一节,我们学习了人工神经元和前馈神经网络的基本结构。一个网络由输入层、隐藏层和输出层构成,数据在其中单向流动。然而,仅仅有网络结构是不够的,我们还需要一种方法来“教会”这个网络完成特定的任务,比如正确分类一张图片。本节课,我们将引入神经网络学习的核心驱动力:目标函数,并学习如何通过梯度下降这一经典优化算法,来寻找使目标函数最小化的网络参数(权重)。这是理解后续所有深度学习模型训练过程的基础。

💡 核心导读

  • 目标函数的定义:理解如何通过期望输出与实际输出的差异来定义一个可量化的“损失”,即目标函数(或损失函数)。
  • 梯度下降的原理:学习为什么不能直接求解偏导数为零,以及梯度下降如何通过“迭代”的方式在复杂的参数空间中寻找最优解。
  • 学习率的重要性:掌握学习率这一超参数如何影响训练过程的收敛性、速度和稳定性。
  • 目标函数的可导性要求:了解为什么激活函数(如Sigmoid)需要处处可导,这与梯度下降算法的可行性息息相关。

从网络输出到目标函数

我们回顾一下前馈神经网络的结构。对于一个输入样本,网络会经过层层计算,最终在输出层产生一个实际输出向量 z = (z1, z2, ..., zc),其中 c 是输出层神经元的个数。同时,对于这个输入样本,我们有一个已知的、正确的期望输出(或称标签)向量 t = (t1, t2, ..., tc)。

一个训练良好的网络,其实际输出 z 应该尽可能地接近期望输出 t。如何量化这个“接近”的程度呢?最直观的方法之一就是计算它们之间的误差。一个经典且广泛使用的度量是均方误差(MSE)。

对于一个单个样本,其目标函数(损失)J 可以定义为所有输出分量误差平方和的一半:

J=12∑k=1c(tk−zk)2J = \frac{1}{2} \sum_{k=1}^{c} (t_k - z_k)^2

这里,k 遍历输出层的每一个神经元。

  • (t_k - z_k) 是第 k 个输出分量的误差。
  • 平方操作 (t_k - z_k)^2 有两个作用:第一,将所有误差转为非负值;第二,放大了较大误差的影响。
  • 前面的系数 1/2 是一个为了后续求导方便的约定,求导时平方项会产生一个因子 2,与 1/2 相乘后恰好为 1,简化了表达式。

注意:上面的公式是针对一个训练样本的损失。在实际训练中,我们通常使用一批(Batch)或多个样本。完整的损失函数应该是所有样本损失的平均或总和。例如,对于 N 个样本,损失函数可写为: Jtotal=1N∑n=1N[12∑k=1c(tk(n)−zk(n))2]J_{total} = \frac{1}{N} \sum_{n=1}^{N} \left[ \frac{1}{2} \sum_{k=1}^{c} (t_k^{(n)} - z_k^{(n)})^2 \right] 为了推导和讲解的清晰,我们通常先分析单个样本的情况,其原理可以自然地推广到批量训练。

我们的目标变得非常明确:调整神经网络中所有的连接权重(参数),使得这个目标函数 J 的值达到最小。当 J 最小时,意味着网络在整个训练集上的平均输出误差最小,此时网络对训练样本的拟合最好;但要注意,训练误差最小并不必然意味着对未见数据的预测(泛化)能力最强,这正是后续要讨论的过拟合问题。

德尔塔(Delta)学习规则:权重的迭代调整

那么,如何调整权重 w 来最小化 J 呢?一个朴素的想法是:如果某个权重增大导致误差增大,那就减小它;反之则增大它。这种思想体现在一个早期的监督学习规则——德尔塔学习规则中。

德尔塔规则给出了单个神经元(或感知机)权重调整量 Δw 的公式:

Δwij=η⋅(di−yi)⋅xj\Delta w_{ij} = \eta \cdot (d_i - y_i) \cdot x_j

让我们拆解这个公式:

  • Δw_{ij}: 从神经元 j 到神经元 i 的连接权重的改变量。
  • η (Eta): 学习率。这是一个非常重要的超参数,它控制了每次权重更新的“步长”。步长太大可能导致震荡甚至发散;步长太小则学习速度过慢。
  • (d_i - y_i): 误差信号。d_i 是神经元 i 的期望输出,y_i 是其实际输出。这个差值直接反映了当前输出的错误程度。
  • x_j: 输入信号。即来自前一层神经元 j 的输出(对于输入层就是原始输入)。这意味着权重的调整幅度也与输入的强度有关。

德尔塔规则的意义:它确立了权重调整的三个核心依据:1) 误差的大小和方向;2) 学习率控制的步长;3) 输入信号的强度。这为后续更复杂的梯度下降算法提供了直观的理解基础。

然而,德尔塔规则主要适用于单层网络或线性可分问题。对于具有多层非线性隐藏层的神经网络,我们需要一个更强大、更通用的优化方法。

梯度下降:在山坡上蒙眼下山

对于复杂的多层神经网络,其目标函数 J(w) 是所有权重参数 w 的高维非线性函数。直接对 J(w) 求偏导数并令其等于零来求解最优权重,在绝大多数情况下是行不通的。原因如下:

  1. 方程过于复杂:J(w) 的表达式涉及多层嵌套的非线性函数,其偏导数方程组可能没有解析解,或者求解极其困难。
  2. 存在多个极值点:非线性函数往往有多个局部极小值点。即使能解出偏导数为零的点,我们也无法区分哪个是全局最小值(最优解),哪个是局部最小值(次优解)。

因此,我们采用一种迭代优化的方法——梯度下降法。

我们可以把目标函数 J(w) 想象成一个多维空间中的“地形图”。假设只有两个权重参数 w1 和 w2,那么 J(w1, w2) 就构成了一个三维曲面。曲面上有的地方高(J 值大,误差大),有的地方低(J 值小,误差小)。我们的目标就是找到这个曲面上的最低点(谷底)。

梯度下降的过程,可以类比为一个被蒙住眼睛的人如何从山坡上走到谷底:

  1. 探查最陡的方向(计算梯度):人用脚感受脚下山坡哪个方向最陡峭。在数学上,函数在某一点最陡峭的上升方向就是该点的梯度(Gradient)。梯度是一个向量,其方向指向函数值增长最快的方向,其大小表示增长的速率。对于目标函数 J(w),其梯度记为 ∇J(w)。
  2. 向相反方向迈步(沿负梯度方向更新):既然要下山(最小化 J),我们就应该朝着与梯度相反的方向,即负梯度方向前进。这是函数值下降最快的方向。权重的更新公式为: w(new)=w(old)−η⋅∇J(w(old))w^{(new)} = w^{(old)} - \eta \cdot \nabla J(w^{(old)}) 其中 η 就是学习率。
  3. 决定步长(选择学习率 η):这一步是迈一大步还是一小步?这就是学习率的作用。
    • 学习率太大:好比巨人一步跨过一个山头,可能直接从当前山谷迈到了对面更高的山坡上,导致损失值不降反增,训练过程震荡甚至发散。
    • 学习率太小:好比蚂蚁下山,每一步都迈得极小,虽然方向正确,但走到谷底需要极其漫长的时间,训练速度过慢。 因此,选择一个合适的学习率是训练成功的关键之一,通常需要根据经验或通过一些自适应算法来确定。
  4. 重复直到“平地”(迭代收敛):重复步骤1-3。每次更新后,我们都在新的位置计算梯度,并继续沿负梯度方向移动。直到梯度变得非常小(接近零),意味着我们来到了一个“平地”(临界点),此时目标函数值不再显著下降,算法收敛。当然,这个比喻还有一个前提:下山路上不能有“悬崖”,即目标函数必须处处可导,否则“探坡度”这一步就无从做起。

这个过程可以用以下代码概念来示意(注意,这是为了说明流程的伪代码,并非真实训练循环):

# 梯度下降伪代码示意
初始化网络权重 w
设置学习率 η
设置最大迭代次数或收敛阈值

for epoch in range(最大迭代次数):
    # 前向传播:计算当前权重下的网络输出和损失 J
    predictions = forward_pass(w, training_data)
    loss = compute_loss(predictions, labels)

    # 反向传播:计算损失 J 关于所有权重 w 的梯度 ∇J(w)
    gradients = compute_gradients(w, training_data, labels) # 这涉及到后续要学的反向传播算法

    # 梯度下降更新:沿负梯度方向更新权重
    w = w - η * gradients

    # 检查是否收敛(例如,梯度范数小于某个阈值,或损失不再下降)
    if converged(gradients, loss):
        break

目标函数的可导性要求:不能有“悬崖”

在“蒙眼下山”的比喻中,我们最后提到“可不能有悬崖”。这对应到数学上,就是要求我们的目标函数 J(w) 必须是处处可导的,至少在我们搜索的路径上是可导的。

为什么?因为梯度下降法依赖于计算梯度 ∇J(w)。如果函数在某点不可导(比如有个“悬崖”或“尖刺”),那么在该点梯度就没有定义,算法就无法决定下一步该往哪里走。

在神经网络中,目标函数 J(w) 的可导性主要取决于激活函数。回顾神经元输出公式:输出 = f(加权和 + 偏置),其中 f 就是激活函数。J(w) 的计算链条中包含了这个激活函数。

因此,为了能够使用梯度下降法,我们选择的激活函数必须是可导的。这正是早期神经网络普遍使用 Sigmoid 函数 f(x) = 1 / (1 + e^{-x}) 或 tanh 函数的重要原因之一。它们在定义域内是平滑且处处可导的。

例如,Sigmoid 函数的导数有一个很好的性质:f'(x) = f(x) * (1 - f(x)),这在其后续的反向传播计算中非常方便。

思考:如果使用一个不可导的函数(比如阶跃函数)作为激活函数,梯度下降法就会失效。这也是为什么在深度学习时代,尽管 ReLU (Rectified Linear Unit) 函数在 x=0 处不可导,但在实践中我们通常将其在该点的导数定义为 0 或 1,从而使其能够应用于基于梯度的优化。

📝 动手练一练

  1. 概念辨析:梯度下降法中,为什么我们选择沿负梯度方向更新参数,而不是正梯度方向或其他随机方向?如果学习率设置得过大,可能会导致哪两种典型问题?
👉 点击查看参考答案
  • 选择负梯度方向的原因:梯度方向是函数值增长最快的方向。为了最小化目标函数(损失函数),我们自然需要朝函数值减少最快的方向移动,即梯度的反方向,也就是负梯度方向。这是在当前点附近下降最快的方向。
  • 学习率过大的问题:
    1. 震荡(Oscillation):参数更新步长太大,可能会在最优解附近来回跳跃,无法稳定收敛。
    2. 发散(Divergence):步长极大时,一次更新可能使损失值暴增,从当前的“山谷”跳到另一个更高的“山坡”,导致训练完全失败,损失值趋于无穷大。
  1. 手算理解:假设一个极其简单的“网络”,只有一个参数 w,其目标函数定义为 J(w) = (w - 5)^2。显然,当 w = 5 时,J 取得最小值 0。
    • a) 计算 J(w) 在 w = 0 处的梯度 ∇J(0)。
    • b) 设学习率 η = 0.1,使用梯度下降公式 w_new = w_old - η * ∇J(w_old),计算从初始点 w0 = 0 开始,经过一次更新后的 w1。
    • c) 计算 w1 处的函数值 J(w1),并与 J(0) 比较,是否下降了?
👉 点击查看参考答案
  • a) 首先求 J(w) 的导数(对于单变量就是梯度): J(w) = (w-5)^2 ∇J(w) = dJ/dw = 2*(w-5) 在 w=0 处:∇J(0) = 2*(0-5) = -10

  • b) 应用梯度下降更新: w1 = w0 - η * ∇J(w0) = 0 - 0.1 * (-10) = 1

  • c) 计算函数值: J(0) = (0-5)^2 = 25 J(1) = (1-5)^2 = 16 显然,J(1) = 16 < J(0) = 25,经过一次梯度下降更新,目标函数值确实下降了,参数 w 从 0 向最优值 5 靠近了一步。

本章小结

本节我们建立了神经网络学习的核心优化框架。我们首先定义了衡量网络性能的目标函数(损失函数),通常采用均方误差等形式。然后,我们指出了直接优化该函数的困难性,从而引入了梯度下降这一迭代优化算法。

梯度下降法通过计算目标函数关于参数的梯度,并沿负梯度方向以一定的学习率更新参数,逐步逼近函数的最小值点。学习率的选择至关重要,需要在收敛速度和稳定性之间取得平衡。同时,为了能够计算梯度,整个网络(特别是激活函数)构成的函数必须是可导的。

今天训练深度网络时,几乎不会再使用这种“朴素”的批量梯度下降,而是使用它的变体:小批量随机梯度下降(mini-batch SGD)、带动量的 SGD、以及 Adam 等自适应方法。但它们的更新骨架仍然是本节这条 w←w−η∇J(w)w \leftarrow w - \eta \nabla J(w),区别只在于“用多少样本估梯度”“要不要记住历史方向”“每个参数是否用不同的步长”。把这条骨架理解透,后续的优化器就只是在这上面做加法。

📋 行动清单

  • 理解目标函数的意义:能够说出为什么需要目标函数,以及均方误差是如何衡量网络输出误差的。
  • 掌握梯度下降的比喻与步骤:能够用自己的话复述“蒙眼下山”的比喻,并说明梯度、负梯度、学习率在其中的作用。
  • 认识学习率的影响:明确学习率过大或过小分别会导致什么问题,理解其作为关键超参数的地位。

—— 小象教研组