回归训练表里少数目标值明显偏离多数样本时,可以保留普通线性回归作为基线,再用 HuberRegressor 做同条件对照。Huber 对较小残差使用平方损失,对较大残差使用绝对值损失,减轻极端目标值的影响,同时仍保留这些样本参与拟合。先回查异常值来源,再决定是否采用更稳健的损失;模型标记不能替代数据审核。
本例人为生成 300 行单特征数据,真实关系为 y=3x+2 加小幅随机噪声;先保留 100 行干净测试数据,再把训练集中的 20 个目标值上移 45。这是受控模拟,已在 Windows、Python 3.11.15、scikit-learn 1.9.1、NumPy 2.4.6 的 CPU 环境运行,没有真实业务样本,也不声称 Huber 在任何数据上都优于普通线性回归。

两种回归方法怎样处理较大的残差
LinearRegression 官方文档说明,普通最小二乘拟合会最小化观测目标和预测值之间的残差平方和。残差平方会让极端偏差对目标函数贡献很大,因此这类污染可能拉动斜率与截距。
HuberRegressor 官方文档给出的切换依据是 |y-Xw-c|/scale与 epsilon 的关系;这里的系数 w、截距 c 和尺度 scale 都由拟合得到。它并非按 y 的绝对大小判异常,也不会自动删除超出阈值的行。
| 配置或属性 | 本例用途 | 应怎样解读 |
|---|---|---|
| epsilon=1.35 | 确定标准化残差的损失切换 | 是训练配置,不是业务误差容限 |
| alpha=0.0 | 关闭本例 Huber 的系数正则项 | 便于和普通最小二乘对照 |
| scale_ | 拟合得到的残差尺度 | 不等于目标列的标准差 |
| outliers_ | 训练样本残差标记 | 反映模型拟合后的残差,不证明原记录错误 |
先建立独立环境,以下固定为本文运行版本。macOS/Linux 激活环境使用 source .venv/bin/activate。
python -m venv .venv
.venv\Scripts\activate
python -m pip install scikit-learn==1.9.1 numpy==2.4.6
先划分,再污染训练目标并做对照
把代码保存为 huber_demo.py,运行 python huber_demo.py。两个模型使用完全相同的训练特征、污染后的训练目标和干净测试集;测试目标保持原值,没有用于挑选 epsilon。检查误差时同时输出斜率与截距,便于看到拟合线是否偏离已知生成关系。
import numpy as np
from sklearn.linear_model import HuberRegressor, LinearRegression
from sklearn.metrics import mean_absolute_error, root_mean_squared_error
from sklearn.model_selection import train_test_split
rng = np.random.default_rng(42)
X = rng.uniform(-3, 3, size=(300, 1))
y_clean = 3 * X[:, 0] + 2 + rng.normal(0, 0.3, size=300)
X_train, X_test, y_train_clean, y_test = train_test_split(
X, y_clean, test_size=100, random_state=42)
y_train = y_train_clean.copy()
contaminated = rng.choice(len(y_train), size=20, replace=False)
y_train[contaminated] += 45
assert len(X_train) == 200 and len(X_test) == 100
assert np.sum(y_train != y_train_clean) == 20
assert np.isfinite(X_train).all() and np.isfinite(y_train).all()
models = {
'ordinary': LinearRegression(),
'huber': HuberRegressor(epsilon=1.35, alpha=0.0, max_iter=1000)
}
print('train_test_rows:', len(X_train), len(X_test))
print('contaminated_training_targets:', len(contaminated))
for name, model in models.items():
model.fit(X_train, y_train)
pred = model.predict(X_test)
assert pred.shape == y_test.shape and np.isfinite(pred).all()
print(name, 'coef:', round(float(model.coef_[0]), 6),
'intercept:', round(float(model.intercept_), 6),
'clean_test_MAE:', round(float(mean_absolute_error(y_test, pred)), 6),
'clean_test_RMSE:', round(float(root_mean_squared_error(y_test, pred)), 6))
huber = models['huber']
normalized_residual = np.abs(y_train - huber.predict(X_train)) / huber.scale_
assert np.array_equal(huber.outliers_, normalized_residual > huber.epsilon)
assert huber.outliers_.shape == (200,)
print('huber_scale:', round(float(huber.scale_), 6))
print('huber_flagged_training_rows:', int(huber.outliers_.sum()))
print('injected_targets_flagged:', int(huber.outliers_[contaminated].sum()))
print('fitted_training_rows:', len(huber.outliers_))
怎样读这个模拟实验的结果
本例实际输出如下。200 行参与训练,100 行保留测试,只有 20 个训练目标被人为改动。
train_test_rows: 200 100
contaminated_training_targets: 20
ordinary coef: 2.183498 intercept: 6.431025 clean_test_MAE: 4.503111 clean_test_RMSE: 4.726193
huber coef: 2.983838 intercept: 2.039493 clean_test_MAE: 0.249959 clean_test_RMSE: 0.318851
huber_scale: 0.214742
huber_flagged_training_rows: 75
injected_targets_flagged: 20
fitted_training_rows: 200
普通回归的斜率约 2.1835、截距约 6.4310,干净测试集 MAE 约 4.5031;Huber 的斜率约 2.9838、截距约 2.0395,MAE 约 0.2500。生成关系的斜率为 3、截距为 2,这次对照中 Huber 更接近它。RMSE 同时给出较大误差的影响,两种模型都在同一测试集计算。
Huber 标记了 75 个训练样本,其中包含全部 20 个被注入污染的目标,也包括其他 55 个样本。75 个标记不等于 75 条错数据;自然噪声和拟合尺度也会影响残差阈值。代码用 abs(y_train-predict)/scale_ > epsilon回算布尔标记,并与 outliers_ 逐条比较。最后 fitted_training_rows 仍为 200,说明被标记样本并没有从这次拟合中删除。
复制运行后,先核对划分数量和污染数量,再看输出是否全部为有限数、回算标记是否与 outliers_ 一致。本文没有用测试集选模型配置;真实任务中应在训练阶段的验证数据上比较候选 epsilon 和正则项,最终测试集留给一次独立评测。
换成真实回归数据时,先查这三个问题
- 极端目标是错误还是合法尾部?先回查单位、导出格式、录入来源和标签时间。合法的大额、高值或罕见事件可能正是目标任务的重要部分,不能因为模型残差大就删除。
- 特征 X 是否也存在极端值?Huber 针对响应残差减轻影响,但对极端特征形成的高杠杆点并没有自动的全面保护。高杠杆样本可能改变拟合线、甚至留下较小残差;需要结合特征范围、散点图与具体业务规则核查。
- 测试数据代表哪种分布?本例故意保留干净测试集,回答“训练目标污染对干净预测的影响”。如果真实使用场景含有合法的极端目标,应保留它们并单独报告尾部误差,不能只展示中间范围的平均分数。
多列特征的量纲差异还要另行处理。Huber 的 scale 作用于残差,不会自动逐列标准化 X;需要缩放时,只在训练数据拟合缩放器。若关系本身强烈非线性、存在分组差异或标签定义混乱,换损失函数不能代替修正模型与数据契约。
epsilon 越小越好吗?
较小 epsilon 会让更多标准化残差进入绝对值损失区域,但可能牺牲一部分对正常噪声的拟合。不要把 outliers_ 标记数量越多当成效果越好,也不要在最终测试集上反复调它。先定义真实错误成本,再用训练阶段的验证集和分组错例比较候选设置。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/30593.html