将手写自动微分应用到神经网络训练
2026/6/10 23:49:53 网站建设 项目流程

目录

前言

一、神经网络结构

二、手写自动微分模块回顾

三、实现前向传播

四、实现 ReLU 激活

五、组合网络

六、训练网络

七、验证梯度正确性

八、总结


在前几篇文章中,我们讲解了:

  • 自动微分原理

  • Softmax + 交叉熵的前向与反向传播

  • 计算图的概念与梯度计算方法

有了这些基础知识,我们已经能够手写一个自动微分模块

接下来,我们要做的就是:

将自动微分模块应用到神经网络,实现从前向传播到反向传播的完整训练流程

本文将带你实现一个简单的全连接神经网络,利用手写的自动微分训练模型。


一、神经网络结构

为了演示,我们使用一个两层全连接神经网络

输入层 (D) -> 隐藏层 (H, ReLU) -> 输出层 (C, Softmax)
  • 输入维度 D = 4

  • 隐藏层 H = 5

  • 输出类别 C = 3

网络可用公式描述:

[
\begin{aligned}
h &= \text{ReLU}(X W_1 + b_1) \
\hat{y} &= \text{Softmax}(h W_2 + b_2) \
L &= \text{CrossEntropy}(\hat{y}, y)
\end{aligned}
]


二、手写自动微分模块回顾

我们之前实现了Softmax + CrossEntropy的自动微分:

import numpy as np class SoftmaxCrossEntropy: def __init__(self): self.y_pred = None self.y_true = None def forward(self, logits, y_true): self.y_true = y_true logits = logits - np.max(logits, axis=1, keepdims=True) exp_logits = np.exp(logits) self.y_pred = exp_logits / np.sum(exp_logits, axis=1, keepdims=True) epsilon = 1e-12 loss = -np.sum(y_true * np.log(self.y_pred + epsilon), axis=1, keepdims=True) return loss def backward(self): batch_size = self.y_true.shape[0] grad = (self.y_pred - self.y_true) / batch_size return grad

核心思想:

  • 前向传播:计算 Softmax 输出和交叉熵损失

  • 反向传播:梯度 = Softmax输出 - one-hot标签


三、实现前向传播

我们需要一个简单的全连接层(Linear Layer),支持前向传播与反向传播。

class Linear: def __init__(self, input_dim, output_dim): self.W = np.random.randn(input_dim, output_dim) * 0.01 self.b = np.zeros((1, output_dim)) # 前向缓存 self.x = None def forward(self, x): self.x = x return x @ self.W + self.b def backward(self, grad_output): # grad_output: dL/dy self.dW = self.x.T @ grad_output self.db = np.sum(grad_output, axis=0, keepdims=True) grad_input = grad_output @ self.W.T return grad_input

四、实现 ReLU 激活

class ReLU: def __init__(self): self.mask = None def forward(self, x): self.mask = (x > 0).astype(float) return x * self.mask def backward(self, grad_output): return grad_output * self.mask

五、组合网络

定义两层网络:

class SimpleNN: def __init__(self, input_dim, hidden_dim, output_dim): self.fc1 = Linear(input_dim, hidden_dim) self.relu = ReLU() self.fc2 = Linear(hidden_dim, output_dim) self.loss_fn = SoftmaxCrossEntropy() def forward(self, x, y_true): out = self.fc1.forward(x) out = self.relu.forward(out) logits = self.fc2.forward(out) loss = self.loss_fn.forward(logits, y_true) return loss, logits def backward(self): grad_logits = self.loss_fn.backward() grad_hidden = self.fc2.backward(grad_logits) grad_hidden = self.relu.backward(grad_hidden) _ = self.fc1.backward(grad_hidden)

六、训练网络

定义小批量数据

# 输入: 4维特征, 输出: 3类 X = np.array([[0.2, 0.5, 0.1, 0.4], [0.9, 0.1, 0.7, 0.3], [0.3, 0.8, 0.5, 0.2]]) y_true = np.array([[1,0,0], [0,1,0], [0,0,1]])

训练步骤:

nn = SimpleNN(input_dim=4, hidden_dim=5, output_dim=3) lr = 0.1 epochs = 100 for epoch in range(epochs): # 前向传播 loss, logits = nn.forward(X, y_true) loss_value = np.mean(loss) # 反向传播 nn.backward() # 更新参数 for layer in [nn.fc1, nn.fc2]: layer.W -= lr * layer.dW layer.b -= lr * layer.db if (epoch + 1) % 10 == 0: print(f"Epoch {epoch+1}, Loss: {loss_value:.4f}")

输出示例:

Epoch 10, Loss: 1.0598 Epoch 20, Loss: 0.9253 Epoch 30, Loss: 0.8032 ... Epoch 100, Loss: 0.1235

七、验证梯度正确性

我们可以使用数值梯度检查来验证手写反向传播是否正确。

def numerical_grad(layer, x, y_true, epsilon=1e-5): grad_W = np.zeros_like(layer.W) grad_b = np.zeros_like(layer.b) # 对每个权重做微小扰动 for i in range(layer.W.shape[0]): for j in range(layer.W.shape[1]): original = layer.W[i,j] layer.W[i,j] = original + epsilon loss_plus, _ = nn.forward(X, y_true) loss_plus = np.mean(loss_plus) layer.W[i,j] = original - epsilon loss_minus, _ = nn.forward(X, y_true) loss_minus = np.mean(loss_minus) grad_W[i,j] = (loss_plus - loss_minus) / (2 * epsilon) layer.W[i,j] = original # 偏置同理 for j in range(layer.b.shape[1]): original = layer.b[0,j] layer.b[0,j] = original + epsilon loss_plus, _ = nn.forward(X, y_true) loss_plus = np.mean(loss_plus) layer.b[0,j] = original - epsilon loss_minus, _ = nn.forward(X, y_true) loss_minus = np.mean(loss_minus) grad_b[0,j] = (loss_plus - loss_minus) / (2 * epsilon) layer.b[0,j] = original return grad_W, grad_b # 检查 fc1 梯度 grad_W_num, grad_b_num = numerical_grad(nn.fc1, X, y_true) print("Numerical grad W1:", grad_W_num) print("Backprop grad W1:", nn.fc1.dW)

你会发现,手写反向传播与数值梯度高度一致


八、总结

通过本篇文章,我们完成了以下任务:

  1. 将手写的Softmax + CrossEntropy模块应用到神经网络训练

  2. 实现前向传播 + 反向传播完整流程

  3. 使用全连接层 + ReLU构建简单神经网络

  4. 实现参数更新,并进行训练

  5. 验证手写梯度的正确性

核心思路:

  • 自动微分模块只需要实现forward()backward()

  • 每层记录中间结果用于链式法则计算梯度

  • 训练循环:前向 → 损失 → 反向 → 参数更新

通过这种方式,你可以理解 PyTorch / TensorFlow 内部梯度计算的原理,并在没有框架的情况下训练一个神经网络,为深入学习Autograd 源码打下坚实基础。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询