Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
---
id: standard-layer
sidebar_position: 10
title: 标准层:机器学习框架之始
description: 深度学习|标准层:机器学习框架之始
title: 标准层:深度学习模型拆解
description: 深度学习|标准层:深度学习模型拆解
last_update:
author: Aurelius
date: 2024-09-30
tags:
- 深度学习
- 模型拆解
- 标准层
- 机器学习框架
- 网络结构
---

## 引言

前文「深度学习|误差逆传播:梯度速解」中我们以 AddLayer 和 MulLayer 为例,引入了神经网络`标准层`封装的实现方式。神经网络中每一层的运算都可以以相同的接口封装成标准层的形式,每一层通过一个具体实现类实现,这样可以通过调用同一接口的不同实现,只需要经过简单的组装或者切换,就可以完成“任意”新网络结构的构建、学习、推理等复杂的工作
前文「深度学习|误差逆传播:梯度速解」中我们以 AddLayer 和 MulLayer 为例,引入了神经网络`标准层`封装的实现方式,又以 SoftmaxWithLoss 层为例讲解了其前后向的计算推导过程,并给出了其标准层的实现

## 常见运算的层
不难看出,神经网络中每一层的运算都可以以相同的接口封装成标准层的形式,每一层通过一个具体实现类实现,这样可以通过调用同一接口的不同实现,只需要经过简单的组装、切换、嵌套等,就可以完成“任意”新网络结构的构建、学习、推理等复杂的工作。

我们之前的演示大多是为了走通简单的学习与推理流程,并没有都遵守标准层的实现。后文的演示将以标准层的接口为基准,对神经网络中涉及的计算过程进行统一封装,达到一个通用的 DeepLearning 框架的效果。
## 运算的层封装

我们之前的演示大多是为了走通简单的学习与推理流程,并没有都遵守标准层的实现。后文的演示将以标准层的接口为基准,对神经网络中涉及的计算过程进行统一封装,对深度学习模型的层中逻辑做拆解和封装,最终达到一个通用的 DeepLearning 框架的效果。

参照 AddLayer 和 MulLayer,我们发现可以将神经网络的标准层抽象成包含了如下三个方法的抽象类:

Expand All @@ -29,22 +32,7 @@ tags:
| forward(self, args) | 正向传播的计算,用于计算推理结果; |
| backward(self, dout) | 逆传播的计算,用于计算梯度; |

我们只需要根据不同层的具体计算逻辑是指这三个方法的不同实现,就可以实现各种网络层次的封装,从而实现一个通用的神经网络框架。

神经网络中常见的层如:

- **全连接层(Fully Connected Layer)**:如 `Affine 层`,每个输入与每个输出节点相连。
- **激活层(Activation Layer)**:如 `ReLU 层`、`Sigmoid 层`、`Tanh 层`等,用于神经元输出,增加非线性表达能力。
- `Softmax-with-Loss 层`:用于输出层的正规化和损失计算。
- **卷积层(Convolutional Layer)**:如 `Conv2D`、`Conv2DTranspose`、`SeparableConv2D` 等,用于以卷积操作提取特征,常用于图像数据处理。
- **池化层(Pooling Layer)**:如 `Max Pooling`、`Average Pooling` 等,在池化窗口内采样,用于减少特征维度,增强模型的局部不变形。
- **归一化层(Normalization Layer)**:如 `Batch Normalization`、`Layer Normalization`、`Instance Normalization` 等,用于加速训练,提高模型的稳定性与泛化能力。
- **丢弃层(Dropout Layer)**:用于防止过拟合,随机丢弃一部分神经元。
- **嵌入层(Embedding Layer)**:用于将稀疏的输入转换为稠密的向量表示。
- **递归层(Recurrent Layer)**:如 `SimpleRNN`、`LSTM`、`GRU` 等,用于处理存在时间依赖关系的序列数据。
- **自注意力层(Self-Attention Layer)**:用于处理序列数据,提取序列中的重要信息。

只要以标准层的方式提供了所有计算逻辑的标准层封装,我们在应对具体繁杂的业务场景时,搭建不同的网络模型就可以不去太过关注这些计算逻辑的实现,只需摘取我们想要的层进行简单的组合,轻易就能够组装成自己想要的复杂网络。接下来我们以标准层的方式来做一些演示。
只需要根据不同层的具体计算逻辑是指这三个方法的不同实现,就可以实现各种网络层次的封装,从而实现一个通用的神经网络框架。接下来我们以标准层的方式来做一些演示。

### Affine 层

Expand All @@ -56,7 +44,12 @@ $$
Y = X \cdot W + B \tag{1}
$$

其中 X 是矩阵表示的输入(上一层的输出),W 是当前层的权重,B 是当前层的偏置,Y 是输出。
其中:

- $X$ 是输入矩阵(或上一层的输出)。
- $W$ 是当前层的权重矩阵。
- $B$ 是当前层的偏置向量。
- $Y$ 是当前层的输出。

根据链式法则,损失函数 L 关于 Y 的梯度为:$\frac{\partial{L}}{\partial{Y}}$。

Expand All @@ -74,129 +67,111 @@ $$
\frac{\partial{L}}{\partial{B}} = \frac{\partial{L}}{\partial{Y}} \cdot \frac{\partial{Y}}{\partial{B}} = \frac{\partial{L}}{\partial{Y}} \tag{4}
$$

---

### Affine 层的数学式

给定 Affine 层的输出公式:

$$
Y = X \cdot W + B
$$

其中:

- $X$ 是输入矩阵(或上一层的输出)。
- $W$ 是当前层的权重矩阵。
- $B$ 是当前层的偏置向量。
- $Y$ 是当前层的输出。

### 梯度计算过程
Affine 层梯度推算过程

在反向传播过程中,我们需要计算损失函数 $L$ 关于输入 $X$、权重 $W$ 和偏置 $B$ 的梯度。这可以通过链式法则完成。

#### 1. 梯度关于 $X$ 的推导
1. 梯度关于 X 的推导

根据链式法则,损失函数 $L$ 关于 $Y$ 的梯度为 $\frac{\partial{L}}{\partial{Y}}$。我们需要计算:

$$
\frac{\partial{L}}{\partial{X}} = \frac{\partial{L}}{\partial{Y}} \cdot \frac{\partial{Y}}{\partial{X}}
\frac{\partial{L}}{\partial{X}} = \frac{\partial{L}}{\partial{Y}} \cdot \frac{\partial{Y}}{\partial{X}}
$$

首先计算 $Y$ 关于 $X$ 的导数:

$$
Y = X \cdot W + B
Y = X \cdot W + B
$$

在这个公式中, $B$ 是常数项,所以它对 $X$ 的导数为 0。我们只关心 $X \cdot W$。

对 $Y$ 求导:

$$
\frac{\partial{Y}}{\partial{X}} = W
\frac{\partial{Y}}{\partial{X}} = W
$$

因此,

$$
\frac{\partial{L}}{\partial{X}} = \frac{\partial{L}}{\partial{Y}} \cdot W^T
\frac{\partial{L}}{\partial{X}} = \frac{\partial{L}}{\partial{Y}} \cdot W^T
$$

(右乘 $W^T$ 是因为 $X$ 和 $Y$ 常常是多维矩阵的情况下需要转置。)

### 2. 梯度关于 $W$ 的推导
2. 梯度关于 W 的推导

同样,我们需要计算:

$$
\frac{\partial{L}}{\partial{W}} = \frac{\partial{L}}{\partial{Y}} \cdot \frac{\partial{Y}}{\partial{W}}
\frac{\partial{L}}{\partial{W}} = \frac{\partial{L}}{\partial{Y}} \cdot \frac{\partial{Y}}{\partial{W}}
$$

计算 $Y$ 关于 $W$ 的导数:

$$
Y = X \cdot W + B
Y = X \cdot W + B
$$

因此,$Y$ 对 $W$ 的导数:

$$
\frac{\partial{Y}}{\partial{W}} = X
\frac{\partial{Y}}{\partial{W}} = X
$$

将其代入公式中,我们得到:

$$
\frac{\partial{L}}{\partial{W}} = X^T \cdot \frac{\partial{L}}{\partial{Y}}
\frac{\partial{L}}{\partial{W}} = X^T \cdot \frac{\partial{L}}{\partial{Y}}
$$

### 3. 梯度关于 $B$ 的推导
3. 梯度关于 $B$ 的推导

最后,我们需要计算:

$$
\frac{\partial{L}}{\partial{B}} = \frac{\partial{L}}{\partial{Y}} \cdot \frac{\partial{Y}}{\partial{B}}
\frac{\partial{L}}{\partial{B}} = \frac{\partial{L}}{\partial{Y}} \cdot \frac{\partial{Y}}{\partial{B}}
$$

计算 $Y$ 关于 $B$ 的导数:

$$
Y = X \cdot W + B
Y = X \cdot W + B
$$

这样,$\frac{\partial{Y}}{\partial{B}}$ 将是一个单位矩阵或 1(因为偏置 $B$ 对 $Y$ 的影响是直接的):

$$
\frac{\partial{Y}}{\partial{B}} = 1
\frac{\partial{Y}}{\partial{B}} = 1
$$

因此,

$$
\frac{\partial{L}}{\partial{B}} = \frac{\partial{L}}{\partial{Y}}
\frac{\partial{L}}{\partial{B}} = \frac{\partial{L}}{\partial{Y}}
$$

### 总结

最终的梯度推导公式为:

1. 关于 $X$ 的梯度:

$$
$$
\frac{\partial{L}}{\partial{X}} = \frac{\partial{L}}{\partial{Y}} \cdot W^T
$$
$$

2. 关于 $W$ 的梯度:

$$
$$
\frac{\partial{L}}{\partial{W}} = X^T \cdot \frac{\partial{L}}{\partial{Y}}
$$
$$

3. 关于 $B$ 的梯度:
$$
\frac{\partial{L}}{\partial{B}} = \frac{\partial{L}}{\partial{Y}}
$$

$$
\frac{\partial{L}}{\partial{B}} = \frac{\partial{L}}{\partial{Y}}
$$

这些公式是反向传播过程中用于更新权重和偏置的重要基础,确保了网络能够有效地学习。

Expand Down Expand Up @@ -293,3 +268,26 @@ $$
Softmax-with-Loss 层的代码实现参见 [Notebook](https://github.com/AfterShip/all-staff-writing-plan.deep-learning-basic/blob/master/runtime/Deep%20Learning.ipynb) 1.3.4 - 4。

使用标准层实现方式组装新的神经网络的代码参见 [runtime/standard-framework/simple_net.py](https://github.com/AfterShip/all-staff-writing-plan.deep-learning-basic/blob/master/runtime/standard-framework/simple_net.py)。其中 [trainer](https://github.com/AfterShip/all-staff-writing-plan.deep-learning-basic/blob/master/runtime/standard-framework/trainer.py) 模块实现了用标准层组装的 SimpleNet 进行手写数字识别任务的学习、推理、效果评估的过程。

## 层的使用

## 常见的层

- **全连接层(Fully Connected Layer)**:如 `Affine 层`,每个输入与每个输出节点相连。
- **激活层(Activation Layer)**:如 `ReLU 层`、`Sigmoid 层`、`Tanh 层`等,用于神经元输出,增加非线性表达能力。
- `Softmax-with-Loss 层`:用于输出层的正规化和损失计算。
- **卷积层(Convolutional Layer)**:如 `Conv2D`、`Conv2DTranspose`、`SeparableConv2D` 等,用于以卷积操作提取特征,常用于图像数据处理。
- **池化层(Pooling Layer)**:如 `Max Pooling`、`Average Pooling` 等,在池化窗口内采样,用于减少特征维度,增强模型的局部不变形。
- **归一化层(Normalization Layer)**:如 `Batch Normalization`、`Layer Normalization`、`Instance Normalization` 等,用于加速训练,提高模型的稳定性与泛化能力。
- **丢弃层(Dropout Layer)**:用于防止过拟合,随机丢弃一部分神经元。
- **嵌入层(Embedding Layer)**:用于将稀疏的输入转换为稠密的向量表示。
- **递归层(Recurrent Layer)**:如 `SimpleRNN`、`LSTM`、`GRU` 等,用于处理存在时间依赖关系的序列数据。
- **自注意力层(Self-Attention Layer)**:用于处理序列数据,提取序列中的重要信息。

只要以标准层的方式提供了所有计算逻辑的标准层封装,我们在应对具体繁杂的业务场景时,搭建不同的网络模型就可以不去太过关注这些计算逻辑的实现,只需摘取我们想要的层进行简单的组合,轻易就能够组装成自己想要的复杂网络。

## 结语

---

**PS:感谢每一位志同道合者的阅读,欢迎关注、点赞、评论!**
Loading