资讯动态

从感知机到反向传播:手写神经网络核心原理与训练避坑指南

发布时间:2026/10/19 10:52:21 来源:尧图企业网站定制
1. 为什么重新整理这份ANN笔记先回答神经网络到底在学什么1.1 一个真实场景能调包但说不清原理前阵子同事A同学跑来找我说他用现成框架搭了个分类模型训练完准确率也还行但领导追问如果数据分布变了模型还能不能用为什么这个网络要设成三层时他完全答不上来。更尴尬的是模型在某个类别上一直表现很差他想调结构却不知道往哪个方向动。我让他把网络里每一层在做什么讲给我听他卡住了。这不是个例。现在各种高级框架把底层封装得相当严实很多初学者接触神经网络的第一印象就是写几行配置跑一下训练看准确率。这套流程当然能跑通简单任务可一旦遇到不收敛、梯度爆炸、过拟合这些问题没有底层的直觉连排查方向都没有。这也是我重新整理这份ANN基础笔记的根本原因——不是要把数学公式背熟而是要把网络在学什么参数怎么更新结构为什么这么设计这几件事串成一条线。这份笔记适合两类人刚入门、想建立直觉的新手以及用过框架但想回头补地基的同学。1.2 把神经网络看成一个带旋钮的函数我自己的理解方式是神经网络本质上就是一个带大量参数的函数。输入是数据输出是预测结果中间那些权重和偏置就是函数的参数。所谓训练就是不断调整这些参数让函数的输出尽可能接近我们想要的目标。举个例子给一张手写数字图片输入是784个像素值输出是0到9十个类别的概率。网络要学的就是一组参数把像素值这个高维输入映射到数字类别这个低维输出上。用数学语言写就是预测值 f(输入; 权重, 偏置)f是网络本身括号里分号后面的部分就是待学习的参数。训练的目标是找到一组参数使得所有样本上的预测误差总和最小。误差怎么量化那就涉及损失函数参数怎么调整那就涉及梯度下降误差信号怎么从输出端传回每一层那就涉及反向传播。这几个概念是一体的单独拎出来看都容易懵但放在调参这条主线上就顺了。1.3 这份笔记的阅读方式我建议从头到尾读一遍不要跳。因为神经网络的前向传播、反向传播、激活函数、损失函数是环环相扣的跳过中间任何一节后面的代码和坑位分析都会看不明白。每一节我都尽量写了为什么要这样而不是只给结论。2. 神经元、权重和偏置从感知机到可微分的跨越2.1 单层感知机的边界问题神经网络的起点是感知机Perceptron这是上世纪提出的一个非常朴素的模型。一个神经元接收若干个输入每个输入乘以一个权重再加上一个偏置然后经过一个阈值判断输出结果z w1*x1 w2*x2 ... wn*xn b 输出 1, 如果 z 大于阈值 输出 0, 如果 z 小于等于阈值这个模型能做非常基础的分类。比如判断一个学生要不要通过考试输入可以是出勤率和作业完成度权重表示这两项的重要性偏置表示整体门槛。训练感知机就是在已知一批样本结果的情况下反复调整这些权重和偏置把分类边界推到一个合适的位置。几何上单个神经元画出来的分类边界是一条直线二维输入时或一个超平面高维输入时。它能完美区分线性可分的数据比如且AND和或OR这两种逻辑关系。线性可分的意思是存在一条直线把两类点完全隔开。2.2 异或困境为什么单层结构走不远感知机的局限性很快就暴露了异或XOR问题。异或的规则是两个输入相同则输出0不同则输出1。把四个点(0,0)、(0,1)、(1,0)、(1,1)画在坐标平面上你会发现没有任何一条直线能把输出为1的两个点和输出为0的两个点分开。它们是斜对角分布的。这个不能分不是感知机参数调得不好而是模型表达能力的天花板就是直线。一个单层感知机本质上只是把输入空间切割成两个区域遇到需要弯折边界的任务就无能为力了。解决办法是加一层中间层也就是隐藏层。先让第一层神经元学会几条直线再让第二层神经元对这些直线做组合。比如XOR解不出来但可以先让隐藏层学到上方区域和右方区域再通过组合得到最终的弯折边界。这个思想后来被数学严格证明过只要隐藏层神经元足够多这种前馈网络可以逼近任意连续函数。但当时的感知机用的是阶跃激活函数参数更新基于错了就调整的规则不连续、不可导多层结构根本没法用梯度方法训练所以研究一度陷入停滞。2.3 从阶跃函数到Sigmoid可微分的意义阶跃函数最大的问题是它不可导。它的输出在阈值处突然从0跳到1导数要么是0要么是无穷大。链式法则要求每一层传递过去的梯度必须是有限且有意义的数阶跃函数直接把这条路堵死了。替代方案是Sigmoid函数sigma(z) 1 / (1 exp(-z))它的形状和阶跃函数类似值域在0到1之间但它是光滑连续的处处可导。更重要的是它的导数有一个非常简洁的形式sigma(z) sigma(z) * (1 - sigma(z))这个性质在反向传播时非常有用后面算梯度的时候你会反复见到它。Sigmoid函数把神经元从硬性开关变成了软性开关输入稍微变化输出会平滑变化变化率正好等于导数。这让梯度下降这种依赖导数的优化方法终于能用在多层网络上了。这也是为什么说可微分是从感知机到现代神经网络的跨越——没有连续可导的激活函数反向传播连起步都做不到。3. 激活函数怎么选非线性、梯度消失与实际取舍3.1 激活函数的本质给线性模型挠弯很多人把激活函数当成一个可有可无的细节其实它是网络表达能力的来源。如果去掉激活函数或者说用线性激活函数那么无论堆多少层整个网络最终等价于一个线性变换。原因很简单线性变换套线性变换结果还是线性变换。你可以自己验算一下W2(W1x b1) b2把括号展开合并之后本质上还是一个Wx b的形式。所以激活函数存在的根本目的是引入非线性。没有它网络再深也只是画直线有了它神经元才能画曲线网络才能拟合复杂函数。这个只有非线性叠加才能产生复杂表达能力的道理跟乘法分配律一样直白但很多初学者忽略。3.2 三个主流激活函数对比实践中最常打交道的三个激活函数是Sigmoid、Tanh和ReLU族。我把它们的核心特点整理成一张表激活函数公式输出范围主要优势主要问题Sigmoid1/(1exp(-z))(0, 1)历史经典输出可解释为概率两端饱和梯度极小输出非零中心Tanh(exp(z)-exp(-z))/(exp(z)exp(-z))(-1, 1)零中心梯度比Sigmoid略好两端仍然饱和深度网络中梯度仍会消失ReLUmax(0, z)[0, ∞)计算极快正区间梯度恒为1负区间梯度为0神经元可能坏死Leaky ReLUmax(αz, z)α通常取0.01(-∞, ∞)保留负区间小梯度缓解坏死参数α需要调效果提升未必明显Sigmoid两端饱和的具体表现可以算一下当z3时sigmoid(3)≈0.95当z5时sigmoid(5)≈0.993。输入从3变到5输出只变了0.04而导数从0.045降到0.0066。如果网络里好几层都处于这种饱和状态反向传播时梯度一层层乘下来最后到前面几层几乎就是0这就是梯度消失。梯度消失的后果是靠近输入层的权重几乎不更新网络学习停滞。深度网络早期难以训练很大程度上就是这个原因。ReLU在正区间的梯度恒为1有效缓解了梯度连乘导致的消失问题计算又便宜所以现代网络默认隐藏层首选ReLU。但它有个坑负区间的梯度直接是0。如果某个神经元的输入长期落在负数区间它的梯度永远是0权重再也不更新相当于神经元坏死。Leaky ReLU给负区间留了一条0.01的小斜坡就是为了防止彻底死掉。3.3 输出层的选择逻辑激活函数的选择还要区分隐藏层和输出层。输出层决定的是网络的输出形式必须和任务匹配二分类用Sigmoid输出一个0到1之间的值表示属于正类的概率。多分类用Softmax把所有类别的输出变成一组非负且和为1的概率。Softmax不是单个神经元上的函数它是针对输出层整体做的归一化。回归任务输出层直接用线性激活函数不做任何压缩因为目标值可能在任意范围。我见过不少新手在回归任务上给输出层加Sigmoid结果预测值永远被压在0到1之间测试集上只要真实值超出这个区间就完全预测不了。这个错误很低级但也很常见。4. 前向传播与反向传播一次参数更新的完整旅程4.1 前向传播逐层计算预测值一次训练迭代可以分成三个阶段前向传播、计算损失、反向传播更新参数。前向传播就是拿着当前的权重和偏置从输入层开始一层一层往下算。每层做两件事先把上一层的输出经过加权求和得到z再把z送入激活函数得到本层的输出a。公式写出来是这样z[1] W[1] x b[1] a[1] sigmoid(z[1]) z[2] W[2] a[1] b[2] a[2] sigmoid(z[2])这里的上标表示层号表示矩阵乘法。前向传播不涉及任何复杂逻辑就是把数据在网络里跑一遍最后得到预测值。初学者容易忽略的一点是前向传播时一定要把每一层的z和a都缓存下来因为反向传播计算梯度时需要用到它们。4.2 反向传播链式法则倒着走反向传播是整个神经网络训练的发动机。它的核心就是高等数学里的链式法则复合函数的导数等于各层导数相乘。网络前向是输入→隐藏→输出误差信号则需要反过来走输出→隐藏→输入逐层求出损失对每个参数的偏导数。先看输出层。假设样本真实标签是y网络预测值是a损失函数是L。我们要算的是损失对某个权重w的偏导数∂L/∂w ∂L/∂a * ∂a/∂z * ∂z/∂w这个式子三个因子各有含义∂L/∂a表示预测值偏离目标的程度∂a/∂z是激活函数的导数∂z/∂w就是该权重对应的输入值。三个因子相乘就是损失对权重的反推梯度。隐藏层的梯度稍微复杂一点因为隐藏层某个神经元的输出会影响到后续多个神经元。此时需要用全导数公式把该神经元对后续所有路径的影响加起来。这就是为什么反向传播的实现往往需要从后往前逐层遍历同时利用前向传播缓存的数据。求到梯度之后参数更新就很机械了w w - 学习率 * ∂L/∂w这个公式就是梯度下降。它的直觉是梯度指出的是损失函数上升最快的方向参数往反方向挪一小步损失就会下降。4.3 一个完整的小数值算例光讲公式不够直观我带着大家手动算一遍。假设有一个2-2-1结构的网络即2个输入、2个隐藏神经元、1个输出神经元。激活函数全部用Sigmoid损失用平方误差L 0.5 * (y - a)^2。给定一个样本x10.5x2-0.2真实标签y1。假设初始参数如下隐藏层w110.1输入1到隐藏1w12-0.3输入2到隐藏1b10.01w210.4输入1到隐藏2w220.2输入2到隐藏2b20.02输出层w_out10.7隐藏1到输出w_out20.5隐藏2到输出b_out0.05先做前向传播z1 0.1*0.5 (-0.3)*(-0.2) 0.01 0.12 a1 sigmoid(0.12) ≈ 0.53 z2 0.4*0.5 0.2*(-0.2) 0.02 0.18 a2 sigmoid(0.18) ≈ 0.545 z_out 0.7*0.53 0.5*0.545 0.05 ≈ 0.694 a_out sigmoid(0.694) ≈ 0.667损失为 L 0.5 * (1 - 0.667)^2 ≈ 0.0554。接着反向传播。先算输出端的梯度∂L/∂a_out a_out - y 0.667 - 1 -0.333 ∂a_out/∂z_out a_out * (1 - a_out) 0.667 * 0.333 ≈ 0.222 ∂L/∂z_out -0.333 * 0.222 ≈ -0.074于是输出层权重的梯度∂L/∂w_out1 ∂L/∂z_out * a1 -0.074 * 0.53 ≈ -0.039 ∂L/∂w_out2 ∂L/∂z_out * a2 -0.074 * 0.545 ≈ -0.040 ∂L/∂b_out -0.074继续往隐藏层传。隐藏层1收到了来自输出端的梯度贡献∂L/∂a1 ∂L/∂z_out * w_out1 -0.074 * 0.7 ≈ -0.052 ∂a1/∂z1 a1 * (1 - a1) 0.53 * 0.47 ≈ 0.249 ∂L/∂z1 -0.052 * 0.249 ≈ -0.013 ∂L/∂w11 ∂L/∂z1 * x1 -0.013 * 0.5 ≈ -0.0065 ∂L/∂w12 ∂L/∂z1 * x2 -0.013 * (-0.2) ≈ 0.0026隐藏层2同理先求∂L/∂a2 ∂L/∂z_out * w_out2 -0.074 * 0.5 -0.037再乘上a2(1-a2)≈0.248得到∂L/∂z2≈-0.0092然后分别乘上x1和x2得到对应的权重梯度。最后用学习率更新。假设学习率lr0.5则输出层第一个权重更新为w_out1_new 0.7 - 0.5 * (-0.039) ≈ 0.7195这个权重朝正方向增加因为梯度是负的意味着增大这个权重能让损失下降。把这套步骤在所有参数上执行一遍就完成了一次完整的训练迭代。你会发现整个过程没有高深技巧就是大量的乘法和加法但正是这些朴素操作在海量样本上反复执行才驱动了网络一点一点逼近最优解。5. 损失函数与梯度下降决定收敛速度的隐藏细节5.1 损失函数的本质是误差的量化方式损失函数的选择往往被初学者当成照着抄就行的配置其实它对训练动态影响巨大。损失函数回答的问题是预测值和真实值之间的差距我们打算怎么计算。两个最常用的损失是均方误差MSE和交叉熵。均方误差对每个样本算平方差然后取平均L 0.5 * (y - a)^2交叉熵在二分类问题中的形式是L -[y * log(a) (1 - y) * log(1 - a)]一个很容易踩的坑就在这输出层用Sigmoid损失却选用平方误差训练会非常慢。原因可以从前面的反向传播公式看出来。此时损失对z的梯度是∂L/∂z (a - y) * a * (1 - a)当输出端严重错误时比如真实标签y1但预测值a接近0此时(a-y)接近-1同时a(1-a)因为a太小而趋近0两个因子相乘梯度反而变得非常小。预测最错的时候梯度反而最弱这就是学习动力不足。换成交叉熵后Sigmoid的a(1-a)项会被损失函数的导数抵消最终梯度变成(a-y)错误越大梯度越强训练速度明显改善。所以我的建议是分类问题输出层用Sigmoid或Softmax时损失函数优先选交叉熵回归问题用线性输出时选均方误差。5.2 为什么平方误差配Sigmoid会学得慢上面那个原理值得再展开说说。很多人觉得误差大就应该梯度大这在回归任务用线性输出时成立但在Sigmoid输出时被破坏了。Sigmoid把输出压在0到1之间当输出远离目标时输入z往往落到了饱和区导数几乎为零。平方误差给的是线性惩罚它没有能力抵消饱和导致的梯度缩小。交叉熵的设计恰好绕开了这个问题因为log函数在接近0或1时斜率非常大预测越离谱惩罚越重和Sigmoid导数相乘后恰好消掉了饱和项。这种配对了才走得快的现象暴露出神经网络各组件的耦合关系激活函数、损失函数不是独立配置项它们合起来决定了梯度信号的形状。理解这一点排错时就不会只盯着单一参数看。5.3 梯度下降的三种采样方式梯度计算的代价取决于用多少数据来估算。最极端的方式是每次随机挑一个样本更新参数这叫随机梯度下降SGD另一种是计算全部样本的平均梯度再更新叫批量梯度下降折中的方案是取一小批样本比如32或64个算平均梯度叫小批量梯度下降Mini-batch SGD。方式每次计算的样本数更新频率稳定程度适用规模批量梯度下降全量低高但容易困在局部最优附近小数据集随机梯度下降1个高低噪声大但有时能跳出局部最优不适合实际训练小批量梯度下降32~256个中中兼顾噪声与稳定性绝大多数实际场景小批量训练的实践理由有两条第一梯度用一小批样本估算计算量可控第二小批量带来的随机噪声能让优化过程更容易跳出尖锐的局部最优点。批量大小也直接影响收敛行为后面讲实际训练坑位时还会提到。6. 网络结构怎么搭层数、宽度与过拟合的博弈6.1 隐藏层到底在做什么理解隐藏层的经典视角是特征提取。拿数字识别来说输入层看到的是像素最浅的隐藏层学到的是边缘和小色块中间层把边缘组合成局部形状更深层把形状组合成部件。这很像人看一张图时先看到线条再看到轮廓最后认出物体。虽然这种逐层抽象的描述更多来自卷积网络的观察但对理解全连接网络的结构设计同样有帮助每一层本质上在构造一种新的表示把原始输入逐步转化为对最终输出更有用的形态。这也解释了为什么网络需要宽度和深度两个维度的容量宽度决定每一层同时能表达多少个不同的特征模板深度决定特征组合的层级数量。6.2 宽度与深度两种容量增长的差别一个常被引用的结论是单隐藏层网络只要宽度足够可以逼近任意连续函数。但证明归证明实际操作中宽度巨大的一层往往不如中等宽度的多层。两者差别在于单层加宽学习到的是一种扁平的表征所有特征都在同一层级组合关系需要靠这一层内部大量神经元互相配合参数利用率低。多层加深每一层都在前一层基础上做组合可以把复杂的映射分解成一系列简单变换参数利用率更高更容易泛化。但深度不是越多越好。层数加深会带来三个问题梯度经过更多次乘法更容易消失或爆炸参数量增大需要更多数据来喂饱训练稳定性下降对初始化、学习率更敏感。我的实践原则是优先从简单结构起步两到三层隐藏层先跑通观察验证集表现再逐步加深。没有数据量支撑的无脑加深只会收获一个过拟合的大模型。6.3 过拟合信号与常规应对过拟合的判断标准不是训练集表现而是训练集和验证集表现之间的差距。如果训练损失持续下降验证损失却不再下降甚至反弹那就是过拟合的典型信号。模型在死记训练样本没有学到可迁移的规律。应对手段从易到难排列增加训练数据包括数据增强对图像做旋转、裁剪等这是最直接有效的。降低模型容量减少层数或每层神经元个数。加正则化。L2正则化在损失函数里加上权重平方和的惩罚项逼迫权重保持较小值Dropout则在训练中随机丢弃一部分神经元迫使网络不依赖少数节点。早停Early Stopping在验证集表现开始恶化时停止训练。实际项目中我通常先通过早停和加大数据把过拟合控制住再去调结构。反过来先堆正则化手段往往会让问题变复杂。7. 训练阶段最容易踩的四个坑7.1 全零初始化会让网络瘫痪把所有权重初始化为零是所有初始化方案里最糟糕的。原因非常直观如果同一层所有神经元的输入权重都相同加上偏置也相同那么前向传播时它们的输出完全一样反向传播时梯度也完全一样更新之后它们仍然一模一样。无论经过多少次训练这些神经元都扮演着同一个角色等于这一层只有一个有效神经元。对称性没有被打破网络实际容量远小于设计值。正确的做法是给权重赋一个小的随机数打破对称性。随机初始化的尺度也有讲究太大会让神经元一开始就进入饱和区梯度极小而学不动太小会让信号在层间传递时逐渐衰减。实践中常见做法是按输入维度缩放随机值具体来说常用的是He初始化或Glorot初始化它们根据每层的输入输出维度设计方差让信号在深层网络中保持大致稳定的尺度。7.2 输入不归一化梯度下降像走迷宫如果某个特征的数值范围是0到1另一个特征是0到10000两个权重被梯度更新的步长差异会非常大。更麻烦的是损失函数在这种数据上的等高线会拉得很扁梯度方向常常指着斜对角而指向最优点方向的分量很小。优化过程会在山谷两侧来回震荡收敛极慢。把每个特征归一化到相近范围损失函数的等高线会更接近圆形梯度方向也更直接指向低处。常用做法是标准化减去均值再除以标准差让每个特征的均值接近0、方差接近1。这一步在表格数据和图像数据上都很有效。我遇到过不少案例模型原本训练几百个epoch效果还差做一次标准化之后几十个epoch就稳了代价只是几行预处理代码。7.3 学习率设置最常被忽略的失控源头学习率可以说是训练参数里最敏感的一个。设置过大参数更新步长跨过了低损失区域损失曲线可能出现不降反升甚至直接发散成NaN设置过小模型确实能收敛但要跑到天荒地老。一个实用的做法是先用一组较大的学习率从0.1或0.01开始观察损失的初期表现再做对数尺度上的搜索。使用自适应优化器这类优化器会自动为每个参数缩放学习率可以降低调参痛苦但即使如此初始学习率仍然需要人工设定。训练中期还可以配合学习率衰减验证损失进入平台期后把学习率降为原来的十分之一往往能再挖出一截提升。这个技巧看起来老套但极其有效。7.4 损失不降时的排查清单模型训练几次迭代后损失纹丝不动甚至变大这种困境几乎每个人都遇过。我的排查顺序通常是检查数据标签有没有错输入有没有大量缺失或异常值训练集和验证集有没有数据泄漏检查输出层和损失函数是否匹配多分类配了Sigmoid而不是Softmax回归配了交叉熵都是常见错配。检查初始化权重是不是全零或过大检查学习率尝试从0.001到0.1之间做几次小实验看损失是否跟着动。跑一个单样本过拟合测试只用一个样本训练看模型能不能记住它。如果连一个样本都拟合不了问题在产品逻辑或网络结构而不是数据规模。这个清单的价值在于把玄学调参变成按环节定位。大多数损失不降的问题根源都出在数据或任务-模型配置错配上而不是网络本身不够深。8. 手写一个最简网络把笔记变成能跑的代码8.1 用纯Python实现2-2-1网络理论讲再多不如让代码跑一遍有感觉。我用纯Python实现一个2-2-1网络在异或数据集上训练。不依赖任何第三方库只用了标准库里的数学函数。import math def sigmoid(z): return 1 / (1 math.exp(-z)) def sigmoid_derivative(a): return a * (1 - a) # 异或数据集 X [[0, 0], [0, 1], [1, 0], [1, 1]] y [0, 1, 1, 0] # 初始化小随机权重 W1 [[0.15, 0.10], [0.20, 0.05]] # 输入层到隐藏层 b1 [0.01, 0.01] W2 [0.30, 0.25] # 隐藏层到输出层 b2 0.01 lr 0.5 epochs 5000 for epoch in range(epochs): total_loss 0.0 for i in range(len(X)): # 前向传播 x1, x2 X[i] z1_1 W1[0][0] * x1 W1[0][1] * x2 b1[0] a1_1 sigmoid(z1_1) z1_2 W1[1][0] * x1 W1[1][1] * x2 b1[1] a1_2 sigmoid(z1_2) z_out W2[0] * a1_1 W2[1] * a1_2 b2 a_out sigmoid(z_out) # 损失(交叉熵) loss -(y[i] * math.log(a_out 1e-8) (1 - y[i]) * math.log(1 - a_out 1e-8)) total_loss loss # 反向传播 delta_out a_out - y[i] dW2_1 delta_out * a1_1 dW2_2 delta_out * a1_2 db2 delta_out delta_1 delta_out * W2[0] * sigmoid_derivative(a1_1) delta_2 delta_out * W2[1] * sigmoid_derivative(a1_2) dW1_11 delta_1 * x1 dW1_12 delta_1 * x2 dW1_21 delta_2 * x1 dW1_22 delta_2 * x2 db1_1 delta_1 db1_2 delta_2 # 更新参数 W2[0] - lr * dW2_1 W2[1] - lr * dW2_2 b2 - lr * db2 W1[0][0] - lr * dW1_11 W1[0][1] - lr * dW1_12 W1[1][0] - lr * dW1_21 W1[1][1] - lr * dW1_22 b1[0] - lr * db1_1 b1[1] - lr * db1_2 if epoch % 1000 0: print(epoch, epoch, loss, round(total_loss / 4, 4)) # 训练完后看预测 for i in range(len(X)): x1, x2 X[i] a1_1 sigmoid(W1[0][0] * x1 W1[0][1] * x2 b1[0]) a1_2 sigmoid(W1[1][0] * x1 W1[1][1] * x2 b1[1]) a_out sigmoid(W2[0] * a1_1 W2[1] * a1_2 b2) print(输入, X[i], 预测, round(a_out, 4), 目标, y[i])这段代码把前面所有的概念都串了起来前向传播负责计算预测交叉熵负责量化差距反向传播用链式法则算出每个参数的梯度梯度下降负责更新。尤其是手动写一次反向传播你会彻底明白为什么框架里那些API叫backward。8.2 跑通异或问题的关键点我最初用平方误差跑这个网络发现收敛非常慢几千轮后仍然不稳定。换成交叉熵之后几百轮就有明显改观。这正好印证了前面讲的内容分类问题要配交叉熵否则Sigmoid饱和区会拖慢训练。另一个细节是训练时的日志输出。如果每一次都打印一批样本的平均损失你会发现早期损失下降快后期进入平台期很正常。不要因为后期损失降得慢就急着调结构先降低学习率再看效果。手写代码跑异或还有个好处你可以很直观地观察权重变化方向确认梯度负方向更新确实在降低损失。8.3 从手写代码到框架的思维切换手写一次之后再回到主流深度学习框架你的视角会完全不同。框架封装了自动求导省去了手工推导每一层的梯度算子库和硬件加速让大规模训练成为可能。但框架仍然需要你回答几个核心问题网络结构怎么定义损失函数选哪个学习率怎么设数据怎么预处理。这些问题的答案不在框架文档里而在对ANN基础的理解里。我对这份笔记最直观的感受是学神经网络动手写一个极小的网络比看十篇教程都管用。不需要多复杂能跑通异或就够了。它能让你把神经元、激活函数、损失、反向传播这些概念从一个一个孤立名词变成一条你亲手验证过的流程线。之后再遇到模型训练出问题你至少知道应该从哪个环节开始检查而不是对着控制台干瞪眼。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑