手写全连接神经网络实战:从两个神经元到反向传播的完整实现

1次阅读
没有评论

共计 3108 个字符,预计需要花费 8 分钟才能阅读完成。

image.webp

神经网络基础概念回顾

神经网络是一种模仿生物神经元结构的计算模型,由输入层、隐藏层和输出层组成。每个神经元接收来自前一层神经元的输入,通过加权求和后经过激活函数处理,输出到下一层。全连接神经网络(Fully Connected Neural Network)是指相邻层之间的每个神经元都相互连接的网络结构。

手写全连接神经网络实战:从两个神经元到反向传播的完整实现

为什么选择这种简单结构作为教学示例

  1. 易于理解 :两个神经元和两个隐藏层的结构足够简单,可以清晰地展示神经网络的运行原理,避免初学者被复杂结构分散注意力。
  2. 便于调试 :小规模网络可以快速验证代码的正确性,尤其是反向传播的实现。
  3. 计算效率高 :小规模网络在训练和推理时计算量小,适合教学演示。
  4. 扩展性强 :理解这种简单结构后,可以轻松扩展到更复杂的网络。

前向传播的数学推导和代码实现

前向传播是指数据从输入层经过隐藏层传递到输出层的过程。对于两个神经元和两个隐藏层的网络,数学推导如下:

  1. 输入层到第一隐藏层
  2. 输入数据 (X) 是一个 (n \times 2) 的矩阵,其中 (n) 是样本数量。
  3. 第一隐藏层的权重 (W_1) 是一个 (2 \times 2) 的矩阵,偏置 (b_1) 是一个 (2) 维向量。
  4. 第一隐藏层的输出 (H_1 = \sigma(X W_1 + b_1)),其中 (\sigma) 是激活函数(如 ReLU 或 Sigmoid)。

  5. 第一隐藏层到第二隐藏层

  6. 第二隐藏层的权重 (W_2) 是一个 (2 \times 2) 的矩阵,偏置 (b_2) 是一个 (2) 维向量。
  7. 第二隐藏层的输出 (H_2 = \sigma(H_1 W_2 + b_2))。

  8. 第二隐藏层到输出层

  9. 输出层的权重 (W_3) 是一个 (2 \times 1) 的矩阵,偏置 (b_3) 是一个标量。
  10. 输出层的输出 (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

反向传播的详细步骤和梯度计算

反向传播是通过链式法则计算损失函数对网络参数的梯度,用于更新权重和偏置。以下是反向传播的数学推导:

  1. 计算输出层的梯度
  2. 损失函数 (L) 对输出 (Y) 的梯度 (\frac{\partial L}{\partial Y} = Y – y_{\text{true}})。
  3. (Y) 对 (W_3) 的梯度 (\frac{\partial Y}{\partial W_3} = H_2^T \cdot \sigma'(H_2 W_3 + b_3))。
  4. 更新 (W_3) 和 (b_3) 的梯度。

  5. 计算第二隐藏层的梯度

  6. (\frac{\partial L}{\partial H_2} = \frac{\partial L}{\partial Y} \cdot W_3^T \cdot \sigma'(H_2 W_3 + b_3))。
  7. (H_2) 对 (W_2) 的梯度 (\frac{\partial H_2}{\partial W_2} = H_1^T \cdot \sigma'(H_1 W_2 + b_2))。
  8. 更新 (W_2) 和 (b_2) 的梯度。

  9. 计算第一隐藏层的梯度

  10. (\frac{\partial L}{\partial H_1} = \frac{\partial L}{\partial H_2} \cdot W_2^T \cdot \sigma'(H_1 W_2 + b_2))。
  11. (H_1) 对 (W_1) 的梯度 (\frac{\partial H_1}{\partial W_1} = X^T \cdot \sigma'(X W_1 + b_1))。
  12. 更新 (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)

常见问题与调试技巧

  1. 梯度消失或爆炸
  2. 使用合适的初始化方法(如 Xavier 初始化)。
  3. 选择适当的激活函数(如 ReLU)。
  4. 调整学习率。

  5. 训练不收敛

  6. 检查损失函数是否正确实现。
  7. 确保梯度计算无误。
  8. 尝试更小的学习率。

  9. 过拟合

  10. 增加训练数据。
  11. 使用正则化(如 L2 正则化)。
  12. 减少网络复杂度。

性能优化建议

  1. 向量化计算 :使用 NumPy 的矩阵运算代替循环,提高计算效率。
  2. 批量训练 :使用小批量梯度下降(Mini-batch Gradient Descent)加速训练。
  3. 并行化 :利用多核 CPU 或 GPU 加速计算。

如何扩展更复杂的网络结构

  1. 增加隐藏层 :通过堆叠更多隐藏层构建深度神经网络。
  2. 改变激活函数 :尝试 ReLU、LeakyReLU 等不同激活函数。
  3. 引入卷积层 :扩展为卷积神经网络(CNN)处理图像数据。
  4. 引入循环层 :扩展为循环神经网络(RNN)处理序列数据。

结语

通过实现这个简单的全连接神经网络,我们深入理解了前向传播和反向传播的核心原理。下一步,可以尝试修改网络结构或实现其他激活函数,进一步巩固对神经网络的理解。希望这篇文章能帮助你从零开始构建自己的神经网络模型!

正文完
 0
评论(没有评论)