损失函数

假设有数据集:,其中是图像,是类型标签。

数据集的平均损失函数为:

image-20240106170707270

Hinge Loss

SVM使用hinge loss铰链损失函数

image-20240106164218178

或者写成这样:

为什么要选择?

todo

squared hinge loss

和hingeloss单调性相同,但是大的值的影响可能会很大。

交叉熵损失函数

Softmax分类使用交叉熵损失函数,将分类问题变成概率问题:

  1. 每个的概率等于0-1的数
  2. 加起来等于1
  3. 保留之前的大小关系

解决方案

  1. 将所有数字变成正数:指数函数
  2. 求和加起来,每个除以和

构造新的损失函数:

  • 正确类别的概率越接近1,那么损失越小
  • 只关心分类正确的概率
  • 也叫做最大似然估计、交叉熵损失函数

正则化

目的:防止过拟合,增大模型的稳定性。

  • L:正则化强度
  • 数据损失:拟合我们的数据
  • 正则化损失:避免在数据集上做的过好
  • 正则化
    • 目的:提高模型的泛化能力
    • L1:
    • L2:
    • 弹性网络:综合考虑L1L2,
    • 比正则化更复杂的泛化技巧:dropout、batch normalization、stochastic depth、fractional pooling

多项式回归中的正则化

由于系数太高,拟合得太好但是不够泛化。可以尝试删除高次项。

image-20240106165916882

优化策略

随机找

最暴力的算法:在可能的n种权重W中找到最优秀的那种。

# assume X_train is the data where each column is an example (e.g. 3073 x 50,000)
# assume Y_train are the labels (e.g. 1D array of 50,000)
# assume the function L evaluates the loss function
 
bestloss = float("inf") # Python assigns the highest possible float value
for num in range(1000):
  W = np.random.randn(10, 3073) * 0.0001 # 生成随机参数
  loss = L(X_train, Y_train, W) # 计算训练集的损失
  if loss < bestloss: # 比大小,找最小
    bestloss = loss
    bestW = W
  print 'in attempt %d the loss was %f, best %f' % (num, loss, bestloss)
 
# prints:
# 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时,准确度约为15.5%,完全随机猜测类只能达到 10%,还是有点效果的。

核心思想:迭代求精。当然,事实证明我们可以做得更好。其核心思想是,找到最好的权重集W是一个非常困难甚至不可能的问题(特别是当W包含整个复杂神经网络的权重时),但是将一组特定的权重W细化为稍微更好的问题是显着的难度较小。换句话说,我们的方法是从一个随机的W开始,然后迭代地改进它,每次都稍微好一点。

我们的策略是从随机权重开始,并随着时间的推移迭代地改进它们,以获得更低的损失

蒙眼徒步旅行者的比喻。您可能会发现对前进有帮助的一个类比是,将自己想象成蒙着眼睛在丘陵地带徒步旅行,并试图到达底部。在 CIFAR-10 数据集中,山丘的维度为 30,730,因为W的维度为 10 x 3073。在山丘上的每个点,我们都会实现特定的损失(地形的高度)。

随机本地搜索

尝试向随机方向伸出一只脚,然后仅在脚下坡时才迈出一步。

W = np.random.randn(10, 3073) * 0.001 # generate random starting W
bestloss = float("inf")
for i in range(1000):
  step_size = 0.0001
  Wtry = W + np.random.randn(10, 3073) * step_size # 在W周围找一个更优秀的
  loss = L(Xtr_cols, Ytr, Wtry)
  if loss < bestloss:
    W = Wtry # 更新W
    bestloss = loss
  print 'iter %d loss is %f' % (i, bestloss)

准确率:21.4%,还行

梯度

最好的方向是可计算的,就是梯度方向。

梯度计算-数值

这是一个通用函数,它接受一个函数f、一个用于计算梯度的向量,并返回x处的梯度grad

def eval_numerical_gradient(f, x):
  """
  a naive implementation of numerical gradient of f at x
  - f should be a function that takes a single argument
  - x is the point (numpy array) to evaluate the gradient at
  """
 
  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:
 
    # evaluate function at 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趋向于零时,梯度被定义为极限,但实际上,使用非常小的值通常就足够了(例如示例中所示的 1e-5)。理想情况下,您希望使用不会导致数值问题的最小步长。此外,在实践中,使用中心差分公式计算数值梯度通常效果更好:

  • 沿负梯度方向更新。在上面的代码中,请注意,为了计算,W_new我们正在梯度的负方向上进行更新df,因为我们希望损失函数减少,而不是增加。

  • 步长的影响。梯度告诉我们函数增长率最陡的方向,但它并没有告诉我们应该沿着这个方向走多远。正如我们将在课程后面看到的,选择步长(也称为学习率)将成为训练神经网络时最重要(也是最令人头痛)的超参数设置之一。在蒙眼下山的比喻中,我们感觉到脚下的山坡向某个方向倾斜,但我们应该采取的步长是不确定的。如果我们小心地洗脚,我们可以期望取得一致但非常小的进步(这对应于小步长)。相反,我们可以选择迈出大的、自信的一步,试图更快地下降,但这可能不会有回报。正如您在上面的代码示例中所看到的,在某些时候,当我们“超越”时,迈出更大的一步会带来更高的损失。

  • 效率:复杂性与参数数量等比例。现代神经网络一般上千万个参数,算一步梯度就要ms级别。

梯度计算-解析

可以直接导出梯度的公式,不过更容易出现错误,一般需要结合数值梯度检查其准确性。

eg:SVM的损失函数

其损失函数的梯度就是:

简单来说,就是看有哪些类导致偏差过大,计算对应类的缩放就是梯度。

梯度下降

梯度下降是目前优化神经网络损失函数最常见最成熟的方法。

计算损失函数的梯度,重复评估梯度然后执行参数更新的过程称为**梯度下降**。

普通梯度下降

梯度下降

小批次梯度下降

在大规模应用中(例如 ILSVRC 挑战),训练数据可能有数百万个示例。因此,为了仅执行单个参数更新而计算整个训练集的完整损失函数似乎很浪费。解决这一挑战的一个非常常见的方法是计算批量训练数据的梯度。例如,在当前最先进的 ConvNet 中,典型的批次包含来自整个 120 万训练集的 256 个示例。然后使用该批处理执行参数更新:

# Vanilla Minibatch Gradient Descent
# 小批次梯度下降
while True:
  data_batch = sample_training_data(data, 256) # 采样256个
  weights_grad = evaluate_gradient(loss_fun, data_batch, weights)
  weights += - step_size * weights_grad # 更新参数

问题1:如果各个方向的导数差异比较大会发生什么?

抖动!会导致下降的速度变慢。

image-20240107162608040

问题2:如果损失函数有局部最小值或鞍点怎么办?

局部最小值:会在梯度为0的点上卡住。

image-20240107162828937

鞍点:在高维空间下不是问题

随机梯度下降

随机梯度下降

随机+动量

用于建模 Nesterov 加速梯度方法的微分方程:理论和见解(A Differential Equation for Modeling NAG Method)| 知乎