scikit-learn 完全指南

scikit-learn 是 Python 最成熟的机器学习库,提供监督学习、无监督学习、模型选择与评估的完整工具链,所有算法遵循统一的 fit/predict/transform 接口。 与其他框架的对比: 适合场景:结构化(表格)数据的分类、回归、聚类;需要快速建立 baseline;特征工程 Pipeline 构建;模型选择与超参调优。 不适合场景:图像/音频/文本深度学习(用 PyTorch完全指南(/pytorchwan-quan-zhi-nan/) 或 HuggingFace Transformers完全指南(/huggingface-tra

分享

官方文档:https://scikit-learn.org/stable/
适用版本:scikit-learn 1.8.0(2026-05-07 核实)


概述

scikit-learn 是 Python 最成熟的机器学习库,提供监督学习、无监督学习、模型选择与评估的完整工具链,所有算法遵循统一的 fit/predict/transform 接口。

与其他框架的对比:

scikit-learn PyTorch XGBoost
定位 经典 ML 算法大全 深度学习框架 梯度提升专用
上手成本 低(统一接口) 高(需手写训练循环)
适合数据量 中小型(内存能加载) 无上限(批量训练) 中大型
GPU 支持 有限(部分算法) 原生 支持
可解释性 高(线性模型、决策树) 低(黑盒)

适合场景:结构化(表格)数据的分类、回归、聚类;需要快速建立 baseline;特征工程 Pipeline 构建;模型选择与超参调优。

不适合场景:图像/音频/文本深度学习(用 PyTorch完全指南HuggingFace Transformers完全指南);超大规模分布式训练。


安装

pip install scikit-learn

核心设计:Estimator 接口

所有 scikit-learn 对象实现统一接口:

方法 适用对象 说明
fit(X, y) Estimator(含 Transformer) 用训练数据拟合模型;无监督只需 X
predict(X) Classifier / Regressor 返回预测标签或数值
predict_proba(X) 支持概率输出的 Classifier 返回每类的概率,形状 (n, n_classes)
transform(X) Transformer 对数据做变换(如归一化、降维)
fit_transform(X) Transformer 等价于 fit(X).transform(X),避免两次调用
score(X, y) Estimator 返回默认评估指标(分类器返回 accuracy,回归返回 R²)
set_params(**params) 所有 Estimator 修改超参数,用于 GridSearch
get_params() 所有 Estimator 获取当前超参数字典

数据预处理(sklearn.preprocessing)

StandardScaler

零均值、单位方差标准化(Z-score)。

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler(copy=True, with_mean=True, with_std=True)
参数 类型 默认值 说明
copy bool True 是否拷贝数据;False 原地修改节省内存,但会修改原数组
with_mean bool True 是否减去均值(中心化);稀疏矩阵设 False 否则报错
with_std bool True 是否除以标准差;False 只中心化不缩放
from sklearn.preprocessing import StandardScaler

X_train = [[0, 0], [0, 0], [1, 1], [1, 1]]
scaler = StandardScaler()
scaler.fit(X_train)
# 务必只在训练集上 fit,测试集只做 transform
X_test = scaler.transform([[2, 2], [-1, -1]])
print(scaler.mean_)    # [0.5 0.5]
print(scaler.scale_)   # [0.5 0.5]

MinMaxScaler

将特征缩放到指定范围 [feature_range[0], feature_range[1]]

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler(feature_range=(0, 1), copy=True, clip=False)
参数 类型 默认值 说明
feature_range tuple[float, float] (0, 1) 目标范围;两端点必须满足 min < max
copy bool True 是否拷贝数据
clip bool False True 时超出训练范围的测试值被裁剪到 feature_range

OneHotEncoder

将类别特征编码为 one-hot 矩阵。

from sklearn.preprocessing import OneHotEncoder

enc = OneHotEncoder(
    categories='auto',
    drop=None,
    sparse_output=True,
    dtype=float,
    handle_unknown='error',
    min_frequency=None,
    max_categories=None,
)
参数 类型 默认值 说明
categories "auto"list[list] "auto" "auto" 从数据中推断;或手动指定每列的类别列表
drop None | "first" | "if_binary" None 删除冗余列;"first" 删第一列(避免多重共线性);"if_binary" 仅对二值特征删除
sparse_output bool True True 返回稀疏矩阵(节省内存);False 返回 ndarray
dtype numeric type float 输出数组的数值类型
handle_unknown "error" | "ignore" | "infrequent_if_exist" "error" 遇到训练时未见过的类别的处理方式;"ignore" 输出全零列
min_frequency int | float | None None 低于此频率的类别归入 infrequent 类
max_categories int | None None 每列最多保留的类别数,超出部分合并为 infrequent
from sklearn.preprocessing import OneHotEncoder
import numpy as np

enc = OneHotEncoder(sparse_output=False, handle_unknown='ignore')
X = [['cat'], ['dog'], ['fish']]
enc.fit(X)

# 测试集有未见类别 'bird',handle_unknown='ignore' 输出全零
print(enc.transform([['dog'], ['bird']]))
# [[0. 1. 0.]
#  [0. 0. 0.]]

LabelEncoder

对目标变量(y)的类别进行整数编码。

from sklearn.preprocessing import LabelEncoder

le = LabelEncoder()
le.fit(['cat', 'dog', 'fish'])
print(le.transform(['dog', 'cat']))  # [1 0]
print(le.inverse_transform([2]))     # ['fish']

LabelEncoder 无超参数,不应用于输入特征(用 OrdinalEncoder 代替)。

PolynomialFeatures

生成多项式特征,用于线性模型捕捉非线性关系。

from sklearn.preprocessing import PolynomialFeatures

poly = PolynomialFeatures(degree=2, interaction_only=False, include_bias=True, order='C')
参数 类型 默认值 说明
degree int | tuple[int,int] 2 多项式最高次数;tuple 指定范围 (min_degree, max_degree)
interaction_only bool False True 只生成交叉项(不含 x² 类纯次方项)
include_bias bool True 是否包含偏置列(全为 1 的列)
order "C" | "F" "C" 输出数组的内存布局;"C" 行优先,"F" 列优先

线性模型(sklearn.linear_model)

LogisticRegression

逻辑回归分类器,适合线性可分的二分类和多分类问题。

from sklearn.linear_model import LogisticRegression

clf = LogisticRegression(
    penalty='l2',
    dual=False,
    tol=1e-4,
    C=1.0,
    fit_intercept=True,
    intercept_scaling=1,
    class_weight=None,
    random_state=None,
    solver='lbfgs',
    max_iter=100,
    multi_class='deprecated',
    verbose=0,
    warm_start=False,
    n_jobs=None,
    l1_ratio=None,
)
参数 类型 默认值 说明
penalty "l1" | "l2" | "elasticnet" | None "l2" 正则化类型;None 无正则;l1solver='liblinear''saga'
C float 1.0 正则化强度的倒数;越小正则越强;必须 > 0
solver str "lbfgs" 优化算法:lbfgs(小数据集)、liblinear(小/稀疏)、saga(大数据集/L1)、newton-cgnewton-choleskysag
max_iter int 100 求解器最大迭代次数;若不收敛会抛出 ConvergenceWarning
class_weight dict | "balanced" | None None 类别权重;"balanced" 自动按类频率倒数加权(处理不平衡数据)
random_state int | None None 随机种子(solver='sag'/'saga'/'liblinear' 时使用)
n_jobs int | None None 多分类时并行数;-1 使用全部 CPU
l1_ratio float | None None ElasticNet 混合比例;0=纯L2,1=纯L1;仅 penalty='elasticnet' 时有效
warm_start bool False True 用上次 fit 的结果初始化,用于增量训练
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

clf = LogisticRegression(max_iter=1000, C=0.1)
clf.fit(X_train, y_train)
print(clf.score(X_test, y_test))        # accuracy
print(clf.predict_proba(X_test[:2]))   # 概率矩阵

LinearRegression

最小二乘线性回归。

from sklearn.linear_model import LinearRegression

reg = LinearRegression(fit_intercept=True, copy_X=True, n_jobs=None, positive=False)
参数 类型 默认值 说明
fit_intercept bool True 是否拟合截距;False 假设数据已中心化
copy_X bool True False 原地修改 X(节省内存,但会修改原始数据)
n_jobs int | None None 计算并行数;仅多目标回归时有效
positive bool False True 强制系数非负(非负约束最小二乘)

Ridge

L2 正则化线性回归(岭回归)。

from sklearn.linear_model import Ridge

reg = Ridge(alpha=1.0, fit_intercept=True, copy_X=True, max_iter=None,
            tol=1e-4, solver='auto', positive=False, random_state=None)
参数 类型 默认值 说明
alpha float | array-like 1.0 正则化系数;越大正则越强;为 0 等价于 LinearRegression;array 时对每个目标单独设置
solver str "auto" 求解器:auto(自动选择)、svdcholeskylsqrsparse_cgsagsagalbfgs
max_iter int | None None 迭代求解器最大迭代次数

Lasso

L1 正则化线性回归(Lasso),产生稀疏解(特征选择)。

from sklearn.linear_model import Lasso

reg = Lasso(alpha=1.0, fit_intercept=True, precompute=False, copy_X=True,
            max_iter=1000, tol=1e-4, warm_start=False, positive=False,
            random_state=None, selection='cyclic')
参数 类型 默认值 说明
alpha float 1.0 L1 正则化系数;越大稀疏度越高(更多系数为零)
max_iter int 1000 坐标下降最大迭代次数
selection "cyclic" | "random" "cyclic" 坐标更新策略;"random" 有时收敛更快

集成方法(sklearn.ensemble)

RandomForestClassifier

随机森林:并行训练多棵决策树,以投票方式聚合结果。

from sklearn.ensemble import RandomForestClassifier

clf = RandomForestClassifier(
    n_estimators=100,
    criterion='gini',
    max_depth=None,
    min_samples_split=2,
    min_samples_leaf=1,
    min_weight_fraction_leaf=0.0,
    max_features='sqrt',
    max_leaf_nodes=None,
    min_impurity_decrease=0.0,
    bootstrap=True,
    oob_score=False,
    n_jobs=None,
    random_state=None,
    verbose=0,
    warm_start=False,
    class_weight=None,
    ccp_alpha=0.0,
    max_samples=None,
    monotonic_cst=None,
)
参数 类型 默认值 说明
n_estimators int 100 决策树数量;越多越稳定,但计算越慢
criterion "gini" | "entropy" | "log_loss" "gini" 节点分裂的衡量标准
max_depth int | None None 树最大深度;None 展开到所有叶子纯净为止(可能过拟合)
min_samples_split int | float 2 内部节点最少样本数才允许分裂;float 表示比例
min_samples_leaf int | float 1 叶子节点最少样本数;增大可减少过拟合
max_features "sqrt" | "log2" | int | float | None "sqrt" 每次分裂考虑的最大特征数;"sqrt" 适合分类,"auto" 已废弃
bootstrap bool True True 使用有放回抽样;False 使用全部训练数据
oob_score bool False True 使用袋外样本估计泛化误差(bootstrap=True 时可用)
n_jobs int | None None 并行树的训练数;-1 使用全部 CPU
class_weight dict | "balanced" | "balanced_subsample" | None None 类别权重
ccp_alpha float 0.0 代价复杂度剪枝参数;0 不剪枝
max_samples int | float | None None bootstrap 时每棵树抽取的样本数;None 等于训练集大小

GradientBoostingClassifier

梯度提升:顺序训练,每棵树拟合前一棵的残差。准确率通常高于随机森林,但训练慢。

from sklearn.ensemble import GradientBoostingClassifier

clf = GradientBoostingClassifier(
    loss='log_loss',
    learning_rate=0.1,
    n_estimators=100,
    subsample=1.0,
    criterion='friedman_mse',
    min_samples_split=2,
    min_samples_leaf=1,
    min_weight_fraction_leaf=0.0,
    max_depth=3,
    min_impurity_decrease=0.0,
    init=None,
    random_state=None,
    max_features=None,
    verbose=0,
    max_leaf_nodes=None,
    warm_start=False,
    validation_fraction=0.1,
    n_iter_no_change=None,
    tol=1e-4,
    ccp_alpha=0.0,
)
参数 类型 默认值 说明
learning_rate float 0.1 每棵树的贡献缩减系数(步长);越小需要越多树
n_estimators int 100 树的数量;与 learning_rate 反向调整(小步长 + 多树)
subsample float 1.0 每棵树训练时的样本比例;< 1 实现随机梯度提升,减少过拟合
max_depth int 3 每棵树的最大深度;梯度提升通常用浅树(3–5)
n_iter_no_change int | None None 早停:连续 N 轮验证集分数无改善则停止
validation_fraction float 0.1 早停时从训练集划分的验证比例

支持向量机(sklearn.svm)

SVC

支持向量机分类,适合高维、小数据集的分类问题。

from sklearn.svm import SVC

clf = SVC(
    C=1.0,
    kernel='rbf',
    degree=3,
    gamma='scale',
    coef0=0.0,
    shrinking=True,
    probability=False,
    tol=1e-3,
    cache_size=200,
    class_weight=None,
    verbose=False,
    max_iter=-1,
    decision_function_shape='ovr',
    break_ties=False,
    random_state=None,
)
参数 类型 默认值 说明
C float 1.0 正则化参数;越小间隔越宽(欠拟合),越大越严格分类(过拟合)
kernel "linear" | "poly" | "rbf" | "sigmoid" | "precomputed" "rbf" 核函数类型;"rbf" 适合大多数场景;"linear" 适合线性可分
degree int 3 多项式核的次数;仅 kernel='poly' 时有效
gamma "scale" | "auto" | float "scale" 核系数;"scale" 使用 1/(n_features * X.var())"auto" 使用 1/n_features
probability bool False True 启用概率估计(启用后会有额外开销,影响训练速度)
cache_size float 200 核矩阵缓存大小(MB);大数据集增大可加速
decision_function_shape "ovr" | "ovo" "ovr" 多分类策略:"ovr" 一对多,"ovo" 一对一

聚类(sklearn.cluster)

KMeans

K-均值聚类,将数据分为 K 个簇。

from sklearn.cluster import KMeans

km = KMeans(
    n_clusters=8,
    init='k-means++',
    n_init='auto',
    max_iter=300,
    tol=1e-4,
    verbose=0,
    random_state=None,
    copy_x=True,
    algorithm='lloyd',
)
参数 类型 默认值 说明
n_clusters int 8 簇的数量 K;需通过肘部法或轮廓系数确定
init "k-means++" | "random" | callable "k-means++" 初始化方法;"k-means++" 加速收敛并提升稳定性
n_init int | "auto" "auto" 不同初始化的运行次数,取最优;"auto"k-means++ 使用 10,random 使用 1
max_iter int 300 单次运行最大迭代次数
algorithm "lloyd" | "elkan" "lloyd" 算法实现;"elkan" 用三角不等式加速,但内存更多
from sklearn.cluster import KMeans
import numpy as np

X = np.array([[1, 2], [1, 4], [1, 0], [10, 2], [10, 4], [10, 0]])
km = KMeans(n_clusters=2, random_state=42, n_init=10)
km.fit(X)
print(km.labels_)         # 每个样本的簇标签
print(km.cluster_centers_) # 簇中心坐标
print(km.inertia_)         # 误差平方和(越小越好)

DBSCAN

基于密度的聚类,不需要预先指定 K,能发现任意形状的簇并识别噪声点。

from sklearn.cluster import DBSCAN

db = DBSCAN(
    eps=0.5,
    min_samples=5,
    metric='euclidean',
    metric_params=None,
    algorithm='auto',
    leaf_size=30,
    p=None,
    n_jobs=None,
)
参数 类型 默认值 说明
eps float 0.5 两个样本被视为邻居的最大距离;核心超参数,需根据数据尺度调整
min_samples int 5 核心点的最少邻居数(含自身);增大可减少噪声标签
metric str | callable "euclidean" 距离度量;支持所有 scipy.spatial.distance 中的距离
algorithm "auto" | "ball_tree" | "kd_tree" | "brute" "auto" 查找最近邻的算法

DBSCAN 的标签中 -1 表示噪声点(不属于任何簇)。


模型选择(sklearn.model_selection)

train_test_split

将数组或矩阵按比例随机分为训练集和测试集。

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    *arrays,
    test_size=None,
    train_size=None,
    random_state=None,
    shuffle=True,
    stratify=None,
)
参数 类型 默认值 说明
*arrays indexable 必填 要分割的数组(X, y, weights 等),所有数组必须等长
test_size float | int | None None float 表示比例(0–1),int 表示绝对数量;None 时默认 0.25
train_size float | int | None None 同上;None 时取 1 - test_size
random_state int | None None 随机种子;固定种子保证结果可复现
shuffle bool True 分割前是否打乱;时序数据应设 False
stratify array-like | None None 按此数组的类分布进行分层抽样;保证训练/测试集类别比例相同

cross_val_score

K 折交叉验证,返回每折的评分数组。

from sklearn.model_selection import cross_val_score

scores = cross_val_score(
    estimator,
    X,
    y=None,
    groups=None,
    scoring=None,
    cv=5,
    n_jobs=None,
    verbose=0,
    fit_params=None,
    params=None,
    pre_dispatch='2*n_jobs',
    error_score=nan,
)
参数 类型 默认值 说明
estimator estimator 必填 实现了 fit 的 sklearn 兼容对象
X array-like 必填 特征矩阵
y array-like | None None 目标变量;无监督学习传 None
scoring str | callable | None None 评估指标;None 使用 estimator 的默认分数;字符串如 "f1_macro""roc_auc"
cv int | CV splitter 5 折数;或自定义 KFold/StratifiedKFold 对象
n_jobs int | None None 并行折数;-1 使用全部 CPU
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris

X, y = load_iris(return_X_y=True)
clf = LogisticRegression(max_iter=1000)
scores = cross_val_score(clf, X, y, cv=5, scoring='accuracy')
print(f"Accuracy: {scores.mean():.3f} ± {scores.std():.3f}")

GridSearchCV

穷举超参数网格,用交叉验证选最优参数组合。

from sklearn.model_selection import GridSearchCV

search = GridSearchCV(
    estimator,
    param_grid,
    scoring=None,
    n_jobs=None,
    refit=True,
    cv=5,
    verbose=0,
    pre_dispatch='2*n_jobs',
    error_score=nan,
    return_train_score=False,
)
参数 类型 默认值 说明
estimator estimator 必填 待搜索的模型,需实现 fit/score
param_grid dict | list[dict] 必填 参数名(含 __ 分隔的 Pipeline 参数)到候选值列表的映射
scoring str | callable | list | dict | None None 评估指标;多指标时用 dict 并需指定 refit
cv int | CV splitter 5 交叉验证折数
refit bool | str True 找到最优参数后用全量数据重新训练;多指标时需指定用哪个指标
n_jobs int | None None 并行候选数;-1 全部 CPU
return_train_score bool False Truecv_results_ 中包含训练集分数(诊断过拟合用)
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris

X, y = load_iris(return_X_y=True)
param_grid = {
    'n_estimators': [50, 100, 200],
    'max_depth': [None, 3, 5],
}
search = GridSearchCV(
    RandomForestClassifier(random_state=42),
    param_grid,
    cv=5,
    scoring='accuracy',
    n_jobs=-1,
)
search.fit(X, y)
print(search.best_params_)   # {'max_depth': None, 'n_estimators': 100}
print(search.best_score_)    # 0.973...
# search.best_estimator_ 是已用全量数据重训的最优模型

RandomizedSearchCV

随机采样超参数空间,比 GridSearchCV 更高效(适合大参数空间)。

from sklearn.model_selection import RandomizedSearchCV

search = RandomizedSearchCV(
    estimator,
    param_distributions,
    n_iter=10,
    scoring=None,
    n_jobs=None,
    refit=True,
    cv=5,
    verbose=0,
    pre_dispatch='2*n_jobs',
    random_state=None,
    error_score=nan,
    return_train_score=False,
)
参数 类型 默认值 说明
param_distributions dict | list[dict] 必填 参数到候选值列表或 scipy 分布对象的映射;连续超参用分布更高效
n_iter int 10 随机采样次数;越大越接近 Grid Search 但耗时越多
random_state int | None None 随机种子

Pipeline(sklearn.pipeline)

Pipeline

将多个预处理步骤和最终估计器串联,避免数据泄漏(fit_transform 仅在训练集执行)。

from sklearn.pipeline import Pipeline

pipe = Pipeline(steps, memory=None, verbose=False)
参数 类型 默认值 说明
steps list[tuple[str, estimator]] 必填 每个元素为 (name, transformer)(name, estimator);最后一步可以是 Estimator
memory None | str | Memory None 缓存已拟合的 Transformer(加速 GridSearch 时相同参数不重新拟合)
verbose bool False True 打印每步执行时间

访问步骤:pipe['scaler']pipe.named_steps['scaler']

超参数路径:pipe.set_params(classifier__C=0.1)stepname__param 格式)。

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_breast_cancer

X, y = load_breast_cancer(return_X_y=True)

# Pipeline 防止数据泄漏:Scaler 只在训练折 fit
pipe = Pipeline([
    ('scaler', StandardScaler()),
    ('clf', LogisticRegression(max_iter=1000)),
])

# 通过 __ 分隔符访问 Pipeline 内的超参数
param_grid = {
    'clf__C': [0.01, 0.1, 1, 10],
    'clf__penalty': ['l2'],
}
grid = GridSearchCV(pipe, param_grid, cv=5, n_jobs=-1)
grid.fit(X, y)
print(grid.best_params_)

评估指标(sklearn.metrics)

分类指标

from sklearn.metrics import (
    accuracy_score, precision_score, recall_score, f1_score,
    confusion_matrix, classification_report, roc_auc_score
)

# 二分类
y_true = [0, 1, 1, 0, 1]
y_pred = [0, 1, 0, 0, 1]

print(accuracy_score(y_true, y_pred))                    # 0.8
print(precision_score(y_true, y_pred))                   # 1.0
print(recall_score(y_true, y_pred))                      # 0.667
print(f1_score(y_true, y_pred))                          # 0.8
print(confusion_matrix(y_true, y_pred))
# [[2 0]
#  [1 2]]
print(classification_report(y_true, y_pred))             # 多指标汇总

average 参数说明(多分类时必须指定):

说明
"binary" 二分类,默认 positive label=1
"micro" 全局统计 TP/FP/FN,适合样本不平衡
"macro" 各类均值(等权),适合类别均衡
"weighted" 按类别样本数加权均值,适合不平衡

回归指标

from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score

y_true = [3, -0.5, 2, 7]
y_pred = [2.5, 0.0, 2, 8]

print(mean_squared_error(y_true, y_pred))        # MSE = 0.375
print(mean_absolute_error(y_true, y_pred))       # MAE = 0.5
print(r2_score(y_true, y_pred))                  # R² = 0.948

完整示例:端到端 ML 工作流

import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, roc_auc_score

# 加载数据
X, y = load_breast_cancer(return_X_y=True)
print(f"Samples: {X.shape[0]}, Features: {X.shape[1]}, Classes: {np.unique(y)}")

# 分层划分训练/测试集(保证类别比例相同)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# 构建 Pipeline(Scaler 只在训练折 fit,防止数据泄漏)
pipe = Pipeline([
    ('scaler', StandardScaler()),
    ('clf', RandomForestClassifier(random_state=42)),
])

# 超参数搜索
param_grid = {
    'clf__n_estimators': [100, 200],
    'clf__max_depth': [None, 5, 10],
    'clf__min_samples_leaf': [1, 2],
}
grid = GridSearchCV(pipe, param_grid, cv=5, scoring='roc_auc', n_jobs=-1, verbose=1)
grid.fit(X_train, y_train)

print(f"Best params: {grid.best_params_}")
print(f"CV ROC AUC: {grid.best_score_:.4f}")

# 测试集评估
y_pred = grid.predict(X_test)
y_prob = grid.predict_proba(X_test)[:, 1]

print(classification_report(y_test, y_pred, target_names=['malignant', 'benign']))
print(f"Test ROC AUC: {roc_auc_score(y_test, y_prob):.4f}")

# 特征重要性(从 Pipeline 中取出 clf)
importances = grid.best_estimator_['clf'].feature_importances_
top_features = np.argsort(importances)[-5:][::-1]
print("Top 5 features:", top_features)

最佳实践

始终在 Pipeline 内做预处理,而非手动拆分: 在 Pipeline 外对整个数据集做 fit_transform,再分训练测试集,会导致测试集信息泄漏到 Scaler 中,使评估结果过于乐观。

# 错误:先 fit scaler 再分割,测试集信息泄漏
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)  # 使用了测试集的均值/方差
X_train, X_test = train_test_split(X_scaled)

# 正确:先分割,再在 Pipeline 内 fit
X_train, X_test = train_test_split(X)
pipe = Pipeline([('scaler', StandardScaler()), ('clf', clf)])
pipe.fit(X_train, y_train)
pipe.predict(X_test)

固定 random_state 保证可复现性: 所有带随机性的操作(分割、模型初始化)都传入 random_state=42,确保实验结果可复现和对比。

类别不平衡时用分层抽样和合适指标: 不平衡数据集上 accuracy 是误导性指标,应用 ROC AUC、F1-macro 或 PR AUC;train_test_split(stratify=y) 保证训练/测试集类别比例一致。

cross_val_score 代替单次 train/test 分割评估: 单次分割结果受随机种子影响大,K 折交叉验证的均值 ± 标准差更可靠。

超参数调优优先用 RandomizedSearchCV 再细化: 参数空间大时(>30 个组合),先用 RandomizedSearchCV 找到大致区间,再用 GridSearchCV 在小范围精调,比直接全网格搜索节省 90% 时间。

使用 joblib.dump/load 持久化 Pipeline: 保存的 Pipeline 含 Scaler 参数,部署时直接 load 即可预测,无需重新 fit。

import joblib

joblib.dump(grid.best_estimator_, 'model.pkl')
model = joblib.load('model.pkl')
model.predict(X_test)

常见陷阱

陷阱:测试集泄漏到预处理步骤

现象: 交叉验证或测试集分数比实际部署效果好得多。

原因: 在 Pipeline 外对全量数据做了 fit_transform,导致 Scaler 的统计量包含了测试集的信息,测试集相当于被"见过"了。

解决: 所有预处理必须放进 Pipeline,只调用 Pipeline.fit(X_train)Pipeline.transform(X_test)

陷阱:GridSearchCV 内外套了不必要的 Scaler

现象: Pipeline 内已有 Scaler,外部又手动对全量数据做了归一化,导致双重标准化(零均值数据再次标准化后变形)。

原因: 不清楚 Pipeline 内部已经做了预处理。

解决: 检查 Pipeline 内已有的步骤,外部不要再做重复预处理。

陷阱:类别特征使用了 LabelEncoder

现象: 模型对类别特征赋予了大小关系(如 cat=0 < dog=1 < fish=2),线性模型或神经网络效果差。

原因: LabelEncoder 产生有序整数,暗示了不存在的大小关系;它只应用于目标变量 y,不应用于输入特征 X。

解决: 输入特征的类别编码使用 OneHotEncoderOrdinalEncoder(仅当类别本身有序时)。

from sklearn.preprocessing import OrdinalEncoder, OneHotEncoder

# 错误:对类别特征用 LabelEncoder(产生错误的有序关系)
le = LabelEncoder()
X[:, 0] = le.fit_transform(X[:, 0])

# 正确:用 OneHotEncoder 在 Pipeline 内处理
pipe = Pipeline([
    ('enc', OneHotEncoder(handle_unknown='ignore')),
    ('clf', LogisticRegression()),
])

陷阱:max_iter 不足导致 ConvergenceWarning

现象: 输出 ConvergenceWarning: Lbfgs failed to converge,但模型看起来还能用。

原因: 求解器在 max_iter 次迭代内没有达到收敛条件,参数优化未完成,模型是次优解。

解决: 增大 max_iter(从 100 → 1000 → 10000),或先对数据做 StandardScaler 加速收敛,或换 solver


参见

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog