做回归模型的同学,不管是上课还是做项目,第一个真正需要“动脑子”的概念,几乎都是损失函数。我第一次在代码里看到model.fit(X, y)这行,其实并不知道底层的拟合过程在干什么,直到去手写了线性回归、把均方误差(MSE)从公式变成代码,才明白模型训练的整个过程其实就是在“伺候”这个损失函数。这篇文章就专门来捋清楚线性回归中的损失函数:它为什么存在、有哪些常见选择、怎么直接影响参数求解,以及实操中怎么用它判断模型状态、排查问题。
内容会从最基本的直觉讲起,覆盖数学原理、Python和sklearn的配合使用、损失曲线可视化,再到正则化和训练日志分析。不管你是刚接触机器学习的小白,还是已经跑过几个模型但没深挖细节的初学者,这篇文章都能帮你把“损失函数”这件事彻底搞清楚,以后面试被问到时也能说得有理有据。
1. 损失函数到底在解决什么问题
1.1 从一个最简单的一元线性回归说起
假设我们有一组数据,横轴是房子的面积,纵轴是房价。我们想找一条直线,让它尽量穿过这些点,好让我们以后看到一个新面积时能预测价格。这件事用数学语言说就是:y = wx + b,其中w是斜率,b是截距。问题是,什么样的w和b才算“好”?
这个“好”字就是损失函数登场的起点。我们需要一个数值化的标准,来衡量当前直线和真实数据点之间的差距。没有这个标准,w和b就没有优化方向;有了这个标准,我们才能说w=3, b=2比w=5, b=-1更好,也才能设计迭代算法一步步找到最优参数。
所以损失函数的核心作用,是把“拟合得好不好”这个抽象判断变成一个人人能计算的数字。数字越小,代表直线离真实数据越近,模型就越准;数字越大,代表偏离越厉害,模型越差。机器学习的训练过程,本质上就是一个不断调整参数、让这个数字变小、再变小、直到收敛的数值优化过程。
1.2 误差如何被量化:残差、平方与绝对值
有了“把好坏变成数字”的大方向,具体怎么算呢?对每个样本,模型会给出一个预测值ŷ_i,真实值是y_i,两者相减就得到误差,也叫残差:e_i = y_i - ŷ_i。
单个样本的残差很好算,但模型面对的是很多样本,不可能只照顾某一个点。所以常见做法是把所有点的误差汇总成一个总指标。最简单的直觉是把所有残差直接相加,但正误差和负误差会互相抵消。比如一个点差+100,另一个点差-100,加起来是0,你可能会误以为模型完美,实际却错得很离谱。
这就引出了两种主流的处理方式:
- 取平方再求和,得到均方误差(MSE);
- 取绝对值再求和,得到平均绝对误差(MAE)。
你可能会问:为什么要平方而不是取绝对值?平方和绝对值到底有什么区别?这就是损失函数选型的核心问题,下面一节专门来对比。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 两大主流损失函数的对比与选型逻辑
2.1 均方误差MSE:为什么它是默认选择
均方误差的公式如下:
code复制MSE = (1/n) * Σ(y_i - ŷ_i)²
这里y_i是真实值,ŷ_i是预测值,n是样本数量。它的计算过程可以拆成四步:先求每个样本的残差,然后把残差平方,再把所有平方值加起来,最后除以样本数得到平均值。除以n的作用是消除样本量的影响,这样比较10000个样本的误差和100个样本的误差才有意义,否则样本越多误差天然越大。
这个损失函数能被广泛使用,有几个非常实在的理由。第一,它在数学上是凸函数,凸函数的意思是整个函数只有一个最低点,不会像连绵起伏的山脉一样有很多局部低谷。做优化的人最爱这种函数,因为只要往低处走,最终一定能走到全局最优。第二,它处处可导,而且导数形式非常干净,对w求偏导后能直接写出梯度表达式,这让梯度下降法的实现变得很简洁。
第三,平方操作放大了大误差的惩罚力度。同样一个模型,如果某个点偏了10,另一个点偏了2,平方后前者贡献100,后者只贡献4。这种“重罚大错”的特性和实际业务感受高度一致——我们在预测房价或销量时,往往更怕出现离谱的大偏差,而不是很多个微小偏差。
第四,从统计学角度看,如果样本噪声服从正态分布,那么最小化MSE等价于最大似然估计。这意味着在某些假设下,用MSE训练出来的参数有很好的统计性质。这也是为什么很多教材直接说“最小二乘法”是线性回归的标准解,所谓“二乘”就是指平方。
2.2 平均绝对误差MAE:对异常值的态度完全不同
平均绝对误差公式是:
code复制MAE = (1/n) * Σ|y_i - ŷ_i|
它把平方换成了绝对值,对每个样本的误差一视同仁。偏了10就是10,偏了2就是2,不会有大误差被平方放大,也没有方向上的抵消问题。
MAE的最大优势是对异常值不敏感。如果数据集里混入了一个因为传感器故障而严重偏离的样本,比如真实房价大概500万,突然出现一个5000万的点,MSE会把它的误差平方到巨大,模型可能为了迁就这一个异常点而整体变形。MAE没有这个烦恼,单个异常点带来的损失是线性的,模型不会为了它牺牲整体拟合效果。
但MAE也有自己的硬伤,那就是在误差为0的位置不可导。绝对值函数在0点有一个尖锐的折角,梯度在0附近不稳定。对于像线性回归这种用梯度下降求解的模型来说,接近最优解时MAE的更新步伐可能会变得很奇怪,甚至出现震荡。实际使用中,如果数据干净、异常值少,MSE的收敛通常比MAE更稳定;如果数据噪声大、异常值多,MAE的鲁棒性会让最终模型更可靠。
2.3 其他常用损失:Huber、Log-Cosh与分位数损失
现实世界的数据不会总满足教科书假设,所以除了MSE和MAE,工程中常用的还有几个“折中方案”。
Huber损失是MAE和MSE的合体:当残差小于某个阈值δ时,它表现得像MSE;当残差大于阈值时,它切换成MAE。这种设计的思路很清晰——在小误差区域用平方获得平滑的梯度,在大误差区域用绝对值避免异常点过度影响模型。实际用Huber损失时,阈值δ是需要调的参数。取δ=1还是δ=5,对异常点的容忍度差别很大。如果你不确定数据里异常点多不多,先设一个中等的δ,比如1.0,然后观察训练结果再做微调。
Log-Cosh损失则像是MSE的自然对数变形,它对异常值的惩罚介于MSE和MAE之间,更妙的是它在所有位置都二次可导,优化更平滑。不过当误差非常大时,数值计算可能出现溢出的风险。分位数损失则适合要预测区间而不是单点的场景,比如我们不仅想知道明天的销量是多少,还想知道销量的10%到90%可能范围,这时候就能派上用场。
| 损失函数 | 公式特点 | 对异常值敏感度 | 可导性 | 适用场景 |
|---|---|---|---|---|
| MSE | 残差平方后均 | 高度敏感 | 处处可导 | 默认选择,噪声不严重时 |
| MAE | 残差绝对值均 | 不敏感 | 0点不可导 | 含严重异常值的数据 |
| Huber | 小误差平方,大误差绝对值 | 较低 | 处处可导 | 工程折中,通用性强 |
| Log-Cosh | 残差的对数cosh | 中等 | 处处可导 | 需要平滑优化的场景 |
| 分位数损失 | 按分位数加权 | 可控 | 仅0点不可导 | 区间预测 |
对绝大多数线性回归需求,先跑一遍MSE就够了,然后在验证集上看效果,如果发现模型被异常点牵着走,再考虑切到Huber或MAE。这也是正规的调参思路,不建议一开始就上复杂的损失函数。
3. 从损失函数到参数求解的完整路线
3.1 损失函数的“地形图”视角
把损失函数和参数的关系画出来,就能得到一个碗状曲面。横轴是w,另一个方向是b,纵轴是损失值。每个参数组合对应曲面上一个点,我们希望找到碗底最低的那个点。
这个“碗”就是用MSE作为损失函数时天然形成的几何结构。因为MSE关于参数是二次函数,曲面是凸的,意味着没有乱七八糟的局部坑洞。你可以把参数w和b想象成地面上的经纬度,损失值是海拔高度,训练过程就是一个人站在山上的某个位置,他的目标是走回最低的山谷。所谓“学习”,其实就是走一步看一步,沿着下降最陡的方向下山。
明白了这个比喻,你就理解了为什么梯度这么重要。梯度是损失函数对参数求偏导得到的矢量,它的方向是损失函数上升最快的方向。那么要和它反着走,就能最快下降。梯度的模长告诉我们当前坡有多陡:坡陡的时候步伐可以大一些,快到谷底的时候坡变缓了,自然就走得慢。这种“利用坡度信息下山”的策略,就是梯度下降法的朴素原理。
3.2 正规方程:走解析解的捷径
线性回归的MSE损失有一个特别之处:因为它对参数是二次的,我们可以直接通过求导并让导数等于0,解出最优参数的解析表达式。这个方法就是正规方程,也叫最小二乘的闭式解:
code复制θ = (XᵀX)⁻¹ Xᵀy
这里X是特征矩阵,y是标签向量,θ是我们要找的参数向量。整个过程不需要迭代,一步算出答案,对新手朋友来说也是最直观的验证方式,你看不到损失下降过程,但得到的参数和梯度下降收敛后的结果应该完全一致。
推导过程并不复杂,核心就是对损失函数J(θ) = (1/2m)||Xθ - y||²求梯度,令梯度等于0,然后解方程。之所以多乘一个1/2,纯粹是求导后好消掉平方带来的2,不影响最优解位置,纯粹是为了公式好看。
但正规方程的局限性也很明显:它需要计算XᵀX的逆矩阵,当特征数量很大时,这个计算量会爆炸。比如特征有10万个,XᵀX就是一个10万乘10万的矩阵,光是存储就要80GB内存,何况求逆。如果特征之间高度共线,XᵀX可能是奇异矩阵,根本求不了逆,这时需要加正则化项来缓解。所以正规方程适合特征规模小、数据量中等的场景,而面对大规模数据和复杂模型,梯度下降系列方法才是通用解。
3.3 梯度下降:让损失函数决定走的每一步
梯度下降的思想非常直观,它不直接解方程,而是从一个初始参数出发,反复执行以下步骤:
第1步,计算当前参数下的损失值。第2步,计算损失函数对每个参数的偏导,也就是梯度。第3步,把参数沿着梯度的反方向更新一步,步长由学习率α控制。第4步,重复上述过程,直到损失值的变化小于某个阈值,或者达到预设的最大迭代次数。
用代码表示就是:
python复制w = 0
b = 0
learning_rate = 0.01
for i in range(1000):
y_pred = w * X + b
error = y_pred - y
# 计算梯度
grad_w = (2 / len(X)) * (error * X).sum()
grad_b = (2 / len(X)) * error.sum()
# 更新参数
w = w - learning_rate * grad_w
b = b - learning_rate * grad_b
这里的学习率α是整个过程中最关键的超参数。如果α太大,参数每次跨的步子太大,可能直接跨过碗底,从这边跳到那边,损失值不降反升,甚至震荡发散。如果α太小,更新步伐缓慢,要跑很多轮才收敛,白白浪费算力。我的经验是,一开始先设0.01这种不上不下的值,看损失曲线是稳定下降还是震荡,再按10倍左右去调0.001或0.1,这样定位最优学习率的效率最高。
还有一个极易被忽略的细节:特征缩放。当某个特征取值范围远大于另一个特征时,损失函数的等高线会被拉成很扁的椭圆,梯度方向并不直接指向碗底,而是呈锯齿状来回摆动。这样训练不仅慢,还容易在接近最优解时来回震荡。把特征标准化到均值0、方差1之后,碗的形状会变得圆润,梯度直接指向中心,收敛就快很多。sklearn的StandardScaler就是干这个事的。
3.4 如何观察损失曲线判断训练状态
在实际项目中,损失函数不只是训练机制的一部分,它也是一种诊断工具。通常我们会把训练集的损失和验证集的损失都记录下来,随着训练轮数增加,画出两条随迭代次数变化的曲线。
- 如果训练损失下降正常,但验证损失先降后升,说明模型开始过拟合了,它在记住训练数据的细节,却失去了对新数据的泛化能力;
- 如果两条曲线都高居不下,损失值几乎不动,可能是学习率太大导致在震荡,也可能是特征没做好预处理;
- 如果两条曲线都在下降但速度很慢,可能是学习率太小,需要加大;
- 如果训练损失比验证损失低很多,说明模型复杂度对当前数据来说太高了,对于线性回归,可以试试加正则化。
即使你以后去做深度学习项目,比如训练yolov8这种目标检测模型,看训练输出的loss/box_loss这类曲线图的思路也完全一样。深度学习框架训练时打印的那些逐渐下降的数字,本质上和线性回归的损失曲线没有区别,它们都是模型在说“我在朝正确的方向走”。很多同学能把模型代码跑通,却不知道当前训练状态是好是坏,就是因为没有看损失日志的习惯。从线性回归开始养成记录并观察损失曲线的习惯,后续学习任何复杂模型都会省力很多。
4. Python和sklearn混合实操复盘
4.1 准备一份可复现的演示数据
很多教程喜欢直接用sklearn自带的数据集,这确实方便,但我更建议先自己构造一份带可控噪声的数据。这样你能确切知道真实的w和b是多少,后面验证模型参数时心里有底。
我构造了一个最简单的一元数据集:真实关系是y = 4 * X + 3,然后加上一组服从正态分布的噪声。为了让噪声更真实,我固定了随机种子,这样每个人跑出来的结果都一样,方便对照复现。
python复制import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
np.random.seed(42)
X = np.random.uniform(0, 10, size=(200, 1))
true_w = 4.0
true_b = 3.0
y = true_w * X.squeeze() + true_b + np.random.normal(0, 2.0, size=200)
这里噪声标准差设成2.0,相对于真实数值范围来说是中等强度的干扰,既不至于让数据乱成一团,也不会让模型完美复原真实参数。划分训练集和测试集时,我用了8:2的比例,随机状态同样固定。
python复制X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
如果你用手算正规方程来验证,这份数据是完全够用的。X是200行1列的矩阵,加一列全1变成带截距项的设计矩阵,套公式就能得到结果,和sklearn的拟合结果做对照时,你会发现两者差异只在浮点精度范围内。
4.2 手写MSE和sklearn回归的对照
先手写一个计算MSE的函数,顺便把MAE也写了,方便后面做对比。这段代码没有任何黑魔法,只是把公式原样翻译成NumPy运算。
python复制def mse(y_true, y_pred):
return np.mean((y_true - y_pred) ** 2)
def mae(y_true, y_pred):
return np.mean(np.abs(y_true - y_pred))
def rmse(y_true, y_pred):
return np.sqrt(mse(y_true, y_pred))
然后分别用两种方式训练模型。第一种是sklearn的标准流程,第二种是手写梯度下降,以便把每一步的损失记录下来。
python复制model = LinearRegression()
model.fit(X_train, y_train)
print(f"sklearn拟合结果: w={model.coef_[0]:.4f}, b={model.intercept_:.4f}")
输出大概是:
code复制sklearn拟合结果: w=3.9857, b=3.0648
可以看到,它和真实参数w=4, b=3已经很接近了。这个差距不是模型不行,而是数据里带了噪声,任何模型都只能用有限样本去估计真实规律,不可能做到零误差。
再看sklearn默认在背后优化什么。LinearRegression的fit方法底层默认使用最小二乘法,也就是直接通过正规方程或SVD去求解析解,并不像神经网络那样逐轮迭代。它目标函数本质上就是MSE加一个正则化项,正则化系数为0时就是纯MSE最小化。所以sklearn拟合出来的参数,理论上就是把MSE降到最低的参数。这也是为什么如果我自己写梯度下降且让它充分收敛,得到的参数会和sklearn几乎一模一样。
4.3 梯度下降实现与损失下降全过程可视化
为了让“损失函数主导训练”这件事变得肉眼可见,我手写了一个批量梯度下降,并保存每一轮的损失值。
python复制def gradient_descent(X, y, lr=0.02, epochs=300):
# 给X加一列1,用来对应截距项
X_design = np.c_[np.ones(X.shape[0]), X]
theta = np.zeros(X_design.shape[1])
loss_history = []
for epoch in range(epochs):
y_pred = X_design @ theta
error = y_pred - y
m = len(y)
# 对各参数求梯度,这里1/2是为了求导方便,实际我们用mse即1/m即可
grad = (1 / m) * X_design.T @ error
theta = theta - lr * grad
loss_history.append(mse(y, X_design @ theta))
return theta, loss_history
theta_manual, loss_hist = gradient_descent(X_train, y_train, lr=0.02, epochs=300)
print(f"手写梯度下降结果: w={theta_manual[1]:.4f}, b={theta_manual[0]:.4f}")
这个实现里我特别想把一个点解释清楚:为什么特征矩阵要加一列全1?因为线性回归的截距项b在数学上也可以看作一个“特征永远是1”的权重。加上这列之后,整个模型就统一成ŷ = Xθ,梯度公式变得非常整齐,这也是正规方程里设计矩阵的来源。
训练完成后,我们画出损失曲线:
python复制plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.plot(loss_hist)
plt.xlabel("Iteration")
plt.ylabel("MSE Loss")
plt.title("Training Loss Curve")
plt.subplot(1, 2, 2)
plt.scatter(X_train, y_train, s=10, alpha=0.6, label="训练数据")
x_line = np.linspace(X_train.min(), X_train.max(), 100)
plt.plot(x_line, theta_manual[1] * x_line + theta_manual[0], color="red", label="拟合直线")
plt.xlabel("X")
plt.ylabel("y")
plt.legend()
plt.show()
你会看到损失曲线在前几十轮迅速下降,之后变得平缓,这是典型的收敛形态。因为MSE的地形本身是一个凸碗,梯度下降从远处下山时坡度陡,一旦走近碗底区域,梯度变小,损失值下降就慢了。如果学习率设置正常,曲线应该是平滑下降、没有明显毛刺的;如果曲线出现了先降后升的锯齿,基本可以断定学习率过大了。
4.4 在测试集上评估并理解回归指标
模型训练好之后,光看训练集的损失是不够的,要在没见过的测试集上衡量真实效果。我用sklearn的指标函数一次性算出MSE、RMSE、MAE和R²:
python复制y_pred_train = model.predict(X_train)
y_pred_test = model.predict(X_test)
print("训练集 MSE:", mse(y_train, y_pred_train))
print("测试集 MSE:", mse(y_test, y_pred_test))
print("测试集 RMSE:", rmse(y_test, y_pred_test))
print("测试集 MAE:", mae(y_test, y_pred_test))
print("测试集 R²:", r2_score(y_test, y_pred_test))
一次典型输出大致是:
code复制训练集 MSE: 3.8721
测试集 MSE: 4.2136
测试集 RMSE: 2.0527
测试集 MAE: 1.6377
测试集 R²: 0.8354
这里RMSE是2.05,可以直观理解为“平均每个样本的预测误差约为2.05个单位”,和数据生成时噪声标准差2.0基本对上,说明模型已经把能学的规律都学到了,剩下的误差主要是数据固有噪声。R²等于0.8354,意味着特征X能解释约83.54%的标签方差,这是一个中等偏上的拟合水平。
R²这个指标特别容易被人误解。它不是准确率,不能简单说模型有83%的概率预测正确。它的分子是残差平方和,分母是标签总方差,衡量的是模型相对“始终预测均值”这种无脑基准提升了多少。如果R²为负,说明模型比无脑猜均值还差,基本就是特征选择或者数据预处理出了大问题。
5. 常见坑点与排查技巧实录
5.1 损失函数一直不降怎么办
经典问题。很多人第一次手写梯度下降,跑了几百轮发现损失值纹丝不动,第一反应是调大学习率,但有时怎么调都没用。我自己排查时会按顺序看三件事。
第一件,看特征量级。如果X的范围是几千到几万,而y的范围是0到1,梯度计算就会出问题,因为梯度的量级受特征量级影响很大,参数更新可能被数值误差吞掉。解决方法是将特征归一化到0附近,比如用标准化(X - X.mean()) / X.std()。
第二件,看损失函数的计算逻辑。有没有把y_true和y_pred传反?有没有在循环里重复累积了数据导致损失越来越大?我见过有人手动写损失时没有除以样本数,结果每次迭代的损失值天然巨大,其实已经在下降了,只是曲线的纵轴刻度太大,看起来像没降。
第三件,换一个小数据集试跑。如果数据量很大,可能是单次批量梯度计算和参数更新的节奏不太合适,改小数据量定位问题会快很多。
5.2 损失值出现NaN或异常大
看到NaN的瞬间,很多人的反应是数据有问题,这确实是个方向,但不是唯一方向。我遇到过的几个常见原因如下:
- 学习率设置过大,参数更新越过合法区域,数值溢出成无穷大,后续计算一发不可收拾;
- 没有对特征做归一化,有些特征值极大,计算梯度时
X.T @ error直接溢出; - 如果数据里有缺失值,并且没有做任何处理,传给NumPy参与运算后也会出现NaN;
- 用Log-Cosh这类损失时,预测残差极大,内部计算溢出,也可能导致NaN。
排查时建议先把学习率降到很小的值比如1e-6,把特征全部标准化,确认数据无缺失。如果NaN仍然存在,就在每次迭代时打印theta和损失值,定位是哪一步开始变成NaN,这样能大幅缩小怀疑范围。
5.3 训练损失很低,但业务效果不好
这是最隐蔽的坑。一个模型在测试集上MSE很低,貌似很完美,但上线后得到的预测结果却很离谱,这通常不是损失函数选错,而是目标指标和业务指标不一致。
比如你在预测某个商品的销量,MSE低意味着平均平方误差小,但业务方可能更关心的是“周销量排名前10%的商品是否预测准确”,这时用MSE做损失函数就未必是最优选择,可能需要换成排名类损失或者直接优化业务目标。另一个常见问题是数据泄露。我见过有人用包含未来信息的特征去预测当前,训练时指标漂亮得惊人,实际部署时那些特征根本拿不到,模型自然崩掉。
还有一点,MSE在存在异常值的数据集上会逼迫模型把预测均值拉向异常点,导致大量正常样本预测偏掉。你看到损失值不高,但那只是被平方聚合掩盖了“大多数样本都有小误差”的平庸表现。画出残差分布图,看一下残差是否随机散布在0附近,是判断模型是否真正拟合的重要一步。残差如果出现明显的喇叭形或弯曲形态,说明可能存在异方差性或非线性关系,这时就要考虑做特征变换或换非线性模型,而不是继续盯着MSE数值调参。
5.4 学习平台练手时最容易忽略的事
最近身边不少同学在“头歌”这类在线实训平台上做线性回归相关练习,经常有人来问我:为什么代码照着教程写,评测却不过?我远程看过几个案例,发现大多数问题并不在损失函数本身,而出在数据读入和维度处理上。
头歌这类平台通常把评测数据封装好,要求你的模型预测结果必须匹配特定接口格式。很多人拿到的原始数据维度是(n,),而sklearn的fit方法要求特征矩阵是二维的(n, 1),直接传一维数组会报错或者被当作多特征处理。也有人是先对整个数据集fit再预测,而评测希望你先划分训练测试集,这样结果当然对不上。
另一个常见误区是把二维标签数组直接传给mean_squared_error这类函数,导致输出是一个矩阵而不是一个标量。评测器期望的是每个样本只对应一个误差值,维度不匹配时整个结果都是错的。
至于“线性回归-python和sklearn混合版”这类练习,本质上就是要求你既会手写核心公式,又会调sklearn接口。我的建议是:手写公式时重点关注数组形状和广播机制,多打印y_pred.shape确认维度和y一致;调sklearn时熟悉LinearRegression的参数和属性。两头都能跑通,才算真正掌握了线性回归,而不是只会复制代码。
关于用yolov8这类深度学习框架画损失函数曲线图的做法,也顺带提一句。yolov8训练时会输出CSV格式的训练日志,里面每一行对应一组的train/box_loss、train/cls_loss等数值,用pandas读进来再matplotlib画图,原理跟画线性回归的损失曲线一模一样。会画线性回归的损失曲线后,读深度学习训练日志你会非常自然,这就是掌握基础概念带来的迁移能力。
6. 把损失函数纳入项目习惯的几点心得
代码能跑通和真正理解模型是两码事。很多人用sklearn写完线性回归,看精度不错,就认为大功告成了,但如果你问他“模型优化的是什么目标函数”“不同损失函数对结果有什么影响”,他可能答不上来。这类问题恰恰是面试和实际项目排障中最常遇到的。
我的建议是每次做回归任务时都养成四个习惯。第一,先定义清楚损失函数,知道自己要让模型优化什么。第二,把训练轮次或迭代过程的损失打印出来并画成曲线,确认模型确实在收敛。第三,同时看MSE和MAE这两个指标,如果差异很大,说明数据里存在极端值的可能性很高。第四,在测试集上评估,并且画残差图确认模型没有系统性的拟合偏差。
这四个习惯花不了多少时间,但能帮你省下大量排查问题的时间。我认识很多从kaggle比赛起步的朋友,他们有句经验之谈:真正做数据分析的人,花最多时间看的不是精度数字,而是损失曲线和残差图。因为数字会骗人,曲线不会。
从线性回归入手理解损失函数,性价比极高。这个模型足够简单,让你能一眼看到全部数学过程,又没有脱离所有机器学习模型共享的训练范式:定义损失、计算梯度、更新参数、循环收敛。这套基本功不但适用于线性回归,迁移到逻辑回归、神经网络、深度学习目标检测都一样成立。把这里的每个概念吃透,之后的学习道路会越走越顺畅。
