1. 从"模型跑通了"到"模型到底有多好":为什么需要一套衡量指标
很多人第一次跑线性回归,心态是这样的:数据丢进去,fit一调,predict一出,看到预测值和真实值差不多,就觉得大功告成。我当年也是这样,直到有一次拿一个模型去汇报,被问了一句"你这个模型误差到底多大,跟瞎猜比强多少",当场卡壳。那一刻我才意识到,训练模型只是上半场,衡量模型才是决定这个模型能不能用的下半场。
线性回归的本质,是找一条直线(或者超平面)去拟合数据点,让整体预测误差尽可能小。但"误差尽可能小"这句话本身是模糊的——小到什么程度算小?用哪个口径去量这个"小"?换一个数据集,这个数值还有可比性吗?这些问题不解决,模型就只是一个黑盒,你没法判断它是否值得信任,也没法在多个模型之间做选择。
这就是 MSE、RMSE、MAE 和 R Squared 这四个指标存在的意义。它们回答的是同一个问题的不同侧面:我的预测值和真实值之间,到底差了多少,这个差距在业务上意味着什么。MSE(均方误差)和 RMSE(均方根误差)放大了大误差的惩罚,MAE(平均绝对误差)对所有误差一视同仁,而 R Squared(决定系数)则换了一个视角,告诉你模型相比"直接用平均值去猜"提升了多少。
这篇文章适合两类人:一类是刚学完线性回归、能跑通代码但说不清模型好坏的初学者;另一类是工作中要用回归模型做预测、需要向他人解释模型效果的从业者。我会把每个指标的数学定义、计算逻辑、适用场景、坑点都拆开讲,并且给出可以直接复现的代码。看完之后,你不仅能算出这些指标,更能说清楚"为什么这个场景我选 MAE 而不是 RMSE"。
提示:这四个指标不是互斥的,实际项目中通常会一起算出来对照着看。单独看任何一个都可能被误导。
在展开之前,先建立一个直觉:假设你在预测房价,真实值是 100 万,你预测成 110 万,误差是 10 万;另一个样本真实值 200 万,你预测成 210 万,误差也是 10 万。从绝对误差看,这两个错误一样大。但从业务角度看,第一个错误是 10% 的偏差,第二个只有 5%,严重程度并不相同。这个例子说明,误差的衡量方式会直接影响你对模型优劣的判断,而不同的指标恰恰对应着不同的"严重程度"定义。
2. MSE:把误差平方之后,到底改变了什么
2.1 MSE 的数学定义与计算过程
MSE,全称 Mean Squared Error,中文叫均方误差。它的计算逻辑非常直白:把每个样本的预测误差先平方,再求平均。
公式写出来是这样:
$$ MSE = \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2 $$
其中 $y_i$ 是第 $i$ 个样本的真实值,$\hat{y}_i$ 是预测值,$n$ 是样本总数。$y_i - \hat{y}_i$ 就是单个样本的残差,平方之后求和再除以样本数。
我用一个具体的小例子带你走一遍。假设有 5 个样本,真实值和预测值如下:
| 样本 | 真实值 | 预测值 | 残差 | 残差平方 |
|---|---|---|---|---|
| 1 | 3.0 | 2.5 | 0.5 | 0.25 |
| 2 | 5.0 | 5.5 | -0.5 | 0.25 |
| 3 | 7.0 | 6.0 | 1.0 | 1.00 |
| 4 | 9.0 | 9.5 | -0.5 | 0.25 |
| 5 | 11.0 | 10.5 | 0.5 | 0.25 |
残差平方和是 0.25 + 0.25 + 1.00 + 0.25 + 0.25 = 2.0,除以 5 得到 MSE = 0.4。
这里有个细节值得注意:残差有正有负,如果直接求平均,正负会相互抵消,可能得到一个接近 0 的值,让你误以为模型很准。平方的作用就是消除符号,让所有误差都变成正向的贡献。这也是为什么 MSE 对异常值特别敏感——一个误差为 10 的样本,平方之后贡献 100,而十个误差为 1 的样本加起来才贡献 10。换句话说,MSE 会"重罚"那些预测得特别离谱的点。
2.2 为什么 MSE 是线性回归的"亲儿子"
如果你看过线性回归的推导,会发现最小二乘法的目标函数就是最小化残差平方和(RSS),而 MSE 只是 RSS 除以样本数。这意味着,当你用最小二乘法训练线性回归时,你实际上就是在优化 MSE。模型训练完成后算出来的 MSE,正是它努力最小化的那个量。
这个关系很重要,因为它解释了一个常见困惑:为什么线性回归对异常值那么敏感?因为 MSE 本身就把异常值的惩罚放大了,模型为了让 MSE 降下来,会不惜牺牲对正常样本的拟合,去迁就那个异常点。我在实际项目中遇到过好几次,数据里混进几个录入错误的极端值,模型整体拟合就被带偏了,换成 MAE 作为评估口径后才发现问题所在。
MSE 的单位是原始目标变量单位的平方。比如预测房价(单位:万元),MSE 的单位就是"万元的平方",这在业务解释上很别扭。你没法跟别人说"我的模型误差是 25 万元的平方",这句话没有意义。这就是 RMSE 存在的理由。
2.3 MSE 的实操代码与注意事项
用 Python 计算 MSE 非常简单,scikit-learn 提供了现成的函数:
from sklearn.metrics import mean_squared_error import numpy as np y_true = np.array([3.0, 5.0, 7.0, 9.0, 11.0]) y_pred = np.array([2.5, 5.5, 6.0, 9.5, 10.5]) mse = mean_squared_error(y_true, y_pred) print(f"MSE: {mse}") # 输出: MSE: 0.4如果你想自己手写一遍加深理解:
mse_manual = np.mean((y_true - y_pred) ** 2) print(f"手动计算 MSE: {mse_manual}") # 输出: 手动计算 MSE: 0.4注意:scikit-learn 在 1.4 版本之后,
mean_squared_error的squared参数被弃用了,默认就是返回 MSE。如果你看到老代码里写squared=False来获取 RMSE,新版本建议直接用root_mean_squared_error。
一个容易踩的坑是样本量对 MSE 的影响。MSE 是一个平均值,理论上不随样本量变化而系统性偏移,但在小样本场景下,个别异常值对 MSE 的拉动会非常明显。我建议在样本量少于几百的时候,算 MSE 的同时一定要看一眼残差的分布,别只盯着一个数字。
3. RMSE:把 MSE 开个根号,为什么就更"好懂"了
3.1 RMSE 与 MSE 的关系:单位对齐的价值
RMSE,Root Mean Squared Error,均方根误差,就是 MSE 开平方:
$$ RMSE = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2} $$
还是用上面那组数据,MSE = 0.4,那么 RMSE = √0.4 ≈ 0.632。
这个 0.632 的意义在于,它的单位和原始目标变量完全一致。如果目标变量是房价(万元),那 RMSE 就是 0.632 万元,你可以直接说"我的模型平均预测偏差大约在 0.63 万元左右"。这种可解释性是 MSE 给不了的。
但这里要澄清一个常见误解:RMSE 不等于平均误差。很多人以为 RMSE 就是"平均每个样本差多少",严格来说不是。由于先平方再开方,RMSE 会比 MAE 略大(除非所有误差都相等),而且它同样对大误差更敏感。RMSE 更准确的解读是"误差的均方根",它衡量的是误差这个随机变量的标准差意义上的量级。
3.2 RMSE 在业务汇报中的实际用法
我在做项目汇报时,RMSE 是出场率最高的指标,原因就是单位对齐。举几个场景:
- 预测销售额(单位:元),RMSE = 3500,意味着模型预测的典型偏差在 3500 元左右。
- 预测配送时长(单位:分钟),RMSE = 4.2,意味着预测时间平均会差 4 分钟左右。
- 预测温度(单位:摄氏度),RMSE = 1.8,意味着预测温度偏差在 2 度以内。
这种表达方式,非技术背景的同事也能立刻理解。相比之下,你说"MSE 是 3.24",对方只会一脸茫然。
不过 RMSE 也有它的"陷阱"。因为平方的存在,少数几个大误差会显著抬高 RMSE,让模型看起来比实际表现差。反过来,如果业务上大误差是不可接受的(比如预测医疗剂量、金融风险敞口),那 RMSE 高恰恰是你需要警惕的信号,这时候它反而是个有用的"报警器"。
3.3 RMSE 的计算与对比实验
from sklearn.metrics import root_mean_squared_error import numpy as np y_true = np.array([3.0, 5.0, 7.0, 9.0, 11.0]) y_pred = np.array([2.5, 5.5, 6.0, 9.5, 10.5]) rmse = root_mean_squared_error(y_true, y_pred) print(f"RMSE: {rmse:.4f}") # 输出: RMSE: 0.6325为了让你直观感受 RMSE 对异常值的敏感,我做一个对比实验。在原来 5 个样本基础上,把第 3 个样本的预测值从 6.0 改成 3.0(制造一个大误差),看看 MSE、RMSE、MAE 各自怎么变:
y_pred_bad = np.array([2.5, 5.5, 3.0, 9.5, 10.5]) mse_bad = mean_squared_error(y_true, y_pred_bad) rmse_bad = root_mean_squared_error(y_true, y_pred_bad) mae_bad = np.mean(np.abs(y_true - y_pred_bad)) print(f"异常情况下 MSE: {mse_bad:.4f}") # 4.4 print(f"异常情况下 RMSE: {rmse_bad:.4f}") # 2.0976 print(f"异常情况下 MAE: {mae_bad:.4f}") # 1.4对比一下:MSE 从 0.4 涨到 4.4,涨了 11 倍;RMSE 从 0.63 涨到 2.10,涨了约 3.3 倍;MAE 从 0.4 涨到 1.4,只涨了 3.5 倍。可以看到,MSE 的放大效应最剧烈,RMSE 次之,MAE 最温和。这个实验很清楚地说明了三个指标对异常值的不同态度。
提示:如果你的数据里异常值很多且你不想让它们主导评估结果,优先看 MAE;如果你恰恰想捕捉异常预测,RMSE 和 MSE 更合适。
4. MAE:为什么它是最"耿直"的误差指标
4.1 MAE 的定义与几何直觉
MAE,Mean Absolute Error,平均绝对误差,计算方式是取残差的绝对值再求平均:
$$ MAE = \frac{1}{n}\sum_{i=1}^{n}|y_i - \hat{y}_i| $$
用最开始那组数据,残差绝对值分别是 0.5、0.5、1.0、0.5、0.5,加起来是 3.0,除以 5 得到 MAE = 0.6。
MAE 的几何直觉非常清晰:它就是每个样本预测偏差的绝对值的平均。没有平方,没有开方,误差是 1 就贡献 1,误差是 10 就贡献 10,线性累加。这种"耿直"带来两个直接后果:一是单位天然对齐,和 RMSE 一样可以直接解释;二是对异常值不敏感,一个极端值不会像在 MSE 里那样被放大成百上千倍。
从优化角度看,MAE 对应的是中位数回归(最小化绝对误差和,最优解是条件中位数),而 MSE 对应的是均值回归(最优解是条件均值)。这个区别在数据分布偏斜时特别重要。举个例子,如果目标变量是收入这种右偏分布,用 MAE 训练的模型会更贴近"典型人群"的收入水平,而用 MSE 训练的模型会被高收入群体拉高预测值。
4.2 MAE 与 RMSE 的取舍:一个真实场景的决策
我做过一个预测用户每日使用时长的项目,数据里有一小撮"重度用户",每天用十几个小时,而绝大多数用户只用几十分钟。这种情况下:
- 用 RMSE 评估,模型会努力去拟合那些重度用户,导致对普通用户的预测偏高。
- 用 MAE 评估,模型更关注大多数普通用户的预测准确性。
最后我们选了 MAE 作为主评估指标,因为业务目标是"给大多数用户一个合理的时长预估",而不是"精确预测极端用户"。这个决策不是拍脑袋,而是先想清楚业务关心的是"平均意义上的准"还是"典型意义上的准",再选对应的指标。
这里给一个简单的判断清单:
| 场景特征 | 推荐指标 | 理由 |
|---|---|---|
| 大误差不可接受,需重点惩罚 | RMSE / MSE | 平方放大异常误差 |
| 数据含较多异常值,想稳健评估 | MAE | 线性累加,不受极端值主导 |
| 需要向非技术方解释误差量级 | RMSE / MAE | 单位与目标变量一致 |
| 目标分布偏斜,关心中位数表现 | MAE | 对应中位数回归 |
| 需要和方差、标准差体系衔接 | MSE / RMSE | 与统计方差同源 |
4.3 MAE 的计算与一个容易忽略的细节
from sklearn.metrics import mean_absolute_error import numpy as np y_true = np.array([3.0, 5.0, 7.0, 9.0, 11.0]) y_pred = np.array([2.5, 5.5, 6.0, 9.5, 10.5]) mae = mean_absolute_error(y_true, y_pred) print(f"MAE: {mae}") # 输出: MAE: 0.6一个容易被忽略的细节是:MAE 在 0 点不可导。这在用梯度下降训练模型时会带来麻烦,因为绝对值函数在原点没有导数,优化过程可能在最优点附近震荡。这也是为什么很多模型训练时用 MSE 作为损失函数(处处可导,优化友好),但评估时用 MAE 来报告结果。训练目标和评估指标不一致,这在实践中是完全正常的,不要觉得别扭。
注意:如果你打算直接用 MAE 作为损失函数训练线性回归,scikit-learn 的
LinearRegression不支持,需要用SGDRegressor配合epsilon_insensitive损失,或者用分位数回归的思路。这是训练和评估分离的一个典型例子。
5. R Squared:换一个维度,回答"比瞎猜强多少"
5.1 R² 的定义:从"误差大小"到"解释了多少变异"
前面三个指标都在回答"误差有多大",而 R Squared(决定系数)回答的是一个完全不同的问题:相比直接用真实值的平均值去预测,我的模型好了多少。
公式是:
$$ R^2 = 1 - \frac{SS_{res}}{SS_{tot}} = 1 - \frac{\sum(y_i - \hat{y}_i)^2}{\sum(y_i - \bar{y})^2} $$
其中 $SS_{res}$ 是残差平方和(模型没解释掉的部分),$SS_{tot}$ 是总平方和(真实值相对均值的总波动)。$\bar{y}$ 是真实值的平均。
理解这个公式的关键在于分母。$SS_{tot}$ 衡量的是"如果我不做任何建模,直接用平均值预测,会产生多大的误差"。分子是"用了模型之后,还剩多大的误差"。两者相除,就是"模型没解释掉的比例"。用 1 减去它,就是"模型解释掉的比例"。
所以 R² 的含义是:目标变量的总变异中,被模型解释掉的比例。R² = 0.85 意味着模型解释了 85% 的波动,剩下 15% 是模型没捕捉到的。
5.2 R² 的取值与那些反直觉的情况
R² 的取值有几个关键点:
- R² = 1:完美预测,残差为 0。
- R² = 0:模型效果和直接用平均值预测一样,等于没学到东西。
- R² < 0:模型比直接用平均值还差。是的,R² 可以是负数,这是很多人第一次见到会惊讶的地方。
R² 为负通常发生在:模型严重过拟合、数据分布和训练时差异巨大、或者模型本身选错了。我在一次时间序列预测中见过 R² = -0.3 的情况,排查后发现是训练集和测试集的时间段差异太大,模型学到的规律在测试集上完全失效。这时候 R² 为负就是一个强烈的警示信号。
R² 还有一个"陷阱":它随着特征数量增加而单调不减。你往模型里加任何特征,哪怕是无意义的噪声,R² 都不会下降(在训练集上)。这就是为什么会有调整后的 R²(Adjusted R²),它对特征数量做了惩罚:
$$ R^2_{adj} = 1 - \frac{(1-R^2)(n-1)}{n-p-1} $$
其中 $n$ 是样本数,$p$ 是特征数。当特征数增加但带来的解释力提升不足以抵消惩罚时,调整 R² 会下降,提示你这个特征可能没必要加。
5.3 R² 的计算与多模型对比实战
from sklearn.metrics import r2_score import numpy as np y_true = np.array([3.0, 5.0, 7.0, 9.0, 11.0]) y_pred = np.array([2.5, 5.5, 6.0, 9.5, 10.5]) r2 = r2_score(y_true, y_pred) print(f"R2: {r2:.4f}") # 输出: R2: 0.9688这个结果说明模型解释了约 96.88% 的变异,表现相当好。但要注意,这个例子只有 5 个样本,R² 在小样本上波动很大,不能太当真。
我做一个更贴近实战的对比。假设我们用同一份数据训练两个模型,一个只用 1 个特征,一个用 3 个特征,看看 R² 和调整 R² 怎么变:
from sklearn.linear_model import LinearRegression from sklearn.metrics import r2_score import numpy as np np.random.seed(42) n = 100 X1 = np.random.randn(n, 1) X2 = np.random.randn(n, 1) X3 = np.random.randn(n, 1) # 真实关系只和 X1 有关,X2、X3 是噪声 y = 3 * X1[:, 0] + np.random.randn(n) * 0.5 # 模型 A:只用 X1 model_a = LinearRegression().fit(X1, y) r2_a = r2_score(y, model_a.predict(X1)) # 模型 B:用 X1、X2、X3 X_all = np.hstack([X1, X2, X3]) model_b = LinearRegression().fit(X_all, y) r2_b = r2_score(y, model_b.predict(X_all)) def adjusted_r2(r2, n, p): return 1 - (1 - r2) * (n - 1) / (n - p - 1) print(f"模型A R2: {r2_a:.4f}, 调整R2: {adjusted_r2(r2_a, n, 1):.4f}") print(f"模型B R2: {r2_b:.4f}, 调整R2: {adjusted_r2(r2_b, n, 3):.4f}")跑出来的结果通常是模型 B 的 R² 略高于模型 A(因为多了两个噪声特征),但调整 R² 反而可能更低。这个实验直观地展示了为什么不能只看 R² 来判断模型好坏,尤其是当你在做特征筛选的时候。
提示:R² 适合用来做模型间的横向对比,但它不能告诉你误差的绝对大小。一个 R² = 0.9 的模型,误差可能是 1,也可能是 1000,取决于目标变量本身的量级。所以 R² 和 RMSE/MAE 要配合着看。
6. 四个指标怎么配合使用:一套可落地的评估流程
6.1 指标组合的推荐搭配
单独看任何一个指标都可能被误导,我的习惯是至少同时看 RMSE 和 R²,再根据数据特点决定要不要加 MAE。具体搭配逻辑是这样的:
- RMSE + R²:RMSE 告诉你误差的绝对量级(业务可解释),R² 告诉你模型相对基线的提升幅度(模型可比较)。这两个组合能覆盖大部分汇报场景。
- MAE + R²:当数据异常值较多、你更关心典型样本的表现时,用 MAE 替代 RMSE。
- MSE:主要用在训练阶段作为损失函数,或者需要和方差体系做数学推导时。日常评估报告里我很少单独展示 MSE,因为它单位不对齐。
一个完整的评估代码模板:
from sklearn.metrics import mean_squared_error, root_mean_squared_error, mean_absolute_error, r2_score import numpy as np def evaluate_regression(y_true, y_pred, n_features=None): mse = mean_squared_error(y_true, y_pred) rmse = root_mean_squared_error(y_true, y_pred) mae = mean_absolute_error(y_true, y_pred) r2 = r2_score(y_true, y_pred) print(f"MSE: {mse:.4f}") print(f"RMSE: {rmse:.4f}") print(f"MAE: {mae:.4f}") print(f"R2: {r2:.4f}") if n_features is not None: n = len(y_true) adj_r2 = 1 - (1 - r2) * (n - 1) / (n - n_features - 1) print(f"调整R2: {adj_r2:.4f}") return {"mse": mse, "rmse": rmse, "mae": mae, "r2": r2} # 使用示例 y_true = np.array([3.0, 5.0, 7.0, 9.0, 11.0]) y_pred = np.array([2.5, 5.5, 6.0, 9.5, 10.5]) evaluate_regression(y_true, y_pred, n_features=1)6.2 从指标反推模型问题:一份排查对照表
指标不只是用来"报告"的,更是用来"诊断"的。我整理了一份从指标异常反推问题的对照表,这是踩了很多坑之后总结出来的:
| 指标表现 | 可能的问题 | 排查方向 |
|---|---|---|
| R² 很高但 RMSE 也很大 | 目标变量量级大,R² 有迷惑性 | 看目标变量的均值和标准差,算相对误差 |
| R² 为负 | 模型比均值基线还差 | 检查训练测试分布、是否过拟合、特征是否有效 |
| RMSE 远大于 MAE | 存在少数大误差样本 | 检查异常值、录入错误、特殊群体 |
| RMSE 和 MAE 接近 | 误差分布均匀,无极端值 | 模型表现稳定,可放心使用 |
| 训练集 R² 高、测试集 R² 低 | 过拟合 | 减少特征、加正则化、增加数据 |
| 调整 R² 远低于 R² | 无效特征过多 | 做特征筛选,剔除噪声特征 |
这张表我在实际工作中反复用到,尤其是"RMSE 远大于 MAE"这一条,几乎每次数据有脏值时都会触发。有一次一个模型的 RMSE 是 MAE 的 5 倍,排查后发现是某个传感器的读数偶尔会跳变到极端值,清洗数据后两个指标就接近了。
6.3 一个完整的端到端示例
把前面所有内容串起来,给一个可以直接跑的完整流程:
import numpy as np from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error, root_mean_squared_error, mean_absolute_error, r2_score # 1. 构造模拟数据 np.random.seed(0) n = 200 X = np.random.randn(n, 2) y = 2.5 * X[:, 0] - 1.8 * X[:, 1] + 4 + np.random.randn(n) * 0.8 # 2. 划分训练测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 3. 训练模型 model = LinearRegression() model.fit(X_train, y_train) # 4. 预测 y_pred = model.predict(X_test) # 5. 评估 mse = mean_squared_error(y_test, y_pred) rmse = root_mean_squared_error(y_test, y_pred) mae = mean_absolute_error(y_test, y_pred) r2 = r2_score(y_test, y_pred) print(f"MSE: {mse:.4f}") print(f"RMSE: {rmse:.4f}") print(f"MAE: {mae:.4f}") print(f"R2: {r2:.4f}")跑完之后,你会得到一组数字。我的建议是:先看 R² 判断模型有没有学到东西,再看 RMSE 判断误差在业务上能不能接受,最后看 RMSE 和 MAE 的比值判断数据里有没有异常值捣乱。这三步走下来,一个模型的基本健康状况就清楚了。
7. 几个我踩过的坑和对应的经验
7.1 别在训练集上算评估指标
这是新手最容易犯的错。在训练集上算 RMSE 和 R²,结果一定很好看,因为模型就是朝着这个目标拟合的。但这个数字没有任何参考价值,它只说明模型"背下了"训练数据。评估指标必须在模型没见过的数据上算,通常是测试集,或者用交叉验证的方式。
我见过有人拿着训练集 R² = 0.99 的模型去上线,结果线上表现一塌糊涂。后来一查,测试集 R² 只有 0.4。这个教训很深刻:训练集的指标是"考试成绩",测试集的指标才是"实战能力"。
7.2 量纲不同时,别直接比较 RMSE
RMSE 是有单位的,不同目标变量的 RMSE 不能直接比大小。预测房价的 RMSE = 5(万元)和预测温度的 RMSE = 5(摄氏度),数字一样但意义完全不同。跨项目比较时,要么统一量纲,要么用相对指标(比如 RMSE 除以目标变量的均值,得到相对误差)。
7.3 R² 高不代表模型有用
R² 高只说明模型解释了大部分变异,但不代表预测精度满足业务需求。举个例子,如果目标变量的波动范围是 0 到 10000,R² = 0.95 听起来很好,但 RMSE 可能是 500,这个误差在业务上可能完全不可接受。R² 是相对指标,RMSE/MAE 是绝对指标,两者缺一不可。
7.4 数据泄漏会让所有指标失真
如果特征里混入了和目标变量高度相关、但在预测时根本拿不到的信息(比如用"最终成交价"去预测"成交价"),所有指标都会异常漂亮,但模型上线就废。这种情况指标本身不会报错,需要你在特征工程阶段就保持警惕。我的习惯是,每加一个特征都问自己一句:"这个特征在真实预测场景下,我拿得到吗?"
7.5 小样本下指标波动大,别过度解读
样本量少于 100 的时候,MSE、RMSE、MAE、R² 都会因为个别样本的变动而剧烈波动。这时候更稳妥的做法是用交叉验证,看指标的均值和方差,而不是单次划分的结果。我一般会在小样本场景下跑 5 折或 10 折交叉验证,把每折的指标都记录下来,看整体分布。
8. 写在最后的一点个人体会
这四个指标我用了很多年,最大的感受是:它们不是用来"证明模型好"的,而是用来"发现模型问题"的。一个健康的评估流程,应该是先假设模型有问题,然后用这些指标去验证假设、定位问题,而不是算出一个好看的 R² 就收工。
另外,指标的选择一定要回到业务。我见过太多人纠结"到底用 RMSE 还是 MAE",其实答案不在数学里,而在业务需求里——你的业务能不能接受偶尔的大误差?你关心的是平均值还是中位数?想清楚这些,指标自然就选出来了。
最后分享一个小习惯:每次评估完模型,我会把四个指标连同目标变量的均值、标准差一起记下来,形成一条"模型档案"。时间久了,你就能从这些数字的变化里看出数据分布有没有漂移、模型有没有退化。这比单次看一个 R² 有用得多。