Logo
Section 1 / 4

编写前向传播代码

了解前向传播的有趣事实及其代码实现。

导入

编写前向传播代码

了解前向传播的有趣事实及其代码实现。

回顾

在上一章中,我们制定了构建用于分类 MNIST 图像的神经网络的计划。我们从串联两个感知机开始,并记录了所有相关矩阵的行数和列数,最终得到了如下结构图:

...
最终确定的神经网络架构

请保留这张示意图,因为我们即将将其转化为代码。编码过程将分为两章完成:在本章中,我们将编写神经网络的分类函数——也就是说,除了训练代码之外的所有网络代码;在下一章中,我们将完成整个网络并将其应用于 MNIST 数据集。

现在,让我们开始编写分类函数。作为参考,我们先回顾一下在课程第一部分中为实现感知机所写的函数。

编写前向传播代码

以下是 forward() 函数的重新展示,这是我们感知机的核心函数之一:

1def forward(X, w): 2 weighted_sum = np.matmul(X, w) 3 return sigmoid(weighted_sum)

forward() 函数实现了称为前向传播的操作,即根据输入计算系统的输出。对于感知机而言,该操作包括加权求和后接一个 sigmoid 激活函数。而对于神经网络来说,这一过程会稍微复杂一些。

事实上,“前向传播”这一名称正是在此体现其意义:将一张 MNIST 图像通过神经网络的过程,本质上是将数据逐层向前传播。

前向传播的第一步与普通感知机相同:

1h = sigmoid(np.matmul(prepend_bias(X), w1))

首先,我们在输入数据中添加偏置项(bias)列;其次,使用第一组权重矩阵 w1w1 计算加权和;最后,将结果传入 sigmoid() 函数。根据代码结构可以看出,这一步完成了隐藏层的计算。

接下来,我们重复类似过程来计算输出层:

1y_hat = softmax(np.matmul(prepend_bias(h), w2))

这里我们使用了第二组权重矩阵,并将加权和结果传入 softmax() 函数而非 sigmoid()。(目前我们尚未实现 softmax() 函数,但很快就会编写。)除此之外,输出层的计算逻辑与隐藏层基本一致:添加偏置项、计算加权和、再通过激活函数处理。

现在,我们将这两行代码封装成一个函数:

1def forward(X, w1, w2): 2 h = sigmoid(np.matmul(prepend_bias(X), w1)) 3 y_hat = softmax(np.matmul(prepend_bias(h), w2)) 4 return y_hat

这段代码还有一个需要注意的地方:此前在实现感知机时,prepend_bias() 函数位于 mnist.py 库中,作为数据加载和预处理的一部分。但在神经网络中,我们需要两次添加偏置项——分别在输入层和隐藏层。因此,我们应该将 prepend_bias() 函数从 mnist.py 移至主网络代码中,以便多次调用。该函数定义如下:

1def prepend_bias(X): 2 return np.insert(X, 0, 1, axis=1)

超越基础网络

编写 Softmax 与分类函数