共计 3108 个字符,预计需要花费 8 分钟才能阅读完成。
神经网络基础概念回顾
神经网络是一种模仿生物神经元结构的计算模型,由输入层、隐藏层和输出层组成。每个神经元接收来自前一层神经元的输入,通过加权求和后经过激活函数处理,输出到下一层。全连接神经网络(Fully Connected Neural Network)是指相邻层之间的每个神经元都相互连接的网络结构。

为什么选择这种简单结构作为教学示例
- 易于理解 :两个神经元和两个隐藏层的结构足够简单,可以清晰地展示神经网络的运行原理,避免初学者被复杂结构分散注意力。
- 便于调试 :小规模网络可以快速验证代码的正确性,尤其是反向传播的实现。
- 计算效率高 :小规模网络在训练和推理时计算量小,适合教学演示。
- 扩展性强 :理解这种简单结构后,可以轻松扩展到更复杂的网络。
前向传播的数学推导和代码实现
前向传播是指数据从输入层经过隐藏层传递到输出层的过程。对于两个神经元和两个隐藏层的网络,数学推导如下:
- 输入层到第一隐藏层 :
- 输入数据 (X) 是一个 (n \times 2) 的矩阵,其中 (n) 是样本数量。
- 第一隐藏层的权重 (W_1) 是一个 (2 \times 2) 的矩阵,偏置 (b_1) 是一个 (2) 维向量。
-
第一隐藏层的输出 (H_1 = \sigma(X W_1 + b_1)),其中 (\sigma) 是激活函数(如 ReLU 或 Sigmoid)。
-
第一隐藏层到第二隐藏层 :
- 第二隐藏层的权重 (W_2) 是一个 (2 \times 2) 的矩阵,偏置 (b_2) 是一个 (2) 维向量。
-
第二隐藏层的输出 (H_2 = \sigma(H_1 W_2 + b_2))。
-
第二隐藏层到输出层 :
- 输出层的权重 (W_3) 是一个 (2 \times 1) 的矩阵,偏置 (b_3) 是一个标量。
- 输出层的输出 (Y = \sigma(H_2 W_3 + b_3))。
以下是前向传播的 Python 实现:
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def forward_pass(X, W1, b1, W2, b2, W3, b3):
# 第一隐藏层
H1 = sigmoid(np.dot(X, W1) + b1)
# 第二隐藏层
H2 = sigmoid(np.dot(H1, W2) + b2)
# 输出层
Y = sigmoid(np.dot(H2, W3) + b3)
return Y, H1, H2
反向传播的详细步骤和梯度计算
反向传播是通过链式法则计算损失函数对网络参数的梯度,用于更新权重和偏置。以下是反向传播的数学推导:
- 计算输出层的梯度 :
- 损失函数 (L) 对输出 (Y) 的梯度 (\frac{\partial L}{\partial Y} = Y – y_{\text{true}})。
- (Y) 对 (W_3) 的梯度 (\frac{\partial Y}{\partial W_3} = H_2^T \cdot \sigma'(H_2 W_3 + b_3))。
-
更新 (W_3) 和 (b_3) 的梯度。
-
计算第二隐藏层的梯度 :
- (\frac{\partial L}{\partial H_2} = \frac{\partial L}{\partial Y} \cdot W_3^T \cdot \sigma'(H_2 W_3 + b_3))。
- (H_2) 对 (W_2) 的梯度 (\frac{\partial H_2}{\partial W_2} = H_1^T \cdot \sigma'(H_1 W_2 + b_2))。
-
更新 (W_2) 和 (b_2) 的梯度。
-
计算第一隐藏层的梯度 :
- (\frac{\partial L}{\partial H_1} = \frac{\partial L}{\partial H_2} \cdot W_2^T \cdot \sigma'(H_1 W_2 + b_2))。
- (H_1) 对 (W_1) 的梯度 (\frac{\partial H_1}{\partial W_1} = X^T \cdot \sigma'(X W_1 + b_1))。
- 更新 (W_1) 和 (b_1) 的梯度。
以下是反向传播的 Python 实现:
def backward_pass(X, y_true, Y, H1, H2, W1, W2, W3, b1, b2, b3):
# 输出层梯度
dY = Y - y_true
dW3 = np.dot(H2.T, dY * Y * (1 - Y))
db3 = np.sum(dY * Y * (1 - Y), axis=0)
# 第二隐藏层梯度
dH2 = np.dot(dY * Y * (1 - Y), W3.T) * H2 * (1 - H2)
dW2 = np.dot(H1.T, dH2 * H2 * (1 - H2))
db2 = np.sum(dH2 * H2 * (1 - H2), axis=0)
# 第一隐藏层梯度
dH1 = np.dot(dH2 * H2 * (1 - H2), W2.T) * H1 * (1 - H1)
dW1 = np.dot(X.T, dH1 * H1 * (1 - H1))
db1 = np.sum(dH1 * H1 * (1 - H1), axis=0)
return dW1, db1, dW2, db2, dW3, db3
完整的 Python 实现代码
以下是一个完整的训练循环实现,包括参数初始化和梯度更新:
# 初始化参数
np.random.seed(42)
W1 = np.random.randn(2, 2)
b1 = np.zeros(2)
W2 = np.random.randn(2, 2)
b2 = np.zeros(2)
W3 = np.random.randn(2, 1)
b3 = np.zeros(1)
# 训练数据
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([[0], [1], [1], [0]])
# 训练循环
learning_rate = 0.1
epochs = 10000
for epoch in range(epochs):
# 前向传播
Y, H1, H2 = forward_pass(X, W1, b1, W2, b2, W3, b3)
# 反向传播
dW1, db1, dW2, db2, dW3, db3 = backward_pass(X, y, Y, H1, H2, W1, W2, W3, b1, b2, b3)
# 更新参数
W1 -= learning_rate * dW1
b1 -= learning_rate * db1
W2 -= learning_rate * dW2
b2 -= learning_rate * db2
W3 -= learning_rate * dW3
b3 -= learning_rate * db3
# 打印损失
if epoch % 1000 == 0:
loss = np.mean((Y - y) ** 2)
print(f'Epoch {epoch}, Loss: {loss:.4f}')
# 测试
Y_pred, _, _ = forward_pass(X, W1, b1, W2, b2, W3, b3)
print('Predictions:', Y_pred)
常见问题与调试技巧
- 梯度消失或爆炸 :
- 使用合适的初始化方法(如 Xavier 初始化)。
- 选择适当的激活函数(如 ReLU)。
-
调整学习率。
-
训练不收敛 :
- 检查损失函数是否正确实现。
- 确保梯度计算无误。
-
尝试更小的学习率。
-
过拟合 :
- 增加训练数据。
- 使用正则化(如 L2 正则化)。
- 减少网络复杂度。
性能优化建议
- 向量化计算 :使用 NumPy 的矩阵运算代替循环,提高计算效率。
- 批量训练 :使用小批量梯度下降(Mini-batch Gradient Descent)加速训练。
- 并行化 :利用多核 CPU 或 GPU 加速计算。
如何扩展更复杂的网络结构
- 增加隐藏层 :通过堆叠更多隐藏层构建深度神经网络。
- 改变激活函数 :尝试 ReLU、LeakyReLU 等不同激活函数。
- 引入卷积层 :扩展为卷积神经网络(CNN)处理图像数据。
- 引入循环层 :扩展为循环神经网络(RNN)处理序列数据。
结语
通过实现这个简单的全连接神经网络,我们深入理解了前向传播和反向传播的核心原理。下一步,可以尝试修改网络结构或实现其他激活函数,进一步巩固对神经网络的理解。希望这篇文章能帮助你从零开始构建自己的神经网络模型!
正文完
