
1. 项目概述理解损失函数的核心价值在机器学习和深度学习的实战中无论你构建的是识别猫狗的卷积神经网络还是预测房价的回归模型模型训练的本质都是一个不断“纠偏”的过程。模型就像一个刚学走路的孩子它一开始会跌跌撞撞给出各种离谱的预测。而我们作为“教练”需要一个清晰、量化的标准来告诉它“你这一步走得有多差差在哪里。”这个标准就是损失函数。它计算的是模型预测值与真实值之间的差距这个差距值我们称之为“损失”。损失函数的值越小说明模型预测得越准。今天要深入聊的就是两位在训练场上举足轻重的“金牌教练”交叉熵损失函数和均方差损失函数。别看它们名字听起来有点学术但在实际项目中选对哪一个直接关系到你的模型是能快速收敛、表现优异还是陷入训练泥潭、死活学不会。交叉熵是分类任务尤其是多分类当之无愧的王者而均方差则在回归问题中稳坐头把交椅。但为什么会有这种分工它们背后各自的数学原理和脾气秉性是什么在实际写代码时又该如何根据你的数据特性做出最合适的选择这篇文章我就结合自己趟过的坑和积累的经验带你彻底搞懂这两位“教练”让你在下次面对损失函数选择时不再犹豫。2. 核心原理深度对比两种损失函数的数学本质与设计哲学要理解为什么交叉熵和MSE适用于不同的场景我们必须深入到它们的数学公式和设计初衷里去。这不仅仅是记住两个公式更是理解它们衡量“错误”的不同视角。2.1 均方差损失函数的原理与特性均方差顾名思义就是所有预测误差平方的平均值。它的公式非常直观MSE (1/n) * Σ (y_true - y_pred)^2其中y_true代表真实值y_pred代表模型预测值n是样本数量。这个公式几乎刻在了每一个初学者的脑子里计算每个样本的预测误差残差平方它为了消除正负号影响并放大大误差然后求平均。MSE的设计哲学是“逐点精确”。它关注的是预测值与真实值在数值上的直接偏离程度。平方操作带来了几个关键特性对大误差的严厉惩罚由于误差被平方一个较大的误差比如差10会比一个较小的误差比如差1对总损失的贡献大得多100倍 vs 1倍。这迫使模型优先去修正那些错得离谱的预测。处处可导且导数简单MSE的导数非常干净d(MSE)/d(y_pred) -2 * (y_true - y_pred)。这个线性导数意味着在梯度下降过程中模型参数的更新幅度与误差大小成正比误差越大调整的步长也越大这在直觉上非常合理。假设误差服从高斯分布从概率论的角度看最小化MSE等价于在假设数据噪声服从高斯分布正态分布的前提下进行最大似然估计。这使得它在处理许多自然界中受多种微小因素影响的连续值如身高、温度、房价时在理论上是最优的。然而正是这些特性也决定了MSE在分类问题上的“水土不服”。在分类问题中模型的输出通常是经过Softmax函数处理后的概率分布。当使用MSE作为损失函数时特别是对于分类任务会出现一个严重问题在模型预测完全错误即概率很低和接近正确但未完全正确的区域梯度可能非常小。想象一下一个二分类问题真实标签是1模型预测概率是0.01错得很离谱和0.8接近正确。MSE在这两种情况下的损失都很大但它的梯度在预测值接近0或1时会变得非常平缓因为导数中包含(y_pred - y_true)当y_pred被sigmoid/softmax挤压到两端时其关于输入的梯度本身就会很小再乘以这个因子可能导致梯度消失导致模型在训练后期收敛缓慢。这就是我们常说的“学习停滞”现象。2.2 交叉熵损失函数的原理与特性交叉熵源于信息论衡量的是两个概率分布之间的差异。在分类任务中真实标签通常被表示为一个“one-hot”向量例如[0, 0, 1, 0]这可以被视为一个真实的概率分布真实类别概率为1其他为0。而模型的输出经过Softmax也是一个概率分布。交叉熵损失就是衡量这两个分布之间的“距离”。对于二分类问题二元交叉熵的公式为BCE - [y_true * log(y_pred) (1 - y_true) * log(1 - y_pred)]对于多分类问题分类交叉熵的公式为CCE - Σ y_true_i * log(y_pred_i)交叉熵的设计哲学是“概率分布差异”。它不关心预测值在数值上离真实值有多远而是关心你分配给正确类别的“概率置信度”有多高。它的核心特性如下对错误置信的严厉惩罚注意公式中的对数项log(y_pred)。当模型对正确类别的预测概率y_pred很低时比如0.001-log(0.001)的值会非常大约等于6.9这意味着损失会急剧增大。反之当预测概率接近1时损失趋近于0。这种非对称的、对数级别的惩罚迫使模型必须对自己的预测“有信心”且信心必须给对地方。提供更陡峭、更合理的梯度交叉熵损失函数与Softmax激活函数配合使用时会得到一个非常优雅且强大的梯度形式。对于第i个类别的输出梯度为y_pred_i - y_true_i。这个梯度清晰直接它等于模型预测的概率分布与真实分布之间的差值。当预测正确但概率不够高时例如真实为1预测为0.7梯度为负且绝对值适中驱动模型继续增大该类的概率。当预测错误时真实为0预测为0.7梯度为正驱动模型降低该错误概率。这个梯度在整个训练过程中都保持着良好的强度有效避免了MSE可能出现的梯度消失问题使得模型学习效率更高。与最大似然估计的等价性最小化交叉熵损失等价于在假设数据标签服从多项式分布对于分类问题的前提下进行最大似然估计。这从统计上为它在分类任务中的优越性提供了理论支撑。注意这里有一个非常重要的实操细节。在计算交叉熵时从数值稳定性出发我们通常不会直接计算log(y_pred)因为y_pred可能非常接近0导致对数运算溢出到负无穷-inf。在实际的深度学习框架如PyTorch的nn.CrossEntropyLoss或TensorFlow的tf.keras.losses.CategoricalCrossentropy中损失函数内部通常将Softmax运算和对数交叉熵计算合并在一起并采用一些数值技巧如log_softmax从而保证计算的稳定性和效率。因此当你使用这些框架时通常不需要在模型最后一层显式添加Softmax损失函数会帮你处理好。3. 应用场景抉择何时该用谁理解了原理我们就能像老中医一样根据“症状”任务类型来“开方”选择损失函数。这个选择没有绝对的银弹但有着清晰的指导原则。3.1 均方差损失函数的典型应用场景MSE是回归问题的标准配置。只要你的目标是预测一个连续的数值MSE在大多数情况下都是首选。房价预测输入房屋面积、地段、房龄等特征输出一个具体的房价数值。MSE会努力让预测价格与实际成交价的平均平方差最小。气温预测根据历史气象数据预测未来某一天的最高温度。股价趋势分析预测下一时间点的股票收盘价注意这里是价格预测而非涨跌分类。图像生成与重建在自编码器或图像去噪任务中目标是让生成的图像在像素值上尽可能接近原始图像MSE或L2损失是常用的损失函数。在这些场景中数据的噪声往往符合中心极限定理近似高斯分布且我们关心的是预测值与真实值在数值尺度上的精确吻合。MSE的对称性和对大误差的敏感性在这里是优点。3.2 交叉熵损失函数的典型应用场景交叉熵是分类问题的“天选之子”尤其是当输出为类别概率时。图像分类如ImageNet输入一张图片输出它属于1000个类别中每一个的概率。分类交叉熵损失是绝对的主流。垃圾邮件检测二分类判断一封邮件是垃圾邮件1还是正常邮件0。使用二元交叉熵损失。自然语言处理中的词性标注或命名实体识别每个单词需要被分类到一个特定的标签类别中这本质上是一个序列分类问题通常在每个时间步使用交叉熵损失。推荐系统的点击率预测预测用户点击某个物品的概率一个介于0和1之间的值这个问题虽然输出是连续值但其本质是估计一个伯努利分布的参数点击概率因此也常用二元交叉熵损失而非MSE。在这些场景中模型的输出被解释为概率。我们并不关心“概率0.9和0.91之间的数值差”我们更关心模型是否将最大的置信度分配给了正确的类别。交叉熵损失函数完美地契合了这一目标。3.3 边界与特殊案例探讨当然现实世界并非总是非黑即白。有一些场景需要你仔细斟酌二分类问题输出为概率如前所述优先使用二元交叉熵。虽然理论上可以用MSE但你会面临训练慢、容易饱和的问题。回归问题但数据包含异常值MSE对异常值非常敏感因为平方项会放大巨大误差的影响。这可能导致模型为了拟合少数异常点而扭曲了对整体趋势的把握。此时可以考虑使用平均绝对误差MAE或L1损失它对异常值的鲁棒性更强。多标签分类问题一个样本可能同时属于多个类别例如一张图片中同时包含“狗”和“草地”。这时通常对每个类别独立地使用二元交叉熵损失即tf.keras.losses.BinaryCrossentropy配合from_logitsFalse的sigmoid输出而不是使用多分类交叉熵。标签平滑技术在分类任务中为了防止模型对训练数据过度自信过拟合有时会使用标签平滑。这会将硬标签如[0, 0, 1]稍微“软化”如[0.05, 0.05, 0.9]。在这种情况下交叉熵损失依然适用且能带来更好的模型泛化能力。实操心得我个人的经验法则是先看任务本质。如果输出是“是什么”类别无脑用交叉熵如果输出是“是多少”数值首选MSE。在回归任务中如果发现模型受异常点影响大再考虑换用MAE或Huber损失一种结合MSE和MAE优点的损失函数。在分类任务中除非有极其特殊的理由否则不要使用MSE。4. 实战代码解析与梯度视角对比理论说得再多不如一行代码来得实在。我们通过一个简单的例子在代码层面感受两者的差异并直观地看看它们的梯度如何影响训练。4.1 使用PyTorch实现对比假设我们有一个极其简单的二分类逻辑回归场景。我们手动实现前向传播和损失计算以便看清每一步。import torch import torch.nn as nn import matplotlib.pyplot as plt # 模拟数据一个样本特征维度为2真实标签为1 x torch.tensor([[1.0, 2.0]]) # 输入特征 y_true torch.tensor([[1.0]]) # 真实标签二分类这里为1 # 初始化模型参数权重和偏置 torch.manual_seed(42) w torch.randn((2, 1), requires_gradTrue) # 权重 b torch.randn((1), requires_gradTrue) # 偏置 # 学习率 learning_rate 0.1 # 存储不同损失函数下的损失值用于后续对比 losses_mse [] losses_bce [] print(初始参数: w , w.data, b , b.data) print(- * 50) # 使用MSE损失进行几次梯度下降迭代 print(使用MSE损失训练:) for epoch in range(20): # 前向传播: z x * w b z x w b # 使用Sigmoid激活函数得到概率y_pred y_pred torch.sigmoid(z) # 计算MSE损失 loss_mse nn.MSELoss()(y_pred, y_true) # 反向传播 loss_mse.backward() # 梯度下降更新参数 (不自动更新方便对比) with torch.no_grad(): w_grad_mse w.grad.clone() b_grad_mse b.grad.clone() # 这里我们先不实际更新只记录损失 w.grad.zero_() b.grad.zero_() losses_mse.append(loss_mse.item()) if epoch % 5 0: print(fEpoch {epoch}: pred{y_pred.item():.4f}, loss{loss_mse.item():.4f}) # 重置参数使用BCE损失 w.data torch.randn((2, 1), requires_gradTrue) b.data torch.randn((1), requires_gradTrue) print(\n -*50) print(使用BCE损失训练:) for epoch in range(20): z x w b y_pred torch.sigmoid(z) # 计算二元交叉熵损失 # 注意nn.BCELoss 期望输入已经是Sigmoid后的概率 loss_bce nn.BCELoss()(y_pred, y_true) loss_bce.backward() with torch.no_grad(): w_grad_bce w.grad.clone() b_grad_bce b.grad.clone() w.grad.zero_() b.grad.zero_() losses_bce.append(loss_bce.item()) if epoch % 5 0: print(fEpoch {epoch}: pred{y_pred.item():.4f}, loss{loss_bce.item():.4f}) # 绘制损失下降曲线 plt.figure(figsize(10, 5)) plt.plot(losses_mse, labelMSE Loss, markero) plt.plot(losses_bce, labelBCE Loss, markers) plt.xlabel(Epoch) plt.ylabel(Loss Value) plt.title(Comparison of Loss Convergence (MSE vs BCE)) plt.legend() plt.grid(True) plt.show()这段代码虽然简单但揭示了一个关键现象在相同的初始参数、相同的数据和相同的学习率下使用BCE损失时模型预测概率y_pred向真实标签1靠近的速度以及损失值下降的速度通常会比MSE快。这是因为在训练初期当预测概率远离真实标签时BCE提供的梯度信号更强、更明确。4.2 梯度计算与可视化分析为了更深入地理解我们可以单独计算并对比在某个特定预测值下两种损失函数对模型参数w的梯度。梯度决定了参数更新的方向和大小。# 让我们分析在某个特定预测值下梯度的差异 def analyze_gradient(y_pred_val): # 模拟一个预测值 y_pred torch.tensor([[y_pred_val]], requires_gradFalse) y_true torch.tensor([[1.0]]) # 假设线性部分z对某个权重w_i的梯度为 x_i根据链式法则dL/dw_i dL/dy_pred * dy_pred/dz * dz/dw_i # 其中 dy_pred/dz y_pred * (1 - y_pred) (Sigmoid导数) # 我们固定 dz/dw_i 1即输入x_i1来简化只比较 dL/dy_pred * dy_pred/dz 这部分。 # 1. 计算MSE损失的梯度分量 (dMSE/dy_pred) # MSE (y_pred - y_true)^2 # dMSE/dy_pred 2*(y_pred - y_true) grad_mse_wrt_ypred 2 * (y_pred - y_true) # 2. 计算BCE损失的梯度分量 (dBCE/dy_pred) # BCE -[y_true*log(y_pred) (1-y_true)*log(1-y_pred)] # 当 y_true 1 时 dBCE/dy_pred - (1 / y_pred) # 当 y_true 0 时 dBCE/dy_pred 1 / (1 - y_pred) # 这里 y_true 1 grad_bce_wrt_ypred -1.0 / y_pred # 3. Sigmoid激活函数的梯度 (dy_pred/dz) sigmoid_grad y_pred * (1 - y_pred) # 4. 综合梯度比例 total_grad_mse grad_mse_wrt_ypred * sigmoid_grad total_grad_bce grad_bce_wrt_ypred * sigmoid_grad # 对于BCE有一个更简洁的最终形式 (y_pred - y_true)我们验证一下 # 根据链式法则和Sigmoid导数BCE损失关于z的梯度确实是 y_pred - y_true grad_bce_final y_pred - y_true return { y_pred: y_pred_val, grad_mse_component: grad_mse_wrt_ypred.item(), grad_bce_component: grad_bce_wrt_ypred.item(), sigmoid_grad: sigmoid_grad.item(), total_grad_mse: total_grad_mse.item(), total_grad_bce: total_grad_bce.item(), grad_bce_final: grad_bce_final.item() } # 分析不同预测值下的梯度 pred_values [0.1, 0.2, 0.5, 0.7, 0.9] results [] for p in pred_values: results.append(analyze_gradient(p)) # 打印结果 import pandas as pd df pd.DataFrame(results) print(df.to_string(indexFalse))运行这段分析代码你会得到一个表格。观察数据你会发现一个核心规律当预测概率y_pred很小时例如0.1模型认为正例可能性很低但真实标签是1MSE的梯度 (total_grad_mse) 绝对值相对较小而BCE的梯度 (total_grad_bce或grad_bce_final) 绝对值很大约为-0.9。这意味着BCE会给模型一个强烈的信号“你错得太离谱了赶紧大幅度调整参数”。当预测概率y_pred接近0.5时模型完全不确定两者的梯度强度可能相近。当预测概率y_pred很大时例如0.9模型已经比较有信心MSE的梯度依然存在因为0.9 ! 1而BCE的梯度 (y_pred - y_true -0.1) 则提供了一个温和而持续的修正力推动概率向1.0靠近。这个梯度特性的差异正是交叉熵在分类任务上训练更快、更稳定的根本原因。它提供的梯度信号与模型的错误程度匹配得更好尤其是在模型预测非常错误或非常正确的极端情况下。5. 高级话题与性能优化实践掌握了基础用法和原理我们再来探讨一些进阶场景和优化技巧这些往往是项目成败的关键。5.1 数值稳定性与框架最佳实践在实战中我们几乎从不手动实现交叉熵损失函数而是依赖深度学习框架。但理解框架的“黑盒”操作能帮你避免很多坑。PyTorch中的nn.CrossEntropyLossvsnn.BCELossvsnn.BCEWithLogitsLossnn.CrossEntropyLoss: 这是多分类任务的标准选择。它内部已经包含了Softmax运算。这意味着你的模型最后一层不应该再添加Softmax激活函数直接输出原始的“分数”logits即可。这个损失函数接受两个输入1模型的原始输出logits形状为[batch_size, num_classes]2真实标签类别索引形状为[batch_size]是LongTensor类型。它计算的是分类交叉熵损失。nn.BCELoss: 这是标准的二元交叉熵损失。它期望的输入是已经经过Sigmoid函数处理后的概率值范围在0到1之间。你需要确保模型的最后一层使用了Sigmoid激活函数。输入形状通常是[batch_size, 1]或[batch_size]。nn.BCEWithLogitsLoss: 这是BCELossSigmoid的组合版本并且内置了数值稳定机制。它期望模型的最后一层直接输出logits而不需要Sigmoid。在内部它使用一种称为“log-sum-exp”的数值稳定技巧来计算损失。在二分类任务中这是最推荐使用的损失函数因为它既方便又稳定。TensorFlow/Keras中的对应项tf.keras.losses.CategoricalCrossentropy: 用于多分类类似PyTorch的CrossEntropyLoss。当设置参数from_logitsTrue时它期望原始logits输入内部做Softmaxfrom_logitsFalse时期望已经过Softmax的概率输入。tf.keras.losses.BinaryCrossentropy: 用于二分类。同样from_logitsTrue是推荐用法它等价于PyTorch的BCEWithLogitsLoss。避坑指南我见过最常见的错误就是混用。比如在PyTorch中模型最后一层用了nn.Softmax然后又用了nn.CrossEntropyLoss这相当于做了两次Softmax会导致梯度计算错误模型无法正常训练。记住一个简单的对应关系多分类用CrossEntropyLoss不要最后一层Softmax二分类用BCEWithLogitsLoss不要最后一层Sigmoid。5.2 类别不平衡与损失函数加权在实际数据集中各类别的样本数量可能差异巨大例如疾病诊断数据中健康样本远多于患病样本。这种情况下如果使用标准交叉熵模型可能会倾向于忽略稀有类别因为把所有样本都预测为多数类也能获得一个不错的总体损失。解决方案是为损失函数引入类别权重。原理在计算总损失时对属于不同类别的样本损失乘以不同的权重。稀有类别的样本权重更大使其对总损失的贡献增加从而迫使模型更多地关注这些难学的样本。PyTorch实现nn.CrossEntropyLoss和nn.BCEWithLogitsLoss都提供了weight参数。weight是一个一维张量长度等于类别数每个元素对应一个类别的权重。通常权重可以设置为类别频率的倒数或者通过其他启发式方法如“逆类别频率”计算。import torch import torch.nn as nn # 假设一个3分类问题类别0、1、2的样本数比例为 100:20:5 # 计算类别权重总样本数 / (类别数 * 该类样本数)是一种常见的平衡策略 class_counts torch.tensor([100., 20., 5.]) total_samples class_counts.sum() num_classes len(class_counts) weights total_samples / (num_classes * class_counts) # 得到 [0.24, 1.2, 4.8] # 权重归一化非必须但有时有助于稳定训练 # weights weights / weights.sum() # 创建带权重的损失函数 criterion nn.CrossEntropyLoss(weightweights) # 在训练循环中criterion会使用这些权重自动调整每个样本的损失贡献更高级的策略除了静态权重还有动态调整的方法如Focal Loss。Focal Loss在标准交叉熵的基础上增加了一个调制因子(1 - p_t)^γ其中p_t是模型对真实类别的预测概率。这个因子会降低那些容易分类的样本p_t大的损失权重让模型更专注于难分类的样本p_t小。这在目标检测等任务中处理前景-背景极度不平衡时非常有效。5.3 自定义损失函数与组合损失有时你的目标可能不是单一的。例如在语义分割任务中你既希望每个像素分类正确用交叉熵又希望分割边界平滑可以用一个基于预测图梯度的正则项。这时你就需要自定义或组合损失函数。class CustomCombinedLoss(nn.Module): def __init__(self, alpha0.5): super().__init__() self.alpha alpha # 控制两个损失项之间的权重 self.ce_loss nn.CrossEntropyLoss() # 假设我们还有一个衡量边界平滑度的辅助损失函数 # self.smoothness_loss SomeEdgeAwareLoss() def forward(self, predictions, targets): # 计算主要分类损失 ce self.ce_loss(predictions, targets) # 计算辅助损失这里用伪代码示意 # smooth self.smoothness_loss(predictions) smooth 0.0 # 假设暂时为0 # 组合损失 total_loss ce self.alpha * smooth return total_loss在自定义损失函数时最关键的是确保所有操作都在PyTorch的张量计算图上完成以便能够自动计算梯度。同时要注意不同损失项的量级可能不同需要通过权重参数alpha仔细调整避免某一项主导了整个训练过程。6. 常见问题排查与调优经验实录即使选对了损失函数训练过程中也可能遇到各种问题。下面是我总结的一些典型问题及其排查思路。6.1 损失值不下降或为NaN这是最令人头疼的问题之一。损失值震荡或不变学习率问题这是首要怀疑对象。学习率太大可能导致损失在最小值附近震荡甚至发散学习率太小则导致下降缓慢。尝试使用学习率预热Warmup或自适应优化器如Adam。数据或标签问题检查输入数据是否做了归一化/标准化。未归一化的数据可能导致梯度爆炸或消失。检查标签是否正确是否存在大量错误标注。模型能力问题模型可能过于简单无法拟合数据的复杂度。尝试增加模型层数或宽度。损失函数与任务不匹配再次确认你是否错误地在回归任务上用了交叉熵或在分类任务上用了MSE。损失值为NaN数值溢出在计算交叉熵时如果预测概率y_pred出现0或1由于浮点数精度log(0)会导致负无穷。务必使用BCEWithLogitsLoss或CrossEntropyLoss设置from_logitsTrue它们内部有数值稳定处理。梯度爆炸这通常伴随着权重中出现非常大的值如1e10。检查学习率是否过高考虑添加梯度裁剪torch.nn.utils.clip_grad_norm_。数据包含NaN或Inf检查你的输入数据集中是否混入了非法值。6.2 模型过拟合与损失函数的关系损失函数在训练集上一直下降但在验证集上很早就开始上升这是典型的过拟合。损失函数本身不直接导致过拟合但它是衡量过拟合现象的工具。训练损失低、验证损失高表明模型记住了训练数据的噪声。应对策略正则化在损失函数中加入正则化项如L1或L2正则化权重衰减。在PyTorch的优化器中可以通过weight_decay参数方便地实现L2正则化。早停持续监控验证集损失当其在连续多个epoch内不再下降时停止训练。数据增强对于图像、文本等数据通过旋转、裁剪、加噪声等方式增加训练数据的多样性这是缓解过拟合最有效的手段之一。Dropout在模型训练时随机“丢弃”一部分神经元防止神经元之间产生复杂的共适应关系。6.3 评估指标与损失值的差异损失值在下降但你看重的业务指标如准确率、F1分数却没有提升甚至下降。根本原因损失函数和评估指标优化目标不一致。例如在类别不平衡的数据集上准确率可能不是好指标因为模型把所有样本都预测为多数类就能获得高准确率但这对业务毫无价值。此时交叉熵损失可能因为关注每个样本的概率误差而持续下降但F1分数同时考虑精确率和召回率可能停滞不前。解决方案选择正确的评估指标根据业务目标选择。对于不平衡分类关注精确率-召回率曲线下的面积PR-AUC或F1分数。对于排序问题关注平均精度均值mAP。考虑使用与指标更对齐的损失函数例如如果想直接优化F1分数可以探索一些可微分的F1损失近似函数但这通常比较复杂且不稳定。更常见的做法是使用交叉熵损失进行训练但使用业务指标进行模型选择和早停。也就是说你保存的是在验证集上业务指标最好的模型而不是损失最低的模型。6.4 一个综合排查清单当模型训练出现问题时可以按以下清单快速排查数据检查输入数据维度对吗有NaN/Inf吗标签范围正确吗分类是整数回归是连续值做了必要的预处理归一化吗损失函数检查任务类型分类/回归和损失函数匹配吗对于分类用的是CrossEntropyLoss/BCEWithLogitsLoss吗对于多标签分类用的是BinaryCrossEntropy吗模型输出检查模型的最终输出层配置正确吗用CrossEntropyLoss时不要Softmax用BCEWithLogitsLoss时不要Sigmoid。前向传播一次看看输出值是否合理没有全部为0或1没有异常大/小的值。优化器与超参检查学习率设置是否合理可以尝试一个较小的值如1e-4开始。是否使用了梯度裁剪权重衰减参数是否合适训练过程监控不仅看损失也看一下在一个小批量数据上的准确率或其它简单指标确认模型在学习。监控权重和梯度的分布可以用TensorBoard或简单的统计打印看是否有梯度消失或爆炸。最后关于损失函数的选择我的个人体会是它和优化器、激活函数一样是深度学习工具箱里最基础但也最核心的部件之一。初期不必追求复杂和花哨牢牢掌握交叉熵用于分类、MSE用于回归这个基本原则就能解决80%的问题。在遇到特殊场景时如不平衡数据、多任务学习再回头来深入理解损失函数的数学本质从而能够定制或组合出适合自己任务的损失函数。记住没有“最好”的损失函数只有“最适合”你当前数据和任务的损失函数。多实验多分析训练曲线和模型预测结果你的直觉会在这个过程中被训练得越来越准。