Day23 pipeline管道

1.转化器和估计器的概念

转化器是用于对数据进行转换或预处理的工具,其核心功能是将输入数据从一种表示形式转换为另一种形式,以便于模型更好地学习或分析。转化器通常需要满足以下特点:

(1)拟合(Fit)和转换(Transform):先通过拟合数据学习转换规则(如均值、方差),再应用规则对数据进行转换。

(2)无监督或有监督:多数转化器是无监督的(如特征缩放),少数可能依赖标签(如标签编码)。

常见类型与场景:

(1)数据预处理

特征缩放:如标准化(StandardScaler)、归一化(MinMaxScaler),将特征值缩放到特定范围。

特征编码:如独热编码(OneHotEncoder)、标签编码(LabelEncoder),将类别型数据转换为数值型。

缺失值处理:如 SimpleImputer,用均值、中位数或众数填充缺失值。

(2)特征工程

多项式特征生成(PolynomialFeatures):从原始特征生成多项式组合特征。

文本向量化:如 TF-IDF Vectorizer,将文本转换为数值型特征矩阵。

估计器是用于从数据中学习模型参数并进行预测的算法或模型,其核心功能是通过拟合训练数据,建立输入特征(X)与输出标签(y)之间的映射关系。估计器通常需要实现以下方法:

(1)fit(X, y):用训练数据拟合模型,学习参数。

(2)predict(X):用拟合好的模型对新数据进行预测。

常见类型与场景

(1)监督学习模型

分类器:如逻辑回归(Logistic Regression)、支持向量机(SVM)、随机森林(Random Forest),用于预测离散标签。

回归器:如线性回归(Linear Regression)、梯度提升树(Gradient Boosting Regressor),用于预测连续数值。

(2)无监督学习模型

聚类算法:如 K 均值(K-Means)、DBSCAN,用于发现数据中的簇结构。

密度估计:如高斯混合模型(GMM),用于估计数据的概率分布。

2.管道工程

在机器学习中,** 管道工程(Pipeline)** 是一种将数据预处理、特征工程和模型训练等多个步骤串联成一个完整工作流的技术。通过管道,开发者可以以标准化、模块化的方式管理整个机器学习流程,避免代码冗余,防止数据泄漏,并简化模型调优和部署。

(1)为什么需要管道?

简化工作流:将数据清洗、特征转换、模型训练等步骤整合为一个对象,避免分步处理的繁琐。

防止数据泄漏(Data Leakage):确保预处理步骤仅在训练集上拟合(fit),再应用于测试集,避免信息从测试集 "泄漏" 到训练阶段。

方便调参与复用:可将整个管道作为单一模型进行超参数搜索(如网格搜索),并方便地保存和复用整个流程。

代码整洁性:用统一接口管理多个步骤,减少重复代码,提升可读性。

(2)管道的核心组件

管道由一系列有序的步骤组成,每个步骤是一个元组(step_name, step_object),其中:

前序步骤:必须是转化器(Transformer),需实现fit()transform()方法(如数据缩放、特征编码)。

最后一步:必须是估计器(Estimator),需实现fit()predict()方法(如分类器、回归器)。

3.ColumnTransformer和Pipeline类

ColumnTransformer:混合特征的差异化预处理

核心作用

当数据包含多种类型的特征(如数值型、类别型、文本型、日期型等)时,不同特征往往需要不同的预处理方法。例如:

数值型特征:需要标准化(StandardScaler)或归一化(MinMaxScaler);

类别型特征:需要独热编码(OneHotEncoder)或目标编码(TargetEncoder);

文本型特征:需要词向量化(TfidfVectorizer)。

ColumnTransformer 允许为不同的列(特征)定义独立的预处理逻辑,并将结果合并为一个特征矩阵,最终输出给模型使用。

关键参数与用法

ColumnTransformer 的构造函数主要参数如下:

复制代码
from sklearn.compose import ColumnTransformer

transformer = ColumnTransformer(
    transformers=[          # 定义各列的处理方式(列表中的每个元素是一个元组)
        ("name1", transformer1, columns1),  # 元组格式:(步骤名, 转化器, 列选择)
        ("name2", transformer2, columns2),
    ],
    remainder="drop"       # 未指定列的处理方式(默认"drop",可选"passthrough"保留)
)

transformers:核心参数,每个元素是一个三元组 (name, transformer, columns),其中:

name:步骤名(用于后续参数调优时的命名空间);

transformer:具体的转化器(如 StandardScalerOneHotEncoder);

columns:需要应用该转化器的列(可以是列名列表、索引列表,或 slice 对象)。

remainder:未被 transformers 覆盖的列的处理方式:

"drop"(默认):丢弃这些列;

"passthrough":保留这些列(不做任何处理);

也可以传入一个自定义转化器,对剩余列进行处理。

Pipeline:全流程标准化串联

核心作用

Pipeline 用于将多个数据处理步骤和模型训练步骤串联成一个单一对象,确保:

数据预处理(转化器)和模型训练(估计器)的流程标准化;

防止数据泄漏(仅在训练集上拟合预处理参数,测试集使用相同参数转换);

方便模型调优(将整个流程作为单一对象进行超参数搜索)。

前 N-1 步必须是转化器(实现 fit()transform()),最后一步必须是估计器(实现 fit()predict())。

接口统一:Pipeline 对象对外提供与估计器一致的接口(fit()predict()score() 等),内部自动按顺序执行各步骤。

关键参数与用法

Pipeline 的构造函数接受一个步骤列表,每个步骤是 (name, estimator) 元组:

复制代码
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression

pipeline = Pipeline([
    ("preprocessor", ColumnTransformer(...)),  # 预处理步骤(转化器)
    ("classifier", LogisticRegression())       # 模型步骤(估计器)
])

ColumnTransformer 解决混合特征的差异化预处理问题,而 Pipeline 解决全流程标准化问题,二者结合可构建完整的机器学习工作流:

典型流程

数据输入:原始数据集(包含多种类型特征);

ColumnTransformer 预处理:对不同列应用不同的转化器(如标准化、独热编码、文本向量化),输出统一的特征矩阵;

Pipeline 串联:将 ColumnTransformer 作为 Pipeline 的预处理步骤,后续接入模型(估计器);

训练与预测:通过 Pipeline.fit() 完成预处理参数学习和模型训练,通过 Pipeline.predict() 完成测试数据的预处理和预测。

4.代码

复制代码
# 导入基础库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import time # 导入 time 库
import warnings

# 忽略警告
warnings.filterwarnings("ignore")

# 设置中文字体和负号正常显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 导入 Pipeline 和相关预处理工具
from sklearn.pipeline import Pipeline # 用于创建机器学习工作流
from sklearn.compose import ColumnTransformer # 用于将不同的预处理应用于不同的列
from sklearn.preprocessing import OrdinalEncoder, OneHotEncoder, StandardScaler # 用于数据预处理(有序编码、独热编码、标准化)
from sklearn.impute import SimpleImputer # 用于处理缺失值

# 导入机器学习模型和评估工具
from sklearn.ensemble import RandomForestClassifier # 随机森林分类器
from sklearn.metrics import classification_report, confusion_matrix # 用于评估分类器性能
from sklearn.model_selection import train_test_split # 用于划分训练集和测试集


# --- 加载原始数据 ---
# 我们加载原始数据,不对其进行任何手动预处理
data = pd.read_csv('data.csv')

print("原始数据加载完成,形状为:", data.shape)
复制代码
# --- 分离特征和标签 (使用原始数据) ---
y = data['Credit Default'] # 标签
X = data.drop(['Credit Default'], axis=1) # 特征 (axis=1 表示按列删除)

print("\n特征和标签分离完成。")
print("特征 X 的形状:", X.shape)
print("标签 y 的形状:", y.shape)

# --- 划分训练集和测试集 (在任何预处理之前划分) ---
# 按照8:2划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 80%训练集,20%测试集

print("\n数据集划分完成 (预处理之前)。")
print("X_train 形状:", X_train.shape)
print("X_test 形状:", X_test.shape)
print("y_train 形状:", y_train.shape)
print("y_test 形状:", y_test.shape)
复制代码
# --- 定义不同列的类型和它们对应的预处理步骤 ---
# 这些定义是基于原始数据 X 的列类型来确定的

# 识别原始的 object 列 (对应你原代码中的 discrete_features 在预处理前)
object_cols = X.select_dtypes(include=['object']).columns.tolist()
# 识别原始的非 object 列 (通常是数值列)
numeric_cols = X.select_dtypes(exclude=['object']).columns.tolist()


# 有序分类特征 (对应你之前的标签编码)
# 注意:OrdinalEncoder默认编码为0, 1, 2... 对应你之前的1, 2, 3...需要在模型解释时注意
# 这里的类别顺序需要和你之前映射的顺序一致
ordinal_features = ['Home Ownership', 'Years in current job', 'Term']
# 定义每个有序特征的类别顺序,这个顺序决定了编码后的数值大小
ordinal_categories = [
    ['Own Home', 'Rent', 'Have Mortgage', 'Home Mortgage'], # Home Ownership 的顺序 (对应1, 2, 3, 4)
    ['< 1 year', '1 year', '2 years', '3 years', '4 years', '5 years', '6 years', '7 years', '8 years', '9 years', '10+ years'], # Years in current job 的顺序 (对应1-11)
    ['Short Term', 'Long Term'] # Term 的顺序 (对应0, 1)
]
# 构建处理有序特征的 Pipeline: 先填充缺失值,再进行有序编码
ordinal_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='most_frequent')), # 用众数填充分类特征的缺失值
    ('encoder', OrdinalEncoder(categories=ordinal_categories, handle_unknown='use_encoded_value', unknown_value=-1)) # 进行有序编码
])
print("有序特征处理 Pipeline 定义完成。")


# 标称分类特征 (对应你之前的独热编码)
nominal_features = ['Purpose'] # 使用原始列名
# 构建处理标称特征的 Pipeline: 先填充缺失值,再进行独热编码
nominal_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='most_frequent')), # 用众数填充分类特征的缺失值
    ('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False)) # 进行独热编码, sparse_output=False 使输出为密集数组
])
print("标称特征处理 Pipeline 定义完成。")


# 连续特征 (对应你之前的众数填充 + 添加标准化)
# 从所有列中排除掉分类特征,得到连续特征列表
# continuous_features = X.columns.difference(object_cols).tolist() # 原始X中非object类型的列
# 也可以直接从所有列中排除已知的有序和标称特征
continuous_features = [f for f in X.columns if f not in ordinal_features + nominal_features]

# 构建处理连续特征的 Pipeline: 先填充缺失值,再进行标准化
continuous_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='most_frequent')), # 用众数填充缺失值 (复现你的原始逻辑)
    ('scaler', StandardScaler()) # 标准化,一个好的实践 (如果你严格复刻原代码,可以移除这步)
])
print("连续特征处理 Pipeline 定义完成。")
复制代码
# --- 构建 ColumnTransformer ---
# 将不同的预处理应用于不同的列子集,构造一个完备的转化器
# ColumnTransformer 接收一个 transformers 列表,每个元素是 (名称, 转换器对象, 列名列表)
preprocessor = ColumnTransformer(
    transformers=[
        ('ordinal', ordinal_transformer, ordinal_features), # 对 ordinal_features 列应用 ordinal_transformer
        ('nominal', nominal_transformer, nominal_features), # 对 nominal_features 列应用 nominal_transformer
        ('continuous', continuous_transformer, continuous_features) # 对 continuous_features 列应用 continuous_transformer
    ],
    remainder='passthrough' # 如何处理没有在上面列表中指定的列。
                           # 'passthrough' 表示保留这些列,不做任何处理。
                           # 'drop' 表示丢弃这些列。
)

print("\nColumnTransformer (预处理器) 定义完成。")
# print(preprocessor) # 可以打印 preprocessor 对象看看它的结构
复制代码
# --- 构建完整的 Pipeline ---
# 将预处理器和模型串联起来
# 使用你原代码中 RandomForestClassifier 的默认参数和 random_state,这里的参数用到了元组这个数据结构
pipeline = Pipeline(steps=[
    ('preprocessor', preprocessor), # 第一步:应用所有的预处理 (ColumnTransformer)
    ('classifier', RandomForestClassifier(random_state=42)) # 第二步:随机森林分类器
])

# --- 1. 使用 Pipeline 在划分好的训练集和测试集上评估 ---

print("--- 1. 默认参数随机森林 (训练集 -> 测试集) ---") 
start_time = time.time() # 记录开始时间

# 在原始的 X_train 上拟合整个Pipeline
# Pipeline会自动按顺序执行preprocessor的fit_transform(X_train),然后用处理后的数据拟合classifier
pipeline.fit(X_train, y_train)

# 在原始的 X_test 上进行预测
# Pipeline会自动按顺序执行preprocessor的transform(X_test),然后用处理后的数据进行预测
pipeline_pred = pipeline.predict(X_test)

end_time = time.time() # 记录结束时间

print(f"训练与预测耗时: {end_time - start_time:.4f} 秒") # 使用你原代码的输出格式

print("\n默认随机森林 在测试集上的分类报告:") # 使用你原代码的输出文本
print(classification_report(y_test, pipeline_pred))
print("默认随机森林 在测试集上的混淆矩阵:") # 使用你原代码的输出文本
print(confusion_matrix(y_test, pipeline_pred))

@浙大疏锦行

相关推荐
武汉格发Gofartlic10 分钟前
FEMFAT许可使用数据分析工具介绍
python·信息可视化·数据分析
N_NAN_N13 分钟前
[蓝桥杯 2024 国 Python B] 设计
java·数据结构·算法·并查集
SuperHeroWu726 分钟前
【AI大模型入门指南】概念与专有名词详解 (一)
人工智能·ai·大模型·入门·概念
love530love27 分钟前
【笔记】NVIDIA AI Workbench 中安装 cuDNN 9.10.2
linux·人工智能·windows·笔记·python·深度学习
量子-Alex32 分钟前
【DETR目标检测】ISTD-DETR:一种基于DETR与超分辨率技术的红外小目标检测深度学习算法
深度学习·算法·目标检测
项目題供诗36 分钟前
黑马python(五)
python
后端小肥肠40 分钟前
【效率核爆2.0】爆款短视频拆解进入流水线时代!Coze+飞书字段捷径自动生成结构化拆解报告
人工智能·aigc·coze
l1o3v1e4ding1 小时前
python-docx 库教程
开发语言·python·c#
小猫咪怎么会有坏心思呢1 小时前
华为OD机试-猴子爬山-dp(JAVA 2025A卷)
java·算法·华为od
酷爱码1 小时前
Python虚拟环境与Conda的使用方式详解
开发语言·python·算法