15、机器学习数学基础与反向传播算法详解

机器学习数学基础与反向传播算法详解

1. 数学基础概述

在机器学习领域,数学是必不可少的工具,尤其是线性代数和微积分。线性代数为处理向量、矩阵和张量等数据数组提供了强大的工具,而微积分则在优化算法和理解函数变化率方面发挥着关键作用。

1.1 进一步学习建议

如果想深入学习机器学习的数学基础,以下是一些推荐的书籍:
- 对于线性代数的全面学习,推荐 Sheldon Axler 的《Linear Algebra Done Right》(Springer,2015)。
- 若要获取包括向量微积分在内的完整实用微积分指南,James Stewart 的《Calculus: Early Transcendentals》(Cengage Learning,2015)是不错的选择。
- 若想深入理解微积分的数学理论,Walter Rudin 的经典著作《Principles of Mathematical Analysis》(McGraw Hill,1976)是难以超越的佳作。

1.2 线性代数基础

线性代数是机器学习的基础,它主要涉及向量、矩阵和张量的处理。在 Python 中,可以使用 NumPy 库的数组类型来表示这些对象。

1.2.1 向量:一维数据

向量是一维的数字数组,数组的大小即为向量的维度。在 Python 代码中,可以使用 NumPy 数组来表示向量。以下是一些向量操作的示例代码:

import numpy as np

# 创建向量
x = np.array([1, 2])
print(x)  # 输出: array([1, 2])
print(x.shape)  # 输出: (2,)

y = np.array([3, 3.1, 3.2, 3.3])
print(y)  # 输出: array([3., 3.1, 3.2, 3.3])
print(y.shape)  # 输出: (4,)

# 访问向量元素
x = np.array([5, 6, 7, 8])
print(x[0])  # 输出: 5
print(x[1])  # 输出: 6

# 向量加法
x = np.array([1, 2, 3, 4])
y = np.array([5, 6, 7, 8])
print(x + y)  # 输出: array([ 6,  8, 10, 12])

# 向量元素-wise 乘法(Hadamard 积)
x = np.array([1, 2, 3, 4])
y = np.array([5, 6, 7, 8])
print(x * y)  # 输出: array([ 5, 12, 21, 32])

# 向量与标量乘法
x = np.array([1, 2, 3, 4])
print(0.5 * x)  # 输出: array([0.5, 1., 1.5, 2.])

# 向量点积
x = np.array([1, 2, 3, 4])
y = np.array([4, 5, 6, 7])
print(np.dot(x, y))  # 输出: 60
print(x @ y)  # 输出: 60
1.2.2 矩阵:二维数据

矩阵是二维的数字数组,同样可以使用 NumPy 数组来表示。以下是矩阵操作的示例代码:

# 创建矩阵
x = np.array([
    [1, 2, 3],
    [4, 5, 6]
])
print(x)
# 输出:
# array([[1, 2, 3],
#        [4, 5, 6]])
print(x.shape)  # 输出: (2, 3)

# 访问矩阵元素
print(x[0][1])  # 输出: 2
print(x[0, 1])  # 输出: 2
print(x[1][0])  # 输出: 4
print(x[1, 0])  # 输出: 4

# 提取矩阵的行
y = x[0]
print(y)  # 输出: array([1, 2, 3])
print(y.shape)  # 输出: (3,)

# 提取矩阵的列
z = x[:, 1]
print(z)  # 输出: array([2, 5])

# 矩阵元素-wise 加法
y = np.array([
    [3, 4, 5],
    [6, 7, 8]
])
print(x + y)
# 输出:
# array([[ 4,  6,  8],
#        [10, 12, 14]])

# 矩阵元素-wise 乘法
print(x * y)
# 输出:
# array([[ 3,  8, 15],
#        [24, 35, 48]])

# 矩阵与标量乘法
print(0.5 * x)
# 输出:
# array([[0.5, 1., 1.5],
#        [2., 2.5, 3.]])
1.2.3 秩 3 张量

在一些场景中,如表示围棋棋盘或图像时,需要使用秩 3 张量。可以将其看作矩阵的堆叠,每个矩阵称为一个平面或通道。以下是创建和操作秩 3 张量的示例代码:

# 创建秩 3 张量
x = np.array([
    [[1, 2, 3],
     [2, 3, 4]],
    [[3, 4, 5],
     [4, 5, 6]]
])
print(x.shape)  # 输出: (2, 2, 3)
print(x[0])
# 输出:
# array([[1, 2, 3],
#        [2, 3, 4]])
print(x[1])
# 输出:
# array([[3, 4, 5],
#        [4, 5, 6]])

张量同样支持元素-wise 加法、元素-wise 乘法和标量乘法。在处理张量时,需要注意索引方案,常见的有 channels_first channels_last 两种,这里推荐使用 channels_first 格式,因为某些 NVIDIA GPU 对该格式有特殊优化。

1.2.4 秩 4 张量

为了提高效率,可能需要一次性处理多个游戏棋盘,此时可以使用秩 4 张量,将多个秩 3 张量打包成一个四维的 NumPy 数组。矩阵和向量是张量的特殊情况,矩阵是秩 2 张量,向量是秩 1 张量,而秩 0 张量就是普通的数字。NumPy 可以处理任意秩的张量,尽管高维张量难以可视化,但代数运算规则是相同的。

1.3 微积分基础:导数与求最大值

在微积分中,函数的变化率称为导数。以下是一些导数的实际例子:
| 数量 | 导数 |
| ---- | ---- |
| 行驶的距离 | 行驶的速度 |
| 浴缸中的水量 | 水流出的速度 |
| 拥有的客户数量 | 获得(或失去)的客户数量 |

导数不是一个固定的量,而是一个随时间或空间变化的函数。当函数递增时,其导数为正;当函数递减时,其导数为负。利用导数的这一性质,可以找到函数的局部最大值或最小值。具体来说,当导数为正时,向右侧移动可以找到更大的值;当导数为负时,向左侧移动。在局部最大值处,导数为零。

对于输入为高维向量、输出为单个数字的函数,其导数是一个与输入维度相同的向量,称为梯度。沿着梯度方向最大化函数的过程称为梯度上升,而最小化函数的过程称为梯度下降。在使用梯度上升时,需要知道目标函数的导数公式。对于简单的代数函数,可以在微积分教材中查找其导数;对于复杂函数,可以使用链式法则来计算其导数。一些深度学习库,如 TensorFlow 和 Theano,会利用链式法则自动计算复杂函数的导数。在 Keras 中定义复杂函数时,无需手动计算梯度,Keras 会将计算工作交给 TensorFlow 或 Theano 处理。

2. 反向传播算法

2.1 符号说明

在处理具有 l 层的前馈神经网络时,需要引入一些符号来方便表示。第 i 层的权重记为 Wᵢ ,偏置项记为 bᵢ 。用 x 表示大小为 k 的输入数据小批量, y 表示网络的输出。同时,引入以下符号:
- y⁽ⁱ⁺¹⁾ 表示第 i 层激活后的输出,即 y⁽ⁱ⁺¹⁾ = σ(Wᵢy⁽ⁱ⁾ + bᵢ) ,其中 σ 是 sigmoid 激活函数, y⁽ⁱ⁺¹⁾ 也是第 i + 1 层的输入。
- z⁽ⁱ⁾ 表示第 i 层未激活的输出,即 z⁽ⁱ⁾ = Wᵢ · y⁽ⁱ⁾ + bᵢ

2.2 前馈神经网络的反向传播算法

根据上述符号约定,前馈神经网络第 i 层的前向传播可以表示为:

graph LR
    A[y⁽ⁱ⁾] --> B[Wᵢ]
    A --> C[bᵢ]
    B --> D[Wᵢy⁽ⁱ⁾]
    C --> D
    D --> E[Wᵢy⁽ⁱ⁾ + bᵢ]
    E --> F[σ]
    F --> G[y⁽ⁱ⁺¹⁾]

y⁽ⁱ⁺¹⁾ = σ(Wᵢy⁽ⁱ⁾ + bᵢ) = f ∘ y⁽ⁱ⁾ 。通过递归应用这个定义,可以将网络的预测表示为 y = fₙ ∘ ··· ∘ f₁(x)

损失函数 Loss(y, ŷ) 可以根据预测值 y 和标签 ŷ 计算得到,并且可以使用链式法则来计算其导数。定义第 i 层的误差项 Δ⁽ⁱ⁾ 后,可以通过以下关系进行反向传播:

graph LR
    A[Δ⁽ⁱ⁺¹⁾] --> B[σ′(z⁽ⁱ⁾)]
    A --> C[Wᵢᵀ]
    B --> D[Δ⁽ⁱ⁺¹⁾ ⊙ σ′(z⁽ⁱ⁾)]
    C --> D
    D --> E[Δ⁽ⁱ⁾]

Δ⁽ⁱ⁾ = (Wᵢ)ᵀ · (Δ⁽ⁱ⁺¹⁾ ⊙ σ′(z⁽ⁱ⁾)) ,其中 表示 Hadamard 积。这个计算过程可以分为两部分,一部分用于处理密集层,另一部分用于处理激活函数:
- Δ⁽ⁱ⁾₍₁₎ = Δ⁽ⁱ⁺¹⁾ ⊙ σ′(z⁽ⁱ⁾)
- Δ⁽ⁱ⁾₍₂₎ = (Wᵢ)ᵀ · Δ⁽ⁱ⁾₍₁₎

最后一步是计算每一层参数 Wᵢ bᵢ 的梯度。有了误差项 Δ⁽ⁱ⁾ 后,就可以根据这些误差项更新神经网络的参数,具体的更新方式可以根据需要选择不同的优化器或更新规则。

2.3 顺序神经网络的反向传播

一般来说,顺序神经网络可能包含更复杂的层,如卷积层或不同的激活函数(如 softmax 激活函数)。但无论网络中具体包含哪些层,反向传播的基本流程是相同的。如果用 g 表示未激活的前向传播函数, Act 表示相应的激活函数,将误差项 Δ 传播到第 i 层需要计算以下转换:
1. 计算激活函数在中间输出 z⁽ⁱ⁾ 处的导数。
2. 计算层函数 g 相对于第 i 层输入的导数。

知道所有的误差项后,通常可以快速推导出该层所有参数的梯度,就像在前馈层中计算权重和偏置项的梯度一样。从这个角度看,每一层都知道如何向前传递数据和向后传播误差,而无需了解周围层的具体结构。

2.4 一般神经网络的反向传播

在非顺序网络中,一个层可能有多个输出、多个输入或两者都有。以下分别讨论不同情况:
- 多个输出 :假设一个层有 m 个输出,例如将一个向量拆分为 m 个部分。对于该层,前向传播可以拆分为 k 个独立的函数。在反向传播时,每个函数的导数可以分别计算,并且每个导数对传递到前一层的误差项 Δ 贡献相同。
- 多个输入和一个输出 :在这种情况下,前向传播由 n 个输入分量通过一个输出单个值的函数计算得到。在反向传播时,从下一层接收一个误差项 Δ ,需要计算 n 个输出误差项并传递给每个输入层。这些导数可以独立计算,分别在各自的输入处进行评估。
- 一般情况( n 个输入和 m 个输出) :通过结合上述两种情况的步骤来处理。每个神经网络,无论其结构多么复杂,局部都可以看作是这种形式。

2.5 反向传播的计算挑战

虽然反向传播从理论上看只是链式法则在特定机器学习算法中的简单应用,但在实际实现时需要考虑很多因素。
- 缓存中间值 :为了计算任何一层的误差项和梯度更新,需要准备好前向传播的相应输入。如果简单地丢弃前向传播的结果,在反向传播时就需要重新计算。因此,应该以高效的方式缓存这些值。例如,在手动实现神经网络时,每一层可以保存自己的输入和输出数据以及输入和输出误差项。在处理大量数据的网络时,需要确保实现既具有计算效率又具有较低的内存占用。
- 重用中间值 :在一些情况下,合理重用中间值可以提高计算效率。例如,在前馈网络中,可以将仿射线性变换和 sigmoid 激活看作一个整体,也可以将它们拆分为两层。由于计算激活函数的反向传播需要仿射线性变换的输出,因此应该保留前向传播的中间信息。另一方面,由于 sigmoid 函数没有参数,可以一次性计算其反向传播: Δ⁽ⁱ⁾ = (Wᵢ)ᵀ (Δ⁽ⁱ⁺¹⁾ ⊙ σ′(z⁽ⁱ⁾)) ,这可能比分两步计算更高效。自动检测哪些操作可以一起执行可以带来显著的速度提升。在更复杂的情况下,如循环神经网络,管理中间状态变得更加重要。

3. 总结与应用建议

3.1 知识总结

  • 线性代数方面 :向量是一维数组,可进行加法、元素 - wise 乘法、与标量乘法和点积等运算;矩阵是二维数组,支持元素 - wise 加法、乘法和标量乘法;秩 3 张量可用于表示如围棋棋盘、图像等,秩 4 张量可用于批量处理多个秩 3 张量。
  • 微积分方面 :导数表示函数的变化率,利用导数正负性可找函数的局部最值,对于高维函数,其导数为梯度,可通过梯度上升或下降来优化函数。
  • 反向传播算法方面 :在不同类型的神经网络(前馈、顺序、非顺序)中,通过引入特定符号,利用链式法则计算误差项和参数梯度,实现参数更新。

3.2 应用建议

  • 线性代数应用
    • 在数据预处理阶段,可使用向量和矩阵运算对数据进行标准化、归一化等操作,提高模型的训练效果。
    • 对于图像、视频等多维数据,使用张量进行表示和处理,结合深度学习模型进行特征提取和分类。
  • 微积分应用
    • 在模型训练过程中,使用梯度下降算法来最小化损失函数,不断调整模型参数。
    • 当遇到复杂的目标函数时,借助深度学习库(如 TensorFlow、Theano)自动计算导数,避免手动计算的繁琐。
  • 反向传播算法应用
    • 在设计神经网络时,根据网络结构和层的类型,合理应用反向传播算法更新参数。
    • 注意缓存中间值和重用中间值,提高计算效率和减少内存占用,特别是在处理大规模数据和复杂网络时。

3.3 操作步骤总结

3.3.1 线性代数操作步骤
  • 向量操作
    1. 使用 np.array 函数将列表转换为向量。
    2. 通过 shape 属性检查向量维度。
    3. 使用 + 进行向量加法, * 进行元素 - wise 乘法, np.dot @ 进行点积运算。
  • 矩阵操作
    1. 使用 np.array 函数将列表的列表转换为矩阵。
    2. 通过 shape 属性检查矩阵维度,使用双下标或 [row, column] 格式访问元素。
    3. 使用 + 进行矩阵加法, * 进行元素 - wise 乘法, * 与标量进行乘法。
  • 张量操作
    1. 使用 np.array 函数创建张量。
    2. 通过 shape 属性检查张量维度,使用下标提取通道。
    3. 支持元素 - wise 加法、乘法和标量乘法。
3.3.2 微积分操作步骤
  • 梯度上升/下降
    1. 确定目标函数。
    2. 计算目标函数的导数(梯度),可手动计算或借助深度学习库。
    3. 根据梯度方向更新参数,进行多次迭代直到满足停止条件。
3.3.3 反向传播操作步骤
  • 前馈神经网络
    1. 定义网络结构和符号。
    2. 进行前向传播,计算各层的输出。
    3. 计算损失函数。
    4. 进行反向传播,计算误差项和参数梯度。
    5. 根据梯度更新参数。
  • 顺序神经网络
    1. 确定网络层和激活函数。
    2. 按照前馈神经网络的步骤进行前向和反向传播,注意不同层的导数计算。
  • 非顺序神经网络
    1. 分析层的输入和输出情况。
    2. 根据输入输出情况分别计算各部分的导数和误差项。
    3. 汇总误差项并传递到前一层。

3.4 流程图总结

graph TD
    A[开始] --> B[线性代数操作]
    A --> C[微积分操作]
    A --> D[反向传播操作]
    B --> B1[向量操作]
    B --> B2[矩阵操作]
    B --> B3[张量操作]
    C --> C1[确定目标函数]
    C1 --> C2[计算导数]
    C2 --> C3[梯度上升/下降]
    D --> D1[前馈神经网络]
    D --> D2[顺序神经网络]
    D --> D3[非顺序神经网络]
    B1 --> E[结束]
    B2 --> E
    B3 --> E
    C3 --> E
    D1 --> E
    D2 --> E
    D3 --> E

通过以上对机器学习数学基础和反向传播算法的介绍,希望能帮助大家更好地理解和应用这些知识,在实际项目中取得更好的效果。同时,不断实践和探索,将这些理论知识与实际问题相结合,才能真正掌握机器学习的核心技术。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值