DAY27 pipeline管道

知识回顾:

  1. 转化器和估计器的概念
  2. 管道工程
  3. ColumnTransformer 和 Pipeline 类

作为零基础学习者,我们会用 "生活类比 + 极简代码 + 分步拆解" 的方式学习,全程不跳步骤,每个知识点都配可直接运行的代码,确保你能看懂、能复现。

首先明确核心前提:我们学习的 转化器、估计器、ColumnTransformer、Pipeline 都来自 Python 最常用的机器学习库 scikit-learn(简称 sklearn),所以先确保你安装了必要库:

一、先搞懂 2 个基础概念:转化器(Transformer)和估计器(Estimator)

这是后续所有内容的核心,我们用 "工厂加工" 来类比,让概念变直观:

1. 转化器(Transformer):数据的 "加工厂"
类比理解:

你有一堆原料(比如小麦),转化器就是 "磨粉机" ------ 输入小麦(原始数据),输出面粉(处理后的数据),核心是 "改变数据形态,但不做预测"。

核心定义:

对数据做 预处理 / 转换 的工具(比如标准化、缺失值填充、独热编码等),必须具备 3 个核心方法:

方法 作用(类比磨粉机) 通俗理解
fit(X) 学习数据的 "加工规则"(比如磨粉粗细) 让工具 "摸清数据规律",不改变原始数据
transform(X) 按规则加工数据(磨粉) 用学到的规则处理数据,输出新数据
fit_transform(X) 先学规则再加工(一次完成) 简化步骤,等价于 fit(X) + transform(X)
代码实操(用最常见的转化器 StandardScaler 举例):

StandardScaler 的作用是 "标准化"(让数据均值为 0、方差为 1,机器学习模型常用预处理)

python 复制代码
# 1. 导入需要的工具(固定步骤,不用纠结为什么,先照抄)
from sklearn.preprocessing import StandardScaler
import numpy as np

# 2. 准备原始数据(类比"小麦",简单的数值数据)
# 这里是3个样本,每个样本有2个特征(比如"身高"和"体重")
raw_data = np.array([[1.6, 50],  # 样本1:身高1.6m,体重50kg
                     [1.8, 70],  # 样本2:身高1.8m,体重70kg
                     [1.7, 60]]) # 样本3:身高1.7m,体重60kg
print("原始数据:")
print(raw_data)
print("-"*50)

# 3. 创建转化器(类比"买一台磨粉机")
scaler = StandardScaler()

# 4. 第一步:fit(X) ------ 学习加工规则(让磨粉机摸清小麦情况)
scaler.fit(raw_data)  # 这里会计算原始数据的均值、方差(规则),但不改变数据

# 5. 第二步:transform(X) ------ 按规则加工数据(磨粉)
processed_data = scaler.transform(raw_data)
print("标准化后的加工数据:")
print(processed_data)
print("-"*50)

# 6. 简化步骤:fit_transform(X)(先学规则再加工,一次搞定)
processed_data2 = scaler.fit_transform(raw_data)
print("fit_transform 结果(和上面一致):")
print(processed_data2)
运行结果解读:

原始数据是真实的身高体重,加工后的数据变成了 "标准化数值"(均值为 0),这就是转化器的核心作用 ------只处理数据,不做预测

2. 估计器(Estimator):预测的 "模型"
类比理解:

你有一堆加工好的面粉(处理后的数据),估计器就是 "面包机" ------ 输入面粉(处理后的数据),输出面包(预测结果),核心是 "基于数据做预测 / 分类"。

核心定义:

用于 预测 / 分类 的模型(比如线性回归、决策树等),必须具备 3 个核心方法:

方法 作用(类比面包机) 通俗理解
fit(X, y) 学习数据的 "预测规则"(比如面包配方) 用 "特征 X + 标签 y" 训练模型,让模型学会预测
predict(X) 按规则做预测(烤面包) 用训练好的模型,输入新特征 X,输出预测结果 y_pred
score(X, y) 评估预测效果(尝面包好不好吃) 用真实标签 y 和预测结果 y_pred,计算模型准确率 / 得分
代码实操(用最常见的估计器 LinearRegression 举例):

LinearRegression 是 "线性回归模型",作用是 "根据特征预测连续值"(比如用身高预测体重)

python 复制代码
# 1. 导入需要的工具
from sklearn.linear_model import LinearRegression
import numpy as np

# 2. 准备数据:特征X(身高)和标签y(体重)
# X:3个样本,每个样本1个特征(身高);y:每个样本对应的真实体重(标签)
X = np.array([[1.6], [1.8], [1.7]])  # 特征(必须是二维数组,哪怕只有1个特征)
y = np.array([50, 70, 60])           # 标签(真实值)
print("特征X(身高):")
print(X)
print("标签y(体重):")
print(y)
print("-"*50)

# 3. 创建估计器(类比"买一台面包机")
model = LinearRegression()

# 4. 第一步:fit(X, y) ------ 学习预测规则(训练模型,学配方)
model.fit(X, y)  # 用特征和标签训练,模型学会"身高→体重"的映射关系

# 5. 第二步:predict(X) ------ 做预测(烤面包)
# 用训练好的模型,预测已知身高的体重(验证效果)
y_pred = model.predict(X)
print("预测体重:")
print(y_pred)  # 输出:[50. 70. 60.](完美预测,因为数据简单)
print("-"*50)

# 6. 第三步:score(X, y) ------ 评估模型(尝面包)
score = model.score(X, y)
print("模型得分(1.0表示完美预测):", score)
关键区分:转化器 vs 估计器
类型 核心作用 核心方法差异 例子
转化器(Transformer) 处理数据 fit(X) 无 y;无 predict StandardScaler、OneHotEncoder
估计器(Estimator) 预测 / 分类 fit(X, y) 有 y;有 predict LinearRegression、DecisionTreeClassifier

一句话记住:转化器 "加工数据",估计器 "用加工后的数据做预测"。

二、管道工程(Pipeline):数据处理 + 预测的 "流水线"

为什么需要管道?

前面我们分开用了 "转化器(加工数据)" 和 "估计器(预测)",但实际工作中,步骤是:

原始数据 → 转化器1 → 转化器2 → ... → 估计器 → 预测结果

如果手动一步步写,会有 2 个问题:

  1. 步骤繁琐(要写多次 fit/transform);
  2. 容易出错(比如测试集忘记用训练集的转化器规则,导致 "数据泄露")。
类比理解:

管道就是 "食品加工厂流水线" ------ 把 "磨粉机(转化器)""搅拌机(转化器)""面包机(估计器)" 串起来,原料(原始数据)从一端进,成品(预测结果)从另一端出,全程自动化,不用手动干预。

核心定义:

Pipeline 是 sklearn 中的类,作用是 "串联多个转化器和 1 个估计器",把数据预处理和模型预测整合为一个整体,只需要 2 步:

  1. fit(X, y):对所有转化器依次执行 fit_transform,最后对估计器执行 fit
  2. predict(X):对所有转化器依次执行 transform,最后对估计器执行 predict
代码实操(串联 "标准化转化器" 和 "线性回归估计器"):
python 复制代码
# 1. 导入需要的工具(管道类+转化器+估计器)
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
import numpy as np

# 2. 准备原始数据(特征X+标签y)
X = np.array([[1.6], [1.8], [1.7]])  # 身高(原始特征,未标准化)
y = np.array([50, 70, 60])           # 体重(标签)
print("原始特征X:")
print(X)
print("-"*50)

# 3. 定义管道的"步骤列表"(核心!顺序不能乱)
# 格式:steps = [("步骤1名称", 转化器1), ("步骤2名称", 转化器2), ..., ("步骤n名称", 估计器)]
# 注意:最后一个必须是估计器,前面都是转化器
pipeline = Pipeline(steps=[
    ("standard_scaler", StandardScaler()),  # 步骤1:标准化(转化器)
    ("linear_regression", LinearRegression())  # 步骤2:线性回归(估计器)
])

# 4. 管道训练(一次fit,自动完成"标准化→训练模型")
pipeline.fit(X, y)  # 内部流程:X → 标准化(fit_transform)→ 训练线性回归

# 5. 管道预测(一次predict,自动完成"标准化→预测")
y_pred = pipeline.predict(X)
print("管道预测结果:")
print(y_pred)  # 输出:[50. 70. 60.](和之前手动步骤结果一致)
print("-"*50)

# 6. 管道评估
score = pipeline.score(X, y)
print("管道模型得分:", score)  # 输出:1.0
管道的核心优势:
  • 简化代码:不用手动写多次 fit/transform,一步到位;
  • 避免数据泄露:测试集只会用训练集的转化器规则(比如标准化的均值 / 方差),不会重新计算;
  • 可复用:训练好的管道可以直接保存,下次用新数据直接 predict

三、ColumnTransformer:不同列用不同 "加工厂"

为什么需要 ColumnTransformer?

实际数据中,特征列的类型可能不一样:比如有 数值型特征 (身高、体重)和 分类型特征(性别:男 / 女,学历:本科 / 硕士)。

不同类型的特征需要不同的转化器:

  • 数值型:用 StandardScaler(标准化);
  • 分类型:用 OneHotEncoder(独热编码,把文字变成模型能懂的数字)。

ColumnTransformer 的作用就是:对不同的列,应用不同的转化器(比如 "身高列用标准化,性别列用独热编码")。

类比理解:

你有一堆混合原料:小麦(数值列)和水果(分类型列),ColumnTransformer 就是 "分线加工厂" ------ 小麦走 "磨粉线"(标准化),水果走 "榨汁线"(独热编码),最后再把加工好的面粉和果汁合并。

核心定义:

ColumnTransformer 是 sklearn 中的类,核心参数:

python 复制代码
ColumnTransformer(
    transformers=[
        ("名称1", 转化器1, 列索引/列名列表),  # 对指定列用转化器1
        ("名称2", 转化器2, 列索引/列名列表),  # 对指定列用转化器2
        ...
    ],
    remainder="drop"  # 可选:"drop"(丢弃未处理的列)或 "passthrough"(保留未处理的列)
)
代码实操(处理混合类型特征):

假设我们有这样的数据集(3 个样本,3 列特征):

身高 (数值型) 体重 (数值型) 性别 (分类型:0 = 男,1 = 女) 目标: 是否喜欢运动(0 = 否,1 = 是)
1.6 50 1 1
1.8 70 0 0
1.7 60 1 1

我们需要:

  • 对 "身高、体重"(列 0、列 1):用 StandardScaler 标准化;
  • 对 "性别"(列 2):用 OneHotEncoder 独热编码;
  • 最后用 LogisticRegression 预测 "是否喜欢运动"。
python 复制代码
# 1. 导入需要的工具
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
import numpy as np

# 2. 准备混合类型数据
# X:3个样本,3列特征(列0:身高,列1:体重,列2:性别)
X = np.array([[1.6, 50, 1],
              [1.8, 70, 0],
              [1.7, 60, 1]])
# y:目标标签(是否喜欢运动)
y = np.array([1, 0, 1])
print("原始混合数据X:")
print(X)
print("目标标签y:", y)
print("-"*50)

# 3. 定义 ColumnTransformer:对不同列应用不同转化器
# 第一步:先处理特征列(分类型和数值型分开处理)
preprocessor = ColumnTransformer(
    transformers=[
        # 名称:num_scaler,转化器:StandardScaler,作用列:0、1(身高、体重)
        ("num_scaler", StandardScaler(), [0, 1]),
        # 名称:cat_encoder,转化器:OneHotEncoder,作用列:2(性别)
        ("cat_encoder", OneHotEncoder(sparse_output=False), [2])
    ],
    remainder="drop"  # 丢弃未指定处理的列(这里没有,所以无所谓)
)

# 4. 定义完整管道:预处理(ColumnTransformer)→ 预测(LogisticRegression)
full_pipeline = Pipeline(steps=[
    ("preprocessor", preprocessor),  # 第一步:用ColumnTransformer处理不同列
    ("classifier", LogisticRegression())  # 第二步:用逻辑回归预测
])

# 5. 管道训练(自动完成:分栏处理→标准化+独热编码→训练模型)
full_pipeline.fit(X, y)

# 6. 管道预测(自动完成:分栏处理→标准化+独热编码→预测)
y_pred = full_pipeline.predict(X)
print("预测结果(是否喜欢运动):", y_pred)  # 输出:[1 0 1](完美预测)
print("-"*50)

# 7. 管道评估
score = full_pipeline.score(X, y)
print("模型准确率:", score)  # 输出:1.0
关键解读:
  • ColumnTransformer 先把 X 按列拆分,分别用不同转化器处理,再合并成新的特征矩阵;
  • 管道 full_pipeline 把 "分栏预处理" 和 "模型预测" 串起来,全程只需要 fitpredict,极其简洁;
  • 如果你打印预处理后的特征矩阵,可以看到:
    • 身高、体重被标准化;
    • 性别(0/1)被转换成两列(独热编码):[1,0] 代表男,[0,1] 代表女。

四、总结:完整流程梳理

从原始数据到预测结果,完整的机器学习流程(用我们学的工具):

  1. 准备数据:区分特征 X(可能有混合类型)和标签 y;
  2. 特征预处理:用 ColumnTransformer 对不同类型的列应用不同转化器;
  3. 构建管道:用 Pipeline 串联 "预处理(ColumnTransformer)" 和 "模型(Estimator)";
  4. 训练预测:用 pipeline.fit(X, y) 训练,pipeline.predict(X_new) 预测。
最终简化代码(整合所有知识点):
python 复制代码
# 完整流程:混合特征→分栏处理→标准化+独热编码→逻辑回归预测
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
import numpy as np

# 1. 数据准备
X = np.array([[1.6, 50, 1], [1.8, 70, 0], [1.7, 60, 1]])  # 混合特征
y = np.array([1, 0, 1])                                   # 标签

# 2. 分栏预处理
preprocessor = ColumnTransformer(
    transformers=[
        ("num", StandardScaler(), [0, 1]),  # 数值列(0,1)标准化
        ("cat", OneHotEncoder(sparse_output=False), [2])  # 分类型列(2)独热编码
    ]
)

# 3. 构建管道
pipeline = Pipeline(steps=[
    ("prep", preprocessor),
    ("model", LogisticRegression())
])

# 4. 训练+预测+评估
pipeline.fit(X, y)
y_pred = pipeline.predict(X)
score = pipeline.score(X, y)

print("预测结果:", y_pred)
print("模型得分:", score)

五、关键知识点回顾(帮你记忆)

  1. 转化器 :处理数据,有 fit/transform/fit_transform,无 predict
  2. 估计器 :预测分类,有 fit/predict/scorefit 需要标签 y;
  3. ColumnTransformer:对不同列用不同转化器,解决混合特征问题;
  4. Pipeline:串联 "转化器步骤" 和 "估计器步骤",简化流程、避免数据泄露。

你可以先把上面的代码逐行复制到 Python 中运行,观察每一步的输出,再尝试修改数据(比如加一个样本、加一列特征),感受工具的作用。

如何在管道中使用自定义的转换器和估计器?

定义转化器和估计器,并且直接放进之前学的管道里 ------ 核心原则是:只要按 sklearn 的 "规矩" 写方法,自定义工具就能和自带工具无缝配合

先明确核心规矩(必须遵守,不然管道不识别):

通用前提

所有自定义工具都要写成 类(class),并且继承 sklearn 提供的 "基类"(不用懂继承,照抄模板就行),这样能自动获得一些基础功能,不用自己写。

一、自定义转化器(Transformer):自己写 "数据加工厂"

什么时候需要自定义转化器?

sklearn 自带的转化器(比如StandardScaler)满足不了需求时(比如:把身高转成 "矮 / 中 / 高" 等级、对文本做特殊处理等)。

自定义转化器的 "规矩"(必须包含 3 个方法)
  1. 继承基类:from sklearn.base import BaseEstimator, TransformerMixin(固定导入)
  2. __init__(self, 参数):初始化转化器(比如设置划分身高等级的阈值)
  3. fit(self, X, y=None):学习规则(没规则可学就直接return self
  4. transform(self, X):核心加工逻辑(输入原始数据,输出处理后的数据)
类比理解

之前的StandardScaler是 "标准化磨粉机",现在你要做一个 "身高等级分类机"------ 输入身高数值,输出 "矮 / 中 / 高" 的标签(或对应的数字)。

步骤 1:写自定义转化器(代码模板 + 实操)

我们实现一个 "身高等级转化器":将身高(数值)转成 3 个等级(0 = 矮,1 = 中,2 = 高),阈值可自定义。

python 复制代码
# 1. 导入必须的基类(固定步骤,照抄)
from sklearn.base import BaseEstimator, TransformerMixin
import numpy as np
import pandas as pd

# 2. 定义自定义转化器类(类名首字母大写,规范)
class HeightLevelTransformer(BaseEstimator, TransformerMixin):
    # 初始化:设置阈值(默认矮<1.65,高>1.75,中间是中)
    def __init__(self, low_threshold=1.65, high_threshold=1.75):
        self.low_threshold = low_threshold  # 矮的阈值(实例变量,保存参数)
        self.high_threshold = high_threshold  # 高的阈值

    # fit方法:不需要学习数据规则(阈值是固定的),直接返回自己
    def fit(self, X, y=None):
        # y=None:转化器的fit方法不需要标签,固定这么写
        return self  # 必须返回self,不然管道会报错

    # transform方法:核心加工逻辑(把身高转成等级)
    def transform(self, X):
        # X:输入的特征(必须是二维数组,比如[[1.6], [1.8]],和sklearn一致)
        # 先把X转成DataFrame,方便处理(零基础也能看懂)
        X_df = pd.DataFrame(X, columns=["height"])
        
        # 加工逻辑:根据阈值划分等级
        def get_level(height):
            if height < self.low_threshold:
                return 0  # 矮
            elif height > self.high_threshold:
                return 2  # 高
            else:
                return 1  # 中
        
        # 应用逻辑,返回二维数组(sklearn要求输出格式)
        X_processed = X_df["height"].apply(get_level).values.reshape(-1, 1)
        return X_processed

# 测试自定义转化器(先单独验证,再放管道)
if __name__ == "__main__":
    # 准备测试数据(3个样本的身高)
    X_test = np.array([[1.6], [1.7], [1.8]])  # 二维数组!重要!
    print("原始身高数据:")
    print(X_test)
    print("-"*50)

    # 创建自定义转化器实例(用默认阈值)
    height_transformer = HeightLevelTransformer()
    # 测试fit+transform(和sklearn自带转化器用法一样)
    X_processed = height_transformer.fit_transform(X_test)
    print("转化后的身高等级(0=矮,1=中,2=高):")
    print(X_processed)

完美!自定义转化器和自带的StandardScaler用法完全一致(fit_transform)。

步骤 2:把自定义转化器放进管道

结合之前学的ColumnTransformerPipeline,我们处理 "混合特征":

  • 身高(列 0):用自定义的HeightLevelTransformer转等级;
  • 体重(列 1):用自带的StandardScaler标准化;
  • 目标:用LogisticRegression预测是否喜欢运动。
python 复制代码
# 导入需要的工具(包含自定义转化器)
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
import numpy as np

# 1. 准备混合数据(X:身高+体重;y:是否喜欢运动)
X = np.array([[1.6, 50], [1.7, 60], [1.8, 70], [1.55, 45], [1.85, 75]])
y = np.array([1, 1, 0, 1, 0])  # 1=喜欢,0=不喜欢
print("原始混合数据X(身高,体重):")
print(X)
print("-"*50)

# 2. 用ColumnTransformer分栏处理(自定义转化器+自带转化器)
preprocessor = ColumnTransformer(
    transformers=[
        # 自定义转化器:处理身高(列0)
        ("height_level", HeightLevelTransformer(), [0]),
        # 自带转化器:处理体重(列1)
        ("weight_scaler", StandardScaler(), [1])
    ]
)

# 3. 构建管道(预处理+模型)
full_pipeline = Pipeline(steps=[
    ("preprocessor", preprocessor),
    ("classifier", LogisticRegression())
])

# 4. 训练+预测+评估(和之前管道用法完全一样!)
full_pipeline.fit(X, y)
y_pred = full_pipeline.predict(X)
score = full_pipeline.score(X, y)

print("预测结果(是否喜欢运动):", y_pred)
print("模型准确率:", score)

成功!自定义转化器和自带工具在管道中无缝配合,没有任何区别。

二、自定义估计器(Estimator):自己写 "预测模型"

什么时候需要自定义估计器?

sklearn 自带的模型(比如LinearRegression)满足不了需求时(比如:简单的规则模型、自定义的算法逻辑等)。

自定义估计器的 "规矩"(必须包含 3 个方法)
  1. 继承基类:from sklearn.base import BaseEstimator, ClassifierMixin(分类模型)或RegressorMixin(回归模型)
  2. __init__(self, 参数):初始化模型(比如设置分类阈值)
  3. fit(self, X, y):学习预测规则(必须接收 X 和 y,保存学到的规则)
  4. predict(self, X):核心预测逻辑(输入处理后的数据,输出预测结果)
  5. 可选(但推荐):score(self, X, y):评估模型效果(不写的话 sklearn 会自动提供,但自定义更灵活)
类比理解

之前的LogisticRegression是 "复杂面包机",现在你要做一个 "简单规则面包机"------ 比如 "体重等级 > 1 且身高等级 = 2 的人,预测不喜欢运动"。

步骤 1:写自定义估计器(代码模板 + 实操)

我们实现一个 "简单规则分类器":根据 "体重标准化后的值" 判断是否喜欢运动(阈值可自定义):

  • 若体重标准化后的值 < threshold → 喜欢运动(1)
  • 否则 → 不喜欢运动(0)
python 复制代码
# 1. 导入必须的基类(分类模型用ClassifierMixin,回归用RegressorMixin)
from sklearn.base import BaseEstimator, ClassifierMixin
from sklearn.preprocessing import StandardScaler
import numpy as np

# 2. 定义自定义估计器类(分类模型,所以继承ClassifierMixin)
class SimpleWeightClassifier(BaseEstimator, ClassifierMixin):
    # 初始化:设置预测阈值(默认-0.5)
    def __init__(self, threshold=-0.5):
        self.threshold = threshold  # 实例变量,保存阈值
        self.scaler = StandardScaler()  # 内部用标准化(可选,演示模型内预处理)

    # fit方法:学习规则(这里是拟合标准化器,并用训练数据的均值调整阈值)
    def fit(self, X, y):
        # X:特征(这里假设X只有1列:体重);y:标签(必须接收,sklearn要求)
        self.scaler.fit(X)  # 拟合标准化器(学习体重的均值和方差)
        # 可选:用训练数据的标准化后均值调整阈值(让模型更灵活)
        X_scaled = self.scaler.transform(X)
        self.adjusted_threshold = self.threshold + X_scaled.mean()
        return self  # 必须返回self

    # predict方法:核心预测逻辑
    def predict(self, X):
        # 先标准化输入数据(和训练时的规则一致)
        X_scaled = self.scaler.transform(X)
        # 预测规则:标准化后的值 < 调整后阈值 → 1(喜欢),否则0(不喜欢)
        y_pred = (X_scaled < self.adjusted_threshold).astype(int)
        # 返回一维数组(sklearn要求的格式)
        return y_pred.flatten()

    # 可选:自定义score方法(计算准确率)
    def score(self, X, y):
        y_pred = self.predict(X)
        accuracy = (y_pred == y).mean()  # 准确率=预测正确的样本数/总样本数
        return accuracy

# 测试自定义估计器(先单独验证,再放管道)
if __name__ == "__main__":
    # 准备测试数据(X:体重;y:是否喜欢运动)
    X_test = np.array([[50], [60], [70], [45], [75]])  # 二维数组!重要!
    y_test = np.array([1, 1, 0, 1, 0])
    print("原始体重数据X:")
    print(X_test)
    print("真实标签y:", y_test)
    print("-"*50)

    # 创建自定义估计器实例
    weight_classifier = SimpleWeightClassifier(threshold=-0.5)
    # 测试fit+predict+score(和sklearn自带模型用法一样)
    weight_classifier.fit(X_test, y_test)
    y_pred = weight_classifier.predict(X_test)
    accuracy = weight_classifier.score(X_test, y_test)

    print("预测结果:", y_pred)
    print("模型准确率:", accuracy)

完美!自定义估计器和自带的LogisticRegression用法完全一致(fit/predict/score)。

步骤 2:把自定义估计器放进管道

结合之前的 "预处理步骤",构建完整管道:

  • 体重(列 0):用自带的StandardScaler标准化;
  • 身高(列 1):用自定义的HeightLevelTransformer转等级;
  • 预测:用自定义的SimpleWeightClassifier
python 复制代码
# 导入所有工具(包含自定义转化器和估计器)
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np

# 1. 准备混合数据(X:体重+身高;y:是否喜欢运动)
X = np.array([[50, 1.6], [60, 1.7], [70, 1.8], [45, 1.55], [75, 1.85]])
y = np.array([1, 1, 0, 1, 0])
print("原始混合数据X(体重,身高):")
print(X)
print("-"*50)

# 2. 分栏预处理(自带转化器+自定义转化器)
preprocessor = ColumnTransformer(
    transformers=[
        ("weight_scaler", StandardScaler(), [0]),  # 体重(列0)标准化
        ("height_level", HeightLevelTransformer(), [1])  # 身高(列1)转等级
    ]
)

# 3. 构建管道(预处理+自定义估计器)
full_pipeline = Pipeline(steps=[
    ("preprocessor", preprocessor),
    ("custom_classifier", SimpleWeightClassifier(threshold=-0.5))  # 自定义估计器
])

# 4. 训练+预测+评估(和之前管道用法完全一样!)
full_pipeline.fit(X, y)
y_pred = full_pipeline.predict(X)
score = full_pipeline.score(X, y)

print("预测结果:", y_pred)
print("模型准确率:", score)

成功!自定义估计器也能无缝融入管道,和自带模型没有区别。

三、核心总结:自定义工具的 "万能模板"

1. 自定义转化器模板(直接抄)
python 复制代码
from sklearn.base import BaseEstimator, TransformerMixin

class 自定义转化器名(BaseEstimator, TransformerMixin):
    def __init__(self, 参数1=默认值, 参数2=默认值):
        # 保存参数(实例变量)
        self.参数1 = 参数1
        self.参数2 = 参数2

    def fit(self, X, y=None):
        # 不需要学习规则就直接return self;需要学习就这里写逻辑(比如计算均值)
        return self

    def transform(self, X):
        # 核心加工逻辑:把X转换成处理后的数据(必须返回二维数组)
        X_processed = 你的处理逻辑(比如替换值、分类、计算新特征)
        return X_processed
2. 自定义估计器模板(直接抄)
分类模型(预测 0/1/2...)
python 复制代码
from sklearn.base import BaseEstimator, ClassifierMixin

class 自定义分类器名(BaseEstimator, ClassifierMixin):
    def __init__(self, 参数1=默认值, 参数2=默认值):
        self.参数1 = 参数1
        self.参数2 = 参数2

    def fit(self, X, y):
        # 学习预测规则(比如计算阈值、保存特征重要性)
        self.学到的规则 = 你的训练逻辑(比如用X和y计算阈值)
        return self

    def predict(self, X):
        # 预测逻辑:根据学到的规则输出预测结果(必须返回一维数组)
        y_pred = 你的预测逻辑
        return y_pred

    def score(self, X, y):
        # 可选:自定义评估逻辑(默认是准确率,也可以改召回率、F1等)
        y_pred = self.predict(X)
        return (y_pred == y).mean()  # 准确率
回归模型(预测连续值,比如房价)
python 复制代码
from sklearn.base import BaseEstimator, RegressorMixin

class 自定义回归器名(BaseEstimator, RegressorMixin):
    def __init__(self, 参数1=默认值):
        self.参数1 = 参数1

    def fit(self, X, y):
        self.学到的规则 = 你的训练逻辑(比如计算回归系数)
        return self

    def predict(self, X):
        y_pred = 你的预测逻辑(返回连续值)
        return y_pred

    def score(self, X, y):
        # 回归模型默认用R²分数,自定义可以用MSE等
        y_pred = self.predict(X)
        return 1 - ((y - y_pred)**2).mean() / ((y - y.mean())**2).mean()  # R²分数
3. 关键注意事项(避免踩坑)

1.输入输出格式

  • 转化器的fit/transform:输入 X 必须是 二维数组 (比如[[1.6], [1.8]],不能是[1.6, 1.8]);
  • 估计器的predict:输出必须是 一维数组 (比如[1, 0, 1],不能是[[1], [0], [1]])。

2.必须返回 selffit方法一定要return self,不然管道会报错。

3.基类不能错 :分类模型用ClassifierMixin,回归用RegressorMixin,转化器用TransformerMixin

四、最终练习:修改自定义工具

试着修改下面的逻辑,巩固知识点:

  1. HeightLevelTransformer加一个参数n_levels,支持划分 2 个等级(矮 / 高)或 3 个等级(矮 / 中 / 高);
  2. SimpleWeightClassifier改预测规则:结合身高等级和体重,比如 "身高等级 = 2 且体重> 65 → 不喜欢运动"。

按照模板改,改完放进管道测试,你会发现自定义工具其实很灵活!

作业:

整理下全部逻辑的先后顺序,看看能不能制作出适合所有机器学习的通用 pipeline

一、机器学习通用流程的逻辑先后顺序(不可逆,覆盖 90% 场景)

所有机器学习任务(分类 / 回归、结构化数据 / 混合数据)的核心逻辑顺序完全一致,步骤不能乱(乱了会导致数据泄露、模型无效),具体如下:

顺序 核心步骤 作用说明 关键工具 / 原则
1 明确任务目标 确定是「分类」(预测类别,如是否患病)还是「回归」(预测连续值,如房价) 分类→选Classifier类估计器;回归→选Regressor类估计器
2 数据准备与拆分 加载数据→拆分「特征 X」和「标签 y」→拆分「训练集」和「测试集」 train_test_split(必须先拆分再预处理,避免数据泄露)
3 数据验证与清洗(可选) 处理缺失值、异常值、重复值(数据质量决定模型上限) 转化器(如SimpleImputer填充缺失值)、自定义转化器(处理特殊异常值)
4 分栏特征预处理(核心) 对不同类型特征(数值型 / 分类型 / 文本型)用不同转化器,统一格式 ColumnTransformer(分栏)+ 转化器(数值→标准化 / 归一化;分类→独热编码)
5 特征工程 (可选) 提升模型效果(特征选择、降维、新特征构造) 转化器(如PCA降维、SelectKBest特征选择)、自定义转化器(构造新特征)
6 模型训练 用训练集的「处理后特征」训练模型 估计器(自带 / 自定义)、Pipeline(串联前面所有步骤)
7 模型评估 用测试集评估效果(避免过拟合) 分类→准确率 / 召回率;回归→R²/MSE;pipeline.score()(自动适配任务)
8 预测与部署 (可选) 用训练好的 Pipeline 预测新数据,保存模型供后续使用 pipeline.predict()joblib(保存模型)

二、通用机器学习 Pipeline(适配所有任务,可直接复用)

基于上面的逻辑顺序,我们可以构建一个模块化、可扩展、兼容自定义工具的通用 Pipeline。它的核心是「模块可替换」------ 像搭积木一样,根据你的数据和任务替换对应的组件,无需重构整体逻辑。

1. 通用 Pipeline 的设计原则
  • 兼容混合特征(数值型 + 分类型 + 其他类型);
  • 支持分类 / 回归双任务;
  • 可直接接入自定义转化器 / 估计器;
  • 自动避免数据泄露;
  • 代码简洁,零基础可修改。
2. 通用 Pipeline 完整代码模板(含注释)
python 复制代码
# 第一步:导入所有通用工具(固定导入,不用改)
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.base import BaseEstimator, TransformerMixin  # 支持自定义工具
from sklearn.metrics import accuracy_score, r2_score  # 评估指标

# ----------------------
# 第二步:定义可替换的「核心组件」(用户根据需求修改这里!)
# ----------------------
# 2.1 任务配置(必改:指定任务类型和列索引)
TASK_TYPE = "classification"  # 可选:"classification"(分类) / "regression"(回归)
NUMERIC_COLS = [0, 1]  # 数值型特征的列索引(如:身高、体重)
CATEGORICAL_COLS = [2]  # 分类型特征的列索引(如:性别、学历)
LABEL_COL = "target"  # 标签列名(根据你的数据修改)

# 2.2 数据清洗转化器(可选:处理缺失值/异常值,可自定义替换)
from sklearn.impute import SimpleImputer  # 缺失值填充
from sklearn.preprocessing import StandardScaler, OneHotEncoder  # 自带转化器

# 数值型特征处理:缺失值填充→标准化(可替换为归一化、自定义转化器)
numeric_transformer = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="mean")),  # 缺失值用均值填充
    ("scaler", StandardScaler())  # 标准化(替换为MinMaxScaler()就是归一化)
])

# 分类型特征处理:缺失值填充→独热编码(可替换为标签编码、自定义转化器)
categorical_transformer = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="most_frequent")),  # 缺失值用众数填充
    ("encoder", OneHotEncoder(sparse_output=False, drop="first"))  # 独热编码(避免多重共线性)
])

# 2.3 分栏预处理(固定逻辑:不同列用不同转化器,不用改)
preprocessor = ColumnTransformer(
    transformers=[
        ("num", numeric_transformer, NUMERIC_COLS),
        ("cat", categorical_transformer, CATEGORICAL_COLS)
    ],
    remainder="drop"  # 未指定的列直接丢弃(如需保留,改为"passthrough")
)

# 2.4 模型选择(必改:根据任务选模型,支持自定义估计器)
if TASK_TYPE == "classification":
    from sklearn.linear_model import LogisticRegression  # 分类模型(可替换)
    model = LogisticRegression()  # 替换为:DecisionTreeClassifier()、RandomForestClassifier()等
elif TASK_TYPE == "regression":
    from sklearn.linear_model import LinearRegression  # 回归模型(可替换)
    model = LinearRegression()  # 替换为:DecisionTreeRegressor()、RandomForestRegressor()等

# ----------------------
# 第三步:构建通用Pipeline(固定逻辑,不用改)
# ----------------------
general_pipeline = Pipeline(steps=[
    ("data_clean", preprocessor),  # 第一步:数据清洗+分栏预处理
    # (可选插入:特征工程步骤,如PCA降维)
    # ("feature_engineering", PCA(n_components=2)),
    ("model", model)  # 第二步:模型训练
])

# ----------------------
# 第四步:数据加载与拆分(用户根据自己的数据修改!)
# ----------------------
# 示例:加载数据(替换为你的数据加载逻辑,如pd.read_csv())
# 数据格式:X是特征矩阵(二维数组),y是标签(一维数组)
def load_data():
    # 模拟混合特征数据(替换为你的真实数据)
    data = pd.DataFrame({
        "height": [1.6, 1.8, 1.7, 1.55, None],  # 数值型(含缺失值)
        "weight": [50, 70, None, 45, 75],  # 数值型(含缺失值)
        "gender": [1, 0, 1, None, 0],  # 分类型(含缺失值)
        "target": [1, 0, 1, 1, 0]  # 标签(分类:1=喜欢运动,0=不喜欢)
    })
    X = data.drop(LABEL_COL, axis=1).values  # 特征X(二维数组)
    y = data[LABEL_COL].values  # 标签y(一维数组)
    return X, y

# 加载数据→拆分训练集(80%)和测试集(20%)(固定逻辑,不用改)
X, y = load_data()
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42  # test_size:测试集比例;random_state:随机种子(保证结果可复现)
)

# ----------------------
# 第五步:训练+评估+预测(固定逻辑,不用改)
# ----------------------
# 1. 训练模型(自动处理:训练集fit→transform;测试集只transform,避免数据泄露)
general_pipeline.fit(X_train, y_train)

# 2. 预测(训练集+测试集)
y_train_pred = general_pipeline.predict(X_train)
y_test_pred = general_pipeline.predict(X_test)

# 3. 评估(根据任务自动选择指标)
if TASK_TYPE == "classification":
    train_score = accuracy_score(y_train, y_train_pred)  # 分类用准确率
    test_score = accuracy_score(y_test, y_test_pred)
    metric_name = "准确率"
elif TASK_TYPE == "regression":
    train_score = r2_score(y_train, y_train_pred)  # 回归用R²分数
    test_score = r2_score(y_test, y_test_pred)
    metric_name = "R²分数"

# 打印结果
print(f"训练集{metric_name}:{train_score:.2f}")
print(f"测试集{metric_name}:{test_score:.2f}")

# 4. 预测新数据(直接用训练好的Pipeline)
new_data = np.array([[1.75, 65, 1]])  # 新数据(数值型+分类型特征)
new_pred = general_pipeline.predict(new_data)
print(f"新数据预测结果:{new_pred[0]}")

三、通用 Pipeline 的「灵活调整方法」(适配所有场景)

这个 Pipeline 的核心是「模块化替换」,你不需要改整体逻辑,只需要根据自己的需求修改「第二步:定义可替换的核心组件」和「第四步:数据加载与拆分」,具体调整场景如下:

1. 场景 1:替换预处理方法(如数值型不用标准化,用归一化)
python 复制代码
# 把数值型转化器的StandardScaler()替换为MinMaxScaler()
from sklearn.preprocessing import MinMaxScaler
numeric_transformer = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="mean")),
    ("scaler", MinMaxScaler())  # 归一化(替换后直接运行,其他逻辑不变)
])
2. 场景 2:接入自定义转化器(如之前的身高等级转化器)
python 复制代码
# 1. 先定义自定义转化器(之前写的HeightLevelTransformer)
class HeightLevelTransformer(BaseEstimator, TransformerMixin):
    def __init__(self, low_threshold=1.65, high_threshold=1.75):
        self.low_threshold = low_threshold
        self.high_threshold = high_threshold
    def fit(self, X, y=None):
        return self
    def transform(self, X):
        X_df = pd.DataFrame(X, columns=["height"])
        def get_level(h):
            return 0 if h < self.low_threshold else 2 if h > self.high_threshold else 1
        return X_df["height"].apply(get_level).values.reshape(-1, 1)

# 2. 替换数值型转化器中的scaler为自定义转化器
numeric_transformer = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="mean")),
    ("custom_transformer", HeightLevelTransformer())  # 接入自定义转化器
])
3. 场景 3:替换模型(如分类任务用随机森林)
python 复制代码
# 分类任务替换模型(其他逻辑不变)
if TASK_TYPE == "classification":
    from sklearn.ensemble import RandomForestClassifier
    model = RandomForestClassifier(n_estimators=100, random_state=42)  # 随机森林
4. 场景 4:适配回归任务(如预测房价)
python 复制代码
# 1. 修改任务配置
TASK_TYPE = "regression"
NUMERIC_COLS = [0, 1]  # 如:面积、房龄(数值型)
CATEGORICAL_COLS = [2]  # 如:户型(分类型)
LABEL_COL = "price"  # 标签:房价(连续值)

# 2. 加载回归数据(替换为你的房价数据)
def load_data():
    data = pd.DataFrame({
        "area": [80, 100, None, 90, 120],  # 面积(数值型)
        "age": [5, 3, 10, None, 2],  # 房龄(数值型)
        "type": [1, 2, 1, 2, None],  # 户型(分类型)
        "price": [150, 200, 120, 180, 250]  # 房价(回归标签)
    })
    X = data.drop(LABEL_COL, axis=1).values
    y = data[LABEL_COL].values
    return X, y

# 3. 直接运行原代码,自动用回归模型和R²指标评估

四、通用 Pipeline 的核心优势(为什么能适配所有任务?)

  1. 模块化设计:每个步骤(数据清洗、预处理、模型)都是独立模块,替换其中一个不影响其他;
  2. 避免数据泄露 :训练集只做fit+transform,测试集只做transform,Pipeline 自动处理,无需手动干预;
  3. 兼容自定义工具:只要自定义转化器 / 估计器遵守 sklearn 基类规则,就能直接接入;
  4. 支持混合特征:ColumnTransformer 自动分栏处理数值型、分类型特征,覆盖 90% 结构化数据场景;
  5. 兼顾分类 / 回归 :通过TASK_TYPE参数切换,自动适配评估指标和模型类型。

五、注意事项(避免踩坑)

  1. 列索引不能错NUMERIC_COLSCATEGORICAL_COLS必须对应你数据的真实列索引(或列名,如用 DataFrame 可直接写列名);
  2. 输入格式要对 :X 必须是二维数组(如[[1.6, 50, 1]]),y 必须是一维数组(如[1, 0, 1]);
  3. 特殊数据扩展 :如果是文本数据,可新增text_transformer(如TfidfVectorizer),在ColumnTransformer中添加("text", text_transformer, TEXT_COLS);如果是图像数据,可新增图像预处理转化器(如归一化、Resize);
  4. 超参数调优 :通用 Pipeline 可结合GridSearchCV调参,例如:
python 复制代码
from sklearn.model_selection import GridSearchCV
param_grid = {"model__n_estimators": [50, 100, 200]}  # 调模型的超参数
grid_search = GridSearchCV(general_pipeline, param_grid, cv=5)
grid_search.fit(X_train, y_train)
print("最优参数:", grid_search.best_params_)

总结

这个通用 Pipeline 的本质是「固定机器学习的核心逻辑顺序,让用户只关注数据和组件选择」。无论你是做分类(如垃圾邮件识别)、回归(如销量预测),还是处理纯数值、混合特征,甚至用自定义工具,都可以基于这个模板快速搭建流程 ------ 无需重复写预处理、拆分数据、避免泄露的代码,专注于优化模型和特征即可。

如何在通用 Pipeline 中实现特征工程?

在通用 Pipeline 中实现特征工程,核心逻辑是 "将特征工程步骤作为独立模块,嵌入到「预处理之后、模型之前」" ------ 既不破坏原有通用流程,又能让特征工程和数据预处理、模型训练无缝衔接。

对于零基础学习者,我们聚焦 3 类最常用、最实用的特征工程(覆盖 90% 场景),用 "代码模板 + 分步嵌入" 的方式讲解,全程基于之前的通用 Pipeline 改造,不用重构整体逻辑。

一、先明确:特征工程在通用 Pipeline 中的位置(关键!)

通用 Pipeline 的核心顺序是:数据加载与拆分 → 预处理(分栏清洗) → 【特征工程】 → 模型训练 → 评估预测

特征工程必须放在 "预处理之后" (因为特征工程需要基于 "干净、标准化" 的数据),"模型之前"(因为模型需要用处理后的优质特征训练)。

嵌入方式超简单:在通用 Pipeline 的 steps 中,新增 "特征工程步骤" 即可,格式和其他模块一致(("步骤名", 特征工程工具))。

二、3 类常用特征工程:嵌入通用 Pipeline 实操

我们基于之前的通用 Pipeline 模板,分别加入「特征选择、特征降维、特征构造」,每类都给完整可运行代码,直接替换就能用。

前置准备:复用通用 Pipeline 的基础组件

先回顾之前的通用 Pipeline 核心模块(后续只新增 / 修改特征工程部分):

python 复制代码
# 固定导入工具(含特征工程所需工具)
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

# 1. 任务配置(分类任务为例)
TASK_TYPE = "classification"
NUMERIC_COLS = [0, 1]  # 身高、体重(数值型)
CATEGORICAL_COLS = [2]  # 性别(分类型)
LABEL_COL = "target"

# 2. 预处理模块(分栏清洗,不变)
numeric_transformer = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="mean")),
    ("scaler", StandardScaler())
])
categorical_transformer = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("encoder", OneHotEncoder(sparse_output=False, drop="first"))
])
preprocessor = ColumnTransformer(
    transformers=[("num", numeric_transformer, NUMERIC_COLS),
                  ("cat", categorical_transformer, CATEGORICAL_COLS)]
)

# 3. 模型(不变)
model = LogisticRegression()
1. 特征工程 1:特征选择(挑 "有用" 的特征,剔除冗余)
作用:

去掉对模型没用的特征(比如噪音特征、重复特征),减少计算量,避免过拟合。

常用工具:

SelectKBest(选择得分最高的 K 个特征)、SelectFromModel(基于模型权重选择特征)。

嵌入步骤:

在 Pipeline 中加入 "特征选择" 模块,放在「预处理」和「模型」之间。

完整代码(新增特征选择部分):
python 复制代码
# 新增:导入特征选择工具
from sklearn.feature_selection import SelectKBest, f_classif  # f_classif:分类任务的特征评分方法

# 构建通用 Pipeline(新增特征选择步骤)
general_pipeline = Pipeline(steps=[
    ("preprocessor", preprocessor),  # 第一步:预处理(不变)
    # 新增:第二步:特征选择(选择得分最高的3个特征)
    ("feature_selection", SelectKBest(score_func=f_classif, k=3)),  # k=3:选3个最好的特征
    ("model", model)  # 第三步:模型(不变)
])

# 后续数据加载、拆分、训练评估(和之前完全一样,直接复用)
def load_data():
    data = pd.DataFrame({
        "height": [1.6, 1.8, 1.7, 1.55, None, 1.75, 1.68],
        "weight": [50, 70, None, 45, 75, 65, 58],
        "gender": [1, 0, 1, None, 0, 1, 0],
        "target": [1, 0, 1, 1, 0, 1, 0]
    })
    X = data.drop(LABEL_COL, axis=1).values
    y = data[LABEL_COL].values
    return X, y

# 训练+评估
X, y = load_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
general_pipeline.fit(X_train, y_train)
y_test_pred = general_pipeline.predict(X_test)
print("测试集准确率:", accuracy_score(y_test, y_test_pred))
关键说明:
  • score_func=f_classif:适合分类任务;如果是回归任务,替换为 f_regression
  • k=3:可根据特征总数调整(比如特征有 5 个,选 k=4);如果想选 "所有得分高于阈值的特征",可设 k="all" 配合后续筛选。
2. 特征工程 2:特征降维(压缩特征维度,避免过拟合)
作用:

当特征太多(比如几十、上百个)时,将高维特征压缩到低维(比如 2 维、3 维),保留核心信息,减少计算量。

常用工具:

PCA(主成分分析,最常用)、TSNE(适合可视化,计算量大)。

嵌入步骤:

和特征选择一样,放在「预处理」和「模型」之间,可单独使用,也可和特征选择搭配。

完整代码(新增特征降维部分):
python 复制代码
# 新增:导入降维工具
from sklearn.decomposition import PCA

# 构建通用 Pipeline(新增PCA降维步骤)
general_pipeline = Pipeline(steps=[
    ("preprocessor", preprocessor),  # 第一步:预处理(不变)
    # 新增:第二步:特征降维(压缩到2维)
    ("feature_reduction", PCA(n_components=2)),  # n_components=2:压缩为2个特征
    ("model", model)  # 第三步:模型(不变)
])

# 后续数据加载、训练评估(和之前完全一样,直接复用)
X, y = load_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
general_pipeline.fit(X_train, y_train)
y_test_pred = general_pipeline.predict(X_test)
print("测试集准确率:", accuracy_score(y_test, y_test_pred))

# 可选:查看降维后的特征形状(验证是否压缩到2维)
X_train_processed = general_pipeline.named_steps["feature_reduction"].transform(
    general_pipeline.named_steps["preprocessor"].transform(X_train)
)
print("降维后训练集特征形状:", X_train_processed.shape)  # 输出 (5, 2):5个样本,2个特征
关键说明:
  • n_components:可设为整数(比如 2、3,指定降维后的维度),或小数(比如 0.95,保留 95% 的信息);
  • 适用场景:特征维度高(比如 > 20 个)、模型过拟合时使用;如果特征少(<10 个),一般不需要降维。
3. 特征工程 3:特征构造(创造新的 "优质特征")
作用:

基于原有特征,构造更有预测力的新特征(比如用 "身高、体重" 算 "BMI 指数",用 "日期" 算 "星期几"),是提升模型效果的核心手段。

实现方式:

自定义转化器(之前学过的技能!),因为构造逻辑往往是自定义的(比如业务相关规则)。

嵌入步骤:

有两种方式(根据需求选):

  • 方式 1:在「分栏预处理内部」构造(针对单类特征,比如数值型特征内部组合);
  • 方式 2:在「预处理之后、模型之前」构造(针对跨类型特征,比如数值型 + 分类型组合)。
实操:方式 1(分栏预处理内部构造,推荐)

比如对数值型特征(身高、体重),构造新特征 "BMI 指数"(BMI = 体重 (kg)/ 身高 (m)²)。

python 复制代码
# 第一步:写自定义特征构造转化器
class FeatureConstructor(BaseEstimator, TransformerMixin):
    def __init__(self):
        pass  # 无参数,可根据需求加(比如BMI阈值)
    
    def fit(self, X, y=None):
        return self  # 不需要学习规则,直接返回
    
    def transform(self, X):
        # X:预处理后的数值型特征(身高、体重,二维数组)
        height = X[:, 0]  # 第一列:身高
        weight = X[:, 1]  # 第二列:体重
        # 构造新特征:BMI指数(体重/身高²)
        bmi = weight / (height ** 2)
        # 合并原有特征和新特征(返回二维数组)
        X_new = np.hstack([X, bmi.reshape(-1, 1)])  # hstack:横向拼接
        return X_new

# 第二步:修改数值型预处理模块,加入特征构造
numeric_transformer = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="mean")),  # 1. 缺失值填充
    ("scaler", StandardScaler()),  # 2. 标准化
    ("feature_construct", FeatureConstructor())  # 3. 新增:构造BMI特征
])

# 第三步:构建Pipeline(后续逻辑不变)
general_pipeline = Pipeline(steps=[
    ("preprocessor", preprocessor),  # 预处理(已包含特征构造)
    ("model", model)
])

# 训练评估(复用之前代码)
X, y = load_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
general_pipeline.fit(X_train, y_train)
print("测试集准确率:", accuracy_score(y_test, general_pipeline.predict(X_test)))
实操:方式 2(跨类型特征构造)

比如用 "性别(分类型)+ 体重(数值型)" 构造新特征 "性别权重"(男性 ×1.2,女性 ×0.8)。

python 复制代码
# 第一步:写跨类型特征构造转化器(输入是预处理后的所有特征)
class CrossFeatureConstructor(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        return self
    
    def transform(self, X):
        # X:预处理后的所有特征(假设:第0列=标准化身高,第1列=标准化体重,第2列=性别独热编码)
        weight = X[:, 1]  # 标准化后的体重
        gender = X[:, 2]  # 性别(0=男,1=女,独热编码后)
        # 构造跨类型特征:性别权重(男:weight×1.2,女:weight×0.8)
        gender_weight = np.where(gender == 0, weight * 1.2, weight * 0.8)
        # 合并原有特征和新特征
        X_new = np.hstack([X, gender_weight.reshape(-1, 1)])
        return X_new

# 第二步:在Pipeline中加入跨类型特征构造(预处理之后)
general_pipeline = Pipeline(steps=[
    ("preprocessor", preprocessor),  # 1. 预处理(分栏清洗)
    ("cross_feature_construct", CrossFeatureConstructor()),  # 2. 跨类型特征构造
    ("model", model)  # 3. 模型
])

# 训练评估(复用之前代码)
X, y = load_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
general_pipeline.fit(X_train, y_train)
print("测试集准确率:", accuracy_score(y_test, general_pipeline.predict(X_test)))
关键说明:
  • 特征构造的核心是 "结合业务逻辑"(比如 BMI 指数对健康相关任务有用),自定义转化器可以灵活实现任何规则;
  • 构造后的新特征会和原有特征合并,模型会自动学习新特征的重要性。

三、特征工程的组合使用(实战推荐)

实际项目中,常将 "特征构造 + 特征选择 + 特征降维" 组合使用,顺序一般是:预处理 → 特征构造 → 特征选择 → 特征降维 → 模型

组合版完整 Pipeline 代码:
python 复制代码
# 导入所有需要的工具
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.decomposition import PCA

# 构建组合特征工程的Pipeline
general_pipeline = Pipeline(steps=[
    ("preprocessor", preprocessor),  # 1. 预处理(含单类型特征构造)
    ("cross_feature", CrossFeatureConstructor()),  # 2. 跨类型特征构造
    ("feature_selection", SelectKBest(score_func=f_classif, k=4)),  # 3. 选择4个最优特征
    ("feature_reduction", PCA(n_components=2)),  # 4. 降维到2维
    ("model", model)  # 5. 模型
])

# 训练评估
X, y = load_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
general_pipeline.fit(X_train, y_train)
print("测试集准确率:", accuracy_score(y_test, general_pipeline.predict(X_test)))

四、关键注意事项(避免踩坑)

1. 顺序不能乱:特征构造→特征选择→特征降维(先创造新特征,再挑有用的,最后压缩维度);

2. 不盲目使用

  • 特征少(<10 个):不用降维,可只做特征构造;
  • 特征噪音多:优先用特征选择;
  • 特征维度极高(>100 个):再用降维;

3. 避免数据泄露 :特征工程的 fit 只能用训练集!Pipeline 会自动处理(训练集 fit+transform,测试集只 transform),不用手动干预;

4. 评估效果:加入特征工程后,要对比 "有无特征工程" 的模型分数(比如准确率、R²),如果分数下降,说明特征工程没做好(比如选了无用特征、降维丢了关键信息)。

五、总结:通用 Pipeline 中特征工程的核心逻辑

特征工程在通用 Pipeline 中是 "可插拔的模块化组件",核心步骤:

  1. 选工具:根据需求选特征构造(自定义转化器)、特征选择(SelectKBest)、特征降维(PCA);
  2. 嵌位置:放在「预处理之后、模型之前」;
  3. 可组合:按 "构造→选择→降维" 顺序组合,适配复杂场景;
  4. 易修改:不需要改其他模块,只新增 / 替换特征工程步骤,完全兼容之前的通用 Pipeline。

通过这种方式,你可以在不破坏原有流程的前提下,灵活加入特征工程,让模型用更优质的特征训练,从而提升预测效果。如果需要针对具体任务(比如回归、文本数据)调整特征工程工具,只需替换对应的组件(比如回归用 f_regression 替代 f_classif),整体逻辑不变!

浙大疏锦行

相关推荐
萧鼎2 小时前
Python PyWavelets(pywt)库完整技术指南:从小波理论到工程实践
开发语言·python
一招定胜负2 小时前
机器学习开篇
人工智能·机器学习
MediaTea2 小时前
Python 装饰器:@property_name.deleter
开发语言·python
中等生2 小时前
Celery 异步任务完全指南:从入门到实战
python·flask
Cherry的跨界思维2 小时前
5、Python长图拼接终极指南:Pillow/OpenCV/ImageMagick三方案
javascript·python·opencv·webpack·django·pillow·pygame
大千AI助手2 小时前
多重共线性:机器学习中的诊断与应对策略
人工智能·机器学习·线性回归·相关性·大千ai助手·多重共线性·线性组合
一招定胜负3 小时前
机器学习预备知识:numpy、pandas、matplotlib库
人工智能·机器学习·numpy
acethanlic3 小时前
使用Ruff进行Python代码Format、lint和fix
python