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

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
评论(没有评论)