
5 个 Scikit-learn Pipeline 技巧,为您的工作流程提速
图片由 Editor | ChatGPT 提供
引言
Scikit-learn 提供的最被低估但功能强大的特性之一,非**流水线(pipelines)**莫属。它们是构建高效、模块化机器学习工作流的绝佳帮手。流水线简化了整个过程——从数据准备和特征工程到建模、微调和验证——同时减轻了数据泄露的风险,使代码可复现,并使其更清晰、更易于维护。
在本文中,我们将通过简洁但中高级别的用例,描述并示例五个流水线技巧,以提升您正在进行的机器学习项目的水平。
初始设置
以下代码及其元素将用于后面列出的几个示例中;因此,建议首先应用这些准备步骤。请注意,我们将在下文中主要使用流行的 **泰坦尼克号生存数据集**。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import pandas as pd import numpy as np 从 sklearn.model_selection 导入 train_test_split, GridSearchCV 从 sklearn.preprocessing 导入 OneHotEncoder, StandardScaler from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.linear_model import LogisticRegression from sklearn.impute import SimpleImputer 从 sklearn.datasets 导入 fetch_openml # 加载数据集(泰坦尼克号生存) titanic = fetch_openml("titanic", version=1, as_frame=True) X = titanic.data[["pclass", "sex", "age", "fare"]] y = titanic.target == "1" # 将数据集拆分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42) # 按类型选择特征 num_features = ["age", "fare"] cat_features = ["pclass", "sex"] |
现在是时候开始本文的重点内容了!
1. 使用 ColumnTransformer 处理混合数据类型
在第一个示例中,我们将实例化一个 ColumnTransformer
对象来定义一个健壮的数据预处理流水线。此类别允许以统一的方式灵活地将不同的转换应用于不同的特征子集,从而简化了混合数据类型和缺失值的处理,而无需繁琐的操作或重复的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 实例化 ColumnTransformer 以实现健壮的数据预处理 preprocessor = ColumnTransformer([ ("num", Pipeline([ ("imputer", SimpleImputer(strategy="median")), ("scaler", StandardScaler()) ]), num_features), ("cat", Pipeline([ ("imputer", SimpleImputer(strategy="most_frequent")), ("onehot", OneHotEncoder(handle_unknown="ignore")) ]), cat_features) ]) pipe = Pipeline([ ("preprocessor", preprocessor), ("model", LogisticRegression(max_iter=1000)) ]) pipe.fit(X_train, y_train) print("准确度:", pipe.score(X_test, y_test)) |
这种方法联合处理数值特征、分类特征和缺失值,将它们集成到整个流水线中,然后训练逻辑回归分类器。
2. 使用自定义转换器进行特征工程
scikit-learn 中的自定义转换器允许我们定义自己的特征级转换步骤(无论是特征工程还是预处理),并将其直接注入到流水线中。一个示例是 TransformerMixin
,它要求我们定义自己的 fit
和 transform
方法,但作为回报,允许我们无缝使用 fit_transform()
。
以下代码通过继承定义了一个自定义转换器类。它应用其逻辑将“年龄”数值特征映射为二进制(0 或 1)值,指示乘客是否为成年人。然后,我们将此自定义转换器逻辑合并到像前一个用例中定义的 ColumnTransformer
中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
从 sklearn.base 导入 BaseEstimator, TransformerMixin # 自定义转换器,根据年龄创建二进制特征“is_adult” 类 IsAdult(BaseEstimator, TransformerMixin): def fit(self, X, y=None): 返回 self def transform(self, X): 返回 (X["age"].fillna(0) >= 18).astype(int).to_frame("is_adult") # 包含自定义转换器的扩展 ColumnTransformer extended = ColumnTransformer([ ("num", Pipeline([ ("imputer", SimpleImputer(strategy="median")), ("scaler", StandardScaler()) ]), num_features), ("cat", Pipeline([ ("imputer", SimpleImputer(strategy="most_frequent")), ("onehot", OneHotEncoder(handle_unknown="ignore")) ]), cat_features), ("is_adult", IsAdult(), ["age"]) ]) pipe = Pipeline([ ("preprocessor", extended), ("model", LogisticRegression(max_iter=1000)) ]) pipe.fit(X_train, y_train) print("使用自定义特征的准确度:", pipe.score(X_test, y_test)) |
3. 跨整个流水线进行超参数调整
此示例表明超参数调整(在众多选项中找到最佳配置)并非仅与机器学习模型的设置有关。它还可以应用于在之前的预处理步骤中做出的选择,如此有趣的示例所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
from sklearn.svm import SVC pipe = Pipeline([ ("preprocessor", preprocessor), ("model", SVC()) ]) param_grid = { "preprocessor__num__imputer__strategy": ["mean", "median"], "model__C": [0.1, 1, 10], "model__kernel": ["linear", "rbf"] } search = GridSearchCV(pipe, param_grid, cv=3) search.fit(X_train, y_train) print("最佳参数:", search.best_params_) print("最佳得分:", search.best_score_) |
请注意,preprocessor
是示例 1 中定义的预处理流水线,即 ColumnTransformer
。此示例的关键在于向搜索网格中添加了一个与预处理步骤相关的超参数,即缺失值填充策略。换句话说,多个模型版本不仅基于模型本身的超参数进行训练,还基于预处理步骤中的特定设置进行训练。
4. 将特征选择集成到流水线中
另一种强大的技术,特别是对于具有许多特征的数据集,是在流水线中动态执行特征选择,以使最终模型更简单。
此示例通过将对 SelectKBest
类的调用合并到整体流水线中(再次,我们使用早期示例中定义的相同 preprocessor
实例)来自动选择信息量最大的预处理特征,然后训练模型。
1 2 3 4 5 6 7 8 9 10 |
从 sklearn.feature_selection 导入 SelectKBest, f_classif pipe = Pipeline([ ("preprocessor", preprocessor), ("feature_selection", SelectKBest(score_func=f_classif, k=5)), ("model", LogisticRegression(max_iter=1000)) ]) pipe.fit(X_train, y_train) print("测试准确度:", pipe.score(X_test, y_test)) |
SelectKBest
类需要一个评分函数或标准来确定要保留用于模型训练的 top-k 特征。它的另一个可能的参数是 score_func=chi2
,它应用卡方检验来选择特征,在分类特征占主导地位时很有用。
5. 堆叠式流水线
我们的最后一个示例展示了如何堆叠多个流水线以构建集成机器学习解决方案。流水线是设计我们自己的高度可定制集成的好方法,尤其是在需要训练和组合不同模型(有时具有不同的预处理步骤)而又不冒数据管理不一致风险的情况下。
在下面的示例中,定义了两个“整体”流水线:一个用于预处理数据并训练逻辑回归分类器,另一个用于应用相同的预处理(为简单起见,它可能有所不同)但改为训练决策树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
from sklearn.ensemble import StackingClassifier from sklearn.tree import DecisionTreeClassifier log_reg_pipe = Pipeline([ ("preprocessor", preprocessor), ("logreg", LogisticRegression(max_iter=1000)) ]) tree_pipe = Pipeline([ ("preprocessor", preprocessor), ("tree", DecisionTreeClassifier(max_depth=5)) ]) stack = StackingClassifier( estimators=[("lr", log_reg_pipe), ("dt", tree_pipe)], final_estimator=LogisticRegression() ) stack.fit(X_train, y_train) print("堆叠准确度:", stack.score(X_test, y_test)) |
然后,使用 StackingClassifier
类堆叠这两个流水线。此类别使用最终估计器来学习组合基础模型预测的最佳方式,从而产生更强大、更具泛化能力的模型。
总结
本文展示了五个富有洞察力的示例,说明了我们如何利用 scikit-learn 流水线来加速并使我们的机器学习工作流更有效、更可定制,在某些情况下,性能更好。从用于混合数据类型的自定义预处理流水线到将超参数调整扩展到预处理步骤,我们揭示了几个技巧和窍门,可将您的机器学习建模项目提升到新的水平。
暂无评论。