scikit-learn 机器学习入门

数据分析的机器学习扩展 —— 从数据预处理到模型部署全流程

一、scikit-learn 概述与设计哲学

scikit-learn 是 Python 生态中最成熟、最广泛使用的机器学习库之一,基于 NumPy、SciPy 和 matplotlib 构建。它提供了统一、简洁、一致的 API 接口,覆盖了从数据预处理、特征工程、模型训练、模型评估到模型部署的完整机器学习流水线。无论你是刚接触机器学习的初学者,还是经验丰富的数据科学家,scikit-learn 都能满足从快速原型验证到生产环境部署的全部需求。

1.1 核心设计理念

scikit-learn 的 API 设计遵循三大核心原则:一致性(所有对象共享统一的接口)、内省性(所有参数值作为公共属性暴露)、限制对象层级(算法以 Python 类形式表示,数据集以 NumPy 数组或 SciPy 稀疏矩阵表示)。这种设计使得不同算法之间的切换几乎不费吹灰之力,极大地降低了学习成本。

1.2 三大核心接口

scikit-learn 将机器学习组件划分为三种基本类型:

类型 接口方法 代表类 用途
Estimator(估计器) fit() 所有模型 基于数据学习参数
Predictor(预测器) predict() 分类器、回归器 对新样本做预测
Transformer(转换器) transform() Scaler、Encoder 数据转换与预处理

这三类接口遵循着统一的调用模式:先用 fit() 学习数据中的参数(如均值、方差、模型权重),再用 predict()transform() 应用到新数据上。这种 fit / predict(或 transform) 的两阶段模式是整个 scikit-learn 设计的基石。

+------------------+ +---------------------+ | 原始数据 (X, y) | -----> | Estimator.fit(X, y) | -----> 学习得到模型参数 +------------------+ +---------------------+ | v +------------------+ +---------------------+ | 新样本 (X_new) | -----> | Estimator.predict | -----> 预测结果 y_pred +------------------+ +---------------------+

设计哲学要点:

  • 统一接口: 无论模型多么复杂,都通过 .fit() 训练和 .predict()(或 .transform())预测
  • 参数可访问: 所有超参数通过构造函数参数设置,学习到的参数以下划线后缀属性访问(如 coef_feature_importances_
  • 组合性: 通过 Pipeline 机制将多个转换器和估计器无缝串联
  • 可复现性: 通过 random_state 参数确保结果可复现
# scikit-learn 统一接口示例 —— 全部遵循 fit/predict 模式 from sklearn.ensemble import RandomForestClassifier from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegression # 所有估计器(estimator)都使用 .fit() model = RandomForestClassifier(n_estimators=100, random_state=42) model.fit(X_train, y_train) # 所有预测器(predictor)都使用 .predict() y_pred = model.predict(X_test) # 所有转换器(transformer)都使用 .fit_transform() 或 .fit() + .transform() scaler = StandardScaler() X_scaled = scaler.fit_transform(X_train)
代码 1:scikit-learn 统一接口调用模式

二、数据预处理与特征工程

真实世界的数据极少是干净整齐的。数据预处理是机器学习流程中最关键、最耗时的环节,直接决定了模型质量的上限。scikit-learn 提供了一套完整的预处理工具链,涵盖数值型数据的标准化与归一化、类别型数据的编码、缺失值处理以及自定义转换。

2.1 数值型数据标准化

大多数机器学习算法(尤其是基于距离和梯度的模型如 SVM、逻辑回归、神经网络)假设所有特征在相似的尺度上。如果某个特征的数值范围远大于其他特征,它可能会主导目标函数,导致模型性能下降。常用的数值缩放方法有以下几种:

转换器 公式 输出范围 适用场景
StandardScaler z = (x - mu) / sigma 无固定范围(均值为0,标准差为1) 数据近似正态分布时首选
MinMaxScaler z = (x - min) / (max - min) [0, 1] 需要有界输出时使用
RobustScaler z = (x - median) / IQR 无固定范围 数据包含异常值时首选
MaxAbsScaler z = x / max(|x|) [-1, 1] 稀疏矩阵的推荐选择
# 数值型数据标准化示例 from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler import numpy as np # 创建示例数据 X = np.array([[1., -1., 2.], [2., 0., 0.], [0., 1., -1.]]) # 1. StandardScaler: 标准化为均值为0、标准差为1的分布 scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # 结果: 每列均值为0,标准差为1 # 查看学习到的参数 print(f"均值: {scaler.mean_}") # 每列均值 print(f"标准差: {scaler.scale_}") # 每列标准差 # 2. MinMaxScaler: 缩放到[0,1]区间 minmax = MinMaxScaler() X_minmax = minmax.fit_transform(X) # 3. RobustScaler: 使用中位数和IQR,对异常值不敏感 robust = RobustScaler() X_robust = robust.fit_transform(X)
代码 2:四种常用数值缩放方法

2.2 类别型数据编码

机器学习模型只能处理数值型数据,因此类别型特征需要转换为数值形式。scikit-learn 提供了两种主要编码方式:OneHotEncoder(独热编码)适用于无序类别,OrdinalEncoder(顺序编码)适用于有序类别。对于目标变量(标签),使用 LabelEncoder

# 类别型数据编码示例 from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder, LabelEncoder # 1. 独热编码:适用于无序类别(如颜色、城市) X_categorical = [['红色'], ['蓝色'], ['绿色'], ['蓝色']] ohe = OneHotEncoder(sparse_output=False) X_ohe = ohe.fit_transform(X_categorical) # [[0. 1. 0.] -- 蓝色 # [0. 0. 1.] -- 绿色 # [1. 0. 0.] -- 红色 # [0. 0. 1.]] -- 绿色 print(f"类别列表: {ohe.categories_}") # 2. 标签编码:适用于目标变量(y) y_labels = ['猫', '狗', '猫', '鸟'] le = LabelEncoder() y_encoded = le.fit_transform(y_labels) # [1 0 1 2] -- 鸟=0, 狗=0, 猫=1 # 3. 处理多个类别列:使用 ColumnTransformer from sklearn.compose import ColumnTransformer from sklearn.impute import SimpleImputer preprocessor = ColumnTransformer( transformers=[ ('num', StandardScaler(), [0, 1, 2]), # 数值列做标准化 ('cat', OneHotEncoder(), [3, 4]) # 类别列做独热编码 ])
代码 3:类别编码与 ColumnTransformer 示例

2.3 自定义转换器与 FunctionTransformer

当内置的转换器无法满足需求时,可以使用 FunctionTransformer 将任意 Python 函数包装为 scikit-learn 兼容的转换器,或者继承 BaseEstimatorTransformerMixin 创建完全自定义的转换器。

# 自定义转换器示例 import numpy as np from sklearn.preprocessing import FunctionTransformer from sklearn.base import BaseEstimator, TransformerMixin # 方式1: 使用 FunctionTransformer 包装函数 def log_transform(X): """对数转换:适用于右偏分布的数据""" return np.log1p(X) # log(1+x) 避免 log(0) 问题 log_transformer = FunctionTransformer(log_transform, validate=True) X_log = log_transformer.fit_transform(X) # 方式2: 创建自定义类(更灵活) class OutlierClipper(BaseEstimator, TransformerMixin): """剪除异常值的自定义转换器""" def __init__(self, lower_percentile=1, upper_percentile=99): self.lower_percentile = lower_percentile self.upper_percentile = upper_percentile def fit(self, X, y=None): self.lower_bound_ = np.percentile(X, self.lower_percentile, axis=0) self.upper_bound_ = np.percentile(X, self.upper_percentile, axis=0) return self def transform(self, X): return np.clip(X, self.lower_bound_, self.upper_bound_) clipper = OutlierClipper(lower_percentile=5, upper_percentile=95) X_clean = clipper.fit_transform(X)
代码 4:FunctionTransformer 与自定义转换器

三、数据集划分与验证策略

在训练机器学习模型之前,必须将数据集划分为训练集和测试集。训练集用于拟合模型参数,测试集用于评估模型在未见数据上的泛化性能。scikit-learn 的 train_test_split 是最基础、最常用的划分工具,但它远不止简单的随机切分。

3.1 train_test_split 核心参数

from sklearn.model_selection import train_test_split # 基础用法:随机划分 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, # 测试集占比 20% random_state=42 # 固定随机种子,确保可复现 ) # 分层抽样:保持分类标签分布一致(分类任务中至关重要) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, stratify=y, # 按 y 的类别比例分层抽样 random_state=42 ) # 验证分层效果 from collections import Counter print(f"原始分布: {Counter(y)}") print(f"训练分布: {Counter(y_train)}") print(f"测试分布: {Counter(y_test)}") # 时间序列数据:不能随机打乱,需按时间顺序划分 X_train, X_test = X[:split_idx], X[split_idx:] y_train, y_test = y[:split_idx], y[split_idx:]
代码 5:train_test_split 的多种用法

划分策略选择指南

  • 分类问题:始终使用 stratify=y 确保训练/测试集中各类别比例与原始数据一致
  • 回归问题:无需分层,但可以按目标变量的分位数分层
  • 时间序列:永远不要随机打乱!使用时间顺序划分
  • 数据量极大(>100万条):可考虑更小的测试比例(如 0.1 或 0.05)
  • 数据量极小(<1000条):使用交叉验证替代单次划分

3.2 交叉验证

当数据量有限时,单次划分的评估结果可能不够稳定。交叉验证通过多次划分和评估来获得更可靠的性能估计。

from sklearn.model_selection import cross_val_score, KFold, StratifiedKFold # 5折交叉验证(回归问题) kfold = KFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score(model, X, y, cv=kfold, scoring='r2') print(f"每折 R² 分数: {scores}") print(f"平均 R²: {scores.mean():.4f} (+/- {scores.std() * 2:.4f})") # 分层K折(分类问题,保持每折中类别比例) skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score(model, X, y, cv=skf, scoring='accuracy') # 自定义评估指标 scores_f1 = cross_val_score(model, X, y, cv=5, scoring='f1_macro') scores_auc = cross_val_score(model, X, y, cv=5, scoring='roc_auc_ovr')
代码 6:交叉验证评估模型

四、监督学习模型详解

scikit-learn 实现了几乎所有主流的监督学习算法,从经典的线性模型到强大的集成学习方法。以下精选六个最常用、最有代表性的模型,覆盖回归与分类两大任务。

4.1 线性回归(LinearRegression)

线性回归是最简单的回归模型,假设目标变量与特征之间存在线性关系。它通过最小化残差平方和(OLS,普通最小二乘法)来求解最优的权重系数。线性回归计算效率极高,且具有良好的可解释性——每个特征的系数直接反映了该特征对目标变量的边际影响。

from sklearn.linear_model import LinearRegression from sklearn.metrics import r2_score, mean_squared_error # 创建并训练模型 lr = LinearRegression() lr.fit(X_train, y_train) # 预测与评估 y_pred = lr.predict(X_test) print(f"R² 分数: {r2_score(y_test, y_pred):.4f}") print(f"RMSE: {np.sqrt(mean_squared_error(y_test, y_pred)):.4f}") # 查看模型系数(可解释性) print(f"截距: {lr.intercept_:.4f}") for i, coef in enumerate(lr.coef_): print(f" 特征 {i} 的系数: {coef:.4f}")
代码 7:线性回归模型训练与评估

4.2 逻辑回归(LogisticRegression)

尽管名称中带有"回归",逻辑回归实际上是最常用的二分类算法之一。它通过 Sigmoid 函数将线性回归的输出映射到 [0,1] 区间的概率值,再根据概率阈值(通常为 0.5)进行分类决策。逻辑回归同样具有出色的可解释性,且通过正则化参数(C 值)可以有效防止过拟合。

from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report, confusion_matrix # 创建逻辑回归模型(带L2正则化) logreg = LogisticRegression( C=1.0, # 正则化强度的倒数(越小正则化越强) penalty='l2', # L2正则化 solver='lbfgs', # 优化算法 max_iter=1000, random_state=42 ) logreg.fit(X_train, y_train) y_pred = logreg.predict(X_test) # 获取预测概率(而非类别) y_prob = logreg.predict_proba(X_test)[:, 1] # 综合评估 print("分类报告:") print(classification_report(y_test, y_pred)) print("混淆矩阵:") print(confusion_matrix(y_test, y_pred)) # ROC-AUC 评估 from sklearn.metrics import roc_auc_score, roc_curve auc = roc_auc_score(y_test, y_prob) print(f"ROC-AUC: {auc:.4f}")
代码 8:逻辑回归分类与评估

4.3 决策树与随机森林

决策树通过树形结构对数据进行递归划分,每个内部节点代表一个特征的判断条件,每个叶节点代表一个预测值或类别。决策树的优势在于:无需特征缩放、天然支持非线性关系、结果可直接可视化和解释。但单棵决策树容易过拟合,对数据中的微小变化非常敏感。

随机森林(RandomForestClassifier / RandomForestRegressor) 通过集成学习(Bagging)的思想,构建多棵决策树并对其预测结果进行投票(分类)或平均(回归),从而显著降低方差、提高泛化能力。随机森林在绝大多数表格数据任务中表现优异,是实战中的"万金油"模型。

from sklearn.ensemble import RandomForestClassifier from sklearn.tree import DecisionTreeClassifier, plot_tree import matplotlib.pyplot as plt # 决策树(可解释性强,但易过拟合) dt = DecisionTreeClassifier(max_depth=5, random_state=42) dt.fit(X_train, y_train) # 随机森林(通常更优的选择) rf = RandomForestClassifier( n_estimators=100, # 树的数量,越大越好但计算成本也越高 max_depth=10, # 每棵树的最大深度 min_samples_split=5, # 内部节点再划分所需的最小样本数 min_samples_leaf=2, # 叶节点的最小样本数 n_jobs=-1, # 使用所有CPU核心并行计算 random_state=42 ) rf.fit(X_train, y_train) # 特征重要性分析(随机森林最重要的附加价值之一) feature_importances = rf.feature_importances_ indices = np.argsort(feature_importances)[::-1] print("特征重要性排名:") for i in range(len(indices)): print(f" {i+1}. 特征 {indices[i]}: {feature_importances[indices[i]]:.4f}")
代码 9:决策树与随机森林

4.4 支持向量机(SVC)

支持向量机的核心思想是在特征空间中找到一个超平面,使不同类别的样本之间的间隔(margin)最大化。SVM 通过核技巧(kernel trick)可以高效地处理非线性分类问题:它将原始数据隐式映射到高维特征空间,在高维空间中寻找线性可分的超平面。常用的核函数包括线性核(linear)、多项式核(poly)和径向基核(rbf)。

from sklearn.svm import SVC # SVM 分类器(RBF核,需要数据标准化!) svm = SVC( kernel='rbf', # 径向基核函数,处理非线性关系 C=1.0, # 正则化参数,越大越容忍错误 gamma='scale', # 核系数,'scale' 使用 1/(n_features * X.var()) probability=True, # 启用概率估计 random_state=42 ) # 重要:SVM 对特征尺度敏感,必须先标准化 from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) svm.fit(X_train_scaled, y_train) y_pred = svm.predict(X_test_scaled) print(f"SVM 准确率: {svm.score(X_test_scaled, y_test):.4f}")
代码 10:支持向量机分类

4.5 K近邻(KNeighborsClassifier)

K近邻(KNN)是最"懒惰"的学习算法:它不做任何模型训练,只是简单存储所有训练样本。当需要对新样本进行预测时,KNN 在训练集中查找与该样本最近的 K 个邻居,然后通过邻居的多数投票(分类)或均值(回归)来做出预测。KNN 对特征尺度极为敏感,必须进行标准化处理。

from sklearn.neighbors import KNeighborsClassifier # KNN 分类器 knn = KNeighborsClassifier( n_neighbors=5, # 邻居数量,K值越小越容易过拟合 weights='distance', # 权重:'uniform'(等权) 或 'distance'(按距离加权) metric='minkowski', # 距离度量方式 p=2 # p=2 为欧氏距离,p=1 为曼哈顿距离 ) # KNN 同样需要标准化 knn.fit(X_train_scaled, y_train) y_pred = knn.predict(X_test_scaled) # 寻找最优 K 值(简单循环调参) best_k, best_score = 1, 0 for k in range(1, 31): knn = KNeighborsClassifier(n_neighbors=k) scores = cross_val_score(knn, X_train_scaled, y_train, cv=5, scoring='accuracy') avg_score = scores.mean() if avg_score > best_score: best_score = avg_score best_k = k print(f"最优 K 值: {best_k}, 交叉验证准确率: {best_score:.4f}")
代码 11:K近邻分类器与 K 值调优

回归任务模型速查

  • LinearRegression — 线性关系,高可解释性
  • Ridge / Lasso — 带正则化的线性回归
  • RandomForestRegressor — 非线性、高准确率
  • SVR — 小数据集、高维特征
  • GradientBoostingRegressor — 梯度提升回归

分类任务模型速查

  • LogisticRegression — 二分类首选基线
  • RandomForestClassifier — 表格数据"万金油"
  • SVC — 中小数据集、高维空间
  • KNeighborsClassifier — 决策边界复杂时
  • GradientBoostingClassifier — 竞赛常用

五、模型评估指标体系

"你怎么衡量模型,你就会得到什么样的模型。"模型评估是机器学习中决定性的一环。scikit-learn 提供了丰富的评估指标,覆盖分类、回归和聚类三大任务。

5.1 分类评估指标

指标 函数 适用场景 说明
准确率 accuracy_score 均衡数据集 预测正确的比例,在类别不平衡时失效
精确率 precision_score 假阳性代价高 预测为阳性的样本中实际为阳性的比例
召回率 recall_score 假阴性代价高 实际为阳性的样本中被正确预测的比例
F1分数 f1_score 精确率与召回率的平衡 精确率和召回率的调和平均数
ROC-AUC roc_auc_score 排序质量评估 模型区分正负类的能力,取值[0.5, 1]
from sklearn.metrics import ( accuracy_score, precision_score, recall_score, f1_score, confusion_matrix, classification_report, roc_auc_score, roc_curve ) # 完整分类报告(一行代码输出所有关键指标) print(classification_report(y_test, y_pred, target_names=['负类', '正类'])) # 手动计算各项指标 accuracy = accuracy_score(y_test, y_pred) precision = precision_score(y_test, y_pred, average='macro') recall = recall_score(y_test, y_pred, average='macro') f1 = f1_score(y_test, y_pred, average='macro') print(f"准确率: {accuracy:.4f}") print(f"精确率: {precision:.4f}") print(f"召回率: {recall:.4f}") print(f"F1分数: {f1:.4f}") # 混淆矩阵可视化 cm = confusion_matrix(y_test, y_pred) print("混淆矩阵:") print(cm) # 预测负类 预测正类 # 实际负类 TN FP # 实际正类 FN TP
代码 12:分类模型综合评估

5.2 回归评估指标

from sklearn.metrics import ( r2_score, mean_squared_error, mean_absolute_error, mean_absolute_percentage_error ) # 回归模型评估 y_pred = lr.predict(X_test) r2 = r2_score(y_test, y_pred) # 决定系数,最大为1 mse = mean_squared_error(y_test, y_pred) # 均方误差 rmse = np.sqrt(mse) # 均方根误差(与y同量纲) mae = mean_absolute_error(y_test, y_pred) # 平均绝对误差 mape = mean_absolute_percentage_error(y_test, y_pred) # 平均绝对百分比误差 print(f"R²: {r2:.4f}") print(f"RMSE: {rmse:.4f}") print(f"MAE: {mae:.4f}") print(f"MAPE: {mape:.4%}")
代码 13:回归模型评估

选择评估指标的核心原则

  • 分类任务:不平衡数据集优先看精确率、召回率和 F1(而非准确率),排序任务看 ROC-AUC
  • 回归任务:RMSE 对异常值敏感(惩罚大误差),MAE 无偏向性,R² 衡量模型解释了多少方差
  • 业务结合:始终从业务视角选择指标——假阳性(FP)和假阴性(FN)哪个成本更高?
  • 泛化验证:永远不要用测试集调参!使用验证集或交叉验证,测试集仅供最终评估

六、Pipeline 流水线:从数据到模型的完整封装

在实际项目中,数据预处理通常包含多个步骤(缺失值填充、标准化、编码、特征选择等),然后将处理后的数据传给模型。如果手动串接这些步骤,代码会变得冗余且容易出错——尤其在交叉验证时,很容易不慎将测试集信息泄漏到训练过程中。

Pipeline 是解决这一问题的标准方案。它将多个转换器和最终的估计器打包成一个整体,具有以下关键优势:

from sklearn.pipeline import Pipeline, make_pipeline from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.compose import ColumnTransformer from sklearn.ensemble import RandomForestClassifier # 方式1: 使用 Pipeline 类(命名步骤) pipeline = Pipeline([ ('imputer', SimpleImputer(strategy='median')), # 步骤1: 缺失值填充 ('scaler', StandardScaler()), # 步骤2: 标准化 ('classifier', RandomForestClassifier()) # 步骤3: 分类器 ]) # 使用 Pipeline(一次 fit 完成所有步骤) pipeline.fit(X_train, y_train) y_pred = pipeline.predict(X_test) # 方式2: 使用 make_pipeline(自动命名步骤) from sklearn.pipeline import make_pipeline pipeline2 = make_pipeline(StandardScaler(), RandomForestClassifier()) # Pipeline + ColumnTransformer 处理异构数据(含数值列和类别列) numeric_features = ['age', 'income', 'amount'] categorical_features = ['gender', 'city', 'education'] numeric_transformer = Pipeline([ ('imputer', SimpleImputer(strategy='median')), ('scaler', StandardScaler()) ]) categorical_transformer = Pipeline([ ('imputer', SimpleImputer(strategy='constant', fill_value='missing')), ('encoder', OneHotEncoder(handle_unknown='ignore')) ]) preprocessor = ColumnTransformer([ ('num', numeric_transformer, numeric_features), ('cat', categorical_transformer, categorical_features) ]) # 最终的完整流水线 full_pipeline = Pipeline([ ('preprocessor', preprocessor), ('classifier', RandomForestClassifier(random_state=42)) ]) full_pipeline.fit(X_train, y_train) y_pred = full_pipeline.predict(X_test)
代码 14:Pipeline 与 ColumnTransformer 完整示例

6.2 GridSearchCV 与 Pipeline 联合调参

Pipeline 与 GridSearchCV 结合后,可以同时对预处理参数和模型超参数进行系统化的网格搜索。参数名称使用双下划线 __ 分隔步骤名和参数名(如 classifier__n_estimators)。

from sklearn.model_selection import GridSearchCV, RandomizedSearchCV # 定义参数网格 param_grid = { # 预处理步骤的参数 'preprocessor__num__scaler': [StandardScaler(), MinMaxScaler(), None], # 模型超参数 'classifier__n_estimators': [50, 100, 200], 'classifier__max_depth': [None, 10, 20], 'classifier__min_samples_split': [2, 5, 10], 'classifier__min_samples_leaf': [1, 2, 4] } # 网格搜索(带交叉验证) grid_search = GridSearchCV( estimator=full_pipeline, param_grid=param_grid, cv=5, # 5折交叉验证 scoring='accuracy', # 评估指标 n_jobs=-1, # 并行计算 verbose=1, # 输出进度信息 return_train_score=True # 返回训练集分数(用于诊断过拟合) ) grid_search.fit(X_train, y_train) # 输出最优结果 print(f"最优参数组合: {grid_search.best_params_}") print(f"最优交叉验证分数: {grid_search.best_score_:.4f}") # 在测试集上评估最优模型 test_score = grid_search.score(X_test, y_test) print(f"测试集分数: {test_score:.4f}") # RandomizedSearchCV(参数空间较大时使用随机搜索更高效) from scipy.stats import randint, uniform random_search = RandomizedSearchCV( estimator=full_pipeline, param_distributions={ 'classifier__n_estimators': randint(50, 500), 'classifier__max_depth': randint(3, 30), 'classifier__min_samples_split': randint(2, 20) }, n_iter=50, # 随机搜索的迭代次数 cv=5, random_state=42 )
代码 15:GridSearchCV 与 Pipeline 联合调参

七、模型保存与部署

训练好的模型需要进行序列化保存,以便在生产环境中加载和使用。scikit-learn 官方推荐的模型持久化方案是 joblib 库(而非 Python 内置的 pickle),因为 joblib 对大型 NumPy 数组进行了优化,序列化和反序列化速度更快。

import joblib import pickle # ====== 使用 joblib(推荐方案)====== # 保存模型到文件 joblib.dump(full_pipeline, 'model_pipeline.joblib') # 加载模型 loaded_pipeline = joblib.load('model_pipeline.joblib') # 使用加载的模型进行预测 predictions = loaded_pipeline.predict(X_new) # ====== 使用 pickle(通用方案)====== # 保存 with open('model_pipeline.pkl', 'wb') as f: pickle.dump(full_pipeline, f) # 加载 with open('model_pipeline.pkl', 'rb') as f: loaded_pipeline = pickle.load(f) # ====== 模型部署最佳实践 ====== # 1. 同时保存模型元信息 model_info = { 'model': full_pipeline, 'features': feature_names, 'target': target_name, 'metrics': {'test_accuracy': test_score}, 'training_date': '2026-05-05', 'sklearn_version': '1.5.0' } joblib.dump(model_info, 'model_with_metadata.joblib') # 2. 模型版本管理:每次迭代使用不同的文件名 import datetime timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S') joblib.dump(full_pipeline, f'model_{timestamp}.joblib')
代码 16:模型保存与加载

模型序列化注意事项

  • Python 版本兼容性:在不同 Python 版本之间序列化和反序列化可能会失败,尽量保持环境一致
  • scikit-learn 版本兼容性:不同版本之间的模型文件可能不兼容,记录版本号
  • 安全风险:不要加载不可信的 pickle/joblib 文件,可能执行任意代码
  • 大模型:随机森林等模型可能体积较大(几百 MB),考虑使用压缩或模型蒸馏
  • 生产部署:推荐使用 ONNX 格式或 MLflow 等模型管理平台

八、完整实战案例:Iris 鸢尾花分类

以下是一个端到端的完整案例,从数据加载到模型部署,涵盖本笔记中介绍的所有核心概念。使用经典的 Iris 数据集进行演示:

# ====== scikit-learn 完整实战案例:鸢尾花分类 ====== import numpy as np import pandas as pd # ------------------------------------------------------------------ # 第一步:加载数据 # ------------------------------------------------------------------ from sklearn.datasets import load_iris data = load_iris() X, y = data.data, data.target feature_names = data.feature_names target_names = data.target_names print(f"数据形状: {X.shape}") print(f"特征: {feature_names}") print(f"类别: {target_names}") # ------------------------------------------------------------------ # 第二步:划分数据集(分层抽样) # ------------------------------------------------------------------ from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, stratify=y, random_state=42 ) print(f"训练集: {X_train.shape}, 测试集: {X_test.shape}") # ------------------------------------------------------------------ # 第三步:构建 Pipeline(数据预处理 + 模型) # ------------------------------------------------------------------ from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.ensemble import RandomForestClassifier pipeline = Pipeline([ ('scaler', StandardScaler()), ('rf', RandomForestClassifier(random_state=42)) ]) # ------------------------------------------------------------------ # 第四步:超参数调优 # ------------------------------------------------------------------ from sklearn.model_selection import GridSearchCV param_grid = { 'rf__n_estimators': [50, 100, 200], 'rf__max_depth': [None, 5, 10], 'rf__min_samples_split': [2, 5] } grid = GridSearchCV(pipeline, param_grid, cv=5, scoring='accuracy', n_jobs=-1) grid.fit(X_train, y_train) print(f"最优参数: {grid.best_params_}") print(f"最优CV分数: {grid.best_score_:.4f}") # ------------------------------------------------------------------ # 第五步:模型评估 # ------------------------------------------------------------------ from sklearn.metrics import classification_report, confusion_matrix y_pred = grid.predict(X_test) print("\n分类报告:") print(classification_report(y_test, y_pred, target_names=target_names)) print("混淆矩阵:") print(confusion_matrix(y_test, y_pred)) test_acc = grid.score(X_test, y_test) print(f"测试集准确率: {test_acc:.4f}") # ------------------------------------------------------------------ # 第六步:模型保存 # ------------------------------------------------------------------ import joblib joblib.dump(grid.best_estimator_, 'iris_rf_pipeline.joblib') print("模型已保存为 iris_rf_pipeline.joblib")
代码 17:完整端到端实战案例

实战关键要点回顾

  • 始终使用 Pipeline:将预处理和模型训练封装在一起,避免数据泄漏,简化部署
  • 分层抽样不可省略:分类任务必须使用 stratify=y,确保训练和测试集分布一致
  • 标准化 SVM 和 KNN 的前置条件:基于距离的模型对特征尺度高度敏感
  • GridSearchCV + Pipeline:一起对预处理参数和模型参数进行联合搜索
  • 多指标评估:不要只看准确率,综合精确率、召回率、F1 和混淆矩阵一起分析
  • 模型版本管理:每次训练保存不同文件名,记录 scikit-learn 版本和训练元数据

九、总结与学习路径建议

scikit-learn 提供了一个优雅、一致且功能强大的机器学习框架。掌握本笔记涵盖的内容,你就能独立完成以下任务:加载并探索数据、进行数据预处理和特征工程、划分训练集和测试集、训练监督学习模型、系统化评估模型性能、通过 Pipeline 构建完整流水线、使用 GridSearchCV 调优超参数、保存和部署模型。

推荐的学习路径

  1. 入门阶段:深入理解 API 设计哲学(fit/predict/transform),掌握 train_test_split 和 StandardScaler
  2. 建模阶段:逐个实践 LinearRegression → LogisticRegression → RandomForestClassifier → SVC,理解每种算法的适用场景
  3. 评估阶段:系统学习分类和回归评估指标,掌握交叉验证和 confusion_matrix 分析
  4. 工程阶段:掌握 Pipeline + ColumnTransformer 构建生产级流水线,联合 GridSearchCV 进行系统调参
  5. 进阶方向:特征选择(SelectKBest / RFE)、降维(PCA / t-SNE)、集成方法(GradientBoosting / XGBoost / LightGBM)、无监督学习(KMeans / DBSCAN)

一句话总结

scikit-learn 通过 fit / predict / transform 统一接口 + Pipeline 流水线封装 + GridSearchCV 系统调参 三大支柱,将机器学习从"艺术"变成可工程化的"流程"。学好 scikit-learn 是成为数据科学家的第一步,也是最重要的一步。