深度学习入门复盘---浅层神经网络完整训练链路
打通浅层神经网络完整训练链路,告别知识点碎片化
前言
前几周分开学习网络层、激活函数、损失函数、反向传播、数值微分等知识点时,单独看每一块理论、代码都能理解,但存在一个很大的问题:知识点完全碎片化,无法串联起一套完整的神经网络训练流程。
单独拿反向传播看得懂,单独看损失函数也明白,但不知道数据从前到后如何流转、损失如何指导权重更新、两种梯度计算方式该分别用在什么场景。
本周我完整复盘前三周全部学习内容,固定一条主线梳理全部知识:
感知机 → 多层神经网络 → 前向传播计算 → 损失函数误差求解 → 梯度反向更新
配合手写计算图手动推导链式求导,对比数值微分、反向传播两套梯度方案,搭建一套完整、闭环的浅层神经网络知识框架,彻底解决知识割裂问题。
一、知识主线梳理:由简到繁完整链路
1. 基础单元:单层感知机
感知机是所有神经网络的最小基础单元,也是多层网络的前身。
- 计算逻辑:输入特征与权重相乘求和,叠加偏置
a=x1w1+x2w2+...+ba = x_1w_1+x_2w_2+...+ba=x1w1+x2w2+...+b - 固有缺陷:仅能拟合线性可分数据,无法处理非线性任务;
- 解决方案:多层叠加 + 激活函数,构建多层感知机(MLP)。
2. 多层神经网络(两层网络:输入层-隐藏层-输出层)
多层网络核心是引入隐藏层+非线性激活函数,让模型具备拟合复杂数据的能力,完整正向数据流称为前向传播。
前向传播完整计算流程
1)输入层→隐藏层
加权求和:a1=X⋅W1+b1a_1 = X \cdot W_1 + b_1a1=X⋅W1+b1
激活引入非线性:z1=Sigmoid(a1)z_1 = Sigmoid(a_1)z1=Sigmoid(a1)
2)隐藏层→输出层
加权求和:a2=z1⋅W2+b2a_2 = z_1 \cdot W_2 + b_2a2=z1⋅W2+b2
输出预测值:y=Sigmoid(a2)y = Sigmoid(a_2)y=Sigmoid(a2)
前向传播只负责根据输入数据算出预测结果,不会更新任何权重参数,想要优化模型,必须计算误差、更新权重。
3. 损失函数:量化模型预测误差
损失函数相当于模型的“扣分标准”,数值越大代表预测结果和真实标签差距越大,训练核心目标就是持续降低损失。
- 二分类任务:交叉熵损失(本文代码使用)
Loss=−∑(t⋅log(y+δ)),δ=1e−7Loss = -\sum(t \cdot log(y + \delta)),\quad \delta=1e-7Loss=−∑(t⋅log(y+δ)),δ=1e−7
增加极小值 δ\deltaδ,防止对数输入为0出现负无穷计算报错; - 回归任务:均方误差 MSE。
4. 梯度:权重更新的核心依据
损失由所有权重 W1,b1,W2,b2W_1,b_1,W_2,b_2W1,b1,W2,b2 共同决定,梯度定义为损失对权重的偏导数:
- 梯度正负:决定权重调大还是调小;
- 梯度绝对值:代表损失对该权重的敏感程度;
- 梯度求解分两种方案:数值微分、反向传播。
5. 两种梯度计算方式对比(核心复盘内容)
5.1 数值微分(数值梯度)
- 核心原理:微小偏移权重,通过损失变化用差分近似求导
grad=f(x+h)−f(x−h)2h,h=1e−4grad=\frac{f(x+h)-f(x-h)}{2h},\quad h=1e-4grad=2hf(x+h)−f(x−h),h=1e−4 - 优点:无需手动推导复杂链式求导,逻辑简单,可当作标准答案;
- 缺点:每一个权重参数都需要两次前向传播,批量数据、深层网络计算速度极慢;
- 使用场景:仅用于梯度校验,验证反向传播代码推导是否出错,不参与正式训练。
5.2 反向传播(解析梯度/链式求导)
- 核心原理:依托计算图,从输出层向输入层反向逐层求导,链式法则相乘得到各层梯度;
- 计算流程:损失 → 输出层梯度 → 隐藏层梯度 → 得到 dW2、db2、dW1、db1dW_2、db_2、dW_1、db_1dW2、db2、dW1、db1;
- 优点:一次前向+一次反向即可算出全部梯度,计算效率极高,适配大数据训练;
- 缺点:需要手动推导求导公式,公式写错会导致梯度完全失效;
- 使用场景:神经网络正式训练唯一梯度计算方式。
6. 完整闭环训练全流程(串联所有零散知识点)
- 数据集划分:训练集、测试集分离;
- 前向传播:输入数据,计算模型预测值;
- 损失计算:通过损失函数得到当前预测误差;
- 反向传播链式求导:计算所有权重梯度;
- 梯度下降更新参数:W=W−lr×gradW = W - lr \times gradW=W−lr×grad;
- 多轮循环迭代,持续降低损失;
- 测试集验证损失,判断模型泛化能力,识别过拟合。
二、计算图链式求导复盘思路
本次复盘不只是背诵公式,手动绘制完整计算图,打通正向、反向数据传递逻辑:
- 正向数据流绘图:
X→W1→a1→Sigmoid→z1→W2→a2→Sigmoid→y→LossX \rightarrow W_1 \rightarrow a_1 \rightarrow Sigmoid \rightarrow z_1 \rightarrow W_2 \rightarrow a_2 \rightarrow Sigmoid \rightarrow y \rightarrow LossX→W1→a1→Sigmoid→z1→W2→a2→Sigmoid→y→Loss - 反向梯度传递推导:
从Loss节点反向逐层求偏导,链式相乘:
∂Loss∂y→∂y∂a2→∂a2∂W2→∂a2∂z1→∂z1∂a1→∂a1∂W1\frac{\partial Loss}{\partial y} \rightarrow \frac{\partial y}{\partial a_2} \rightarrow \frac{\partial a_2}{\partial W_2} \rightarrow \frac{\partial a_2}{\partial z_1} \rightarrow \frac{\partial z_1}{\partial a_1} \rightarrow \frac{\partial a_1}{\partial W_1}∂y∂Loss→∂a2∂y→∂W2∂a2→∂z1∂a2→∂a1∂z1→∂W1∂a1 - 对应关系:每一段求导结果,完全对应代码里反向传播梯度计算公式,做到理论和代码一一对应。
三、配套验证代码(梯度对比实验)
通过代码直观验证数值梯度、反向传播梯度误差,佐证理论结论,运行后可打印梯度误差,判断反向传播推导正确性。
import numpy as np
# ---------------------- 基础工具函数 ----------------------
# Sigmoid激活函数,限制数值防止exp溢出
def sigmoid(x):
x = np.clip(x, -500, 500)
return 1 / (1 + np.exp(-x))
# Sigmoid导数,反向传播链式求导核心
def sigmoid_deriv(x):
s = sigmoid(x)
return s * (1 - s)
# 二分类交叉熵损失函数
def cross_entropy(y_pred, y_true):
delta = 1e-7 # 避免log(0)负无穷
return -np.sum(y_true * np.log(y_pred + delta))
# 数值微分:数值梯度,仅用于梯度校验
def numerical_gradient(func, weight):
h = 1e-4
grad = np.zeros_like(weight)
# 遍历权重所有参数,中心差分计算梯度
for idx in np.ndindex(weight.shape):
original = weight[idx]
weight[idx] = original + h
loss_h = func(weight)
weight[idx] = original - h
loss_l = func(weight)
grad[idx] = (loss_h - loss_l) / (2 * h)
weight[idx] = original
return grad
# ---------------------- 两层神经网络封装类 ----------------------
class TwoLayerNet:
def __init__(self, input_dim, hidden_dim, output_dim):
# 初始化权重、偏置
self.params = {
"W1": np.random.randn(input_dim, hidden_dim) * 0.01,
"b1": np.zeros(hidden_dim),
"W2": np.random.randn(hidden_dim, output_dim) * 0.01,
"b2": np.zeros(output_dim)
}
# 前向传播,缓存中间变量供反向传播使用
def forward(self, x):
W1, b1 = self.params["W1"], self.params["b1"]
W2, b2 = self.params["W2"], self.params["b2"]
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = sigmoid(a2)
return y, z1, a1
# 计算损失
def loss(self, x, label):
y, _, _ = self.forward(x)
return cross_entropy(y, label)
# 反向传播:解析梯度,正式训练使用
def backprop_grad(self, x, label):
batch_size = x.shape[0]
W1, b1 = self.params["W1"], self.params["b1"]
W2, b2 = self.params["W2"], self.params["b2"]
y, z1, a1 = self.forward(x)
# 输出层梯度
dy = (y - label) / batch_size
dW2 = np.dot(z1.T, dy)
db2 = np.sum(dy, axis=0)
# 隐藏层链式求导梯度
dz1 = np.dot(dy, W2.T)
da1 = sigmoid_deriv(a1) * dz1
dW1 = np.dot(x.T, da1)
db1 = np.sum(da1, axis=0)
grads = {"W1": dW1, "b1": db1, "W2": dW2, "b2": db2}
return grads
# 获取数值梯度,校验专用
def num_grad(self, x, label):
loss_func = lambda w: self.loss(x, label)
grads = {}
grads["W1"] = numerical_gradient(loss_func, self.params["W1"])
grads["b1"] = numerical_gradient(loss_func, self.params["b1"])
grads["W2"] = numerical_gradient(loss_func, self.params["W2"])
grads["b2"] = numerical_gradient(loss_func, self.params["b2"])
return grads
# ---------------------- 梯度对比测试入口 ----------------------
if __name__ == "__main__":
np.random.seed(1)
# 构造少量测试数据,降低数值梯度计算耗时
test_x = np.random.rand(10, 2)
test_label = np.random.randint(0, 2, (10, 1))
net = TwoLayerNet(input_dim=2, hidden_dim=8, output_dim=1)
ana_grads = net.backprop_grad(test_x, test_label)
num_grads = net.num_grad(test_x, test_label)
# 计算两种梯度平均误差
err_W1 = np.mean(np.abs(ana_grads["W1"] - num_grads["W1"]))
err_W2 = np.mean(np.abs(ana_grads["W2"] - num_grads["W2"]))
print("===== 数值梯度 VS 反向传播梯度校验结果 =====")
print(f"W1梯度平均误差:{err_W1:.10f}")
print(f"W2梯度平均误差:{err_W2:.10f}")
print("判定标准:误差小于1e-4,代表反向传播链式求导公式正确")
print("总结:数值梯度仅校验,反向传播用于模型完整训练")

四、复盘总结
- 解决核心痛点:此前单独学习激活函数、损失函数、反向传播都能看懂,但各模块相互割裂,无法梳理完整训练链路;
- 梳理逻辑:以
感知机→多层网络→前向传播→损失计算→梯度更新为主线串联全部知识点,形成完整知识闭环; - 推导实操:手写计算图完成链式求导推导,从数学层面理解梯度传递逻辑;
- 梯度分层使用:清晰区分数值微分、反向传播优缺点与适用场景,建立规范的代码调试流程;
- 长期收益:搭建起浅层神经网络完整基础框架,后续学习深层网络、论文时能够快速理解底层训练逻辑,不会再出现基础断层。
文末
如果你也刚入门深度学习,出现知识点看得懂、拼不起完整训练流程的问题,可以按照这条主线复盘梳理,配合梯度校验代码实操,能快速打通底层逻辑。有任何反向传播、矩阵维度相关问题欢迎评论交流!
更多推荐


所有评论(0)