机器学习项目通常需要执行一系列数据预处理步骤,然后是学习算法。单独管理这些步骤可能很麻烦且容易出错。这时就需要用到 sklearn
管道。本文将探讨管道如何自动化机器学习工作流中的关键方面,例如数据预处理、特征工程和机器学习算法的集成。
通过我的书《进阶数据科学》启动您的项目。它提供了带有可运行代码的自学教程。
让我们开始吧。

管道的力量
图片来自 Quinten de Graaf。部分权利保留。
概述
这篇博文分为三部分;它们是:
- 什么是管道?
- 通过高级转换提升我们的模型
- 在管道中处理缺失数据
什么是管道?
管道用于自动化和封装各种转换步骤以及最终估计器的序列,将其整合到一个对象中。通过定义管道,您可以确保将相同的步骤序列应用于训练数据和测试数据,从而提高模型的重现性和可靠性。
让我们演示一下管道的实现,并将其与没有管道的传统方法进行比较。考虑一个简单的场景,我们想根据房屋质量预测房价,使用 Ames Housing 数据集中的“OverallQual”特征。以下是使用管道和不使用管道进行 5 折交叉验证的并排比较。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 导入必要的库 import pandas as pd from sklearn.model_selection import cross_val_score 来自 sklearn.linear_model 导入 LinearRegression from sklearn.pipeline import Pipeline # 准备数据并设置线性回归 Ames = pd.read_csv('Ames.csv') y = Ames['SalePrice'] linear_model = LinearRegression() # 不使用管道执行 5 折交叉验证 cv_score = cross_val_score(linear_model, Ames[['OverallQual']], y).mean() print("不使用管道的示例,'OverallQual' 的平均 CV R² 分数:{:.3f}".format(cv_score)) # 使用管道执行 5 折交叉验证 pipeline = Pipeline([('regressor', linear_model)]) pipeline_score = cross_val_score(pipeline, Ames[['OverallQual']], y, cv=5).mean() print("使用管道的示例,'OverallQual' 的平均 CV R² 分数:{:.3f}".format(pipeline_score)) |
两种方法产生的结果完全相同
1 2 |
不使用管道的示例,'OverallQual' 的平均 CV R² 分数:0.618 使用管道的示例,'OverallQual' 的平均 CV R² 分数:0.618 |
下面是用图来说明这个基本管道。
这个例子使用的是一个只有一个特征的简单场景。但是,随着模型变得更加复杂,管道可以处理多个预处理步骤,例如缩放、编码和降维,然后再应用模型。
在对 sklearn 管道有了基本了解的基础上,让我们扩展我们的场景,加入特征工程——这是提高模型性能的关键步骤。特征工程包括从现有数据中创建新特征,这些新特征可能与目标变量具有更强的关联性。在我们的例子中,我们认为房屋质量与居住面积的交互作用可能比单独使用任何一个特征更能预测房价。以下是使用管道和不使用管道进行 5 折交叉验证的另一种并排比较。
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 30 31 32 33 34 35 |
# 导入必要的库 import pandas as pd from sklearn.model_selection import cross_val_score 来自 sklearn.linear_model 导入 LinearRegression from sklearn.pipeline import Pipeline from sklearn.preprocessing import FunctionTransformer # 准备数据并设置线性回归 Ames = pd.read_csv('Ames.csv') y = Ames['SalePrice'] linear_model = LinearRegression() # 不使用管道执行 5 折交叉验证 Ames['OWA'] = Ames['OverallQual'] * Ames['GrLivArea'] cv_score_2 = cross_val_score(linear_model, Ames[['OWA']], y).mean() print("不使用管道的示例,'Quality Weighted Area' 的平均 CV R² 分数:{:.3f}".format(cv_score_2)) # 使用管道 # 定义“QualityArea”的转换函数 def create_quality_area(X): X['QualityArea'] = X['OverallQual'] * X['GrLivArea'] return X[['QualityArea']].values # 使用函数设置 FunctionTransformer quality_area_transformer = FunctionTransformer(create_quality_area) # 使用工程特征“QualityArea”的管道 pipeline_2 = Pipeline([ ('quality_area_transform', quality_area_transformer), ('regressor', linear_model) ]) pipeline_score_2 = cross_val_score(pipeline_2, Ames[['OverallQual', 'GrLivArea']], y, cv=5).mean() # 输出平均 CV 分数,四舍五入到小数点后四位 print("使用管道的示例,'Quality Weighted Area' 的平均 CV R² 分数:{:.3f}".format(pipeline_score_2)) |
两种方法再次产生相同的结果
1 2 |
不使用管道的示例,'Quality Weighted Area' 的平均 CV R² 分数:0.748 使用管道的示例,'Quality Weighted Area' 的平均 CV R² 分数:0.748 |
此输出表明,通过使用管道,我们将特征工程封装在模型训练过程中,使其成为交叉验证的组成部分。使用管道后,每次交叉验证折叠都会在管道内生成“Quality Weighted Area”特征,从而确保我们的特征工程步骤得到正确验证,避免数据泄露,从而产生更可靠的模型性能估计。
下面是用图来说明我们在管道的预处理步骤中如何使用 FunctionTransformer
。
上述管道确保我们的特征工程和预处理工作准确地反映了模型性能指标。随着我们继续前进,我们将进入更高级的领域,展示管道在处理各种预处理任务和不同类型变量时的稳健性。
想开始学习进阶数据科学吗?
立即参加我的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
通过高级转换提升我们的模型
我们的下一个例子将包括立方转换、工程特征和分类编码,并包含未经任何转换的原始特征。这说明了管道如何处理混合数据类型和转换,将预处理和建模步骤整合到一个统一的过程中。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# 导入必要的库 import pandas as pd from sklearn.model_selection import cross_val_score 来自 sklearn.linear_model 导入 LinearRegression from sklearn.pipeline import Pipeline from sklearn.compose import ColumnTransformer from sklearn.preprocessing import FunctionTransformer, OneHotEncoder # 准备数据并设置线性回归 Ames = pd.read_csv('Ames.csv') y = Ames['SalePrice'] linear_model = LinearRegression() # 应用立方转换的函数 def cubic_transformation(x): return x ** 3 # 创建“QualityArea”的函数 def create_quality_area(X): X['QualityArea'] = X['OverallQual'] * X['GrLivArea'] return X[['QualityArea']].values # 设置立方和质量区域转换的 FunctionTransformer cubic_transformer = FunctionTransformer(cubic_transformation) quality_area_transformer = FunctionTransformer(create_quality_area) # 设置 ColumnTransformer 进行预处理 preprocessor = ColumnTransformer( transformers=[ ('cubic', cubic_transformer, ['OverallQual']), ('quality_area_transform', quality_area_transformer, ['OverallQual', 'GrLivArea']), ('onehot', OneHotEncoder(drop='first', handle_unknown='ignore'), ['Neighborhood', 'ExterQual', 'KitchenQual']), ('passthrough', 'passthrough', ['YearBuilt']) ]) # 使用预处理器和线性回归创建管道 pipeline_3 = Pipeline([ ('preprocessor', preprocessor), ('regressor', linear_model) ]) # 使用 5 折交叉验证评估管道 pipeline_score_3 = cross_val_score(pipeline_3, Ames, y, cv=5).mean() # 输出平均 CV 分数,四舍五入到小数点后四位 print("带增强转换的平均 CV R² 分数:{:.3f}".format(pipeline_score_3)) |
特征工程是一门艺术,通常需要创造性的技巧。通过对“OverallQual”特征应用立方转换,我们假设质量和价格之间的非线性关系可以得到更好的捕捉。此外,我们设计了一个“QualityArea”特征,我们认为它可能比单独的特征与销售价格有更显著的交互作用。我们还通过采用独热编码来处理分类特征“Neighborhood”、“ExterQual”和“KitchenQual”,这是准备文本数据以进行建模的关键步骤。我们将其直接传递给模型,以确保“YearBuilt”的有价值的时间信息不会被不必要地转换。上面的管道产生以下结果
1 |
带增强转换的平均 CV R² 分数:0.850 |
该管道取得了令人印象深刻的 0.850 的平均 CV R² 分数,证明了深思熟虑的特征工程和预处理对模型性能的重大影响。它突出了管道的效率和可扩展性,并强调了它们在构建稳健的预测模型中的战略重要性。下面是用图来说明这个管道。
这种方法真正的优势在于其统一的工作流程。通过将特征工程、转换和模型评估优雅地组合成一个单一、连贯的过程,管道极大地提高了我们预测模型的准确性和有效性。这个高级示例强化了一个概念,即借助管道,复杂性不会以清晰度或性能为代价。
在管道中处理缺失数据
大多数数据集,尤其是大型数据集,实际上通常包含缺失值。如果忽视这些缺失值,可能会导致预测模型出现严重的偏差或错误。在本节中,我们将演示如何将数据插补无缝集成到我们的管道中,以确保我们的线性回归模型对这些问题具有鲁棒性。
在上一篇文章中,我们深入研究了缺失数据,手动插补 Ames 数据集中的缺失值,而未使用管道。在此基础上,我们现在介绍如何简化和自动化管道框架内的插补,提供一种更有效且不易出错的方法,即使对于初学者也适用。
我们选择使用 SimpleImputer
来处理“BsmtQual”(地下室质量)特征的缺失值,该特征是我们的数据集中一个分类变量。SimpleImputer
将用常量“None”替换缺失值,表示没有地下室。插补后,我们使用 OneHotEncoder
将此分类数据转换为适合我们线性模型的数值格式。通过将此插补嵌套在我们的管道中,我们确保在训练和测试阶段都正确应用了插补策略,从而防止了任何数据泄露,并通过交叉验证来维护模型评估的完整性。
以下是我们如何将其集成到管道设置中
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# 导入必要的库 import pandas as pd from sklearn.model_selection import cross_val_score 来自 sklearn.linear_model 导入 LinearRegression from sklearn.pipeline import Pipeline from sklearn.compose import ColumnTransformer from sklearn.preprocessing import FunctionTransformer, OneHotEncoder from sklearn.impute import SimpleImputer # 加载数据 Ames = pd.read_csv('Ames.csv') y = Ames['SalePrice'] linear_model = LinearRegression() # 应用立方转换的函数 def cubic_transformation(x): return x ** 3 # 创建“QualityArea”的函数 def create_quality_area(X): X['QualityArea'] = X['OverallQual'] * X['GrLivArea'] return X[['QualityArea']].values # 设置立方和质量区域转换的 FunctionTransformer cubic_transformer = FunctionTransformer(cubic_transformation) quality_area_transformer = FunctionTransformer(create_quality_area) # 在嵌套管道中准备 BsmtQual 插补和编码 bsmt_qual_transformer = Pipeline([ ('imputer', SimpleImputer(strategy='constant', fill_value='None')), ('encoder', OneHotEncoder(handle_unknown='ignore')) ]) # 设置 ColumnTransformer 进行所有预处理 preprocessor = ColumnTransformer( transformers=[ ('cubic', cubic_transformer, ['OverallQual']), ('quality_area_transform', quality_area_transformer, ['OverallQual', 'GrLivArea']), ('onehot', OneHotEncoder(drop='first', handle_unknown='ignore'), ['Neighborhood', 'ExterQual', 'KitchenQual']), ('bsmt_qual', bsmt_qual_transformer, ['BsmtQual']), # 添加 BsmtQual 处理 ('passthrough', 'passthrough', ['YearBuilt']) ]) # 使用预处理器和线性回归创建管道 pipeline_4 = Pipeline([ ('preprocessor', preprocessor), ('regressor', linear_model) ]) # 使用 5 折交叉验证评估管道 pipeline_score = cross_val_score(pipeline_4, Ames, y, cv=5).mean() # 输出平均 CV 分数,四舍五入到小数点后四位 print("带插补和转换的平均 CV R² 分数:{:.3f}".format(pipeline_score)) |
在我们的管道中使用 SimpleImputer
有助于有效处理缺失数据。当与其余的预处理步骤和线性回归模型结合使用时,完整的设置使我们能够评估我们预处理选择对模型性能的真实影响。
1 |
带插补和转换的平均 CV R² 分数:0.856 |
下面是我们包含缺失数据插补的管道的图示。
这种集成展示了 sklearn 管道的灵活性,并强调了诸如插补等关键预处理步骤如何无缝地包含在机器学习工作流中,从而提高了模型的可靠性和准确性。
进一步阅读
API
教程
Ames 住房数据集和数据字典
总结
在本帖中,我们探讨了 sklearn 管道的用法,最终在处理线性回归上下文中的缺失值时集成了复杂的数据插补。我们演示了数据预处理步骤、特征工程和高级转换的无缝自动化,以优化模型性能。本帖中强调的方法不仅在于保持工作流的效率,还在于确保我们期望构建的预测模型的一致性和准确性。
具体来说,你学到了:
- sklearn 管道的基础概念及其如何封装一系列数据转换和最终估计器。
- 当集成到管道中时,特征工程可以通过创建新的、更具预测性的特征来增强模型性能。
- 在管道中战略性地使用
SimpleImputer
来有效处理缺失数据,防止数据泄露并提高模型可靠性。
您有任何问题吗?请在下面的评论中提出您的问题,我将尽力回答。
很棒的文章!帮助我理解了这里描述的每个过程的细微之处。
非常感谢您的评论。我很高兴知道您从这篇帖子中受益!