从 0 到 1 写一个神经网络训练循环:别再只会 `model.fit()` 了
从 0 到 1 写一个神经网络训练循环:别再只会 model.fit() 了
作者 | Echo_Wish
很多人学深度学习的时候,其实都经历过一个阶段:
刚接触的时候,觉得 神经网络特别神秘。
后来接触到 PyTorch / TensorFlow,写代码越来越简单:
model.fit(x, y)
训练就开始了。
准确率还挺高。
于是很多人就停在这里了。
但如果你问一句:
神经网络到底是怎么训练的?
很多人就开始沉默了。
其实说白了,神经网络训练本质就做三件事:
1️⃣ 前向计算(Forward)
2️⃣ 计算损失(Loss)
3️⃣ 反向传播更新参数(Backward)
今天这篇文章,我想带大家 不用任何深度学习框架,只用 Python + NumPy,从零写一个 神经网络训练循环。
你会发现:
神经网络其实没有那么神秘,它本质上就是一堆数学计算。
我们一步一步来。
一、先看整体结构:训练循环到底在干什么
在正式写代码之前,先看一个训练循环的逻辑结构。
一个完整的训练循环其实就是:
数据输入
↓
前向传播
↓
计算损失
↓
反向传播
↓
更新参数
↓
重复很多次
所以我们接下来要做的事情就是:
把这五步用 Python 写出来。
二、先实现一个最简单的神经网络
我们先做一个最简单的网络:
输入层 (2)
↓
隐藏层 (3)
↓
输出层 (1)
也就是:
2 → 3 → 1
先初始化参数。
import numpy as np
np.random.seed(42)
# 输入 -> 隐藏层
W1 = np.random.randn(2, 3)
b1 = np.zeros((1, 3))
# 隐藏层 -> 输出层
W2 = np.random.randn(3, 1)
b2 = np.zeros((1, 1))
这里我们用 随机数初始化权重。
这是神经网络的常见做法。
如果全部初始化为 0,模型就学不出东西。
三、实现前向传播(Forward)
神经网络本质上就是:
y = activation(Wx + b)
我们先实现一个 ReLU 激活函数:
def relu(x):
return np.maximum(0, x)
然后写前向传播:
def forward(X):
global W1, b1, W2, b2
# 第一层
z1 = X @ W1 + b1
a1 = relu(z1)
# 第二层
z2 = a1 @ W2 + b2
y_pred = z2
cache = (X, z1, a1, z2)
return y_pred, cache
这里的 cache 很重要。
因为 反向传播需要用到前向传播的中间结果。
所以要把它们保存下来。
四、定义损失函数
我们用最简单的 均方误差(MSE):
Loss = (y_pred - y)^2
代码实现:
def mse_loss(y_pred, y_true):
loss = np.mean((y_pred - y_true) ** 2)
return loss
如果预测和真实值差距越大,loss 就越大。
训练的目标就是:
让 loss 变小。
五、手写反向传播(核心)
这是很多人觉得最难的地方。
其实逻辑非常简单:
我们只需要计算 梯度。
先写一个简单版本。
def backward(cache, y_pred, y_true, lr=0.01):
global W1, b1, W2, b2
X, z1, a1, z2 = cache
m = y_true.shape[0]
# dLoss/dz2
dz2 = (y_pred - y_true) / m
# 输出层梯度
dW2 = a1.T @ dz2
db2 = np.sum(dz2, axis=0, keepdims=True)
# 传播到隐藏层
da1 = dz2 @ W2.T
dz1 = da1 * (z1 > 0)
dW1 = X.T @ dz1
db1 = np.sum(dz1, axis=0, keepdims=True)
# 梯度下降更新参数
W1 -= lr * dW1
b1 -= lr * db1
W2 -= lr * dW2
b2 -= lr * db2
这段代码本质上就是在做:
梯度下降
公式其实很简单:
W = W - learning_rate * gradient
这就是深度学习最核心的优化方式。
六、写完整训练循环
现在我们可以把所有东西串起来。
先准备一点训练数据。
# 模拟数据
X = np.random.randn(100, 2)
y = (X[:,0] + X[:,1]).reshape(-1,1)
目标其实很简单:
y = x1 + x2
然后写训练循环。
epochs = 1000
for epoch in range(epochs):
y_pred, cache = forward(X)
loss = mse_loss(y_pred, y)
backward(cache, y_pred, y, lr=0.01)
if epoch % 100 == 0:
print(f"epoch {epoch}, loss {loss}")
运行后你会看到:
loss 不断下降
说明网络正在 学习规律。
七、为什么理解训练循环很重要
很多人学习深度学习的时候,一直在用:
model.fit()
其实这样会带来一个问题:
不理解模型是怎么训练的。
一旦遇到问题,比如:
- 梯度爆炸
- 模型不收敛
- loss 不下降
就完全不知道怎么排查。
但如果你自己写过训练循环,就会发现:
神经网络其实就是:
矩阵乘法
+
激活函数
+
梯度下降
所有复杂的深度学习框架,本质上都是在做这些事情。
八、我个人的一点感受
这些年我见过很多做 AI 的工程师。
有些人非常依赖框架:
TensorFlow
PyTorch
Keras
模型一跑就几十层。
但如果你问:
反向传播是怎么实现的?
很多人其实说不清。
我一直觉得:
真正理解一件事情,最好的方式就是自己实现一遍。
哪怕只是一个最简单的版本。
当你从零写出一个神经网络训练循环时,你会突然发现:
原来深度学习没有那么神秘。
它只是把数学、算法和工程实现结合在一起而已。
结尾
今天我们做了一件非常有意思的事情:
用 纯 Python + NumPy,实现了一个完整的神经网络训练循环。
核心步骤其实只有五步:
初始化参数
前向传播
计算损失
反向传播
更新权重
理解了这些,你再去看:
- PyTorch
- TensorFlow
- JAX
就会非常清晰。
- 点赞
- 收藏
- 关注作者
评论(0)