共计 3055 个字符,预计需要花费 8 分钟才能阅读完成。
为什么需要掌握 BP 前馈手算能力?
在实际工作和学习中,手算 BP 神经网络的前馈过程有几个重要的应用场景:

- 模型调试:当神经网络出现异常输出时,手算可以帮助我们定位问题所在,是权重初始化不当、激活函数选择错误还是其他原因。
- 面试考核:很多机器学习相关岗位的面试都会考察候选人对神经网络基础原理的理解程度。
- 加深理解:通过手算可以更直观地理解神经网络的工作机制,而不仅仅是调包使用。
数学准备
在开始手算前,我们需要明确几个关键公式:
前馈计算公式
对于第 l 层的神经元,其输出计算为:
$$
z^{(l)} = W^{(l)T}a^{(l-1)} + b^{(l)}
$$
$$
a^{(l)} = g(z^{(l)})
$$
其中:
– $W^{(l)}$ 是第 l 层的权重矩阵
– $a^{(l-1)}$ 是上一层的激活输出
– $b^{(l)}$ 是偏置项
– $g(\cdot)$ 是激活函数
常用激活函数及其导数
Sigmoid 函数
$$
\sigma(z) = \frac{1}{1+e^{-z}}
$$
$$
\sigma'(z) = \sigma(z)(1-\sigma(z))
$$
ReLU 函数
$$
ReLU(z) = max(0,z)
$$
$$
ReLU'(z) = \begin{cases}
1 & \text{if} z > 0 \
0 & \text{otherwise}
\end{cases}
$$
例题演示:3 层神经网络(2-3-1)
让我们通过一个具体的例子来演示前馈计算过程。网络结构为 2 -3-1,即:
– 输入层:2 个神经元
– 隐藏层:3 个神经元
– 输出层:1 个神经元
给定参数
假设我们已经训练好了一个模型,参数如下:
- 输入:$x = [0.5, 0.8]$
- 第一层权重:
$$
W^{(1)} = \begin{bmatrix}
0.1 & 0.3 & 0.5 \
0.2 & 0.4 & 0.6
\end{bmatrix}
$$ - 第一层偏置:$b^{(1)} = [0.1, 0.2, 0.3]$
- 第二层权重:
$$
W^{(2)} = \begin{bmatrix}
0.7 \
0.8 \
0.9
\end{bmatrix}
$$ - 第二层偏置:$b^{(2)} = [0.4]$
激活函数使用 Sigmoid。
计算步骤
第一步:输入层到隐藏层
-
计算加权和:
$$
z^{(1)} = W^{(1)T}x + b^{(1)}
$$
展开计算:
$$
\begin{bmatrix}
0.1 & 0.2 \
0.3 & 0.4 \
0.5 & 0.6
\end{bmatrix}
\begin{bmatrix}
0.5 \
0.8
\end{bmatrix}
+
\begin{bmatrix}
0.1 \
0.2 \
0.3
\end{bmatrix}
=
\begin{bmatrix}
0.1\times0.5 + 0.2\times0.8 + 0.1 \
0.3\times0.5 + 0.4\times0.8 + 0.2 \
0.5\times0.5 + 0.6\times0.8 + 0.3
\end{bmatrix}
=
\begin{bmatrix}
0.31 \
0.67 \
1.03
\end{bmatrix}
$$ -
应用激活函数:
$$
a^{(1)} = \sigma(z^{(1)}) = \begin{bmatrix}
\frac{1}{1+e^{-0.31}} \
\frac{1}{1+e^{-0.67}} \
\frac{1}{1+e^{-1.03}}
\end{bmatrix}
\approx
\begin{bmatrix}
0.5769 \
0.6615 \
0.7369
\end{bmatrix}
$$
第二步:隐藏层到输出层
- 计算加权和:
$$
z^{(2)} = W^{(2)T}a^{(1)} + b^{(2)}
$$
展开计算:
$$
\begin{bmatrix}
0.7 & 0.8 & 0.9
\end{bmatrix}
\begin{bmatrix}
0.5769 \
0.6615 \
0.7369
\end{bmatrix} -
0.4
= 0.7\times0.5769 + 0.8\times0.6615 + 0.9\times0.7369 + 0.4
\approx 1.7539
$$ -
应用激活函数:
$$
a^{(2)} = \sigma(z^{(2)}) = \frac{1}{1+e^{-1.7539}} \approx 0.8524
$$
维度验证
让我们验证一下各层输出的维度是否正确:
- 输入层:2 维向量
- $W^{(1)}$:2×3 矩阵
- $W^{(1)T}x$:3×2 × 2×1 = 3×1
- 加上偏置 (3×1) 后仍然是 3×1
- $W^{(2)}$:3×1 矩阵
- $W^{(2)T}a^{(1)}$:1×3 × 3×1 = 1×1
- 加上偏置 (1×1) 后仍然是 1×1
维度检查通过!
易错点警示
在手算过程中,有几个常见错误需要特别注意:
- 矩阵转置顺序错误:
- 正确:$W^{(l)T}a^{(l-1)}$
-
错误:$a^{(l-1)T}W^{(l)}$(维度不匹配)
-
忽略偏置项:
-
偏置项对网络性能影响很大,不能遗漏
-
激活函数应用范围:
- 注意激活函数是逐元素应用的
-
不要对整个向量直接应用
-
维度不匹配:
- 每次矩阵乘法前都要检查维度
-
例如:$W^{(l)}$ 的列数必须等于 $a^{(l-1)}$ 的行数
-
数值计算错误:
- 特别是 Sigmoid 函数的值在 0 - 1 之间
- 如果得到大于 1 或小于 0 的值,肯定计算有误
计算验证
为了验证我们的手算结果,下面用 Numpy 实现相同的计算:
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# 输入数据
x = np.array([[0.5], [0.8]])
# 第一层参数
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
b1 = np.array([[0.1], [0.2], [0.3]])
# 第二层参数
W2 = np.array([[0.7], [0.8], [0.9]])
b2 = np.array([[0.4]])
# 前馈计算
z1 = np.dot(W1.T, x) + b1
a1 = sigmoid(z1)
z2 = np.dot(W2.T, a1) + b2
a2 = sigmoid(z2)
print("隐藏层输出:", a1)
print("最终输出:", a2)
运行结果应该与我们的手算结果一致:
隐藏层输出:[[0.57688526]
[0.66150316]
[0.7368894]]
最终输出:[[0.85241335]]
延伸思考
掌握了基础的前馈计算后,可以尝试以下扩展练习:
- 改变网络结构,比如增加隐藏层数量或神经元数量
- 尝试不同的激活函数(ReLU、tanh 等)
- 使用不同的输入值,观察输出如何变化
- 尝试实现完整的 BP 算法,包括反向传播
- 探索权重初始化对输出的影响
手算练习模板
为了方便练习,这里提供一个空白的手算模板:
| 计算步骤 | 公式 | 数值 | 维度检查 |
|---|---|---|---|
| 输入层 | $a^{(0)} = x$ | ||
| 隐藏层加权和 | $z^{(1)} = W^{(1)T}a^{(0)} + b^{(1)}$ | ||
| 隐藏层激活 | $a^{(1)} = g(z^{(1)})$ | ||
| 输出层加权和 | $z^{(2)} = W^{(2)T}a^{(1)} + b^{(2)}$ | ||
| 输出层激活 | $a^{(2)} = g(z^{(2)})$ |
总结
通过这个详细的例题演示,我们一步步完成了 BP 神经网络的前馈计算过程。从数学公式到实际计算,再到代码验证,这一完整的流程帮助我们深入理解了神经网络的基本工作原理。
手算虽然繁琐,但对于真正掌握神经网络的内在机制非常有帮助。建议读者按照这个例子自己动手计算一遍,然后尝试不同的网络结构和参数,观察输出的变化规律。只有通过这样的实践,才能对神经网络有更深入的理解。
当你熟练掌握了前馈计算后,可以继续学习反向传播算法,这是训练神经网络的核心。理解这两个过程后,你就掌握了神经网络最基础也是最重要的部分。
