一、线性回归的从零开始实现

1.请详细分析以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 初始化参数
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)

// 定义模型
def linreg(X, w, b): #@save
"""线性回归模型"""
return torch.matmul(X, w) + b

// 定义损失函数
def squared_loss(y_hat, y): #@save
"""均方损失"""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2

// 定义优化算法
def sgd(params, lr, batch_size): #@save
"""小批量随机梯度下降"""
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()

// 训练
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss

for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y) # X和y的小批量损失
# 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
# 并以此计算关于[w,b]的梯度
l.sum().backward()
sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数
with torch.no_grad():
train_l = loss(net(features, w, b), labels)
print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')

这段代码实现了一个从零开始的线性回归模型训练,包括参数初始化、前向传播、损失计算、梯度计算、参数更新和训练循环。

📌 1. 参数初始化

1
2
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)   初始化权重
b = torch.zeros(1, requires_grad=True) 初始化偏置

  • w 初始化为服从正态分布 N(0, 0.01) 的随机值,形状 (2,1),代表 2 个输入特征对应的权重。
  • b 初始化为 0,是一个可训练的标量torch.zeros(1))。
  • requires_grad=True 使得 PyTorch 跟踪梯度,以便在后续反向传播时更新 wb

    📌 2. 线性回归模型

    1
    2
    3
    def linreg(X, w, b):  @save
    """线性回归模型"""
    return torch.matmul(X, w) + b

    数学表达式

  • torch.matmul(X, w): 计算矩阵 Xw矩阵乘法X 形状 (batch_size, 2)w 形状 (2,1),结果是 (batch_size,1))。
  • + b:广播机制(b 形状 (1,))自动扩展到 (batch_size,1),完成加法。

📌 3. 损失函数(均方误差 MSE)

1
2
3
def squared_loss(y_hat, y):  @save
"""均方损失"""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2

数学表达式

  • y.reshape(y_hat.shape): 确保 yy_hat 形状一致,避免形状不匹配错误。
  • ** 2 / 2: 计算平方误差除以 2,方便后续求导(对均方误差 MSE 取导数时,1/2 系数会被抵消)。

    📌 4. 随机梯度下降(SGD)优化算法

    1
    2
    3
    4
    5
    6
    def sgd(params, lr, batch_size):  @save
    """小批量随机梯度下降"""
    with torch.no_grad(): 关闭梯度计算(不影响梯度图)
    for param in params: 遍历所有参数(w, b)
    param -= lr * param.grad / batch_size 参数更新
    param.grad.zero_() 清空梯度,避免累积
  • 核心公式

    • param -= lr * param.grad / batch_size: 梯度下降更新公式lr 是学习率。
    • param.grad.zero_(): 清除梯度,防止梯度累积(PyTorch 默认梯度是累加的)。

    📌 5. 训练循环

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    lr = 0.03           学习率
    num_epochs = 3 训练轮次
    net = linreg 选择模型
    loss = squared_loss 选择损失函数

    for epoch in range(num_epochs): 训练多个周期
    for X, y in data_iter(batch_size, features, labels): 迭代小批量数据
    l = loss(net(X, w, b), y) 计算损失 l
    l.sum().backward() 计算损失对 w, b 的梯度
    sgd([w, b], lr, batch_size) 更新参数

    计算整个数据集的损失
    with torch.no_grad(): 关闭梯度计算,提高计算效率
    train_l = loss(net(features, w, b), labels)
    print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')

    训练过程详细解析

  1. 初始化超参数

    • 设定学习率 lr=0.03
    • 训练 num_epochs=3
    • 选定 linreg 作为模型,squared_loss 作为损失函数。
  2. 开始训练

    • 遍历 num_epochs 轮,每轮遍历整个数据集
    • 在每一轮中,用 data_iter 取出随机的小批量数据
      1
      for X, y in data_iter(batch_size, features, labels):
      这里 data_iter(batch_size, features, labels)一个生成器,每次返回 batch_size 个随机样本。
  3. 计算损失并反向传播

    1
    2
    l = loss(net(X, w, b), y)   计算损失
    l.sum().backward() 计算梯度
    • net(X, w, b): 计算预测值 y_hat
    • loss(y_hat, y): 计算损失 l,形状 (batch_size, 1)
    • l.sum().backward(): 对损失求梯度w.gradb.grad 被更新)。
  4. 更新参数

    1
    sgd([w, b], lr, batch_size)   进行梯度更新
    • sgd([w, b], lr, batch_size): 调用 SGD 更新 wb
  5. 计算整个数据集的损失

    1
    2
    3
    with torch.no_grad():
    train_l = loss(net(features, w, b), labels)
    print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
    • 关闭梯度计算(提高性能)。
    • 计算整个 features(全数据集)的损失 train_l
    • 计算平均损失 train_l.mean() 并打印。

    📌 6. 代码执行示例
    假设 batch_size = 10,数据集大小 1000,模型训练 3 轮:

    1
    2
    3
    epoch 1, loss 0.032411
    epoch 2, loss 0.000998
    epoch 3, loss 0.000092

    损失随着训练逐渐下降,说明模型正在收敛。

2.请依次回答以下问题

1
2
3
4
5
1.如何理解学习率,学习率是越大越好还是越小越好
2.如何理解requires_grad=True,什么是跟踪梯度,梯度又是什么
3.with torch.no_grad()作用是什么
4.param -= lr * param.grad / batch_size 这里为什么要除以batch_size,更新参数时不是直接减去梯度就可以吗?
5.l.sum().backward() 如何理解

1. 如何理解学习率,学习率是越大越好还是越小越好?
📌 学习率(learning rate, lr)的定义
学习率决定模型参数每次更新的步长大小。在梯度下降中,学习率 lr 控制参数 wb 的变化幅度:

  • η (lr) 是学习率,决定每次参数更新的幅度。
  • ∇L(θ) 是损失函数 L 对参数 θ(如 wb)的梯度。

📌 学习率是越大越好,还是越小越好?

  • 学习率太大:容易震荡发散,损失不降反增。例如:
    • lr=1.0:参数更新幅度过大,跳过最优解,模型无法收敛。
  • 学习率太小:收敛速度慢,训练时间长,可能陷入局部最优。例如:
    • lr=0.0001:参数更新太小,需要大量训练轮次才能收敛。
  • 合适的学习率:能在合理时间内收敛,找到较优的解。例如:
    • lr=0.01 ~ 0.1:通常是合适的学习率范围。

💡 如何选择学习率?

  1. 经验选择:通常 lr ∈ [0.001, 0.1]
  2. 学习率衰减:逐渐减小学习率,防止后期震荡。
  3. 学习率搜索:使用 Learning Rate FinderGrid Search 选取最佳 lr

2. 如何理解 requires_grad=True?什么是跟踪梯度,梯度又是什么?
📌 requires_grad=True 的作用
在 PyTorch 中,requires_grad=True 使得 Tensor 能够记录梯度,即让 PyTorch 追踪所有涉及该变量的计算,并自动计算梯度

1
2
w = torch.tensor([2.0, -3.4], requires_grad=True)   开启梯度计算
b = torch.tensor([4.2], requires_grad=True) 偏置项也需要计算梯度
  • 这样,在 wb 参与计算时,PyTorch 会构建计算图,并在反向传播时自动计算梯度。

📌 什么是梯度(Gradient)?
梯度是损失函数对参数的导数,表示损失函数对该参数的敏感程度

  • 如果 ∇L(w) > 0,说明 w 太大了,需要减少 w
  • 如果 ∇L(w) < 0,说明 w 太小了,需要增加 w

梯度下降公式:

PyTorch 自动计算梯度,无需手动求导。

3. with torch.no_grad() 作用是什么?
torch.no_grad() 用于禁止计算梯度,避免不必要的计算图构建,提高计算效率。例如:

1
2
with torch.no_grad():
y_pred = model(X) 预测时不需要计算梯度

应用场景

  • 模型推理(inference) 时,不需要计算梯度,加速计算。
  • 在训练中计算损失,但不影响梯度:
    1
    2
    with torch.no_grad():
    loss = loss_fn(model(X), y)
  • 防止内存占用过大,因为 PyTorch 计算梯度时会存储计算图,而 no_grad() 可以避免这个额外的存储开销。

4. param -= lr * param.grad / batch_size 这里为什么要除以 batch_size
📌 为什么要除以 batch_size
小批量随机梯度下降(Mini-batch SGD) 中,每个 batch_size 个样本计算一个 平均损失,求导后得到的是 所有样本的梯度之和,所以需要除以 batch_size 进行 归一化

1
param -= lr * param.grad / batch_size   归一化梯度

💡 数学推导
假设:

  • L_totalbatch 里所有样本的损失总和
  • PyTorch 默认 .backward() 计算的是 L_total 的梯度
  • 如果我们想要基于 L_mean(平均损失) 进行梯度下降其梯度是:
  • 所以在参数更新时,需要除以 batch_size

总结

  • 如果不除以 batch_size,梯度会随着 batch_size 增大而增大,导致训练不稳定。
  • 除以 batch_size,梯度与 batch_size 无关,更稳定。

5. l.sum().backward() 如何理解?
l.sum().backward() 用于计算损失对参数的梯度,并存储在 w.gradb.grad 里。

📌 代码分析

1
2
l = loss(net(X, w, b), y)   计算损失,形状是 (batch_size, 1)
l.sum().backward() 计算梯度

  • l 形状是 (batch_size, 1),每个样本都有一个损失值。
  • .sum() 计算所有样本损失的总和
  • .backward() 计算损失对 wb 的梯度

📌 为什么要 sum() 后再 backward()
PyTorch 默认 .backward() 计算标量(单个值)对参数的梯度,如果 l(batch_size, 1),直接 l.backward() 会报错:

1
RuntimeError: grad can be implicitly created only for scalar outputs

所以需要 .sum() 变成标量,再 backward() 计算梯度。

总结

  • l.sum().backward() 计算的是所有样本的损失总和的梯度
  • 如果我们使用 mean(),可以 l.mean().backward(),这样梯度会更稳定。

二、解析解的求解过程及正规方程的解释

1.解析解的求解过程

解析解是通过数学公式直接计算得到的模型参数,通常用于线性回归等线性模型。在线性回归中,我们通过最小化预测误差的平方和来求得最佳的模型参数。这种方法被称为最小二乘法。

具体步骤如下:

  1. 问题设定

    假设我们有一个线性回归问题,模型形式为:

    其中,( w ) 是参数向量,( b ) 是偏置项,( x ) 是特征向量,( y ) 是目标变量。

    为了简化计算,通常会将偏置 ( b ) 合并到参数向量 ( w ) 中。这是通过扩展特征向量 ( x ) 来实现的,将其增加一个元素1,形成新的特征向量 ([1, $x_1$, $x_2$, …, $x_n$])。这样,偏置 ( b ) 就成为新参数向量 ( w ) 的第一个元素。

  2. 数据准备

    组织训练数据,构建设计矩阵 ( X ) 和目标向量 ( y )。

    设我们有 ( m ) 个训练样本,每个样本的特征向量为 ( $x_i$ ),目标值为 ($ y_i$ ),则设计矩阵 ( X ) 的大小为 ( m $\times$ (n+1) )(包括扩展的1),目标向量 ( y ) 的大小为 ( m $\times$ 1 )。

  3. 应用正规方程

    正规方程是用于求解线性回归参数的闭式解,公式如下:

    • $ X^T $:这是设计矩阵 ( X ) 的转置,大小为 ( (n+1) $\times$ m )。
    • $X^T X $:这是一个 ( (n+1) $\times$ (n+1) ) 的矩阵,是 ( X ) 转置后与 ( X ) 相乘的结果。
    • $(X^TX)^{-1} $ :这是矩阵 ( $X^T$ X ) 的逆矩阵,计算逆矩阵是求解正规方程的关键步骤。
    • $X^T y $:这是一个 ( (n+1) $\times$ 1 ) 的向量,是 ( X ) 转置后与目标向量 ( y ) 相乘的结果。

    通过计算这些矩阵操作,可以直接得到参数向量 ( w )。

  4. 验证和应用

    • 验证:使用求得的参数 ( w ) 预测目标变量,评估模型的性能,如计算均方误差(MSE)等。
    • 应用:将模型用于新的、未见过的数据,进行预测。

2.正规方程的解释

正规方程(Normal Equation)是在线性回归中,通过最小二乘法直接求解参数的闭式解。其公式为:
$w = (X^T X)^{-1} X^T y$
以下是正规方程的详细解释:

  1. 矩阵运算

    • $( X^T X $):这是一个对称矩阵,代表了特征的协方差矩阵。它的大小为 ( (n+1) $\times$ (n+1) ),其中 ( n ) 是特征的数量,加1是为了包含偏置项。
    • $ (X^T X)^{-1} $:计算这个矩阵的逆矩阵是正规方程中的关键一步。如果 ( $X^TX$ ) 是满秩的(即行列式不为零),则该矩阵可逆。否则,可能需要使用正则化方法(如岭回归)来处理。
  2. 参数求解

    正规方程通过直接计算,给出参数 ( w ) 的最优解,这个解使得预测的平方误差之和最小。因此,正规方程为线性回归问题提供了一种高效的解法,尤其适用于特征数量较少的情况。

  3. 适用范围和限制

    • 适用范围:正规方程适用于线性回归模型,特别是在数据集较小、特征数量有限时,计算效率较高。
    • 限制:当特征数量 ( n ) 较大时,计算 $ X^T X $ 的逆矩阵可能变得计算量很大。此外,如果 $ X^T X $ 是奇异的或接近奇异,计算逆矩阵会非常不稳定,容易导致数值不稳定。
  4. 与梯度下降法的对比

    • 梯度下降法:是一种迭代优化算法,通过不断更新参数来逼近最优解。适用于大规模数据和高维特征。
    • 正规方程:直接求解,得到精确解,适用于小规模数据,但计算量在高维时会显著增加。

3.小结

解析解通过数学公式直接计算模型参数,在线性回归中主要利用正规方程来实现。正规方程的核心在于构建特征矩阵并计算其逆矩阵,以得到最优的参数解。虽然正规方程在计算高效性上有其局限性,但它提供了一种直接而精确的方法,适用于特定条件下的线性模型求解。

三、前向传播、反向传播和计算图

1.请详细解释前向传播和反向传播的含义

在神经网络和机器学习中,前向传播(Forward Propagation)反向传播(Back Propagation) 是两个核心概念,它们分别对应了数据流动和参数更新的两个阶段。下面我将详细解释这两个过程的含义及其在整个训练过程中的作用。

一、前向传播(Forward Propagation)
定义:
前向传播是指将输入数据通过神经网络逐层传递,经过各层的加权求和、加偏置、非线性激活等操作,最终计算出网络的输出预测结果的过程。

详细步骤:

  1. 输入数据传入:

    • 网络的第一层(输入层)接收原始数据。
    • 例如,对于一张图片,输入层会接收像素值。
  2. 每一层的计算:

    • 线性变换: 对于每个神经元,先进行加权求和: 其中 $w_i$ 为权重,$x_i$ 为输入,$b$ 为偏置。
    • 非线性激活: 将加权求和的结果 $z$ 传入激活函数(如 Sigmoid、ReLU、Tanh 等),得到该层神经元的输出 $a$:
  3. 层与层之间的传递:

    • 当前层的输出 $a$ 会成为下一层的输入,直到到达输出层。
  4. 计算损失(Loss):

    • 最终输出层得到预测值后,通过损失函数(如均方误差、交叉熵等)将预测值与实际标签进行比较,计算出损失值。
    • 这个损失值反映了网络预测的准确性,是后续反向传播更新参数的依据。

意义:
前向传播的主要作用是把输入数据映射到输出空间,并计算出预测结果和损失,从而为模型的学习提供反馈信息。

二、反向传播(Back Propagation)

定义:
反向传播是一种通过链式法则计算损失函数相对于每个参数(权重和偏置)的梯度的方法。利用这些梯度,结合优化算法(如梯度下降),对网络参数进行更新,以期降低整体损失。

详细步骤:

  1. 从输出层开始计算误差:

    • 根据损失函数,先计算输出层误差,即预测值与真实值之间的差异。
  2. 利用链式法则逐层传递误差:

    • 反向传播的核心在于利用链式法则,将输出层的误差向前传播到每一层,计算出每个参数对最终损失的贡献。
    • 对于某一层的权重 $w$ 来说,其梯度可以表示为: 其中 $L$ 为损失函数,$z$ 为加权求和的结果。
  3. 计算每层的梯度:

    • 通过逐层计算,可以得到每个神经元、每个连接的梯度值。这些梯度告诉我们,当前参数增加或减少一点点,如何影响最终的损失。
  4. 更新参数:

    • 得到所有梯度之后,利用优化算法(如随机梯度下降、Adam 等),按照梯度下降的方向对参数进行调整: 其中 $\eta$ 是学习率,控制每次更新的步长。

意义:
反向传播是神经网络学习的核心机制,通过有效地计算梯度并更新参数,模型能够逐步“学习”到数据中的模式,从而提高预测准确度。

三、总结和联系

  • 前向传播:

    • 负责将输入数据映射为输出预测。
    • 计算过程中涉及权重、偏置和激活函数。
    • 计算出的损失值为模型学习提供方向。
  • 反向传播:

    • 利用链式法则从输出层开始向前计算每个参数的梯度。
    • 通过梯度下降等方法更新模型参数,以减小损失。
    • 实现了模型参数的“自动调整”过程,使得模型不断改进。

二者相辅相成,前向传播提供了预测和损失,而反向传播则根据损失反馈对网络进行调整。这一循环过程不断重复,直至损失收敛到较低水平,从而训练出一个性能良好的模型。

这种机制使得复杂的神经网络能够通过大量数据进行自我优化,也奠定了现代深度学习技术的基础。

2.请详细解释反向传播时的链式法则,如何理解这里的z为加权求和的结果

在反向传播过程中,我们的目标是计算损失函数对每个参数(如权重和偏置)的梯度。由于神经网络的输出往往是多个简单函数组合而成的复合函数,因此直接求导可能非常复杂。这时就需要用到链式法则,它帮助我们将复杂的求导过程拆分成一系列简单的部分。

  • 链式法则的基本原理

假设有两个函数的复合函数:

则复合函数可以写为:

根据链式法则,其导数为:

这意味着我们可以分别计算 $f$ 对 $z$ 的导数和 $g$ 对 $x$ 的导数,然后将它们相乘,从而得到 $y$ 对 $x$ 的整体导数。

  • 在反向传播中的应用

在神经网络中,通常每个神经元的计算可以分为两个主要部分:

  1. 加权求和(线性变换):
    每个神经元接收输入 $x$,通过权重 $w$ 和偏置 $b$ 进行加权求和:

    这里的 $z$ 就是神经元的加权求和结果。

  2. 激活函数(非线性变换):
    将加权求和结果 $z$ 传入激活函数 $f$ 得到输出 $a$:

假设我们有一个损失函数 $L$(比如均方误差或交叉熵),它是最终输出 $a$ 与真实标签之间的差异。我们希望计算损失 $L$ 对权重 $w$ 的梯度,即 $\frac{\partial L}{\partial w}$。

根据链式法则,我们可以将这个求导过程分解为三个部分:

具体解释如下:

  • $\frac{\partial L}{\partial a}$:
    这是损失函数 $L$ 对神经元输出 $a$ 的梯度,表示输出 $a$ 的微小变化对整体损失的影响。

  • $\frac{\partial a}{\partial z}$:
    这是激活函数 $f$ 的导数,也称为激活函数梯度,表示加权求和结果 $z$ 变化时,输出 $a$ 如何随之变化。

  • $\frac{\partial z}{\partial w}$:
    由于 $z = \sum_{i}w_i x_i + b$,对 $w$ 求导时,其他项(包括偏置 $b$)均为常数,因此:

    这反映了输入 $x$ 在加权求和中的贡献。

将这些部分相乘,就能得到每个权重 $w$ 对最终损失 $L$ 的影响,也就是:

关于 $z$——加权求和的理解

在这个过程中,$z$ 是加权求和的结果,它是神经元计算的一个关键中间变量,作用有以下几点:

  1. 中间变量:
    $z$ 将输入数据 $x$ 与权重 $w$ 以及偏置 $b$ 进行线性组合,是激活函数 $f$ 的输入。可以看作是对输入特征进行初步“融合”。

  2. 梯度传播的桥梁:
    在反向传播时,梯度需要从损失函数逐层“传回”到每个参数。由于激活函数是直接作用在 $z$ 上,我们需要先计算 $f’(z)$,然后借助 $z$ 和 $w$ 的关系(例如 $\frac{\partial z}{\partial w}=x$)将梯度传递到权重上。

  3. 非线性引入点:
    如果没有 $z$ 这个中间变量,整个神经网络将只进行线性变换。通过在 $z$ 后面接上激活函数 $f$,神经网络引入了非线性因素,使得网络可以学习更加复杂的映射。

总结

  • 链式法则在反向传播中用于将复合函数的求导分解为多个部分。对于每个神经元,我们通过计算:

    从而逐层计算每个参数对最终损失的贡献。

  • $z$ 作为加权求和的结果,不仅是神经元内部计算的一个中间步骤,更是激活函数作用的“桥梁”。它将线性部分(加权求和)和非线性部分(激活函数)连接起来,使得梯度能够通过链式法则有效地从输出层传回每一层的参数。

这种机制使得神经网络可以逐层更新参数,从而不断降低损失,实现学习与优化。

四、信息熵、相对熵、交叉熵

1.KL 散度(相对熵)的概念

KL 散度(Kullback–Leibler Divergence),又称为相对熵(Relative Entropy),用来衡量两个概率分布 $P$ 和 $Q$ 之间的“差异”或“不一致程度”。在离散情形下,KL 散度定义为:

其中,$\log$ 可以取自然对数(信息论中有时也取对数底 2,视具体场景而定)。若 $P$ 和 $Q$ 越接近,则该值越小;当 $P = Q$ 时,$D{\mathrm{KL}}(P \,|\, Q) = 0$。
需要注意的是,KL 散度并不是一个对称度量,即 $D
{\mathrm{KL}}(P \,|\, Q)$ 一般不等于 $D_{\mathrm{KL}}(Q \,|\, P)$,也不满足三角不等式,因此它并不是严格意义上的“距离”。

2.KL 散度与熵、交叉熵的关系

  1. 熵(Entropy)

    对于单个分布 $P$,其熵定义为

  2. 交叉熵(Cross Entropy)

    对于分布 $P$ 和 $Q$,它们之间的交叉熵定义为

  3. KL 散度与熵、交叉熵的关系
    将 KL 散度的定义展开:

    将上式中的第一项写成 $-H(P)$,第二项写成 $-H(P, Q)$,可得:

    由此看出,KL 散度也可以解释为:

    换言之,KL 散度 = 交叉熵 - 熵

3.图片中推理的分析:为何“$H(Q) - H(P)$”一般不成立?

根据题目中给出的图片,KL 散度的推导大致写为:

到这里都没有问题。但如果直接把 $\sum{i=1}^n P(x_i)\log Q(x_i)$ 替换为 $\sum{i=1}^n Q(x_i)\log Q(x_i)$,再进一步写成 $-H(Q)$,就会得到

然而,这一步通常是不正确的,除非 $P$ 与 $Q$ 完全相同(即 $P(x_i) = Q(x_i)$ 对所有 $i$ 成立)。原因是:

  • $\sum{i=1}^n P(x_i)\log Q(x_i)$ 与 $\sum{i=1}^n Q(x_i)\log Q(x_i)$ 中,前者是“用 $P$ 加权的 $\log Q(x_i)$”;后者是“用 $Q$ 加权的 $\log Q(x_i)$”。两者一般不相等。
  • $H(Q)$ 定义为 $-\sum{i=1}^n Q(x_i)\,\log Q(x_i)$,它与 $\sum{i=1}^n P(x_i)\,\log Q(x_i)$ 的加权分布不同。只有当 $P = Q$ 时,这两种加权方式才会一致。

因此,图片中如果把最后一项直接写成 $H(Q)$ 并得到 $H(Q) - H(P)$,那只在 $P = Q$ 的极端情形下才成立。一般情况下,我们只能得到

所以,KL 散度在一般情况下并不会等于 $H(Q) - H(P)$

4.KL 散度的性质

  1. 非负性
    $D_{\mathrm{KL}}(P \,|\, Q) \ge 0$,且当且仅当 $P = Q$ 时取 0。这可以从吉布斯不等式 (Gibbs’ inequality) 得到。

  2. 不对称性
    一般情况下,$D{\mathrm{KL}}(P \,|\, Q) \neq D{\mathrm{KL}}(Q \,|\, P)$,因此它不是一个对称度量,也不是严格的距离。

  3. 与交叉熵的关系
    如上所示,$D{\mathrm{KL}}(P \,|\, Q) = H(P, Q) - H(P)$。所以当我们最小化 KL 散度时,也可以等价地最小化交叉熵,因为在给定 $P$ 的情况下,$\min{Q} D{\mathrm{KL}}(P \,|\, Q)$ 等价于 $\min{Q} H(P, Q)$。

  4. 在机器学习中的应用

    • 在分类问题中,最常见的损失函数之一就是交叉熵损失。最小化交叉熵等价于最小化 KL 散度,从而使模型分布 $Q$ 逼近真实分布 $P$。
    • 在自编码器、变分推断等领域中,也经常使用 KL 散度度量分布之间的差异。

5.结论

  1. KL 散度 $D_{\mathrm{KL}}(P \,|\, Q)$ 的定义和常见推导式都是正确的;它也可以理解为“交叉熵 - 熵”。
  2. 在图片中,如果把 $\sum{i=1}^n P(x_i)\log Q(x_i)$ 直接替换为 $\sum{i=1}^n Q(x_i)\log Q(x_i)$(即 $-H(Q)$),然后得到 $H(Q) - H(P)$,这个推理一般情况下是不成立的。只有当 $P$ 和 $Q$ 完全相同时,这才会变为 0(此时 KL 散度也确实是 0)。
  3. 正确的表达应该是: 能简化为 $H(Q) - H(P)$。

简要总结

  • KL 散度衡量的是分布 $P$ 和 $Q$ 之间的差异程度,它不对称且不满足三角不等式。
  • 从信息论角度,它等于交叉熵减去熵:$D_{\mathrm{KL}}(P \,|\, Q) = H(P,Q) - H(P)$。
  • 图片中的推理如果把 $\sum_i P(x_i)\log Q(x_i)$ 当成 $\sum_i Q(x_i)\log Q(x_i)$,就会得到错误的结论 $H(Q) - H(P)$。这是因为加权分布不同,只有在 $P=Q$ 时两者才相同,一般情况并不成立。

五、Softmax回归

下面我们来分步骤详细分析 Softmax 回归中的公式推导过程。通常在介绍 Softmax 回归时,我们会从模型输出的概率分布交叉熵损失出发,然后推导得到相应的梯度(偏导数)公式。以下内容与您提供的公式编号(如 (3.4.3)、(3.4.8)、(3.4.9)、(3.4.10))相呼应。

  1. 模型假设与 Softmax 函数

在分类任务中,Softmax 回归假设我们对一个样本 $\mathbf{x}$ 的输出 $\mathbf{o}$(又称“logits”,即未归一化的得分)经过 Softmax 函数后得到类别概率分布。设类别总数为 $q$,则对于第 $j$ 类的预测概率为:

这里,$\mathbf{o} = (o_1, o_2, \dots, o_q)$ 是可学习的线性变换输出(或者神经网络最后一层输出),还未经过任何归一化。

  1. 交叉熵损失函数

对于一个训练样本 $(\mathbf{x}, \mathbf{y})$,其中 $\mathbf{y} = (y_1, y_2, \dots, y_q)$ 是“真实标签分布”(通常是 One-hot 编码,只有一个分量为 1,其余为 0),我们使用交叉熵损失作为目标函数。交叉熵(或负对数似然)定义为:

由于 $\mathbf{y}$ 是 One-hot 编码,假设真实类别为 $c$,那么 $y_c = 1$ 且 $y_j = 0$($j \neq c$),此时损失可写成:

不过在推导梯度时,我们一般保留求和形式。

  1. 将 Softmax 代入交叉熵:得到公式 (3.4.9)

把 $\hat{y}j = \frac{\exp(o_j)}{\sum{k=1}^q \exp(o_k)}$ 代入交叉熵定义 (3.4.3) 中,得到

将对数拆分为两部分:

其中,

因此上式变为

将求和拆开:

由于 $\sum_{j=1}^q y_j = 1$(标签分布是一个概率分布,对 One-hot 来说也成立),可简化为

这就是您图片中所示的 (3.4.9) 公式的主要推导过程。

  1. 对 logits $o_i$ 求偏导:得到公式 (3.4.10)

接下来,我们需要计算

把这两个部分分别求导:

  1. 第一部分 $\displaystyle -\sum_{j=1}^q y_j \, o_j$

    只对 $o_i$ 的那一项有贡献,因为对于 $j \neq i$,$\frac{\partial o_j}{\partial o_i} = 0$。对 $j = i$,$\frac{\partial o_i}{\partial o_i} = 1$。因此

  2. 第二部分 $\displaystyle \log \bigl(\sum_{k=1}^q \exp(o_k)\bigr)$

    先记 $S = \sum_{k=1}^q \exp(o_k)$。则这一部分为 $\log S$。对数求导的链式法则给出

    而 $\exp(oi) / \sum{k=1}^q \exp(o_k)$ 正好是 $\hat{y}_i$。因此

将两部分结果相加,得到

所以,对于所有 $i$ 组成的向量形式,可以写成

这就是 Softmax 回归中非常简洁、重要的结论:损失对 logits 的梯度 = 预测概率 - 真实分布

  1. 直观理解
  • 为什么梯度是 $\hat{y}_i - y_i$
    直观上,如果模型预测概率 $\hat{y}_i$ 大于真实分布 $y_i$,那么梯度为正值($\hat{y}_i - y_i > 0$),表示我们需要“往反方向”更新参数以减小对该类别的过度预测;反之亦然。

  • 与二分类的逻辑回归对比
    在二分类逻辑回归中,模型输出 $\sigma(o)$(Sigmoid 函数),损失对 $o$ 的导数同样是“预测值减去真实值”。而在多分类情况下,Softmax 函数就是多分类的“推广版”,同样保留了这一简单形式的梯度。

  1. 总结
    1. Softmax 函数:$\displaystyle \hat{y}j = \frac{\exp(o_j)}{\sum{k=1}^q \exp(o_k)}$。
    2. 交叉熵损失:$\displaystyle \ell(\mathbf{y}, \hat{\mathbf{y}}) = -\sum_{j=1}^q y_j \log(\hat{y}_j)$。
    3. 二者结合:将 Softmax 代入交叉熵,经过对数分解和求和,得到简化形式 (3.4.9)。
    4. 求梯度:对未归一化的 logits $o_i$ 求偏导,最终结果为 (3.4.10):

这表明了在多分类问题中使用 Softmax 回归时,损失函数对 logits 的梯度同样具有非常简洁的形式。这也是为什么在实现神经网络的反向传播时,Softmax 与交叉熵常常“合并”为一个层来处理,既简化了计算,也避免了数值上的不稳定。

简单来说,(3.4.9) 展示了交叉熵在带有 Softmax 函数时的展开形式,(3.4.10) 则给出了它对 logits 的梯度。它们是 Softmax 回归中最核心的两个公式:一个告诉我们如何计算损失(目标函数),另一个告诉我们如何更新参数(反向传播梯度)。

六、循环神经网络

1.RNN的相关知识

循环神经网络(Recurrent Neural Network, RNN)是一类专门用于处理序列数据的神经网络,其独特之处在于能够利用之前时刻的信息来辅助当前时刻的决策。下面我们将从多个角度详细介绍RNN的基本概念、原理、常见变体以及实际应用,帮助你深入学习机器学习相关知识。

  1. 基本原理与结构

序列数据处理

  • 序列依赖性:传统的前馈神经网络处理每个输入时都相互独立,而许多实际问题(如自然语言、时间序列数据等)中,前后数据之间存在依赖关系。RNN通过引入循环结构,使得前一时刻的状态能够影响当前的计算,从而捕捉序列中的上下文信息。

基本结构

  • 隐藏状态的递归更新:在标准RNN中,假设在时间步 t,输入为 $x_t$,隐藏状态为 $h_t$,输出为 $y_t$。隐藏状态的更新公式通常为:

其中:

  • $W_{xh}$ 是输入到隐藏层的权重矩阵,
  • $W_{hh}$ 是隐藏层到隐藏层的权重矩阵,
  • $b_h$ 是偏置,
  • $f(\cdot)$ 一般为非线性激活函数(如tanh或ReLU)

  • 输出生成:输出可以根据隐藏状态计算得到,例如:

其中 $g(\cdot)$ 可能是softmax(在分类问题中)或其他合适的激活函数。

  1. 训练过程:反向传播与时间展开

    反向传播(Backpropagation Through Time, BPTT)

  • 时间展开:由于RNN在时间上具有递归结构,训练时需要将网络沿时间轴展开,形成一个深度网络,然后应用反向传播算法计算梯度。
  • 梯度传递:梯度在时间上反向传递时,每个时间步都会影响最终的梯度计算。这种方式使得模型能够学习到序列中前后数据的依赖关系。
  1. 面临的挑战:梯度消失与梯度爆炸

梯度消失

  • 当序列很长时,梯度在不断的链式相乘下可能会迅速衰减,使得前面时间步的参数更新非常微弱,从而难以捕捉长时依赖关系。

梯度爆炸

  • 与梯度消失相反,梯度在某些情况下会指数级增长,导致模型不稳定。通常会采用梯度裁剪(Gradient Clipping)来缓解这一问题。
  1. RNN的变体:改进长时依赖学习

由于原始RNN在处理长序列时存在上述问题,研究者提出了几种改进模型:

长短期记忆网络(LSTM)

  • 结构:LSTM通过引入专门的门控机制(输入门、遗忘门、输出门)来控制信息流:
    • 遗忘门 $f_t$ 决定丢弃多少前一时刻的记忆;
    • 输入门 $i_t$ 控制当前输入信息的保留;
    • 输出门 $o_t$ 决定隐藏状态的输出。
  • 核心公式其中 $\sigma$ 表示sigmoid函数,$\odot$ 表示元素级乘法。

门控循环单元(GRU)

  • 特点:GRU是LSTM的简化版本,它将输入门和遗忘门合并为一个更新门,同时减少了参数量,具有较高的计算效率。
  • 结构:主要包含更新门和重置门,通过简化的公式来实现类似LSTM的效果。
  1. 其他扩展与应用

双向RNN(Bidirectional RNN)

  • 原理:双向RNN同时考虑序列的正向和反向信息,将两个方向的隐藏状态拼接或融合后进行输出计算,通常能提高模型对上下文信息的捕捉能力。

注意力机制(Attention Mechanism)

  • 概念:在某些任务(如机器翻译)中,注意力机制可以让模型在生成每个输出时有选择性地关注输入序列中的不同部分,极大提升模型性能。

应用场景

  • 自然语言处理:如语言模型、机器翻译、文本生成、情感分析等。
  • 语音识别:将连续的语音信号转换为文本信息。
  • 时间序列预测:如股票走势、天气预报等领域,通过捕捉历史数据中的模式进行预测。
  • 视频处理:在视频分类、动作识别等任务中,通过处理时间连续的帧信息来实现更准确的判断。

总之,循环神经网络作为深度学习中处理序列数据的重要模型,通过其循环结构和记忆能力在诸多领域取得了显著成效。掌握RNN的原理、变体及其训练技巧,对于深入理解机器学习及应用于实际问题具有重要意义。希望以上详细介绍能为你的学习提供有益的帮助。

2.如何理解梯度剪裁

梯度剪裁(Gradient Clipping)是一种在神经网络训练过程中常用的技术,主要用来缓解梯度爆炸问题,从而稳定训练过程。下面详细解释这一技术的原理、方法和实际应用:

  1. 梯度爆炸问题简介
  • 梯度爆炸:在反向传播过程中,尤其是处理长序列(如RNN中的时间展开)时,梯度值可能会在连续的链式相乘下呈指数级增长。当梯度数值非常大时,参数更新可能过于剧烈,导致网络权重发散,模型无法收敛。
  • 出现场景:除了RNN,梯度爆炸在一些深层网络中也可能出现。当网络层数过多或者激活函数选择不当时,梯度数值可能失控。
  1. 梯度剪裁的基本思想

梯度剪裁的核心思想是:在进行参数更新前,检测梯度的大小,如果梯度的范数(或具体值)超过预设的阈值,则对其进行缩放,使其保持在合理的范围内。这样可以避免梯度更新过大,从而减轻梯度爆炸问题。

  1. 梯度剪裁的常用方法

3.1 按范数剪裁(Clipping by Norm)

  • 原理:计算所有梯度的整体范数(通常使用L2范数)。如果范数超过了设定的阈值,则将梯度整体缩放,使得它们的范数等于该阈值。
  • 公式

    如果 $|g|$ 表示梯度的L2范数,阈值为 $\theta$ ,则剪裁后的梯度 $g’$ 为:

    当 $|g| \leq \theta$ 时,不做任何处理;当 $|g| > \theta$ 时,对梯度进行缩放。

  • 优点:保持了梯度方向不变,只改变了其大小,使得参数更新既平稳又保持一定方向性。

3.2 按值剪裁(Clipping by Value)

  • 原理:直接对每个梯度的分量进行限制,即将梯度中的每个元素限制在一个固定的区间内(例如 $[-v, v]$)。
  • 方法:如果梯度中某个分量大于 $v$ 则设置为 $v$,小于 $-v$ 则设置为 $-v$。
  • 优点与缺点:这种方法实现简单,但可能会改变梯度的方向,尤其是当不同元素的梯度差异较大时,效果可能不如按范数剪裁稳定。
  1. 梯度剪裁的实际应用
  • RNN和LSTM/GRU训练:在训练循环神经网络时,由于反向传播需要通过多个时间步,梯度爆炸现象较为常见。使用梯度剪裁可以使训练过程更稳定,加速收敛。
  • 深层神经网络:对于层数较多的网络,梯度剪裁也常被用来防止参数更新时的剧烈波动。
  • 深度学习框架支持:例如TensorFlow和PyTorch都提供了内置的梯度剪裁函数。使用时,只需设置好阈值,框架会自动对计算得到的梯度进行剪裁。
  1. 选择合适的阈值
  • 经验法则:阈值的选择往往依赖于具体任务和模型结构,通常需要通过实验进行调试。过低的阈值可能导致梯度更新不足,影响模型学习;过高则不能有效防止梯度爆炸。
  • 动态调整:部分研究也尝试使用自适应策略,根据训练过程中的梯度统计信息动态调整剪裁阈值。
  1. 总结
    梯度剪裁是一种有效的技术,用于防止在深度网络(尤其是RNN)训练过程中因梯度爆炸而导致的不稳定问题。主要方法包括按范数剪裁和按值剪裁,其中按范数剪裁更为常用,因为它能在保持梯度方向的同时控制梯度大小。理解并正确使用梯度剪裁,有助于你在面对复杂序列数据或深层网络时,实现更稳定高效的模型训练。

通过理论学习和大量的实践,你可以更好地掌握梯度剪裁的技巧,并应用于各种机器学习任务中。