使用Scikit-Learn管道简化机器学习项目
#python #datascience #machinelearning #scikitlearn

在此随后的教程中,我想演示如何使用Scikit学习管道模块。

如果您需要通过上一个教程,请检查here

但是,在回顾中,第一教程将机器学习项目的不同阶段分开的行业标准介绍到模块化脚本中,每个阶段都可以处理不同的过程(数据加载,数据预处理,模型选择等)

脚本模块化的优点包括代码组织,代码可重复性,协作和团队合作,可维护性和调试。

现在,让我们简要地谈论Scikit Learn Learn Learn Pipeline模块。
Scikit Learn(Sklearn)管道是用于简化,简化和组织机器学习工作流程的强大工具。从本质上讲,这是将一系列数据处理和建模步骤自动化为一个凝聚力单元的一种方法。它允许将多个数据处理和建模步骤链接到一个统一的对象中。

模块化脚本 +管道实现=最佳行业实践

(本教程的本质是要展示我们如何在模块化脚本中使用管道模块以实现更简化的行业标准)

这是可以使用Scikit-Learn管道的机器学习过程的各个方面的列表:

数据预处理

  • 归纳缺失值。
  • 缩放和标准化功能。
  • 编码分类变量。
  • 处理离群值。

功能工程

  • 创建新功能或转换现有功能。
  • 应用降低降低技术(例如PCA)

模型培训和评估

  • 构建一系列数据预处理和建模步骤。
  • 交叉验证和评估。

超参数调整

  • 使用网格搜索或随机搜索找到最佳的超参数。

模型选择

  • 将不同的模型与相同的预处理步骤进行比较。
  • 选择基于交叉验证结果的最佳模型。

预测和推理

  • 将整个预处理和建模管道应用于新数据。

部署和生产

  • 将整个管道包装到可部署的单元中。

管道序列化和持久性

  • 将整个管道保存到磁盘以备将来使用。

在展示其中一些步骤之前,值得一提的是,Scikit的一般前景学习管道倾向于遵循这种模式:

  • 导入必要的库
  • 创建单个变压器和估计器
  • 构建管道
  • 适合和转换数据
  • 适合最终估计器

出于说明目的,我将使用已发货的海出数据来演示其中的一些步骤(因为此数据非常简单):

  1. 数据预处理(插补,缩放,编码)
  2. 模型培训和评估(包括交叉验证)
  3. 超参数调整(GridSearchCV)
  4. 预测和推理

load_data.py 脚本

import seaborn as sns

def load_tips_data():
    df = sns.load_dataset('tips')
    return df

if __name__ == "__main__":
    tips_data = load_tips_data()
    print(tips_data.head())

preprocess_data.py 脚本;这里的管道正在使用

from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from load_data import load_tips_data #first .py script

class PreprocessingTransformer(BaseEstimator, TransformerMixin):
    def __init__(self):
        self.numeric_features = ['total_bill', 'size']
        self.categorical_features = ['sex', 'smoker', 'day', 'time']

        self.numeric_preprocessor = Pipeline(steps=[
            ('imputer', SimpleImputer(strategy='mean')),
            ('scaler', StandardScaler())
        ])

        self.categorical_preprocessor = Pipeline(steps=[
            ('imputer', SimpleImputer(fill_value='missing', strategy='constant')),
            ('onehot', OneHotEncoder(handle_unknown='ignore'))
        ])

        self.preprocessor = ColumnTransformer(
            transformers=[
                ('numeric', self.numeric_preprocessor, self.numeric_features),
                ('categorical', self.categorical_preprocessor, self.categorical_features)
            ]
        )

    def fit(self, X, y=None):
        self.preprocessor.fit(X, y)
        return self

    def transform(self, X):
        return self.preprocessor.transform(X)

# Create an instance of PreprocessingTransformer
preprocessor_instance = PreprocessingTransformer()

# Load and preprocess data
tips_data = load_tips_data() # load_tips_data from the first script
X = tips_data.drop('tip', axis=1)
y = (tips_data['tip'] > 5).astype(int)

# Fit the preprocessor & transform the data
preprocessor_instance.fit(X, y)
transformed_data = preprocessor_instance.transform(X)

train_model.py - 管道也使用

from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from load_data import load_tips_data #calling a script
from preprocess_data import PreprocessingTransformer #calling a script
from sklearn.pipeline import Pipeline

class TrainAndEvaluateModelTransformer(BaseEstimator, TransformerMixin):
    def __init__(self):
        pass

    def fit(self, X, y):
        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

        self.model = RandomForestClassifier(random_state=42)
        self.model.fit(X_train, y_train)

        return self

    def transform(self, X):
        y_pred = self.model.predict(X)
        return y_pred

if __name__ == "__main__":
    # Load and preprocess data
    tips_data = load_tips_data()
    preprocessor = PreprocessingTransformer()  # Create an instance without passing data
    preprocessed_data = preprocessor.fit_transform(tips_data)  # Fit and transform
    X = preprocessed_data
    y = (tips_data['tip'] > 5).astype(int)

    # Create a pipeline with TrainAndEvaluateModelTransformer
    model_pipeline = Pipeline([
        ('model', TrainAndEvaluateModelTransformer())
    ])

    # Fit and evaluate the model
    y_pred = model_pipeline.fit_transform(X, y)
    accuracy = accuracy_score(y, y_pred)
    print(f"Model Accuracy: {accuracy}")

hyperparameter_tuning.py-还使用了另一个管道;

from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
from load_data import load_tips_data
from preprocess_data import PreprocessingTransformer  # Import the PreprocessingTransformer
from sklearn.pipeline import Pipeline

if __name__ == "__main__":
    # Load and preprocess data
    tips_data = load_tips_data()
    X = tips_data.drop('tip', axis=1)
    y = (tips_data['tip'] > 5).astype(int)

    # Create a pipeline for preprocessing and model training
    model_pipeline = Pipeline([
        ('preprocessor', PreprocessingTransformer()),  # Use the custom preprocessing transformer
        ('model', RandomForestClassifier(random_state=42))
    ])

    # Define hyperparameter grid
    param_grid = {
        'model__n_estimators': [50, 100, 150],
        'model__max_depth': [None, 10, 20],
        'model__min_samples_split': [2, 5, 10]
    }

    # Perform GridSearchCV
    grid_search = GridSearchCV(model_pipeline, param_grid, cv=5)
    grid_search.fit(X, y)

    # Get the best tuned model
    best_tuned_model = grid_search.best_estimator_

    print("Best Hyperparameters:", best_tuned_model.named_steps['model'].get_params())

最后,这些整个模块化脚本可以序列化,并准备好使另一个程序用作腌制文件(生产):

serialized_model.py

import joblib
from load_data import load_tips_data
from preprocess_data import PreprocessingTransformer
from train_model import TrainAndEvaluateModelTransformer
from hyperparameter_tuning import GridSearchCV, RandomForestClassifier

def serialize_model(model, filename):
    joblib.dump(model, filename)
    print(f"Model serialized and saved as '{filename}'")

if __name__ == "__main__":
    data = load_tips_data()
    X = PreprocessingTransformer().fit_transform(data)
    y = (data['tip'] > 5).astype(int)
    trained_model = TrainAndEvaluateModelTransformer().fit(X, y)
    tuned_model = GridSearchCV(RandomForestClassifier(random_state=42), param_grid={
        'n_estimators': [50, 100, 150],
        'max_depth': [None, 10, 20],
        'min_samples_split': [2, 5, 10]
    }, cv=5).fit(X, y)
    serialize_model(tuned_model, "best_model.pkl")

这已经有点长的教程,但是我相信主要的想法已经通过:在机器学习工作流的模块化脚本中使用Scikit Learn Learn Pipeline。

最后,您认为在数据科学阶段实施Scikit学习管道的一些好处是什么?

  1. 代码简单: 使用管道,您可以将ML管道的所有步骤组合到一个对象中,从而使代码更加简洁,更易于理解。
  2. 数据泄漏预防: 管道可确保仅将数据转换步骤应用于适当的数据集,从而避免培训和测试集之间的数据泄漏。
  3. 超参数网格搜索: 当与GridSearchCV结合使用时,管道允许在整个管道上进行超参数调整,包括数据预处理和模型参数。
  4. 模型部署: 使用管道,您可以导出整个管道,包括所有预处理步骤和训练有素的模型,以在生产环境中部署。