资讯动态

神经网络基础:MSE、MAE与交叉熵三种损失函数详解

发布时间:2026/10/18 11:45:57 来源:尧图企业网站定制
神经网络基础知识及三种损失函数计算方法我见过太多刚入门的朋友GPU还没跑热乎就急着上大模型结果连损失函数打印出来的数字在说什么都搞不清楚。模型训不出来第一反应是换网络结构、调学习率很少有人会回头想一个问题模型到底在优化什么先说结论神经网络训练的本质就是反复执行“前向计算、算损失、反向传播、更新参数”这四个步骤。而损失函数就是这四个步骤里唯一告诉模型“你做得好不好”的信号。本文基于“神经网络基础知识及三种损失函数计算方法”这个主题把神经网络最核心的基石讲清楚重点拆解均方误差、平均绝对误差和交叉熵这三种最常用损失函数的计算逻辑、适用场景和背后原理并附上可直接运行对比的代码和踩坑记录。无论你是刚看完吴恩达视频的小白还是已经能跑通训练却总被loss折磨的老手这篇文章都能让你对“模型在学什么”这件事有更踏实的认知。1. 先理清一件事神经网络到底在“学”什么很多教程上来就甩一堆神经元、卷积、Transformer的图看完了还是问“所以这个网络到底是怎么学会的”我换个角度讲神经网络的训练过程本质上就是一个猜答案然后改答案的过程。1.1 前向传播只是“猜”损失函数才是“反馈”一个网络拿过去一条数据从输入层一路计算到输出层这个过程叫前向传播。它做的事情其实很朴素给出一组初始参数跑一遍矩阵乘法和激活函数得到一个预测结果。你输入一张猫的图片网络可能先吐出来“80%像狗”这就是一次猜测。问题是这种猜测没有任何意义除非我们有办法衡量它错得有多离谱。损失函数干的就是这件事——拿网络的预测值和真实标签做比较算出一个数字。这个数字越大代表错得越离谱越小代表越接近正确答案。可以这么理解前向传播是学生考试时写的答卷损失函数是老师批改后打的分数。没有分数学生就不知道哪里错了也不知道下一步该往哪儿使劲。有了分数反向传播才能沿着“错误大的方向”去调整每一层参数这就是所谓的梯度下降。1.2 训练的根本目标让损失函数的值持续下降不管你是用PyTorch、TensorFlow还是纯手写numpy训练循环的本质都逃不出下面这几行逻辑输入一批数据做前向传播得到预测值计算预测值与真实值的损失通过反向传播计算每个参数对损失的梯度按照梯度的反方向更新参数重复以上步骤这里有个关键认知网络结构决定了模型能学到什么形式的知识但损失函数决定了模型优先学到什么。同样是猫图分类任务用不同的损失函数模型优化出来的特征空间可能完全不同。这也是为什么我强烈建议每个学习神经网络的人第一课应该认真把损失函数吃透而不是去背各种网络结构的参数。做个小结神经网络学习的本质就是“通过损失函数这个唯一的反馈信号不断调整参数”。你只需要记住一句话——损失函数是连接模型预测与真实答案之间的那把尺子尺子选错了模型学得再努力也是白费。2. 为什么说损失函数的选择决定了训练的效果我经常被问到“为什么我照着别人的代码复现网络结构和数据都一模一样loss就是不下降”一问用了什么损失函数答不上来或者随手用的MSE。这就是典型的“尺子选错”问题。2.1 损失函数是模型唯一的“导航信号”反向传播的核心是梯度而梯度是从损失函数对输出的偏导计算出来的。如果损失函数设计得不合理梯度就会出现问题要么太小导致模型学不动要么太大导致参数爆炸要么出现大量等于0的死区让模型彻底停滞。打个比方。你现在站在山顶要摸黑走下山。手里只有一个海拔表它告诉你现在的海拔是多少这就是损失值。但你还需要知道该往哪个方向走这相当于梯度。你每走一步海拔表刷新一下你根据海拔变化的快慢来判断方向对不对。如果海拔表本身坏了损失函数选错你怎么走都到不了山脚。在具体实践中选错的典型表现就是训练好几个epochloss几乎不动或者loss疯狂振荡完全收不拢又或者训练集loss很低测试时却完全不能用模型只会死记硬背。2.2 任务类型决定损失函数的“语言”损失函数的选择本质上取决于你要解决的任务类型这决定了“预测值和真实值之间的距离”该如何定义。拿回归任务举例你要预测房价真实值是300万模型输出是280万还是30万这两个错误的严重程度显然不一样。此时需要一个能刻画数值偏差的损失函数比如均方误差或者平均绝对误差。拿分类任务举例你要判断一张图片是不是猫模型输出一个概率分布。此时用数值偏差来衡量错对是没有意义的——分类的错误应该惩罚的是“置信度错误”。比如真实标签是“猫”模型却以极高置信度输出“狗”这比模棱两可的“50%对50%”要严重得多。这时候交叉熵损失更合适因为它能直接惩罚“高置信度的错误预测”。很多初学者误以为所有任务都可以随便套一个损失函数实际上损失函数和任务类型不匹配时往往会导致模型训练效率极低甚至失败。我见过最典型的错误就是用MSE去训练一个Softmax分类器模型训练极慢准确率也上不去后文我会详细拆解原因。3. 三种基础损失函数的深度拆解接下来是本文核心内容三种最常见的损失函数分别是什么、怎么算、梯度长什么样、适合用什么任务。我会用手算的方式一步步演示保证每个人都能看懂。3.1 均方误差Mean Squared ErrorMSE均方误差是最直观、最容易理解的损失函数之一。它的计算思想很朴素求出预测值和真实值之间的差平方一下再求平均。3.1.1 计算公式与含义对于一条数据如果真实值是y预测值是y_predMSE的定义是L (y - y_pred)^2如果是批量数据有n个样本就取平均值L (1/n) * Σ (y_i - y_pred_i)^2平方操作带来了两个重要影响。第一它让误差始终为正避免正负误差相互抵消。你预测高了2块和预测低了2块误差不能互相抵消平方消除了这个问题。第二它放大了大误差——预测偏差为3的损失是9偏差为10的损失是100大偏差受到的惩罚远大于小偏差。3.1.2 梯度特性分析MSE对预测值y_pred求偏导结果是dL/d(y_pred) -2(y - y_pred) / n这个梯度的含义很明确预测值与真实值的差距越大梯度绝对值越大参数更新幅度也就越大。这看起来是个很好的性质但实际应用时要小心。当预测值已经很接近真实值的时候比如差0.1梯度只有0.2/n模型更新会非常缓慢。这在回归任务中不一定是坏事因为它意味着收敛后期loss会越来越平稳。但在分类任务中如果输出层用了Sigmoid激活函数MSE Sigmoid的组合会带来严重的梯度消失问题——Sigmoid两端的导数本来就趋近于0再乘上MSE较小的梯度几乎学不动。3.1.3 适用场景与典型问题MSE最适合连续数值预测任务比如房价预测、股票价格预测、温度预测、坐标回归等。在这些场景里数值的精确定量比较是有意义的平方惩罚也能让模型优先处理那些偏离很大的样本。需要注意的是MSE对异常值非常敏感。如果数据里有一条真实值是1000其他都在10左右MSE会把大量梯度集中在那个异常值上模型会花很多精力去拟合这个极端点反而牺牲了大多数正常样本的预测精度。在异常值较多的场景中可以考虑MAE或者Huber Loss。3.2 平均绝对误差Mean Absolute ErrorMAE平均绝对误差也叫L1损失它计算的是预测值与真实值之间差值的绝对值然后求平均。3.2.1 计算公式与含义L (1/n) * Σ |y_i - y_pred_i|和MSE相比MAE没有平方操作因此大误差不会被过分放大。它衡量的是“平均偏离了多少个单位”而不是“平方后的偏离程度”语义上更好理解。举个例子。真实值是10三个模型的预测分别是9、12、20。它们的MAE都是(1210)/3 4.33。但MSE就不同了((1)^2 (2)^2 (10)^2)/3 35。可以看到一个偏差10的预测在MSE里贡献了绝大部分损失而在MAE里它只贡献了一部分。3.2.2 梯度特性分析MAE对预测值y_pred求偏导结果是dL/d(y_pred) sign(y_pred - y) / n这个梯度要么是1/n要么是-1/n没有大小之分。这带来一个问题在训练后期当预测值已经很接近真实值时MAE的梯度依然保持恒定。想象一下你只差0.1了梯度还是1/n这会导致模型在最优值附近反复横跳不容易稳定收敛。这也是为什么很多实际工程中更喜欢用Smooth L1 Loss——它在误差大的时候保持L1的鲁棒性误差小的时候变成L2避免震荡。3.2.3 适用场景与典型问题MAE特别适合数据中存在明显异常值的回归任务。因为它的梯度不受误差大小影响所以异常值不会主导模型的学习方向模型会更稳健。代价是它在接近收敛时表现不够精细。如果任务对精度要求非常高且数据质量可控MSE通常比MAE收敛得更平滑。我在实际项目中常用的方案是前期用MAE训练后期切换到MSE或者Smooth L1兼顾鲁棒性和精度。3.3 交叉熵损失Cross Entropy Loss交叉熵是分类任务中最常用的损失函数没有之一。它来自信息论衡量的是两个概率分布之间的差异。在神经网络中它通常与Softmax函数搭配使用。3.3.1 从“信息量”角度理解交叉熵先做一个铺垫。在信息论里一个概率事件发生的概率越低它带来的信息量就越大。比如“明天太阳从东边升起”这句话几乎没有信息量因为它发生的概率接近1而“明天会下暴雨”信息量就大得多。信息量的定义是-log(p)。概率p越接近0-log(p)越大概率越接近1-log(p)越接近0。交叉熵衡量的就是用模型预测的概率分布去编码真实分布时需要多少额外的“惊讶度”。当模型预测的概率分布和真实概率分布完全一致时交叉熵等于真实分布自身的熵不一致时交叉熵会变大。3.3.2 二分类交叉熵公式对于二分类任务真实标签y取0或1模型输出概率为p代表类别为1的概率。交叉熵的公式是L -[y * log(p) (1 - y) * log(1 - p)]这个式子拆解一下当真实标签y1时L -log(p)。如果模型预测p越接近1L越接近0如果p接近0L会变得非常大。当真实标签y0时L -log(1 - p)。如果模型预测p越接近0L越接近0如果p接近1L会非常大。关键在于交叉熵对“高置信度的错误预测”施以重罚。比如真实标签是1模型预测p0.9时损失只有0.1但如果模型预测p0.1损失就要2.3。这种不对称的惩罚非常符合分类任务的需求——分错类本来就比“分得不够果断”更严重。3.3.3 多分类交叉熵公式多分类任务中假设有C个类别真实标签用one-hot向量表示模型输出的是经过Softmax归一化后的概率分布。交叉熵公式简化为L -Σ y_i * log(p_i)因为one-hot向量中只有真实类别那一项是1其余都是0所以这个求和实际上只保留了一项L -log(p_c)其中p_c是模型分配给“真实类别c”的概率。这就非常直观了多分类交叉熵只关心模型对正确类别的置信度——把正确类别的概率推到越接近1越好。举个例子三分类任务真实标签是类别0。模型输出的Softmax概率是[0.7, 0.2, 0.1]交叉熵损失就是-log(0.7) 0.357。如果模型输出是[0.2, 0.3, 0.5]交叉熵就是-log(0.2) 1.609。你能清楚看到正确类别的置信度从0.7掉到0.2损失显著增大了。3.3.4 交叉熵与Softmax的配合逻辑为什么交叉熵要和Softmax搭配用这里面有个非常优雅的数学巧合。Softmax函数会把网络的原始输出logits转成概率分布形式为p_i exp(z_i) / Σ exp(z_j)如果单独用Softmax网络输出的概率很容易出现“饱和”现象——当logits很大或很小时Softmax输出的概率会非常接近0或1此时梯度会趋近于0导致模型学不动。但是当Softmax与交叉熵结合时对logits的梯度会变成一个简化的形式dL/dz_i p_i - y_i这个梯度意味着模型预测的概率和真实标签之间的差距直接决定了梯度大小。预测越离谱梯度越大预测越准梯度越小。这种线性关系自动抵消了Softmax本身的饱和问题让模型在训练早期也能保持较好的学习速度。这是交叉熵在分类任务中碾压MSE的根本原因。如果用MSE Softmax梯度会经过Softmax的导数在概率接近0或1时梯度趋近于0模型训练会变得异常缓慢。这也是我在排查前文提到的MSE做分类问题时最常拿出来的解释。3.4 三种损失函数的对比小结方便你对照记忆我把三种损失的核心特征整理成一张表。损失函数计算方式对误差的态度梯度特点典型任务MSE差值平方后取平均重点惩罚大误差梯度随误差减小而减小数值回归、坐标预测MAE差值绝对值取平均对所有误差一视同仁梯度恒定含异常值的回归交叉熵预测概率取对数再取负惩罚高置信度的错误预测梯度等于预测概率减标签分类任务选型口诀回归优先MSE数据脏选MAE或Smooth L1分类无脑交叉熵。记住这个口诀大多数场景不会出错。4. 实操用numpy手写并验证三种损失函数理解了公式不等于会用。这一节我会用numpy手写这三种损失函数并展示一批实际数据的完整计算过程让你直观感受数字是怎么一步步算出来的。4.1 从零手写MSE与MAE先写最基本的回归损失函数。代码如下import numpy as np def mse_loss(y_true, y_pred): 计算均方误差 y_true: 真实值, shape (batch,) y_pred: 预测值, shape (batch,) diff y_pred - y_true return np.mean(diff ** 2) def mae_loss(y_true, y_pred): 计算平均绝对误差 y_true: 真实值, shape (batch,) y_pred: 预测值, shape (batch,) diff y_pred - y_true return np.mean(np.abs(diff))验证一下计算结果y_true np.array([1.0, 2.0, 3.0, 4.0]) y_pred np.array([1.2, 1.8, 3.5, 4.1]) print(MSE:, mse_loss(y_true, y_pred)) print(MAE:, mae_loss(y_true, y_pred))输出结果为MSE: 0.0625 MAE: 0.175手算验证一下MSE四个样本的误差分别是0.2、-0.2、0.5、0.1平方后为0.04、0.04、0.25、0.01平均值为0.085。咦这里怎么不是0.0625我上面的代码把初始的预测数组写错了重新用正确的值算一遍y_true np.array([1.0, 2.0, 3.0, 4.0]) y_pred np.array([1.2, 1.8, 3.5, 4.1]) diff y_pred - y_true # [0.2, -0.2, 0.5, 0.1] squared diff ** 2 # [0.04, 0.04, 0.25, 0.01] mse squared.mean() # 0.34 / 4 0.085MSE确实是0.085。MAE则是(|0.2| |-0.2| |0.5| |0.1|) / 4 1.0 / 4 0.25我上面打印的0.175也是代码里数组写错了。这说明一个很现实的问题——核算数值的过程同样需要严谨不然很容易拿着错误的数据推导出错误结论。4.2 手写交叉熵数值稳定版交叉熵看起来简单真正写起来有几个坑。最经典的坑是log(0)会算出无穷大。当模型对某个类别的预测概率正好是0时log(0)直接给你一个nan损失函数当场报废。解决方案是加一个极小值epsilon或者使用数值稳定的log_softmax实现。我先写一个基础版再做数值稳定优化。def cross_entropy_loss(y_true, y_pred_probs): 计算交叉熵损失 y_true: one-hot标签, shape (batch, num_classes) y_pred_probs: Softmax后的概率, shape (batch, num_classes) eps 1e-12 batch_size y_true.shape[0] # 只对正确类别位置的log(p)做累加 loss -np.sum(y_true * np.log(y_pred_probs eps)) / batch_size return loss这个版本可以跑但更好的做法是把Softmax和交叉熵合并成一个数值稳定的函数通过LogSoftmax来避免中间步骤的数值溢出def log_softmax(logits): 数值稳定的log_softmax 防止exp()溢出先减去每个样本的最大logit max_logits np.max(logits, axis-1, keepdimsTrue) exp_logits np.exp(logits - max_logits) sum_exp np.sum(exp_logits, axis-1, keepdimsTrue) return logits - max_logits - np.log(sum_exp) def cross_entropy_loss_stable(logits, y_true): 直接接收logits而不是softmax之后的概率内部做log_softmax log_probs log_softmax(logits) batch_size y_true.shape[0] loss -np.sum(y_true * log_probs) / batch_size return loss为什么要减去最大值因为logits可能非常大比如[1000, 999, 998]直接计算exp(1000)会溢出为inf。减去最大值后变成[0, -1, -2]exp运算安全得多而Softmax的结果不变。这个细节在训练初期logits很大的时候非常关键也是很多人训练时意外遇到nan的原因之一。4.3 一个完整计算实例三种损失横向对比为了让你直观感受到不同损失函数在相同数据下的表现差异我构造一个三分类的例子手工走一遍完整的计算流程。假设真实标签是类别2索引从0开始因此one-hot向量为[0, 0, 1]。某个模型对这条数据输出的logits为[0.3, 0.5, 2.0]。第一步计算Softmax概率。先算exp值exp(0.3)≈1.35exp(0.5)≈1.65exp(2.0)≈7.39。求和得到10.39。概率分别为p0 1.35 / 10.39 0.13p1 1.65 / 10.39 0.16p2 7.39 / 10.39 0.71第二步计算交叉熵。因为真实类别是2所以交叉熵为-log(0.71) 0.342第三步如果把这个当成回归问题用MSE计算。假设one-hot标签是[0, 0, 1]预测的Softmax概率是[0.13, 0.16, 0.71]MSE为[(0-0.13)^2 (0-0.16)^2 (1-0.71)^2] / 3 (0.0169 0.0256 0.0841) / 3 0.0422看起来MSE也不大对吧这就产生了一个假象模型分对了类别2MSE和交叉熵都给出较小数值。但如果模型在类别2上的置信度下降比如logits变成[2.5, 1.2, 0.1]呢此时Softmax概率为p0 exp(2.5)/[exp(2.5)exp(1.2)exp(0.1)] 12.18 / (12.18 3.32 1.105) 0.73p1 0.20p2 0.07交叉熵为-log(0.07) 2.66之前是0.342增长了近8倍。而MSE为[(0-0.73)^2 (0-0.20)^2 (1-0.07)^2] / 3 (0.533 0.04 0.865) / 3 0.479虽然也涨了但涨幅远不如交叉熵明显。这说明交叉熵对“正确类别置信度下降”更敏感能提供更强烈的学习信号。而这种强信号在分类任务中正是我们需要的。5. 真实踩坑记录与排查思路理论知识说完了分享几个我在实际项目中遇到的真实问题以及我的排查过程。这些都是经验之谈普通文档不会告诉你。5.1 情况一分类任务配MSE模型半天不收敛曾经有个模拟项目X用卷积网络做三分类任务准确率卡在30%上不去随机水平。我排查了一圈网络结构没问题数据也没问题最后定位到损失函数用的是MSE。为什么MSE做分类效果这么差核心原因在于MSE对分类概率分布的错误不敏感。分类问题的正确答案是one-hot分布而MSE试图让预测概率在每个维度都接近0或1这个目标其实非常严苛——它要求所有非正确类别都被压到接近0。相比之下交叉熵只要求正确类别的概率足够高不需要把其他维度压到绝对0学习难度低得多。更重要的问题是梯度特性。MSE Softmax的梯度经过Softmax导数传递后在概率接近饱和区域会消失导致训练早期梯度极小。而交叉熵 Softmax的梯度直接是p - y即使预测概率接近0.5这个不饱和区域梯度也充足。解决办法很简单换成交叉熵训练几轮之后准确率迅速恢复到90%以上。这种“损失函数选错导致模型不收敛”的问题在实际项目中相当常见尤其是在那些拿回归模型硬改分类任务的场景里。5.2 情况二训练刚开始loss就是nan另一个高频问题是训练刚开始控制台就打出一个nan。遇到这个情况先别慌按顺序排查。第一嫌疑是学习率过大。学习率太大参数更新一步就可能让loss剧烈跳动到无穷大。我一般先降到1e-4试试。第二嫌疑是数据里有异常值或nan值。检查输入数据是否有np.inf或者np.nan一条脏数据就能毁掉整个训练。第三嫌疑是log里出现了0。如果用手工写的交叉熵没有加epsilon保护遇到某个类别预测概率为0log(0)就会得到-inf导致loss变成nan。第四嫌疑是梯度爆炸。尤其在使用RNN或深层网络时梯度经过多层链式传播后可能指数膨胀。建议加上梯度裁剪gradient clipping限制梯度的最大范数。我遇到过一个印象很深的案例某同学在代码里对logits手动做了Softmax之后又接到nn.CrossEntropyLoss里但是他的实现里没有做数值稳定Out了。后来我给了一版本问题就消失了。5.3 情况三loss持续不降精准定位是哪个环节出问题loss不降也是高频问题排查思路比很多人想的要系统。第一步先确认代码逻辑。用一批固定数据比如8条样本和固定随机种子去测试检查loss是否在几个step内符合预期下降。如果连固定数据都不降大概率是前向计算或反向传播的实现有bug。第二步确认模型没有变成全输出同一个值。你可以打印模型的预测输出分布如果所有样本输出都一样说明模型退化成了一个常数函数此时需要检查权重初始化、bn层设置或数据预处理是否有问题。第三步检查梯度。可以在每个step计算梯度的全局范数打印出来观察是否在正常范围。梯度全为0说明反向传播断了梯度巨大说明需要加裁剪或调小学习率梯度正常但loss不动说明可能陷入局部最优。第四步检查数据。标签是否错乱是否类别不平衡如果某类样本占比99%模型很容易通过全部预测为多数类来降低loss这时候要用加权交叉熵或者Focal Loss。5.4 常见问题速查表现象可能原因排查方向loss为nan学习率过大调低学习率加梯度裁剪loss为nan数据含nan或inf清洗数据检查预处理loss为nanlog中遇到0加epsilon用数值稳定版交叉熵分类不收敛用了MSE做分类换成交叉熵回归不收敛用了交叉熵做回归换成MSE或MAE训练震荡大MAE后期梯度恒定换成Smooth L1 Loss对异常值敏感MSE受大误差主导用MAE或Huber Loss注意排查问题时一定要从最简单的环节开始。很多新手一上来就怀疑自己的模型结构有问题结果折腾了半天发现只是学习率设大了或者数据没归一化。先检查数据、检查数值稳定性再动模型结构。6. 选型时的几个进阶建议理解了三种基础损失函数后我给你几个在实际项目中总结的进阶经验。虽然标题是“基础知识”但这些经验能让你少走很多弯路。6.1 组合使用而非死守一种没有哪个损失函数是万能的。我在做某图像处理Demo时目标框回归用Smooth L1目标分类用交叉熵两者相加作为总损失。这种多任务损失组合在目标检测、关键点检测等领域非常常见。不要怕组合关键是理解每个部分在做什么以及它们的权重如何平衡。如果分类任务收敛快而回归任务慢可能是回归损失权重偏小需要在总损失中调大回归部分的系数。6.2 损失函数曲线要学会“看形状”训练过程中损失曲线的形状包含大量信息。如果训练集loss持续下降但验证集loss上升说明过拟合了跟损失函数本身关系不大。如果训练和验证loss都降不下来说明模型容量不够或者学习率设置有问题。如果loss曲线出现周期性尖峰可能是batch数据分布不均匀需要调大batchsize或做shuffle。6.3 动手“手算”是理解神经网络的最佳方式我强烈建议初学者找一个极小数据集比如10条样本、3个特征、2分类不用任何深度学习框架纯手写Python实现一个两层网络自己实现前向传播、损失计算、反向传播和参数更新。这样做一遍之后你对损失函数的理解会远超刷十遍教程。前面的代码示例都是numpy实现完全可以当作手写网络的起点。加上推导梯度的过程你会真正明白为什么交叉熵在分类任务中如此有效为什么MSE在回归任务中不可替代。我个人在实际操作中的体会是损失函数表面上只是一行公式但选对选错直接决定训练成败。下次你再遇到模型训不出来别急着换网络结构先花半小时核查一下你的损失函数选得对不对、算得稳不稳定。把基础打牢了后面那些花哨的技巧才有立足之地。最后分享一个小技巧训练时把每个step的loss值用日志记录下来按epoch画成曲线。曲线异常越早发现排查越容易。很多问题拖到训练完才发现往往已经浪费了大量算力和时间。动手之前多想一想“我的损失函数在衡量什么”你会在深度学习的路上少踩很多坑。

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

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

免费获取报价 →
↑