斯坦福大学深度学习公开课cs231n学习笔记(3)最优化方法:梯度下降

本文深入探讨了图像分类任务中的最优化方法——随机梯度下降法。详细讲解了评分函数与损失函数的概念,以及如何通过梯度下降法对参数进行优化。文中还对比了几种不同的优化策略,并介绍了梯度的计算方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

继续cs231n课程的学习,最优化方法部分:随机梯度下降法

前面课程介绍了图像分类任务的两个关键部分:
(1)评分函数。将原始图像像素映射为分类评分值。
(2)损失函数。根据分类评分和训练集图像数据实际分类的一致性,来衡量参数集的好坏。损失函数有不同的实现方式(例如:Softmax或SVM)。线性函数的形式是: ,而SVM实现的公式是:

     (1)
对于图像数据xi,如果基于参数集W做出的分类预测与真实情况相一致,此时的损失值 就很低。但也可能出现预测与真实情况不一致的现象,此时需要参数最优化(Optimization),即寻找使得损失函数值最小化的参数集W的过程。

损失函数可视化

这里讨论的损失函数一般是定义在高维空间中的(例如,在CIFAR-10中一个线性分类器的权重矩阵大小是[10x3073],矩阵共有30730个参数)。此时要将参数可视化就很困难。可以在1个维度或者2个维度的方向上对高维空间进行切片。例如,随机生成一个权重矩阵W(该矩阵与高维空间中的一个点对应),然后沿着某个维度方向前进,并记录损失函数值的变化。例如,生成一个随机方向W1,然后沿着该方向,根据不同的a值来计算L(W+aW1)损失值,该过程将生成一个x轴是a值,y轴是损失函数值的图表。同样也可以用在两维度上,通过改变a,b的值计算损失值L(W+aW1+bW2),生成二维图像,其中a,b可以分别表示x轴和y轴,损失函数值用颜色表示。如下图所示:


上图中,左:a值与对应损失值的变化图表。中和右:两维度方向上的损失值切片图,蓝色部分表示低损失值,红色部分表示高损失值。注意损失函数的分段线性结构,这可以用数学解释。

              (2)

从公式可以看出,样本的损失值是以W为参数的max函数的累加和(零阈值是由于 max(0,-)函数)。W的每一行(即wj),有时候它前面是一个正号(当它是错误分类的时候),有时候是一个负号(当它是正确分类的时候)。假设有一个很简单的数据集,它包含3个一维度的点,数据集有3个类别。完整的无正则化SVM损失值计算如下:

  (3)

由于数据xi和权重wj都是数字。上面式子中包含wj的线性函数,且每一项都会与0比较,取两者的最大值。该过程用图表示为:


根据SVM的损失函数的碗状外形,可以猜测这是一个凸函数。但是将f 函数扩展到神经网络后,目标函数就不是凸函数了,图像会变成凹凸不平的复杂形状。 由于max操作,损失函数中存在一些不可导点(kinks),所以损失函数不可微,因为在这些点处的梯度是没有定义的,但是次梯度(subgradient)是存在且常被使用的。

最优化

损失函数值的大小可以评价参数 的好坏。所以最优化的目标是找到使得损失函数取值最小的 W。深度学习调优面对的不仅仅是凸函数,而是最优化一个神经网络,简单地使用凸函数最优化技巧并不能解决问题。

策略1:一个很差的idea:随机搜索

由于W的好坏很容易判断,所以可以考虑随机地赋值W,然后找到好的结果对应的W。过程如下:
# 假设X_train的每一列对应一个数据样本(例如3073 x 50000)
# 假设Y_train是数据样本的类别标签(大小为50000的一维数组)
# 假设函数L对损失函数进行评价

bestloss = float("inf") # Python assigns the highest possible float value
for num in xrange(1000):
  W = np.random.randn(10, 3073) * 0.0001  # generate random parameters
  loss = L(X_train, Y_train, W)           # get the loss over the entire training set
  if loss < bestloss:                     # keep track of the best solution
    bestloss = loss
    bestW = W
  print 'in attempt %d the loss was %f, best %f' % (num, loss, bestloss)

# 输出:
# in attempt 0 the loss was 9.401632, best 9.401632
# in attempt 1 the loss was 8.959668, best 8.959668
# in attempt 2 the loss was 9.044034, best 8.959668
# in attempt 3 the loss was 9.278948, best 8.959668
# in attempt 4 the loss was 8.857370, best 8.857370
# in attempt 5 the loss was 8.943151, best 8.857370
# in attempt 6 the loss was 8.605604, best 8.605604
# ... (trunctated: continues for 1000 lines)
从上面的代码可以发现,我们尝试了许多随机生成的权重W,其中有些W对应的损失值比另一些要小。我们可以把随机搜索中找到的最好的权重W取出,然后用到测试集中。
# 假设X_test大小为[3073 x 10000], Y_test为[10000 x 1]
scores = Wbest.dot(Xte_cols)  # 10 x 10000, the class scores for all test examples
# 找到每列中分值最大的索引(即预测的分类)
Yte_predict = np.argmax(scores, axis = 0)
# 计算准确率(正确预测的部分)
np.mean(Yte_predict == Yte)
# 返回 0.1555
测试集中表现最好的权重W的准确率是15.5%,而随机查找的准确率是10%,这个准确率对于一个不费脑的策略来说是不错的。
核心思想:迭代求精。要想找到最佳的权重 是一个非常困难甚至不可能的问题(特别是包含了整个复杂神经网络的权重 W),但是通过优化一组特定的权重使得结果稍好一点,这个问题是不那么困难的。即从一个随机的 开始,然后迭代地改进它,使得每次迭代结果都稍微好一点。
蒙眼徒步者的比喻:想象一个蒙着眼睛的徒步者,正走在一个山地地形上,而他的目标是要慢慢地走到山底。在CIFAR-10的例子中,这座山维度是30730(因为W是3073x10)。徒步者在山上踩的每一个点都对应着一个损失值,该损失值可以看成是该点的海拔。

策略2:局部随机搜索

第一个策略的方法是每走一步都尝试几个随机的方向,如果某个方向是向下的,就向该方向走一步。另一种方法:开始先选择一个随机的权重W,然后生成一个随机的扰动δW ,只有当W+δW对应的损失值变小时,才进行更新。具体代码如下:
W = np.random.randn(10, 3073) * 0.001 # generate random starting W
bestloss = float("inf")
for i in xrange(1000):
  step_size = 0.0001
  Wtry = W + np.random.randn(10, 3073) * step_size
  loss = L(Xtr_cols, Ytr, Wtry)
  if loss < bestloss:
    W = Wtry
    bestloss = loss
  print 'iter %d loss is %f' % (i, bestloss)
使用跟以前相同数量的损失函数评估(1000),该方法在测试集上的分类精度为21.4%。比策略1效果更好,但仍然浪费计算资源。

策略3:梯度跟随

前两个策略中,在权重空间中搜索能够降低损失函数L值的方向时,是随机的选择。但其实是可以直接计算出最好的方向来的,这个方向就是损失函数的梯度(gradient)。在蒙眼徒步者的比喻中,就是人感受脚下山体的倾斜程度,然后朝着最陡峭的下降方向下山。
在一维函数中,斜率表示函数在某一点处的瞬时变化率。而梯度是斜率的一般化表示,它不是一个数值,而是一个向量,梯度是由各个维度的斜率组成的向量(或者称为导数)。一维函数的求导公式如下:

         (4)
当函数有多个参数时,称为偏导数,此时梯度是由在各个维度上偏导数构成的向量。

梯度计算

计算梯度有两种方法:一是缓慢的近似方法(数值梯度法),实现相对简单。另一种(分析梯度法)计算快,结果精确,但是容易出错,并且需要微分。

利用有限差值来计算梯度

公式(4)给出了数值计算梯度的方法。下面的代码是输入为函数 f 、向量 x,计算出f梯度的通用函数实现,结果返回函数 在点x处的梯度:
def eval_numerical_gradient(f, x):
  """  
  一个求函数f在x处的数值梯度的简单实现
  - f是只有一个参数的函数
  - x是需要计算梯度的点
  """ 

  fx = f(x) # 在原点计算函数值
  grad = np.zeros(x.shape)
  h = 0.00001

  # 对x中所有的索引进行迭代
  it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
  while not it.finished:

    # 计算x+h处的函数值
    ix = it.multi_index
    old_value = x[ix]
    x[ix] = old_value + h  # 增加h
    fxh = f(x)             # 计算f(x + h)
    x[ix] = old_value      # 存到前一个值中 (非常重要)

    # 计算偏导数
    grad[ix] = (fxh - fx) / h  # 坡度
    it.iternext()              # 到下个维度

  return grad
代码中对所有维度进行迭代,其中每个维度上产生一个很小的变化量h,通过观察函数值的变化,计算函数在该维度上的偏导数。最后把所有的梯度结果存储在变量grad中。
在数学公式中,h的取值趋近于0,然而在实际中,h可以用一个很小的数值(比如例子中的1e-5)代替。理想情况是: 使用尽可能小的数值。实际中常使用中心差值公式(centered difference formula):
     (5)
可以使用上面的公式来计算任意函数在任意点处的梯度。计算CIFAR-10在某些随机点处损失函数的梯度过程如下:
# 要想使用上面的代码,需要一个只有一个参数的函数
# (参数就是权重)包含了X_train和Y_train
def CIFAR10_loss_fun(W):
  return L(X_train, Y_train, W)

W = np.random.rand(10, 3073) * 0.001               # 随机权重向量
df = eval_numerical_gradient(CIFAR10_loss_fun, W)  # 得到梯度
梯度告诉我们损失函数在每个维度上的斜率,并以此进行更新:
loss_original = CIFAR10_loss_fun(W)           # 初始损失值
print 'original loss: %f' % (loss_original, )

# 查看不同步长的效果
for step_size_log in [-10, -9, -8, -7, -6, -5,-4,-3,-2,-1]:
  step_size = 10 ** step_size_log
  W_new = W - step_size * df                 # 权重空间中的新位置
  loss_new = CIFAR10_loss_fun(W_new)
  print 'for step size %f new loss: %f' % (step_size, loss_new)

# 输出:
# original loss: 2.200718
# for step size 1.000000e-10 new loss: 2.200652
# for step size 1.000000e-09 new loss: 2.200057
# for step size 1.000000e-08 new loss: 2.194116
# for step size 1.000000e-07 new loss: 2.135493
# for step size 1.000000e-06 new loss: 1.647802
# for step size 1.000000e-05 new loss: 2.844355
# for step size 1.000000e-04 new loss: 25.558142
# for step size 1.000000e-03 new loss: 254.086573
# for step size 1.000000e-02 new loss: 2539.370888
# for step size 1.000000e-01 new loss: 25392.214036
在梯度的负方向上更新:在上面的代码中,为了计算新的权重W_new,需要朝着梯度 df 的负方向更新,损失函数值不断减小。
步长的影响:梯度表示出函数变化率最大的方向,但是没有说明在这个方向上应该走多远,此时需要设定步长,步长(也叫学习率)是神经网络训练中最重要(也是最头痛)的超参数之一。以蒙眼徒步者下山为例,我们可以感觉出地形在不同方向上的倾斜程度,但是应该跨出多大的步长是不确定的。如果谨慎地小步走,结果可能比较稳定,但是进展较慢。但是如果想尽快下山,就需要大步走,但结果可能不尽如人意。上面的代码中,在某些点如果步长过大,可能越过最低点,使得损失值更大了。
效率问题:计算数值梯度的复杂性是和参数的数量成线性相关的。在CIFAR-10中有30730个参数,此时损失函数每走一步需要计算30731次损失函数的梯度。而神经网络可能有上千万个参数,显然这个策略并不适合大规模的数据。

用微分分析计算梯度

上面方法,使用有限差值计算梯度是比较简单,但只是近似(因为h值选取了一个很小的值,但真正的梯度定义中h是趋向0的)。另一种计算梯度的方法是用微分分析,该方法使用公式计算梯度,速度快,但是容易出错。在实际操作时,常需要将微分分析的结果和数值梯度法的结果作比较,来检查结果的正确性,即梯度检查过程。
例如,SVM在某一点处的损失函数:
                        (6)
上式对 求微分,得到:
   (7)
其中1为指示函数,当括号里表达式成立时,值为1,反之值为0;式(7)只是对应正确分类W行向量的梯度,那些的W行向量的梯度是:
                      (8)

梯度下降

计算出损失函数的梯度后,程序迭代计算梯度并对参数进行更新,这个过程称为梯度下降,一般普通实现为:
# 普通的梯度下降

while True:
  weights_grad = evaluate_gradient(loss_fun, data, weights)
  weights += - step_size * weights_grad    # 进行梯度更新
上面的循环在所有的神经网络核心库中都存在,虽然也有其他最优化方法(如LBFGS),但目前为止,梯度下降法是在神经网络损失函数最优化时最常用的。
小批量梯度下降(Mini-batch gradient descent):在大规模的应用中(如ILSVRC挑战赛),训练数据可以达到百万级。如果仅仅是为了得到一个参数,计算整个训练集就太浪费了。一个常用的方法是计算训练集中的小批量(batches)数据。例如,在高水平的卷积神经网络中,小批量数据包含256个例子,调优过程如下:

# 普通的小批量数据梯度下降

while True:
  data_batch = sample_training_data(data, 256) # 256个数据
  weights_grad = evaluate_gradient(loss_fun, data_batch, weights)
  weights += - step_size * weights_grad # 参数更新
通过计算小批量数据的梯度可以快速地收敛,进行更频繁的参数更新。

总结


上图中,数据集中的(x,y)是给定的。权重W随机开始,并且可以改变。在前向传播时,评分函数计算出分类评分并存储在向量 中。损失函数包含两个部分:计算出分类评分 和实际标签y之间的差异的数据损失,以及关于权重的函数正则化损失。梯度下降过程中计算权重的梯度,然后用来实现参数的更新。

ps:南方的冬天真的好冷,没有暖气,怎一个冷字了得~~

参考:

http://cs231n.github.io/optimization-1/

https://zhuanlan.zhihu.com/p/21360434?refer=intelligentunit

https://zhuanlan.zhihu.com/p/21387326?refer=intelligentunit

<think>好的,用户现在想了解PINN(物理信息神经网络)的优化器选择。首先,我需要回忆一下PINN的基本概念和应用场景。PINN结合了物理方程和数据驱动的方法,通常用于解决偏微分方程等问题。优化器的选择对训练效果至关重要,所以用户可能是在实际应用中遇到了训练困难,或者想了解如何选择合适的优化器来提升模型性能。 接下来,我需要整理常见的优化器有哪些,以及它们在PINN中的表现。传统优化器如Adam、L-BFGS、SGD等都是常用的,但各有优缺点。比如Adam适合处理高维参数和非平稳目标函数,而L-BFGS在参数较少时收敛更快,但内存消耗大。此外,可能需要提到一些改进方法,如自适应学习率、学习率调度、混合优化策略,或者结合二阶优化方法。 然后,用户的真实需求可能不仅仅是列举优化器,而是希望了解如何根据具体问题选择优化器,以及相关的调参技巧。比如,PINN的训练通常面临梯度消失或爆炸、多目标平衡等问题,优化器的选择如何影响这些方面。可能需要给出一些实际案例,比如先用Adam进行粗调,再用L-BFGS微调,或者使用学习率衰减策略。 还要注意用户可能对优化器的原理不太熟悉,需要简要解释每个优化器的特点,但避免过于技术化。同时,结合PINN的特殊性,比如物理残差项和数据项的平衡,优化器需要处理不同部分的梯度,可能需要特定的策略,如权重分配或梯度归一化。 最后,确保回答结构清晰,分点说明,并且给出实际建议,帮助用户根据具体情况做出选择。需要检查是否有遗漏的重要优化器或最新研究,比如2023年是否有新的优化方法被提出用于PINN,如果有的话也应包含进去,但可能目前主流还是Adam和L-BFGS的组合。 总结步骤:首先介绍PINN及其优化挑战,然后分点讨论各优化器及其优缺点,接着提改进策略,最后给出选择建议。需要保持语言简洁,用例子说明,如使用混合优化器的案例,并确保LaTeX格式正确,如提到的数学公式用$$包围。</think>在物理信息神经网络(PINN)中,优化器的选择直接影响模型收敛速度和精度。以下从优化器类型、适用场景和改进策略分步说明: --- ### 1. **PINN的优化挑战** PINN需同时最小化**数据拟合项**和**物理方程残差项**,例如损失函数: $$ \mathcal{L} = \lambda_d \mathcal{L}_\text{data} + \lambda_p \mathcal{L}_\text{PDE} $$ 其中梯度可能因两项量级差异导致失衡,需优化器具备适应性。 --- ### 2. **常用优化器对比** | 优化器 | 优点 | 缺点 | 适用场景 | |-----------------|-----------------------------|-----------------------------|-------------------------| | **Adam** | 自适应学习率,对初始参数鲁棒 | 易陷入局部最优,后期震荡 | 高维参数、非平稳目标 | | **L-BFGS** | 二阶收敛快,精度高 | 内存消耗大,需平滑梯度 | 参数少(<1e4)、精细调优 | | **SGD+Momentum**| 避免局部最优,内存低 | 需手动调学习率,收敛慢 | 简单问题或预训练阶段 | --- ### 3. **优化器改进策略** #### (1) **混合优化(Hybrid Strategy)** - **粗调+微调**:先用Adam训练1e4~1e5步,再切换L-BFGS提升精度。 - **示例**: ```python # 伪代码示例 model.train() optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) for epoch in range(10000): # Adam阶段 loss = compute_pinn_loss() loss.backward() optimizer.step() optimizer = torch.optim.LBFGS(model.parameters()) # 切换L-BFGS for epoch in range(1000): def closure(): loss = compute_pinn_loss() loss.backward() return loss optimizer.step(closure) ``` #### (2) **自适应学习率** - 使用**学习率调度器**(如ReduceLROnPlateau)动态调整: $$ \eta_{t+1} = \gamma \cdot \eta_t \quad (\text{当损失停滞时}) $$ - **梯度裁剪**:限制梯度范数,防止物理方程项梯度爆炸。 #### (3) **损失加权平衡** - 动态调整$\lambda_d$和$\lambda_p$,例如: $$ \lambda_p^{(t)} = \frac{\|\nabla \mathcal{L}_\text{data}\|}{\|\nabla \mathcal{L}_\text{PDE}\| + \epsilon} $$ --- ### 4. **选择建议** - **简单PDE问题**:直接使用L-BFGS。 - **复杂/高维问题**:Adam起步,后期转L-BFGS。 - **多尺度/刚性方程**:结合自适应加权(如NTK权重法)和梯度归一化。 --- ### 5. **最新进展(2023)** - **二阶优化器**:如Hessian-Free方法,显式计算曲率信息。 - **物理自适应优化器**:如Phy-Taylor,融合方程特性到更新规则中。 通过合理选择优化器和改进策略,可显著提升PINN的训练效率和泛化能力。建议根据问题复杂度逐步尝试不同组合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值