查看全课大纲(第 29 / 35 节)
- 1.特征工程简介
- 2.特征工程的处理流程
- 3.数据与特征概述
- 4.数据统计、相似度与数据探索
- 5.数据探索工具 Pandas_profiling
- 6.工具应用:《泰坦尼克》分析报告
- 7.构造一个微型链家租房数据集用于演示
- 8.数据预处理综述
- 9.缺失值处理实操
- 10.异常值处理实操
- 11.连续值特征构建
- 12.类别型数据特征构建
- 13.特征幅度缩放
- 14.特征离散化
- 15.特征编码:让类别数据参与建模
- 16.特征变换
- 17.时间特征
- 18.特征衍生
- 19.自动化特征工具
- 20.特征选择综述
- 21.特征选择方法
- 22.过滤式特征选择方法实操
- 23.包裹式特征选择方法
- 24.嵌入式特征选择
- 25.混序法与混合法特征选择
- 26.特征降维综述
- 27.案例实操:特征降维
- 28.特征监控概述与指标
- 29.Iris 数据集特征漂移检测案例
- 30.特征监控实战:用分布对比检测数据漂移
- 31.实时特征监控
- 32.电商销量预测项目背景介绍
- 33.数据预处理和特征工程(一)
- 34.数据预处理和特征工程(二)
- 35.数据建模
Iris 数据集特征漂移检测案例
约 10 分钟
小象实战讲义 · 特征工程精讲与实践
在上一节我们学习了特征监控的基本概念和核心指标,本节我们将通过一个具体的案例——鸢尾花(Iris)数据集,来实际操作如何检测特征漂移。我们将学习如何对比训练数据和线上数据的分布差异,理解数据漂移对模型效果的影响,并掌握用代码实现漂移检测的核心逻辑。
💡 核心导读
- 数据漂移检测的核心思路:对比参考数据集(如训练集)与当前数据集(如线上数据)的特征分布差异
- Iris 数据集的特点:包含三种鸢尾花的四个特征(花萼长度/宽度、花瓣长度/宽度),数据按类别顺序排列
- 漂移检测的实现方法:通过计算特征统计量(如均值、分布)的差异来量化漂移程度
- 标签漂移的检测:当数据包含标签时,可以同时检测特征漂移和标签漂移
- 实践工具的逻辑:理解漂移检测工具的核心计算逻辑,而非依赖特定第三方库
数据集准备与探索
首先我们加载并探索 Iris 数据集。Iris 数据集是机器学习中最经典的数据集之一,包含 150 个样本,每个样本有 4 个特征和 1 个标签(三种鸢尾花品种)。
import pandas as pd
import numpy as np
from sklearn import datasets
# 加载 Iris 数据集
iris = datasets.load_iris()
# 创建 DataFrame
iris_frame = pd.DataFrame(iris.data, columns=iris.feature_names)
# 查看数据基本信息
print("数据集形状:", iris_frame.shape)
print("\n前5行数据:")
print(iris_frame.head())
print("\n数据描述统计:")
print(iris_frame.describe())数据集形状: (150, 4)
前5行数据:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
数据描述统计:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
count 150.000000 150.000000 150.000000 150.000000
mean 5.843333 3.057333 3.758000 1.199333
std 0.828066 0.435866 1.765298 0.762238
min 4.300000 2.000000 1.000000 0.100000
25% 5.100000 2.800000 1.600000 0.300000
50% 5.800000 3.000000 4.350000 1.300000
75% 6.400000 3.300000 5.100000 1.800000
max 7.900000 4.400000 6.900000 2.500000现在为数据添加标签列,并查看标签分布:
import pandas as pd
import numpy as np
# 创建包含标签的完整数据集(硬编码构造)
data = [
[5.1, 3.5, 1.4, 0.2, 0],
[4.9, 3.0, 1.4, 0.2, 0],
[4.7, 3.2, 1.3, 0.2, 0],
[4.6, 3.1, 1.5, 0.2, 0],
[5.0, 3.6, 1.4, 0.2, 0],
[5.4, 3.9, 1.7, 0.4, 0],
[4.6, 3.4, 1.4, 0.3, 0],
[5.0, 3.4, 1.5, 0.2, 0],
[4.4, 2.9, 1.4, 0.2, 0],
[4.9, 3.1, 1.5, 0.1, 0],
[7.0, 3.2, 4.7, 1.4, 1],
[6.4, 3.2, 4.5, 1.5, 1],
[6.9, 3.1, 4.9, 1.5, 1],
[5.5, 2.3, 4.0, 1.3, 1],
[6.5, 2.8, 4.6, 1.5, 1],
[5.7, 2.8, 4.5, 1.3, 1],
[6.3, 3.3, 4.7, 1.6, 1],
[4.9, 2.4, 3.3, 1.0, 1],
[6.6, 2.9, 4.6, 1.3, 1],
[5.2, 2.7, 3.9, 1.4, 1],
[6.3, 3.3, 6.0, 2.5, 2],
[5.8, 2.7, 5.1, 1.9, 2],
[7.1, 3.0, 5.9, 2.1, 2],
[6.3, 2.9, 5.6, 1.8, 2],
[6.5, 3.0, 5.8, 2.2, 2],
[7.6, 3.0, 6.6, 2.1, 2],
[4.9, 2.5, 4.5, 1.7, 2],
[7.3, 2.9, 6.3, 1.8, 2],
[6.7, 2.5, 5.8, 1.8, 2],
[7.2, 3.6, 6.1, 2.5, 2]
]
iris_frame_with_target = pd.DataFrame(
data,
columns=['sepal length (cm)', 'sepal width (cm)',
'petal length (cm)', 'petal width (cm)', 'target']
)
# 查看不同标签的样本数量分布
label_distribution = iris_frame_with_target['target'].value_counts().sort_index()
print("不同品种鸢尾花的样本数量分布:")
for label, count in label_distribution.items():
print(f" 品种 {label}: {count} 个样本")不同品种鸢尾花的样本数量分布:
品种 0: 10 个样本
品种 1: 10 个样本
品种 2: 10 个样本特征漂移检测的核心逻辑
特征漂移检测的核心是比较两份数据(参考数据和当前数据)的特征分布差异。在实际工程中,我们通常使用专门的工具库(如原课中使用的 evidently)来生成详细的漂移报告。这里我们通过手写核心逻辑来理解漂移检测的原理。
场景一:无漂移情况(自己和自己比)
import numpy as np
# 创建示例数据(花萼长度特征)
sepal_length_data = [5.1, 4.9, 4.7, 4.6, 5.0, 5.4, 4.6, 5.0, 4.4, 4.9,
7.0, 6.4, 6.9, 5.5, 6.5, 5.7, 6.3, 4.9, 6.6, 5.2,
6.3, 5.8, 7.1, 6.3, 6.5, 7.6, 4.9, 7.3, 6.7, 7.2]
# 场景一:无漂移(自己和自己比)
reference_1 = np.array(sepal_length_data)
current_1 = np.array(sepal_length_data) # 完全相同的数据
mean_ref_1 = reference_1.mean()
mean_cur_1 = current_1.mean()
shift_rate_1 = abs(mean_cur_1 - mean_ref_1) / mean_ref_1 if mean_ref_1 != 0 else np.inf
print("场景一(无漂移) - 花萼长度:")
print(f" 参考数据均值: {mean_ref_1:.4f}")
print(f" 当前数据均值: {mean_cur_1:.4f}")
print(f" 均值相对偏移率: {shift_rate_1:.4%}")
print(f" 漂移检测结果: {'未检测到漂移' if shift_rate_1 < 0.05 else '检测到漂移'}")场景一(无漂移) - 花萼长度:
参考数据均值: 5.8433
当前数据均值: 5.8433
均值相对偏移率: 0.0000%
漂移检测结果: 未检测到漂移场景二:有漂移情况(数据切分对比)
在实际应用中,我们通常将数据切分为训练集(参考数据)和测试集/线上数据(当前数据)进行对比。由于 Iris 数据集是按类别顺序排列的,直接按顺序切分会导致明显的分布差异。
import numpy as np
# 创建完整的花瓣宽度数据(按品种顺序:前10个品种0,中间10个品种1,最后10个品种2)
petal_width_data = [0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1,
1.4, 1.5, 1.5, 1.3, 1.5, 1.3, 1.6, 1.0, 1.3, 1.4,
2.5, 1.9, 2.1, 1.8, 2.2, 2.1, 1.7, 1.8, 1.8, 2.5]
# 场景二:有漂移(前20个样本 vs 后10个样本)
reference_pw = np.array(petal_width_data[:20]) # 前20个样本(品种0和品种1)
current_pw = np.array(petal_width_data[20:]) # 后10个样本(品种2)
mean_ref_pw = reference_pw.mean()
mean_cur_pw = current_pw.mean()
shift_rate_pw = abs(mean_cur_pw - mean_ref_pw) / mean_ref_pw
print("场景二(有漂移) - 花瓣宽度:")
print(f" 参考数据(前20)花瓣宽度均值: {mean_ref_pw:.4f}")
print(f" 当前数据(后10)花瓣宽度均值: {mean_cur_pw:.4f}")
print(f" 均值相对偏移率: {shift_rate_pw:.4%}")
print(f" 漂移检测结果: {'未检测到漂移' if shift_rate_pw < 0.05 else '检测到漂移'}")
print(f"\n分布差异分析:")
print(f" 参考数据包含品种: 0和1(花瓣较窄)")
print(f" 当前数据包含品种: 2(花瓣较宽)")
print(f" 这种差异是由于数据未随机打乱,按顺序切分导致的")场景二(有漂移) - 花瓣宽度:
参考数据(前20)花瓣宽度均值: 0.8000
当前数据(后10)花瓣宽度均值: 2.0400
均值相对偏移率: 155.0000%
漂移检测结果: 检测到漂移
分布差异分析:
参考数据包含品种: 0和1(花瓣较窄)
当前数据包含品种: 2(花瓣较宽)
这种差异是由于数据未随机打乱,按顺序切分导致的漂移检测的工程实践
在实际工程中,特征漂移检测通常包含以下步骤:
- 数据准备:收集参考数据集(训练数据)和当前数据集(线上数据)
- 特征分析:对每个特征计算统计量(均值、标准差、分位数等)
- 分布对比:使用统计检验(如 KS 检验、卡方检验)或距离度量(如 JS 散度、Wasserstein 距离)量化分布差异
- 漂移判定:根据预设阈值判断是否发生显著漂移
- 报告生成:生成可视化报告,展示漂移检测结果
原课程中使用 evidently 工具库生成的 HTML 报告包含以下核心内容:
- 数据概览:两份数据的基本信息(样本数、特征数)
- 特征漂移检测:每个特征的分布对比和漂移判定
- 数值型特征:显示分布直方图、统计量对比
- 类别型特征:显示类别频率对比
- 标签漂移检测:当包含标签时,显示标签分布的变化
对于标签漂移检测,其核心逻辑是对比参考数据和当前数据中各类别的比例变化。例如在分类问题中,如果训练时各类别平衡,但线上数据中某个类别显著增多,就可能影响模型效果。
📝 动手练一练
- 概念辨析:在 Iris 数据集的漂移检测案例中,为什么按顺序切分数据(前100个样本作为参考,后50个样本作为当前)会导致检测到强烈的特征漂移?这种漂移是真实的数据分布变化吗?
👉 点击查看参考答案
这种漂移不是真实的数据分布变化,而是由于数据排列顺序导致的假性漂移。原因如下:
- Iris 数据集中的样本是按类别顺序排列的:前50个是 setosa(品种0),中间50个是 versicolor(品种1),最后50个是 virginica(品种2)
- 当取前100个样本作为参考数据时,包含了 setosa 和 versicolor 两个品种
- 当取后50个样本作为当前数据时,只包含 virginica 一个品种
- 不同品种鸢尾花的特征分布差异很大(如 virginica 的花瓣明显更大更宽)
- 这种差异是由于数据切分方式不合理造成的,而不是线上数据分布真的发生了变化
在实际工程中,正确的做法应该是:
- 在划分训练集和测试集时进行随机打乱(shuffle)
- 确保训练集和测试集的类别分布基本一致
- 如果线上数据确实发生了类别分布变化,那才是需要关注的真实漂移
- 代码实践:使用上面提供的花瓣宽度数据,尝试不同的切分方式(如随机选择20个样本作为当前数据),重新计算漂移率,观察结果变化。
👉 点击查看参考答案
import numpy as np
# 花瓣宽度数据(30个样本)
petal_width_data = [0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1,
1.4, 1.5, 1.5, 1.3, 1.5, 1.3, 1.6, 1.0, 1.3, 1.4,
2.5, 1.9, 2.1, 1.8, 2.2, 2.1, 1.7, 1.8, 1.8, 2.5]
# 随机选择20个样本作为参考数据,10个样本作为当前数据
np.random.seed(42) # 固定随机种子以确保可重复性
all_indices = list(range(30))
np.random.shuffle(all_indices)
ref_indices = all_indices[:20]
cur_indices = all_indices[20:]
reference_random = np.array([petal_width_data[i] for i in ref_indices])
current_random = np.array([petal_width_data[i] for i in cur_indices])
mean_ref_random = reference_random.mean()
mean_cur_random = current_random.mean()
shift_rate_random = abs(mean_cur_random - mean_ref_random) / mean_ref_random
print("随机切分 - 花瓣宽度:")
print(f" 参考数据均值: {mean_ref_random:.4f}")
print(f" 当前数据均值: {mean_cur_random:.4f}")
print(f" 均值相对偏移率: {shift_rate_random:.4%}")
print(f" 漂移检测结果: {'未检测到漂移' if shift_rate_random < 0.05 else '检测到漂移'}")
print(f"\n对比分析:")
print(f" 顺序切分的偏移率: 155.00%")
print(f" 随机切分的偏移率: {shift_rate_random:.2%}")
print(f" 随机切分后漂移显著减小,因为两个数据集的品种分布更均衡")随机切分 - 花瓣宽度:
参考数据均值: 1.0850
当前数据均值: 1.4700
均值相对偏移率: 35.4839%
漂移检测结果: 检测到漂移
对比分析:
顺序切分的偏移率: 155.00%
随机切分的偏移率: 35.48%
随机切分后漂移显著减小,因为两个数据集的品种分布更均衡💡 为什么随机切分后仍显示“检测到漂移”? 这里只有 30 条样本、且三个品种的花瓣宽度差异很大,5% 的固定阈值对小样本而言过于敏感——随机切分能把偏移率从 155% 大幅压低到 35%,但不保证一定低于阈值。工程实践中,单看均值偏移率只是一种快速信号,通常还需结合统计检验(如 KS 检验)或按业务实际校准阈值,避免小样本下的误报。
本章小结
本节我们通过 Iris 数据集的实际案例,深入理解了特征漂移检测的核心逻辑和工程实践。我们学习了:
- 漂移检测的基本原理:通过对比参考数据(训练集)和当前数据(线上数据)的特征分布差异来检测漂移
- Iris 数据集的特点:数据按类别顺序排列,直接顺序切分会导致假性漂移
- 漂移量化方法:使用均值相对偏移率等指标量化漂移程度
- 工程实践要点:正确划分数据集、选择合适的统计检验、设置合理的漂移阈值
📋 行动清单
- 在自己的项目中尝试实现基础的特征漂移检测逻辑
- 分析现有模型,思考哪些特征最容易发生漂移
- 设计一个简单的漂移监控方案,明确监控频率和报警阈值
—— 小象教研组