020、深度学习入门:神经网络基础与反向传播

张开发
2026/4/4 23:30:57 15 分钟阅读

分享文章

020、深度学习入门:神经网络基础与反向传播
昨天调一个三层的全连接网络loss死活不降。打印梯度发现第一层的权重全是零——反向传播根本没传过去。同事凑过来看了一眼“你激活函数梯度写错了吧”一查代码果然在tanh求导的地方少了个平方。这种低级错误让我想起刚入门时反向传播的链式法则绕得人头晕。今天咱们就掰开揉碎讲清楚神经网络到底是怎么“学习”的。从实际问题切入梯度去哪了假设你在训练一个简单的分类网络结构是输入层(784) - 隐藏层(128) - 输出层(10)。前向推理代码大概长这样defforward(x):z1x w1b1# 第一层线性变换a1np.tanh(z1)# 激活函数z2a1 w2b2 outputsoftmax(z2)returnoutput前向传播容易理解就是数据一层层流过去。问题出在反向传播模型输出和真实标签的误差怎么反过来告诉每一层的权重该往哪个方向调整反向传播链式法则的工程实现反向传播的核心是链式求导。但工程实现时我们通常从后往前计算“局部梯度”。以两层网络为例损失函数L对w1的梯度要经过三层传递∂L/∂w1 ∂L/∂z2 · ∂z2/∂a1 · ∂a1/∂z1 · ∂z1/∂w1实际编程时我们不会真的展开这个式子而是用计算图的思想逐层回传。下面这个反向传播的实现很典型defbackward(x,y,output):# 输出层梯度dz2output-y# softmax交叉熵的梯度就是这个简洁形式记住能省事# 第二层权重梯度dw2a1.T dz2# 这里形状要对齐转置经常忘db2np.sum(dz2,axis0)# 往第一层回传da1dz2 w2.T# 注意这里w2要转置维度匹配容易出错dz1da1*(1-a1**2)# tanh的导数我当初就是这里少写了平方# 第一层权重梯度dw1x.T dz1 db1np.sum(dz1,axis0)returndw1,db1,dw2,db2注意看第11行tanh的导数是1 - tanh(x)^2。如果你写成1 - tanh(x)梯度值会小很多前面层的权重几乎更新不动。这就是开头说的bug来源。激活函数选型别只看准确率新手常纠结用什么激活函数。ReLU确实流行但死亡ReLU问题在嵌入式设备上很头疼——某些神经元一旦输出全零就再也激活不了。我的经验是中间层用LeakyReLU给负值留个0.01的斜率避免死亡二分类输出层用sigmoid多分类用softmax回归任务用线性层tanh在RNN里还行但深度网络容易梯度消失# 激活函数实现别写太复杂defleaky_relu(x,alpha0.01):returnnp.where(x0,x,x*alpha)# 向量化操作比for循环快几十倍# 反向传播时梯度要对应defleaky_relu_grad(x,alpha0.01):dxnp.ones_like(x)dx[x0]alpha# 小于0的位置梯度为alphareturndx初始化权重不是小事权重初始化不对网络可能一开始就“死”了。比如全用零初始化所有神经元输出相同梯度也相同相当于一个神经元。常用的Xavier初始化实现起来很简单definit_weights(input_size,output_size):limitnp.sqrt(6/(input_sizeoutput_size))returnnp.random.uniform(-limit,limit,(input_size,output_size))但要注意Xavier假设激活函数是线性的对ReLU系激活函数效果打折扣。这时用He初始化方差为2/n更合适。调试技巧梯度检查当你自己实现反向传播时一定要做梯度检查。用数值梯度验证解析梯度defgrad_check(layer_func,x,w,epsilon1e-7):grad_analyticbackward(x,w)# 你的反向传播结果grad_numericnp.zeros_like(w)foriinrange(w.size):w_plusw.copy()w_minusw.copy()w_plus.flat[i]epsilon w_minus.flat[i]-epsilon loss_pluslayer_func(x,w_plus)loss_minuslayer_func(x,w_minus)grad_numeric.flat[i](loss_plus-loss_minus)/(2*epsilon)diffnp.linalg.norm(grad_analytic-grad_numeric)/np.linalg.norm(grad_analyticgrad_numeric)returndiff1e-7# 小于这个阈值基本就对了这个操作计算量大训练时别用但验证新网络结构时能救命。个人经验别重复造轮子但要懂轮子现在深度学习框架很成熟TensorFlow/PyTorch一行代码就搞定反向传播。但我建议初学者至少手写一次全连接网络的反向传播包括矩阵维度变换、激活函数求导、损失函数求导。这个过程能让你真正理解梯度消失/爆炸是怎么发生的试试点乘操作连乘十次批量训练时梯度怎么平均注意axis参数dropout在反向传播时要做什么mask要记住实际项目中我通常用框架搭原型但遇到性能瓶颈或部署到嵌入式设备时经常要拆开网络手动优化。这时候对反向传播的理解就值钱了。最后给个实用建议把常用层的梯度公式写成小抄贴在显示器边上比如卷积层的im2col梯度、LSTM的梯度流。调试时对照着看能省下大量瞎猜的时间。神经网络训练不收敛时先检查梯度再调学习率最后怀疑数据——这个顺序能帮你少走弯路。

更多文章