🏠 总目录📚 本教程 06 · 数据漂移与概念漂移
📑 本页目录(点开跳转)

06 · 数据漂移与概念漂移

38 分钟 | ⭐ 分清它们,才知道该修什么


🎯 一句话

数据漂移:进来的东西变了。 概念漂移:进来的东西没变,但它和答案的关系变了前者重训能修,后者重训未必能修 —— 而且它更难发现。

指标掉了输入分布变了吗?突变还是渐变?变了管道坏了先修,别重训数据漂移重训 / 加权突变渐变有标签的样本上准了吗?没变概念漂移重训+调窗口标签漂移重校准即可 ⭐掉了还准,只是概率偏了两个提问,把四种情况分干净 —— 而它们的处理方式完全不同💀 最贵的错误:管道坏了的时候去重训 —— 那会把错误数据永久固化进模型⭐ 最省力的情况:标签漂移只需重新校准概率,比重训便宜得多
两个提问就能把四种情况分干净,而它们的处理方式完全不同。💀 最贵的错误:管道坏了的时候去重训 —— 那会把错误数据永久固化进模型。⭐ 最省力的情况:标签漂移只需重新校准概率。

📐 一、用概率写清楚区别

模型学的是 $P(y \mid x)$,而你看到的数据由 $P(x, y) = P(y|x)\,P(x)$ 决定。

类型 变的是 直觉
数据漂移(covariate shift) $P(x)$ 变,$P(y\|x)$ 不变 来的人变了,但「什么样的人会买」这个规律没变
概念漂移(concept drift) $P(y\|x)$ 变了 同样的人,现在的行为和以前不一样了
标签漂移(prior shift) $P(y)$ 变 正样本整体比例变了(如欺诈率上升)

💡 一组对照例子(同一个信贷模型):

   数据漂移:
   营销渠道换了 → 来的用户从 35 岁白领变成 22 岁学生
   → 「收入高的人违约率低」这条规律【依然成立】
   → 重新训练(用新人群的数据)就能修好  ✅

   概念漂移:
   经济环境变了 → 同样收入、同样年龄的人,违约率整体上升
   → 「收入 → 违约率」这条【关系本身变了】
   → 必须用新数据重训,而且【旧数据可能有害】⭐

   标签漂移:
   风控策略放松 → 通过率提高 → 坏账率从 2% 涨到 5%
   → 模型排序可能还对,但【输出的概率不再校准】
   → 修法可能只需要重新校准,不用重训  ⭐

最后一行是个实用的省力点标签漂移常常只需要调整阈值或做一次概率重校准, 比重训模型便宜得多。先判断类型,再决定动作。


🔍 二、怎么检测数据漂移

方法 1:逐特征统计检验

特征类型 常用方法 说明
数值 KS 检验 比较两个分布,无需假设正态
数值 PSI(群体稳定性指数)⭐ 工业界最常用,有公认阈值
类别 卡方检验 / PSI

PSI 的公式和阈值(记住这个就够用):

$$\text{PSI} = \sum_i (p_i^{\text{线上}} - p_i^{\text{训练}}) \cdot \ln\frac{p_i^{\text{线上}}}{p_i^{\text{训练}}}$$

   把特征分成 10 个桶(按训练集的分位数切)
   算每个桶的占比变化

   PSI < 0.1     稳定       ✅
   0.1 ~ 0.25    有变化     ⚠️ 关注
   PSI > 0.25    显著漂移   🔴 该查了
import numpy as np

def psi(base, live, bins=10):
    """base/live 是同一个特征的两批取值。按 base 的分位数切桶。"""
    edges = np.quantile(base, np.linspace(0, 1, bins + 1))
    edges[0], edges[-1] = -np.inf, np.inf        # ⭐ 防止线上出现超界值被丢掉
    b = np.histogram(base, edges)[0] / len(base)
    l = np.histogram(live, edges)[0] / len(live)
    eps = 1e-6                                    # ⭐ 防 log(0)
    b, l = np.clip(b, eps, None), np.clip(l, eps, None)
    return float(np.sum((l - b) * np.log(l / b)))

⚠️ PSI 的两个坑

说明
分桶必须按训练集切,且边界要存下来 每次按各自分位数切,永远算不出漂移 💀
样本量小时不稳 少于 1000 条时 PSI 波动很大,别急着告警

方法 2:对抗验证(一次看全部特征)⭐⭐

   ① 训练集样本标成 0,线上样本标成 1
   ② 训一个分类器区分它们
   ③ 如果 AUC ≈ 0.5  → 两批数据分不开 → 没有漂移  ✅
      如果 AUC 明显 > 0.5 → 有漂移 🔴

   ⭐ 更值钱的是第 ④ 步:
   ④ 看这个分类器的【特征重要性】—— 排在最前的就是漂移的元凶

🔗 这个方法你在 Kaggle 23 见过 (那里用它检测训练集和测试集的分布差异)。 同一个工具,换个场景:那里比的是训练集 vs 测试集,这里比的是训练集 vs 线上流量。

它比逐特征检验强的地方能发现「联合分布」的漂移 —— 每个特征单看都正常,但它们的组合变了(比如「高收入 + 年轻」这个组合突然变多)。


🕵️ 三、概念漂移:难在哪、怎么发现

   数据漂移:看输入就能发现       ← 不需要标签  ✅
   概念漂移:必须知道【真实答案】才能发现  ← 需要标签  💀

这就是它难的根本原因:标签常常要等很久(第 7 章专门讲这个)。

三种形态

   ① 突变(sudden)
      政策变了、竞品上线、疫情开始
      └ 表现:某一天指标断崖式下跌

   ② 渐变(gradual)⭐ 最危险
      用户习惯慢慢改变
      └ 表现:每天掉一点点,单看哪天都在正常波动内
      └ 三个月后回头看,已经掉了 20%

   ③ 周期性(recurring)
      季节、节假日、工作日/周末
      └ 不是"坏",但模型要能处理

② 最危险的原因任何单日的告警阈值都不会触发对策:除了日环比,一定要看「和 30 天前比」「和 90 天前比」。

一个便宜的检测手段:滑动窗口 AUC

   把有标签的样本按时间排,算每周的 AUC

   周1  0.82
   周2  0.81
   周3  0.81
   周4  0.79   ← 单看是正常波动
   周5  0.78
   周6  0.76   ← 和周1比已经掉了 0.06  🔴

   ⭐ 关键是【和基线比】,不是和上周比

🛠️ 四、发现了漂移,做什么

别急着重训。按这个顺序判断

   ① 是数据管道坏了吗?
      → 突变 + 只有个别特征异常 + 缺失率变化
      → 【先修管道,绝不重训】⭐(重训会把错误固化,见第 1 章)

   ② 是数据漂移吗(P(x) 变)?
      → 输入分布变了,但有标签的样本上模型还准
      → 用新数据重训 / 加样本权重

   ③ 是概念漂移吗(P(y|x) 变)?
      → 输入分布没怎么变,但预测准确率掉了
      → 必须用新数据重训,且【要考虑丢掉太旧的数据】⭐

   ④ 是标签漂移吗(P(y) 变)?
      → 排序还对,但概率不准了
      → 可能只需重新校准,不用重训  ⭐ 最便宜
情况 该做的 不该做的
管道故障 修管道 重训
数据漂移 重训 / 加权 / 扩样本 ❌ 只调阈值
概念漂移 重训 + 调整训练窗口 ❌ 无脑用全量历史
标签漂移 重校准 / 调阈值 ❌ 大动干戈重训

🔗 重训练的具体策略(窗口多长、多久一次、怎么验证)→ 第 16 章


💀 五、一个"PSI 全绿但模型已经烂了"的真事故

这是概念漂移最典型的样子,也是漂移监控最尴尬的时刻

   某外卖平台的配送时长(ETA)预估模型
   特征:距离、时段、天气、商家出餐历史、骑手密度……

   某个月开始,用户投诉"预计 30 分钟实际 45 分钟"暴涨

   查漂移监控:
   · 全部 47 个特征的 PSI 都 < 0.06        ✅ 全绿
   · 对抗验证 AUC = 0.52                   ✅ 分不开
   · 预测值分布:均值 31.2 → 31.5 分钟     ✅ 几乎没动

   → 监控体系一个字都没说

真正发生的事

   平台调整了骑手的接单规则(一次最多并单从 3 单改成 5 单)
   → 【输入特征一个都没变】:距离一样、时段一样、骑手密度一样
   → 但【同样的输入,实际配送时长普遍多了 4 分钟】

   ⭐ 这就是教科书定义里的 P(y|x) 变了 —— 而且是外部规则造成的
检测手段 结果 为什么
PSI / KS ❌ 全绿 它们只看 $P(x)$
对抗验证 ❌ AUC 0.52 同上,只看输入
预测分布 ❌ 没动 模型的输入没变,输出自然没变
滑动窗口 MAE 6.1 → 9.8 分钟 唯一看了真实答案的
用户投诉率 ✅ 涨了 3 倍 最晚,也最贵

⭐⭐ 这个事故给出的是本章最重要的结论漂移监控(PSI / 对抗验证)只能发现数据漂移,对概念漂移是完全失明的。 无论你的漂移看板多绿,都必须另外有一条"看真实答案"的线。

💡 这个案例还有一个附加教训概念漂移的原因常常在你的系统之外 —— 政策、规则、竞品、天气、上下游的业务决策。 对策:让业务方的重大变更(规则调整、活动、定价)以事件的形式打到你的监控时间轴上, 排查时能一眼对上。🔗 第 11 章会讲这条"变更时间轴"。


🔬 六、对抗验证:完整可跑版

第二节说了思路,这里给能直接抄的实现。

import numpy as np, pandas as pd, lightgbm as lgb
from sklearn.model_selection import cross_val_predict
from sklearn.metrics import roc_auc_score

def adversarial_validation(train_df, live_df, n=50000, seed=0):
    """返回 (AUC, 每个特征的重要性)。AUC≈0.5 说明两批数据分不开。"""
    rng = np.random.default_rng(seed)
    a = train_df.sample(min(n, len(train_df)), random_state=seed)
    b = live_df.sample(min(n, len(live_df)), random_state=seed)
    X = pd.concat([a, b], ignore_index=True)
    y = np.r_[np.zeros(len(a)), np.ones(len(b))]          # ⭐ 训练集=0,线上=1

    # ⭐ 一定要打乱,否则按时间排列的数据在 CV 里会泄漏
    idx = rng.permutation(len(X)); X, y = X.iloc[idx], y[idx]

    clf = lgb.LGBMClassifier(n_estimators=200, learning_rate=.05,
                             num_leaves=31, verbose=-1)
    # ⭐ 用交叉验证的 out-of-fold 概率算 AUC,避免自己评自己
    oof = cross_val_predict(clf, X, y, cv=5, method="predict_proba")[:, 1]
    auc = roc_auc_score(y, oof)

    clf.fit(X, y)
    imp = pd.Series(clf.feature_importances_, index=X.columns) \
            .sort_values(ascending=False)
    return auc, imp
   怎么读结果:
   AUC ≤ 0.55        两批数据基本一致        ✅
   0.55 ~ 0.70       有可察觉的漂移          ⚠️ 看 imp 前几名
   0.70 ~ 0.90       明显漂移                🔴
   > 0.90            💀 几乎可以完美区分
                     —— 先怀疑【泄漏了时间类特征】(如 event_date、id 自增)

⚠️ AUC > 0.9 时的第一反应不该是"漂移好严重",而是 "我是不是把时间戳、自增 ID 这类特征喂进去了" —— 这类特征天然能完美区分新旧数据,会把真正的漂移信号淹掉。 规则:对抗验证前先剔除所有单调递增/时间相关的字段。

⚠️ 漂移检测的六个坑

后果 怎么修
分桶边界每次重算 永远算不出漂移(两边各自等分位)💀 边界随模型一起保存 ⭐
样本量太小就报警 1000 条样本下 PSI 天然波动 0.02~0.05 定最小样本量;小样本时看置信区间
对抗验证喂了时间类特征 AUC 虚高到 0.99,掩盖真信号 先剔除 id / 时间戳 ⭐
只看特征不看标签延迟后的指标 对概念漂移完全失明 💀💀 必须有滑动窗口的真实指标
把周期性当成漂移 每周一、每个月初都告警 用同比(第 8 章)而不是环比
发现漂移就自动重训 管道故障时把错误固化 💀 先分类再动作(第四节那张表)⭐

📐 一个能省很多误报的数字

   PSI 的随机波动和样本量的关系(10 桶、无真实漂移时的经验值):

   样本量     PSI 的典型随机波动
   500        0.05 ~ 0.12     ← 阈值 0.1 会天天报警  💀
   2,000      0.02 ~ 0.05
   10,000     0.005 ~ 0.015
   100,000    < 0.005          ← 这时 0.1 才是个有意义的阈值  ✅

   ⭐ 结论:PSI 的经典阈值(0.1 / 0.25)默认你有【上万条】样本。
      分人群、分渠道拆细之后,每个格子的样本量会掉得很快 ——
      这是「拆细之后到处都在报警」的真正原因。

🔗 七、和站内其他章的关系

相关的地方 和这一章的关系
第 1 章四种变差方式 这一章展开其中的 ②③
第 5 章数据层监控 漂移检测是它的一部分
Kaggle 23对抗验证 同一个工具,换了场景
数学原理 08 分布变了意味着测试分布≠训练分布

✅ 检查点

  1. 数据漂移、概念漂移、标签漂移分别是什么变了?
  2. 用信贷的例子说明三者的区别。
  3. 哪一种最省力?为什么?
  4. PSI 的三档阈值是什么?它有哪两个坑?
  5. 对抗验证怎么做?它比逐特征检验强在哪?
  6. 为什么概念漂移比数据漂移难发现?
  7. 概念漂移的三种形态里哪种最危险?对策是什么?
  8. 发现漂移后的判断顺序是什么?哪种情况绝不能重训?
  9. ETA 那个事故里,为什么 PSI、对抗验证、预测分布全都没报?唯一报出来的是什么?
  10. 这个事故给出的最重要结论是什么?附加教训呢?
  11. 对抗验证 AUC > 0.9 时,第一反应应该是什么?
  12. 漂移检测的六个坑里,哪两个会让你对概念漂移完全失明 / 把错误固化?
  13. PSI 的经典阈值默认你有多少样本?「拆细之后到处都在报警」的真正原因是什么?
👀 答案
  1. 数据漂移 P(x) 变、P(y|x) 不变;概念漂移 P(y|x) 变;标签漂移 P(y) 变。
  2. 数据漂移:渠道换了,来的人从 35 岁白领变 22 岁学生,但"收入高违约低"仍成立。概念漂移:经济变了,同样收入年龄的人违约率整体上升,关系本身变了。标签漂移:风控放松,坏账率 2%→5%,排序可能还对但概率不再校准
  3. 标签漂移。常常只需要重新校准或调阈值,比重训便宜得多。
  4. PSI <0.1 稳定、0.1~0.25 关注、>0.25 显著漂移。两个坑:分桶必须按训练集切且边界存下来(各自切永远算不出漂移)、样本量小于 1000 时不稳
  5. 训练集标 0、线上标 1,训分类器区分;AUC≈0.5 无漂移,明显>0.5 有漂移;更值钱的是看它的特征重要性找出元凶。强在能发现联合分布的漂移——每个特征单看正常但组合变了。
  6. 因为数据漂移看输入就能发现(不需要标签),概念漂移必须知道真实答案(需要标签),而标签常常要等很久。
  7. 渐变最危险——每天掉一点点,任何单日阈值都不触发,三个月后已掉 20%。对策:除了日环比,要看和 30 天前、90 天前比
  8. ①管道坏了吗 ②数据漂移吗 ③概念漂移吗 ④标签漂移吗。管道故障时绝不能重训——会把错误固化。
  9. 因为平台改了骑手并单规则,输入特征一个都没变(距离、时段、骑手密度全一样),变的是"同样输入下实际配送时长多了 4 分钟",即 P(y|x)。PSI 全 <0.06、对抗验证 AUC 0.52、预测均值 31.2→31.5,它们全都只看 P(x)。唯一报出来的是滑动窗口 MAE:6.1 → 9.8 分钟(唯一看了真实答案的指标)。
  10. 漂移监控(PSI/对抗验证)只能发现数据漂移,对概念漂移是完全失明的——无论漂移看板多绿,都必须另外有一条"看真实答案"的线。附加教训:概念漂移的原因常常在你的系统之外(政策、规则、竞品、上下游业务决策),所以要让业务方的重大变更以事件的形式打到监控时间轴上
  11. 不是"漂移好严重",而是"我是不是把时间戳、自增 ID 这类单调特征喂进去了"——它们天然能完美区分新旧数据,会把真正的漂移信号淹掉。对抗验证前先剔除所有时间相关字段。
  12. 只看特征不看标签延迟后的真实指标 → 对概念漂移完全失明;发现漂移就自动重训 → 管道故障时把错误固化。后者的修法是先分类再动作
  13. 默认你有上万条样本(500 条时 PSI 的随机波动就有 0.05~0.12,阈值 0.1 会天天报警)。「拆细之后到处报警」的真正原因是分人群/分渠道后每个格子的样本量掉得很快,PSI 的随机波动随之变大。

🛑 可以停在这里

走神救援

三种漂移数据漂移 P(x)变(来的人变了,规律没变)、概念漂移 P(y|x)变(同样的人行为变了,关系本身变了)、标签漂移 P(y)变(正例比例变,排序可能还对但概率不再校准)。⭐标签漂移最省力——常常只要重新校准或调阈值,不用重训检测数据漂移PSI(<0.1稳定 / 0.1~0.25关注 / >0.25显著漂移;两个坑:分桶必须按训练集切且边界存下来、样本<1000时不稳);⭐⭐对抗验证(训练集标0线上标1,训分类器,AUC≈0.5说明无漂移;更值钱的是看它的特征重要性找元凶;比逐特征强在能发现联合分布漂移)。⭐概念漂移难在必须有标签才能发现(数据漂移看输入就行)。三形态里渐变最危险——每天掉一点点,任何单日阈值都不触发,三个月后掉20% → 要看和30天前、90天前比,不只是日环比。发现漂移后的顺序:①管道坏了吗(💀绝不重训,会把错误固化) ②数据漂移(重训/加权)③概念漂移(重训+调整训练窗口,丢掉太旧的数据)④标签漂移(重校准即可)。💀一个"PSI全绿但模型已经烂了"的真事故:外卖 ETA 模型,平台把骑手并单上限从 3 改到 5,输入特征一个都没变(PSI 全 <0.06、对抗验证 AUC 0.52、预测均值 31.2→31.5),但同样输入下实际时长多了 4 分钟;唯一报出来的是滑动窗口 MAE 6.1→9.8。⭐⭐结论:PSI / 对抗验证只能发现数据漂移,对概念漂移完全失明——漂移看板再绿,也必须另有一条"看真实答案"的线。附加教训:概念漂移的原因常在你的系统之外(政策/规则/竞品)→ 把业务方的重大变更打到监控时间轴上。⚠️对抗验证 AUC>0.9 时先怀疑喂了时间戳/自增ID,不是"漂移严重"。六个坑:分桶边界每次重算、样本太小就报警、喂了时间类特征、只看特征不看真实指标、把周期性当漂移、发现漂移就自动重训。📐PSI 的经典阈值默认你有上万条样本:500 条时随机波动就有 0.05~0.12 —— 这才是"拆细之后到处都在报警"的真正原因

下一节 👉 07-没有标签怎么发现退化.md ⭐⭐

打卡记录保存在你的浏览器里,首页能看到总进度