期末机器学习

KNN

python 复制代码
 from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data
y = iris.target
 X_train, X_test, y_train, y_test = train_test_split(X, y)

 from sklearn.neighbors import KNeighborsClassifier
 knn = KNeighborsClassifier(n_neighbors=1)
 knn.fit(X_train, y_train)
 print("accuracy: ", knn.score(X_test, y_test)))
 y_pred = knn.predict(X_test)

三分

"Threefold split " 通常指的是 将数据集划分为三部分,即把数据分成三份,用于不同的目的,常见的划分包括:

  1. 训练集(Training set)
  • 用来训练模型,让模型学习数据中的规律。
  1. 验证集(Validation set)
  • 用来调参和选择模型,帮助判断模型在未见数据上的表现,防止过拟合。
  1. 测试集(Test set)
  • 用来最终评估模型性能,检验模型的泛化能力。
python 复制代码
 X_trainval, X_test, y_trainval, y_test = train_test_split(X, y)
 X_train, X_val, y_train, y_val = train_test_split(X_trainval, y_trainval)

 val_scores = []
 neighbors = np.arange(1, 15, 2)

 for i in neighbors:
    knn = KNeighborsClassifier(n_neighbors=i)
    knn.fit(X_train, y_train)
    val_scores.append(knn.score(X_val, y_val))
    
 print(f"best validation score: {np.max(val_scores):.3}")
 best_n_neighbors = neighbors[np.argmax(val_scores)]
    
 print("best n_neighbors:", best_n_neighbors)
 knn = KNeighborsClassifier(n_neighbors=best_n_neighbors)
    
 knn.fit(X_trainval, y_trainval)
 print(f"test-set score: {knn.score(X_test, y_test):.3f}")

交叉验证

Cross-validation + test set " 是一种常见的机器学习模型评估策略,它结合了 交叉验证独立测试集,用来既调优模型又进行最终的性能评估。

流程

首先,将数据集拆分成两部分:

  • 训练集 + 验证集(合起来叫训练-验证集,用于训练和调参)

  • 测试集(完全独立,用于最终评估模型性能)

  • 在训练-验证集上进行交叉验证(通常是 K 折交叉验证):

    • 交叉验证用于:

      • 训练模型
      • 调整超参数
      • 选择模型架构
    • 通过多次划分训练-验证集,可以得到更稳定的模型性能估计,避免过拟合

    • 根据交叉验证结果选择最优超参数后,

      训练-验证集的全部数据 来重新训练模型

假设数据集有1000条样本:

  • 先划分:800条(训练+验证集),200条(测试集)
  • 在800条数据上做5折交叉验证(每折640训练,160验证),用来选最优超参数
  • 用整个800条数据和选出的超参数训练最终模型
  • 用剩下200条测试集评估模型性能
python 复制代码
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.neighbors import KNeighborsClassifier
import numpy as np

# 1. 划分训练-验证集和测试集
X_trainval, X_test, y_trainval, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 2. 在训练-验证集上进行交叉验证调参
neighbors = np.arange(1, 15, 2)
best_score = 0
best_k = 1
for k in neighbors:
    knn = KNeighborsClassifier(n_neighbors=k)
    scores = cross_val_score(knn, X_trainval, y_trainval, cv=5)
    mean_score = scores.mean()
    if mean_score > best_score:
        best_score = mean_score
        best_k = k

print(f"Best k from cross-validation: {best_k}")

# 3. 用训练-验证集全部数据训练最终模型
final_model = KNeighborsClassifier(n_neighbors=best_k)
final_model.fit(X_trainval, y_trainval)

# 4. 用测试集评估最终模型
test_score = final_model.score(X_test, y_test)
print(f"Test set accuracy: {test_score:.3f}")

网格搜索

GridSearchCV 是 scikit-learn 提供的一个用于超参数调优的工具,它结合了 网格搜索(Grid Search)交叉验证(Cross-Validation),能帮你自动搜索给定参数网格中表现最好的超参数组合。

你给定一组超参数的所有可能组合;

GridSearchCV 会对每种组合进行训练和交叉验证,计算性能指标;

最终返回表现最好的参数组合

python 复制代码
from sklearn.datasets import load_iris
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV

# 加载数据
iris = load_iris()
X, y = iris.data, iris.target

# 定义模型
knn = KNeighborsClassifier()

# 定义超参数网格
param_grid = {
    'n_neighbors': [1, 3, 5, 7, 9],
    'weights': ['uniform', 'distance'],  # 权重参数
    'metric': ['euclidean', 'manhattan']  # 距离度量方式
}

# 定义 GridSearchCV 对象,cv=5 表示5折交叉验证
grid_search = GridSearchCV(knn, param_grid, cv=5, scoring='accuracy')

# 执行搜索
grid_search.fit(X, y)

# 输出最佳参数和对应得分
print("Best parameters:", grid_search.best_params_)
print("Best cross-validation accuracy:", grid_search.best_score_)

数据预处理

常见的预处理操作包括:

1.1 缺失值处理(Missing Value Handling)

  • 删除:df.dropna()
  • 填补:
    • 均值填充:df.fillna(df.mean())
    • 中位数填充、众数填充
    • 插值法、KNN填补、模型预测填补等

1.2 类别变量编码(Categorical Encoding)

  • Label Encoding:将类别转换为整数

  • One-Hot Encoding:将每个类别变为一个新的二进制列

    python 复制代码
    from sklearn.preprocessing import OneHotEncoder
    
    enc = OneHotEncoder()
    enc.fit_transform(data)

1.3 特征缩放(Feature Scaling)

  • 标准化(Standardization):将数据转换为均值为 0,标准差为 1

    python 复制代码
    from sklearn.preprocessing import StandardScaler
    
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
  • 归一化(Normalization):将值压缩到 [0, 1] 区间

    python 复制代码
    from sklearn.preprocessing import MinMaxScaler
    
    scaler = MinMaxScaler()
    X_norm = scaler.fit_transform(X)

标准化处理

标准化是对数据进行线性变换,使每一列(特征)变成:

z= (x-u) / σ

其中:

  • x:原始特征值
  • μ:该特征的均值
  • σ:该特征的标准差
python 复制代码
# 导入所需库
from sklearn.linear_model import Ridge                    # 岭回归模型
from sklearn.model_selection import train_test_split      # 数据集划分函数
from sklearn.preprocessing import StandardScaler          # 特征标准化工具
from sklearn.datasets import load_iris

# 加载数据
data = load_iris()
X = data.data
y = data.target

# 假设 X 是特征数据,y 是目标变量(如房价)
# 将数据划分为训练集和测试集(默认比例是 75% 训练 / 25% 测试)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 初始化标准化器,并用训练数据拟合它(只对训练集做拟合,避免数据泄露)
scaler = StandardScaler()
scaler.fit(X_train)

# 对训练数据进行标准化转换
X_train_scaled = scaler.transform(X_train)

# 创建 Ridge 回归模型(默认 alpha=1.0),在标准化后的训练数据上进行拟合
ridge = Ridge()
ridge.fit(X_train_scaled, y_train)

# 对测试集进行相同的标准化转换(使用训练集拟合的 scaler)
X_test_scaled = scaler.transform(X_test)

# 计算模型在标准化后的测试集上的 R² 分数(决定系数)
score = ridge.score(X_test_scaled, y_test)
print("Ridge 模型在测试集上的 R² 分数:", score)

Scikit-Learn API

API 通用设计原则 含义
所有模型都实现 .fit() 训练模型/提取参数
分类/回归模型实现 .predict() 输出预测结果
分类器可以用 .predict_proba() 输出分类概率
.score() 通常用于模型评估 分类是准确率,回归是 R² 分数
所有预处理器(如Scaler)都支持 .fit_transform() 简化数据处理流程

Estimator(估计器)

用于训练模型或变换数据。它包含两个主要方法

方法名 作用
.fit(X, y) 拟合数据(训练)
.fit(X) 用于无监督学习
python 复制代码
from sklearn.linear_model import LinearRegression

model = LinearRegression()
model.fit(X_train, y_train)   # 拟合训练数据

Predictor(预测器)

拥有 .predict() 方法,表示这是一个可以进行预测的模型。

方法名 作用
.predict(X) 返回输入样本的预测值
.score(X, y) 返回预测精度(如 R², accuracy)
python 复制代码
y_pred = model.predict(X_test)
score = model.score(X_test, y_test)

Transformer(变换器)

拥有 .transform() 方法,用于对数据进行转换(如标准化、PCA、特征提取)。

方法名 作用
.fit(X) 估计变换参数
.transform(X) 应用变换
.fit_transform(X) 组合 .fit().transform()
python 复制代码
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

Model Selection 工具(如交叉验证、网格搜索)

工具 用途
train_test_split() 拆分训练集/测试集
cross_val_score() 交叉验证评估模型性能
GridSearchCV() 网格搜索自动调参
python 复制代码
from sklearn.model_selection import cross_val_score

cross_val_score(model, X, y, cv=5)

标准机器学习流程总结

python 复制代码
# 1. 导入库和数据
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

# 2. 加载数据
iris = load_iris()
X, y = iris.data, iris.target

# 3. 拆分数据
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 4. 数据预处理
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# 5. 模型训练与预测
model = LogisticRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)

# 6. 评估模型
print("准确率:", model.score(X_test, y_test))

Scikit-Learn 中的模型和处理器都遵循一套统一的接口:

方法 说明
estimator.fit(X[, y]) 用于模型训练或数据拟合。y 是监督学习才需要的标签(如分类、回归)。
estimator.predict(X) 对输入数据进行预测(适用于监督学习模型,如分类、回归)。
estimator.transform(X) 对数据进行变换(适用于预处理器、降维、特征提取等)。
estimator.fit_transform(X[, y]) .fit().transform(),常用于数据预处理阶段。

Classification(分类)

  • 任务:预测类别标签(离散变量)
  • API:使用 .fit(X, y).predict(X)
  • 常用算法:
    • LogisticRegression()
    • KNeighborsClassifier()
    • SVC()(支持向量机)
    • RandomForestClassifier()

2️⃣ Preprocessing(预处理)

  • 任务:对输入数据进行清洗、标准化、缩放等处理
  • API:主要用 .fit_transform().transform()
  • 常用工具:
    • StandardScaler()(标准化)
    • MinMaxScaler()(归一化)
    • OneHotEncoder()(独热编码)
    • PolynomialFeatures()(生成多项式特征)

3️⃣ Regression(回归)

  • 任务:预测连续数值(如房价、温度等)
  • API:.fit(X, y).predict(X)
  • 常用算法:
    • LinearRegression()
    • Ridge(), Lasso()
    • SVR()
    • RandomForestRegressor()

4️⃣ Dimensionality Reduction(降维)

  • 任务:将高维数据压缩成低维表示(用于可视化、去噪等)
  • API:.fit(X) + .transform(X)
  • 常用算法:
    • PCA()(主成分分析)
    • TruncatedSVD()
    • Isomap(), TSNE()(用于非线性降维)

5️⃣ Clustering(聚类)

  • 任务:将数据自动分组(无监督学习)
  • API:.fit(X) + .predict(X)(部分支持)或 .fit_predict(X)
  • 常用算法:
    • KMeans()
    • DBSCAN()
    • AgglomerativeClustering()

6️⃣ Feature Selection(特征选择)

  • 任务:从原始特征中选出最相关的一部分
  • API:.fit() + .transform()(或 .fit_transform()
  • 常用方法:
    • SelectKBest()
    • RFE()(递归特征消除)
    • SelectFromModel()(基于模型的选择)

7️⃣ Feature Extraction(特征提取)

  • 任务:从原始数据中提取新的有用特征
  • API:.fit_transform(X) 最常用
  • 常用方法:
    • CountVectorizer()(词频向量)
    • TfidfVectorizer()(TF-IDF文本特征)
    • PCA()(也属于降维+特征提取)

pipeline

在机器学习中,管道(Pipelines)是一种将多个预处理步骤和模型训练步骤组合成一个可重复、可序列化的工作流程的方法。使用管道有以下优势:

基本用法

python 复制代码
# 导入Ridge回归模型
from sklearn.linear_model import Ridge
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

# 假设 df 是特征数据,target 是目标变量(房价)
X, y = df, target

# 将数据划分为训练集和测试集,默认比例为 75%训练,25%测试
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 初始化标准化器,拟合训练集数据
scaler = StandardScaler()
scaler.fit(X_train)

# 对训练集进行标准化处理
X_train_scaled = scaler.transform(X_train)

# 使用标准化后的训练数据拟合 Ridge 回归模型
ridge = Ridge().fit(X_train_scaled, y_train)

# 同样对测试集进行标准化(必须用训练集拟合的标准化器)
X_test_scaled = scaler.transform(X_test)

# 计算模型在测试集上的R²得分,评估预测性能
score = ridge.score(X_test_scaled, y_test)
print("Test set R² score:", score)

Pipeline 把标准化和 Ridge 回归组合在一起,完整解释如下:

python 复制代码
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge

# 创建一个管道,依次执行:标准化 -> Ridge回归
pipe = make_pipeline(StandardScaler(), Ridge())

# 用训练数据训练整个管道(先标准化再训练Ridge模型)
pipe.fit(X_train, y_train)

# 在测试集上评估管道模型性能,返回 R² 分数
score = pipe.score(X_test, y_test)
print("Test set R² score:", score)

流程

取名

在使用 Pipeline 创建机器学习工作流时,给每个步骤(Step)自定义名字,而不是像 make_pipeline 那样自动生成名称。这样做的好处是方便后续访问特定步骤、调参和模型解释。

python 复制代码
from sklearn.pipeline import make_pipeline

 knn_pipe = make_pipeline(StandardScaler(), KNeighborsRegressor())
 print(knn_pipe.steps)

输出:
[('standardscaler', StandardScaler()),
 ('kneighborsregressor', KNeighborsRegressor())]

取名

python 复制代码
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge

pipe = Pipeline([
    ('scaler', StandardScaler()),    # 给标准化步骤命名为 'scaler'
    ('ridge', Ridge())               # 给岭回归步骤命名为 'ridge'
])

pipe.fit(X_train, y_train)
print(pipe.score(X_test, y_test))

# 访问某个步骤
print(pipe.named_steps['ridge'])

Pipeline and GridSearchCV

python 复制代码
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsRegressor

# 创建管道:先标准化数据,再使用 KNN 回归
knn_pipe = make_pipeline(StandardScaler(), KNeighborsRegressor())

# 设置参数搜索空间,这里搜索 n_neighbors 从 1 到 9
param_grid = {'kneighborsregressor__n_neighbors': range(1, 10)}

# 使用 GridSearchCV 进行网格搜索,cv=10 表示使用 10 折交叉验证
grid = GridSearchCV(knn_pipe, param_grid, cv=10)

# 在训练数据上拟合模型并搜索最优参数
grid.fit(X_train, y_train)

# 输出最优参数
print(grid.best_params_)

# 使用测试集评估最优模型性能
print(grid.score(X_test, y_test))

make_pipeline(StandardScaler(), KNeighborsRegressor())

  • 自动创建一个两步的流水线:

    1. 对数据进行 标准化(StandardScaler)
    2. 应用 KNN 回归模型

param_grid = {'kneighborsregressor__n_neighbors': range(1, 10)}

  • kneighborsregressor__n_neighbors
    • 注意双下划线 __:用于指定流水线中某一步的参数
    • kneighborsregressorPipeline 自动生成的名字(小写类名)
    • n_neighborsKNeighborsRegressor 的超参数(近邻个数)

GridSearchCV(...)

  • 自动在 param_grid 指定的参数范围中进行交叉验证(cv=10),选择性能最优的组合

grid.best_params_

  • 返回使交叉验证得分最高的参数组合,例如:{'kneighborsregressor__n_neighbors': 5}

grid.score(X_test, y_test)

  • 使用最佳模型(含标准化和最优超参数)在测试集上进行性能评估,默认使用 R² 作为评分指标

利用管道(Pipeline)构建一个组合模型:先标准化、再生成多项式特征、最后用 Ridge 回归拟合,并通过 GridSearchCV 同时调节多项式的阶数和 Ridge 的正则化强度(alpha)

python 复制代码
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline

# 加载糖尿病数据集
diabetes = load_diabetes()
X = diabetes.data
y = diabetes.target

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
    X, y, random_state=0
)

# 创建管道:标准化 -> 多项式特征生成 -> Ridge 回归
pipe = make_pipeline(
    StandardScaler(),
    PolynomialFeatures(),
    Ridge()
)

# 设置参数网格:同时搜索多项式阶数和 Ridge 的正则化系数
param_grid = {
    'polynomialfeatures__degree': [1, 2, 3],
    'ridge__alpha': [0.001, 0.01, 0.1, 1, 10, 100]
}

# 网格搜索,使用默认评分标准(R²),可设定 cv=5 等
grid = GridSearchCV(pipe, param_grid=param_grid, cv=5)

# 在训练数据上拟合模型并搜索最佳参数
grid.fit(X_train, y_train)

# 输出最优参数组合
print("Best parameters:", grid.best_params_)

# 输出模型在测试集上的得分
print("Test set R² score:", grid.score(X_test, y_test))

输出:
Best parameters: {'polynomialfeatures__degree': 2, 'ridge__alpha': 10}
Test set R² score: 0.457

编码

有序编码

python 复制代码
import pandas as pd

# 创建一个包含3列数据的 DataFrame
df = pd.DataFrame({
    'boro': ['Manhattan', 'Queens', 'Manhattan', 'Brooklyn', 'Brooklyn', 'Bronx'],
    'salary': [103, 89, 142, 54, 63, 219],
    'vegan': ['No', 'No', 'No', 'Yes', 'Yes', 'No']
})

# 把 'boro' 这一列转换成分类变量后,用 .cat.codes 得到每个类别对应的整数编码
df['boro_ordinal'] = df.boro.astype("category").cat.codes

# 显示结果
df
index boro salary vegan boro_ordinal
0 Manhattan 103 No 2
1 Queens 89 No 3
2 Manhattan 142 No 2
3 Brooklyn 54 Yes 0
4 Brooklyn 63 Yes 0
5 Bronx 219 No 1

.cat.codes 背后的逻辑

.cat.codes按字母顺序 给每个类别编码的。例如:

复制代码
Sorted categories: ['Brooklyn', 'Bronx', 'Manhattan', 'Queens']
对应编码:       [    0      ,   1    ,     2      ,    3     ]
python 复制代码
 df['boro_ordinal'] = df.boro.astype("category").cat.codes
 df

这种编码是有序编码(ordinal),但很多模型(如线性模型)会把它当作有大小关系的数值来处理。

如果类别本身无序(nominal) ,应使用 pd.get_dummies() 进行 独热编码(One-Hot Encoding) 更合理。

无序编码

python 复制代码
import pandas as pd

df = pd.DataFrame({
    'boro': ['Manhattan', 'Queens', 'Manhattan', 'Brooklyn', 'Brooklyn', 'Bronx'],
    'salary': [103, 89, 142, 54, 63, 219],
    'vegan': ['No', 'No', 'No', 'Yes', 'Yes', 'No']
})

pd.get_dummies(df)

输出:

text 复制代码
   salary  boro_Bronx  boro_Brooklyn  boro_Manhattan  boro_Queens  vegan_No  vegan_Yes
0     103           0              0               1            0         1          0
1      89           0              0               0            1         1          0
2     142           0              0               1            0         1          0
3      54           0              1               0            0         0          1
4      63           0              1               0            0         0          1
5     219           1              0               0            0         1          0
  • 每个分类变量(boro, vegan)被转换成了若干个 二元变量(0或1)
  • 对于 boro
    • 原有 4 个类别,生成了 4 个变量:boro_Bronx, boro_Brooklyn, boro_Manhattan, boro_Queens
  • 对于 vegan
    • 生成了两个变量:vegan_No, vegan_Yes

每一行中,只有一个 boro 的列值是 1,只有一个 vegan 的列值是 1,其余是 0。

python 复制代码
pd.get_dummies(df, columns=['boro'])

只对 df 中的 boro 列进行独热编码(One-Hot Encoding),而不是自动对所有非数值列都处理。

输出:

text 复制代码
   salary vegan  boro_Bronx  boro_Brooklyn  boro_Manhattan  boro_Queens
0     103    No           0              0               1            0
1      89    No           0              0               0            1
2     142    No           0              0               1            0
3      54   Yes           0              1               0            0
4      63   Yes           0              1               0            0
5     219    No           1              0               0            0

线性模型用于回归问题

线性回归一种用于建模因变量(目标值)与一个或多个自变量(特征)之间线性关系的监督学习算法。其核心假设是目标值可以表示为特征的加权和,加上一个随机误差项。

数学表达式

目标 :通过最小化误差项(如均方误差)来估计参数 β

线性回归的变体

普通最小二乘法(OLS)

原理:通过最小化残差平方和(RSS)来估计参数。


*

岭回归(Ridge Regression)

原理 :在OLS基础上加入L2正则化项,防止过拟合。

*

Lasso回归(Lasso Regression)

原理 :在OLS基础上加入L1正则化项,实现特征选择。

使用Scikit-learn实现线性回归、岭回归和Lasso回归:

python 复制代码
import numpy as np
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from sklearn.datasets import make_regression

# 生成模拟数据
X, y = make_regression(n_samples=1000, n_features=10, noise=0.1, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 1. 普通线性回归
lr = LinearRegression()
lr.fit(X_train, y_train)
y_pred_lr = lr.predict(X_test)
print(f"Linear Regression MSE: {mean_squared_error(y_test, y_pred_lr):.4f}")

# 2. 岭回归(alpha=0.1)
ridge = Ridge(alpha=0.1)
ridge.fit(X_train, y_train)
y_pred_ridge = ridge.predict(X_test)
print(f"Ridge Regression MSE: {mean_squared_error(y_test, y_pred_ridge):.4f}")

# 3. Lasso回归(alpha=0.1)
lasso = Lasso(alpha=0.1)
lasso.fit(X_train, y_train)
y_pred_lasso = lasso.predict(X_test)
print(f"Lasso Regression MSE: {mean_squared_error(y_test, y_pred_lasso):.4f}")

# 查看Lasso选择的特征(非零权重)
selected_features = np.where(lasso.coef_ != 0)[0]
print(f"Selected features by Lasso: {selected_features}")

空值处理

丢弃含有缺失值的列(Dropping Columns),然后用逻辑回归(LogisticRegressionCV)做分类,最后通过交叉验证评估模型性能。

python 复制代码
from sklearn.linear_model import LogisticRegressionCV
from sklearn.model_selection import train_test_split, cross_val_score
import numpy as np

# 1. 数据拆分,保证y类别比例一致
X_train, X_test, y_train, y_test = train_test_split(X_, y, stratify=y)

# 2. 找出训练集中包含NaN的列,nan_columns是布尔数组,True表示该列含NaN
nan_columns = np.any(np.isnan(X_train), axis=0)

# 3. 丢弃含NaN的列,留下没有缺失值的列
X_drop_columns = X_train[:, ~nan_columns]

# 4. 用丢弃后的数据做逻辑回归交叉验证,5折交叉验证中用的LogisticRegressionCV内又设了cv=5
scores = cross_val_score(LogisticRegressionCV(cv=5), X_drop_columns, y_train, cv=10)

# 5. 计算平均得分
np.mean(scores)
  • np.any(np.isnan(X_train), axis=0) 返回的是一个布尔数组,表示每一列是否存在缺失值。
  • 代码中用 X_train[:, ~nan_columns] 是基于列索引过滤掉含有NaN的列。
  • 这种做法是简单粗暴的缺失值处理策略------直接删除包含缺失值的整列特征,可能导致信息丢失,但能避免填充带来的偏差。
  • LogisticRegressionCV(cv=5)cv=5 是交叉验证折数参数
  • cross_val_score(..., cv=10) 外层又用10折交叉验证,内层LogisticRegressionCV 又用了5折,通常这样做效果较好。

均值填充

python 复制代码
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler  # 注意是 StandardScaler,不是 StandardScalar
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
import numpy as np

# 1. 找出训练集中含有 NaN 的列
nan_columns = np.any(np.isnan(X_train), axis=0)

# 2. 删除含 NaN 的列,只保留没有缺失值的特征列
X_drop_columns = X_train[:, ~nan_columns]

# 3. 构造一个pipeline,包含数据标准化和逻辑回归模型
logreg = make_pipeline(StandardScaler(), LogisticRegression())

# 4. 10 折交叉验证计算模型得分
scores = cross_val_score(logreg, X_drop_columns, y_train, cv=10)

# 5. 计算平均交叉验证得分
np.mean(scores)
python 复制代码
from sklearn.pipeline import make_pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
import numpy as np

# 构建流水线:中位数填充 -> 标准化 -> 逻辑回归
mean_pipe = make_pipeline(
    SimpleImputer(strategy='median'),  # 用中位数填充缺失值
    StandardScaler(),                  # 对数据进行标准化
    LogisticRegression()               # 应用逻辑回归模型
)

# 10折交叉验证,评估模型性能
scores = cross_val_score(mean_pipe, X_train, y_train, cv=10)

# 计算并打印平均分数
print(f"Mean cross-validation accuracy: {np.mean(scores):.3f}")

KNN 插补(KNN Imputation) 来填补缺失值

python 复制代码
from sklearn.pipeline import make_pipeline
from sklearn.impute import KNNImputer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
import numpy as np

# 构建一个流水线:KNN 插补 -> 标准化 -> 逻辑回归
knn_pipe = make_pipeline(
    KNNImputer(n_neighbors=5),    # 用 K=5 的邻居平均值填补缺失值
    StandardScaler(),             # 标准化
    LogisticRegression()          # 分类模型
)

# 使用交叉验证来评估模型效果
scores = cross_val_score(knn_pipe, X_train, y_train, cv=10)

# 输出平均交叉验证得分
print(f"Mean cross-validation accuracy: {np.mean(scores):.3f}")
  • KNNImputer() 是 Scikit-learn 中用于填补缺失值的工具。
  • 工作机制是:
    • 对于每个有缺失值的样本,找到K 个最相似 (邻近)且该特征非空的样本;
    • 用这 K 个样本的该特征的平均值来填补缺失。

参数说明:

  • n_neighbors=5:默认找 5 个最近邻。
  • 相似性通过样本中其他非缺失特征计算欧几里得距离。

模型驱动的缺失值填补方法(Model-Driven Imputation)

通过训练一个回归模型来预测缺失值。具体来说是用 IterativeImputer + RandomForestRegressor 来填补缺失值,然后将填补后的数据标准化,再使用逻辑回归模型进行分类任务,最后使用交叉验证来评估模型性能。

python 复制代码
from sklearn.experimental import enable_iterative_imputer  # 启用实验性 IterativeImputer
from sklearn.impute import IterativeImputer
from sklearn.ensemble import RandomForestRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
import numpy as np

# 定义使用随机森林作为预测器的 Iterative Imputer(迭代填补器)
rf_imp = IterativeImputer(estimator=RandomForestRegressor(), random_state=0)

# 构建完整流水线:填补缺失值 -> 标准化 -> 逻辑回归
rf_pipe = make_pipeline(
    rf_imp,
    StandardScaler(),
    LogisticRegression()
)

# 使用交叉验证评估整个流程
scores = cross_val_score(rf_pipe, X_rf_imp, y_train, cv=10)

# 输出平均准确率
print(f"Mean cross-validation accuracy: {np.mean(scores):.3f}")

IterativeImputer 是一种先进的缺失值填补方法,工作方式如下:

  • 对每一个包含缺失值的特征,使用该特征以外的其它特征作为输入,构建一个回归模型来预测缺失值。
  • 然后用预测值填补。
  • 迭代进行,每轮都会更新预测。

默认使用的是 BayesianRidge 回归器,但你可以自定义为其他回归器(如这里的 RandomForestRegressor

注意

X_rf_imp 应该是原始特征数据(X_train),你可能不需要单独构建 X_rf_imp,因为 cross_val_score 会在每一折中应用 fittransform

enable_iterative_imputer 是必须的,否则 IterativeImputer 不能被导入(因为它是 experimental);

填补结果的质量依赖于所选模型(这里是 RandomForestRegressor())。

常见的线性模型

OLS,普通最小二乘

是最常用的线性回归方法,目标是通过最小化预测值与真实值之间的平方误差和,来求解模型参数。

损失函数

python 复制代码
from sklearn.linear_model import LinearRegression
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split

# 生成一个简单的线性回归数据集
X, y = make_regression(n_samples=100, n_features=1, noise=10, random_state=0)

# 拆分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 创建 OLS 线性回归模型并训练
model = LinearRegression()
model.fit(X_train, y_train)

# 模型系数和截距
print("Weight w:", model.coef_)
print("Intercept b:", model.intercept_)

# 模型评分(R²)
print("R^2 on test set:", model.score(X_test, y_test))

岭回归

Ridge Regression(岭回归) 是一种对普通最小二乘法(OLS)的改进,通过L2 正则化来解决过拟合问题,特别适用于多重共线性或高维数据场景。

意义

  • 防止模型参数过大,抑制过拟合
  • 特别适用于特征之间存在**共线性(高度相关)**的情况
  • 与 Lasso 不同,Ridge 不会让参数变成 0,所以不适合做特征选择
python 复制代码
from sklearn.linear_model import Ridge
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# 加载数据
X, y = load_diabetes(return_X_y=True)

# 拆分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 特征标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 创建岭回归模型并训练
ridge = Ridge(alpha=1.0)
ridge.fit(X_train_scaled, y_train)

# 模型评分
print("R^2 on test set:", ridge.score(X_test_scaled, y_test))

为什么正则化

  • 限制模型复杂度(防止过拟合)
  • 更好地泛化到新数据
  • 在高维数据中提升稳定性
  • 特征选择(如 Lasso)

R平方系数

R2 是回归模型中常用的性能指标,全称叫 Coefficient of Determination(决定系数),用来衡量模型对数据的拟合程度。

  • R2=1R^2 = 1R2=1:完美拟合,模型把所有点都预测对了。
  • R2=0R^2 = 0R2=0:模型跟直接使用均值预测一样差。
  • R2<0R^2 < 0R2<0:模型比用均值还糟(说明模型在"胡说八道")。
python 复制代码
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
from sklearn.model_selection import train_test_split

# 假设 X 和 y 已经准备好
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

model = LinearRegression()
model.fit(X_train, y_train)

y_pred = model.predict(X_test)

# 计算 R^2 分数
print("R^2 score:", r2_score(y_test, y_pred))

特征处理

特征预处理(包括分类特征和连续特征)

python 复制代码
cat_preprocessing = make_pipeline(
   SimpleImputer(strategy='constant', fill_value='NA'),
   OneHotEncoder(handle_unknown='ignore'))

cont_preprocessing = make_pipeline(
   SimpleImputer(),
   StandardScaler())

preprocess = make_column_transformer(
   (cat_preprocessing, make_column_selector(dtype_include='object')),
   remainder=cont_preprocessing)

X_train, X_test, y_train, y_test = train_test_split(
   X, y, random_state=0)

cross_val_score(
   make_pipeline(preprocess, LinearRegression()),
   X_train, y_train, cv=5)
  • 预处理管道

    • 分类特征(cat_preprocessing):

      python 复制代码
      cat_preprocessing = make_pipeline(
          SimpleImputer(strategy='constant', fill_value='NA'),
          OneHotEncoder(handle_unknown='ignore'))

      SimpleImputer(strategy='constant', fill_value='NA'): 对分类特征的缺失值进行填充,使用常量 'NA' 填充。

      OneHotEncoder(handle_unknown='ignore'): 对分类特征进行独热编码,handle_unknown='ignore' 参数表示在训练集没有出现的类别在测试集中会被忽略(不会引发错误)。

    • 连续特征(cont_preprocessing):

      python 复制代码
      cont_preprocessing = make_pipeline(
          SimpleImputer(),
          StandardScaler())

      SimpleImputer(): 对连续特征的缺失值进行插补,默认采用均值填充。

      StandardScaler(): 标准化连续特征,使其均值为0,标准差为1,确保不同特征的量纲相同,有助于模型收敛。

  • 列转换器(preprocess):

    python 复制代码
    preprocess = make_column_transformer(
        (cat_preprocessing, make_column_selector(dtype_include='object')),
        remainder=cont_preprocessing)

    make_column_transformer: 用于对不同类型的特征应用不同的预处理方式。

    make_column_selector(dtype_include='object'): 选择数据类型为 object 的列(通常是分类特征)应用 cat_preprocessing 流程。

    remainder=cont_preprocessing: 对于其余的特征(即非分类特征),应用 cont_preprocessing 流程。

  • 拆分数据集:

    X_train, X_test, y_train, y_test = train_test_split(
    X, y, random_state=0)

  • 使用 train_test_split 将数据集 X 和目标值 y 拆分为训练集和测试集。random_state=0 用于确保结果可复现。

  • 交叉验证(cross_val_score):

    cross_val_score(
    make_pipeline(preprocess, LinearRegression()),
    X_train, y_train, cv=5)

  • make_pipeline(preprocess, LinearRegression()): 创建一个包含数据预处理和线性回归模型的流水线。

  • cross_val_score(...): 使用交叉验证评估模型的性能,cv=5 表示使用5折交叉验证。

  • 这个过程会对训练数据 X_train 和目标数据 y_train 进行5折交叉验证,输出每一折的得分。

变换目标值

在目标值(y_train)偏态(skewed)分布的情况下,如何通过对目标值进行转换来提升模型表现

当目标变量 y(比如房价)**偏态严重(Skewed)**时,很多回归模型(比如线性回归)假设误差是正态分布的,这种假设会被破坏,导致模型拟合不好。

为了解决这个问题,可以对 y 做对数变换(np.log),建模之后再还原(np.exp)。

普通线性回归

python 复制代码
cross_val_score(make_pipeline(preprocess, LinearRegression()),
                X_train, y_train, cv=5)

用标准线性回归模型对未变换的 y_train 进行预测。

使用 make_pipeline 把预处理流程 preprocessLinearRegression() 串联起来。

cross_val_score 做 5 折交叉验证,返回的是 R² 得分。

对数变换目标值的线性回归

python 复制代码
from sklearn.compose import TransformedTargetRegressor

log_regressor = TransformedTargetRegressor(
    LinearRegression(), func=np.log, inverse_func=np.exp)

cross_val_score(make_pipeline(preprocess, log_regressor),
                X_train, y_train, cv=5)

解释:

  • TransformedTargetRegressor(...) 是一个回归器包装器,作用是:
    • 在模型拟合前,对 y_train 应用 np.log(目标值变换)。
    • 在预测时,用 np.exp 还原回原始空间。
  • 这种方法可以帮助模型更好地拟合偏态目标,比如房价、工资等。

总结

对训练目标值 y_train 应用 np.log

拟合 LinearRegression

预测后将结果用 np.exp 还原成原来的量纲。

用交叉验证评估这个变换后的模型。

对比项 Linear Regression Ridge Regression
正则化 ❌ 无 ✅ L2 正则化(防止过拟合)
抗多重共线性 强,适合特征高度相关
目标变换 支持(如 log) 支持
适用数据 小数据集、特征独立性好 特征数量多、特征相关性强
模型复杂度控制 ✅ α 超参数控制模型复杂度

Triazine数据集

加载 Triazine 数据集

python 复制代码
triazines = fetch_openml('triazines')
triazines.data.shape
  • 你从 OpenML 下载了 Triazine 数据集
  • triazines.data.shape 会显示数据集的 特征数量样本数量

目标变量的分布

python 复制代码
pd.Series(triazines.target).hist()
  • 这行代码绘制了 目标变量的直方图 。它是通过将 triazines.target 转换为 pd.Series 来进行的,目的是观察目标变量的分布。通常用于查看是否有偏态,是否需要对目标变量进行变换等。

数据集分割

python 复制代码
X_train, X_test, y_train, y_test = train_test_split(triazines.data, triazines.target)
  • train_test_split 将数据集分为 训练集测试集
    • X_train, X_test 分别是训练和测试数据的特征。
    • y_train, y_test 分别是训练和测试数据的目标值。

交叉验证:线性回归

python 复制代码
cross_val_score(LinearRegression(), X_train, y_train, cv=5)
  • 使用 线性回归(Linear Regression) 模型,并通过 5折交叉验证 计算模型的 平均得分(R²)
    • cv=5 表示将数据分为 5 份,进行 5 次训练和评估。
    • cross_val_score 返回每一折交叉验证的得分,通常是 ,表示模型的拟合优度。

交叉验证:Ridge 回归

python 复制代码
cross_val_score(Ridge(), X_train, y_train, cv=5)
  • 使用 Ridge 回归(岭回归,带有L2正则化)进行类似的交叉验证。

网格搜索:Ridge 回归

python 复制代码
param_grid = {'alpha': np.logspace(-3, 3, 13)}
grid = GridSearchCV(Ridge(), param_grid, cv=RepeatedKFold(10, 5),
                    return_train_score=True)
grid.fit(X_train, y_train)
  • param_grid 设置了 Ridge 回归的超参数 alpha 的搜索范围。
    • np.logspace(-3, 3, 13) 生成从 10^-3 到 10^3 之间的 13 个对数均匀分布的 alpha 值。
  • GridSearchCV 用于在给定的 param_grid 中搜索最佳的 alpha 值。
    • cv=RepeatedKFold(10, 5) 表示使用 10折交叉验证5次重复,有助于提高交叉验证的稳定性。
    • return_train_score=True 将返回每次训练的得分,可以用来检查过拟合的情况。
  • grid.fit(X_train, y_train) 会执行网格搜索,找到最佳的 alpha 值,并根据该值重新训练模型。

线性分类模型

线性分类模型的核心思想是:

用一个线性函数对样本特征进行加权,并根据加权总和来进行类别划分。

Logistic Regression

可输出预测类别和概率

python 复制代码
from sklearn.linear_model import LogisticRegression

model = LogisticRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
y_prob = model.predict_proba(X_test)  # 输出概率

Multinomial Logistic Regression(多项式逻辑回归)

Multinomial Logistic Regression 是逻辑回归的扩展,用于处理多分类问题 (即标签不是两个类,而是三个及以上)。

它适用于互斥的多个类别,比如:

  • 动物分类(猫、狗、鸟)
  • 学生成绩等级(A, B, C, D)
  • 新闻分类(政治、体育、科技等)

与二分类逻辑回归的区别

特性 二分类逻辑回归 多项式逻辑回归
输出 一个概率值(属于正类的概率) 每个类的概率(所有类概率之和为1)
模型 一个权重向量 + 偏置 每个类一个权重向量 + 偏置(除一个基准类)
预测 sigmoid(w·x) softmax(W·x)
python 复制代码
from sklearn.linear_model import LogisticRegression

model = LogisticRegression(multi_class='multinomial', solver='lbfgs')
model.fit(X_train, y_train)
y_pred = model.predict(X_test)

multi_class='multinomial': 明确使用多项式逻辑回归

solver='lbfgs': 适用于多类softmax的优化器

在实际中处理多类别分类(Multi-Class Classification in Practice )时,我们会使用不同的策略和工具来让模型适用于有 3个及以上标签类别 的问题。

方法 简要说明
OvR (One-vs-Rest) 将多类任务变为多个二分类器(每一类 vs 其他所有类),最后选概率最高的
OvO (One-vs-One) 对于每两个类别都训练一个分类器,共 K(K−1)/2K(K-1)/2 个
Softmax / Multinomial 直接同时考虑所有类,使用 softmax 输出每个类的概率(如多项式逻辑回归)
python 复制代码
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
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, random_state=0)

# 训练多类逻辑回归模型(使用Softmax)
clf = LogisticRegression(multi_class='multinomial', solver='lbfgs')
clf.fit(X_train, y_train)

# 评估模型
print("Accuracy:", clf.score(X_test, y_test))

决策树、随机深林、集成方法

为什么用决策树?(Why Trees?)

  • 非线性建模能力
    决策树能自动处理非线性关系,无需像线性模型那样对数据做复杂变换。
  • 较少的数据预处理需求
    不需要对特征做缩放(scale)或正态化,也不假设特征分布。
  • 行业应用广泛
    决策树在工业界应用多,原因是灵活且效果好。
  • 单棵树的可解释性强
    通过树的结构可以直观理解决策逻辑,但单棵树容易过拟合,稳定性较差。

少预处理(Less Pre-processing)

  • 传统线性模型对输入分布和尺度敏感,决策树不敏感。
  • 不需要对特征进行归一化或标准化处理。
  • 适合混合型特征(数值型 + 类别型)。

决策树分类(Decision Trees for Classification)

  • 利用一系列二元问题(binary questions)将数据不断划分,比如"某个特征是否大于某个阈值?"。
  • 通过递归分割,将样本划分到不同的叶节点,叶节点对应类别预测。
  • 划分标准常用:信息增益(ID3)、信息增益率(C4.5)、基尼指数(CART)。

构建决策树(Building Tree)

  • 从根节点开始,对当前数据集选择最佳特征及阈值进行二分。
  • 递归对子节点继续划分,直到满足停止条件:
    • 叶节点纯度足够高(基本属于同一类别)。
    • 树达到最大深度。
    • 样本数不足以继续分割。

决策树划分准则

决策树选择最佳特征切分节点时,需要度量当前节点的"纯度",用来判断切分前后数据的分布改进情况。常用的两种准则是:

1. 基尼指数(Gini Index)

基尼指数衡量节点的"纯度",越小代表节点越纯(样本越趋向单一类别)。


2. 交叉熵(Cross-Entropy) / 信息熵(Entropy)


二分类的特殊情况

预测与回归树(Regression Trees)


回归树的损失函数(衡量划分效果)

简单实现

python 复制代码
from sklearn.datasets import load_breast_cancer
   
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(cancer.data,cancer.target,
                                                   stratify=cancer.target,
                                                   random_state=0)
from sklearn.tree import DecisionTreeClassifier

tree = DecisionTreeClassifier(max_depth=2)
tree.fit(X_train, y_train)

导入乳腺癌数据集加载函数。这个数据集是常用的二分类数据集,包含了乳腺癌的特征和标签

加载乳腺癌数据集,返回一个字典样式的对象,包含数据特征 cancer.data 和目标标签 cancer.target

使用 train_test_split 将数据随机拆分成训练集和测试集。

参数说明:

  • stratify=cancer.target 表示按标签比例分层抽样,保证训练集和测试集中正负样本比例一致,避免样本分布不均。
  • random_state=0 保证拆分过程可复现。

导入决策树分类器类。

初始化决策树分类器对象:

  • max_depth=2:限制树的最大深度为2,防止过拟合,也让模型更简单、可解释。

用训练数据拟合决策树模型,构建一棵最大深度为2的决策树。

决策树模型的参数调优(超参数搜索)

python 复制代码
from sklearn.model_selection import GridSearchCV

param_grid = {'max_depth': range(1, 7)}  # 设定max_depth参数的候选值范围1到6

grid = GridSearchCV(
    DecisionTreeClassifier(random_state=0),  # 使用决策树分类器
    param_grid=param_grid,                    # 传入调参范围
    cv=10                                    # 10折交叉验证
)

grid.fit(X_train, y_train)  # 在训练集上进行网格搜索和交叉验证

作用和流程:

  • 目标 :寻找决策树的最佳 max_depth(最大深度)参数,避免树过深过拟合或过浅欠拟合。
  • 预剪枝(Pre-pruning):通过限制树的最大深度来防止过拟合,提前停止树的生长。
  • param_grid指定了max_depth的取值范围为1到6,GridSearchCV会分别尝试这些参数。
  • 使用cv=10,表示用10折交叉验证来评估每个max_depth的性能,减少偶然性。
  • grid.fit()会自动训练多个模型,计算每个参数组合的平均验证得分,找到表现最佳的参数。

对另一个参数调优

python 复制代码
from sklearn.model_selection import GridSearchCV

param_grid = {'max_leaf_nodes': range(2, 20)}  # 设置候选叶子节点数量范围,从2到19

grid = GridSearchCV(
    DecisionTreeClassifier(random_state=0),  # 基础模型是决策树分类器
    param_grid=param_grid,                   # 使用我们定义的参数范围
    cv=10                                    # 10折交叉验证评估每组参数的效果
)

grid.fit(X_train, y_train)  # 在训练集上训练所有候选模型并选出最优参数

max_leaf_nodes 的作用:

  • 限制决策树的最大叶子节点数
  • 是一种预剪枝(pre-pruning)策略,可以防止树结构过于复杂导致过拟合。
  • 更小的叶子节点数可能欠拟合,更大的则可能过拟合。

简化决策树

代价复杂度剪枝(Cost-Complexity Pruning) ,是**后剪枝(post-pruning)*策略的一种,主要用于*简化已生成的决策树,防止过拟合。

我们希望找到一棵在训练集上误差小结构又不太复杂 的树。

通过调整 α,我们可以生成一系列子树,从大到小,最终选择在交叉验证上效果最好的。

python 复制代码
import numpy as np
from sklearn.model_selection import GridSearchCV
from sklearn.tree import DecisionTreeClassifier

# 设置 alpha 的搜索范围,从 0 到 0.03,分成 20 个点
param_grid = {
    'ccp_alpha': np.linspace(0., 0.03, 20)
}

# 建立 GridSearchCV 来自动交叉验证选择最优 alpha
grid = GridSearchCV(
    estimator=DecisionTreeClassifier(random_state=0),
    param_grid=param_grid,
    cv=10,  # 10折交叉验证
    scoring='accuracy',  # 评分指标,也可以换成 'f1'、'roc_auc' 等
    return_train_score=True  # 如果你想获取训练集上的得分
)

# 拟合模型,搜索最优 alpha
grid.fit(X_train, y_train)

决策树剪枝的内建方法

Scikit-learn 中决策树剪枝的内建方法 ------ cost_complexity_pruning_path,这是比 GridSearchCV 更高效、结构更清晰的 成本复杂度剪枝(Cost-Complexity Pruning) 实现方式。

python 复制代码
clf = DecisionTreeClassifier(random_state=0)

# 计算剪枝路径(不同 ccp_alpha 下的子树复杂度和误差)
path = clf.cost_complexity_pruning_path(X_train, y_train)

# 提取 alpha 值序列和对应的叶节点总 impurity(误差)
ccp_alphas, impurities = path.ccp_alphas, path.impurities
变量名 含义
ccp_alphas 不同剪枝强度的超参数 α 值序列(从小到大)
impurities 每个 α 值对应的总叶子节点 impurity(训练误差)

小 α :更大、更复杂的树
大 α:更简单、被更多剪枝的树

总结

cost_complexity_pruning_path 是一种快速生成一系列可剪枝子树的方法,不需要手动设定 α 范围。

能帮助你可视化模型复杂度与性能的关系,找到最佳剪枝点(最佳 α)。

GridSearchCV 更直接且计算更快,适合做剪枝调优。

决策树剪枝策略的比较(Best Post-Pruned vs Pre-Pruned)

比较项 Pre-pruning(预剪枝) Post-pruning(后剪枝)
定义 在树构建过程中提前停止分裂 树先完全构建,再进行修剪
控制参数 max_depthmin_samples_split ccp_alpha(成本复杂度剪枝)
优点 快速、节省计算资源 更灵活、更有可能找到最优结构
缺点 可能提前剪掉有用信息 初期构建大树可能过拟合 & 计算耗时
最佳策略 通常用网格搜索调优参数 cost_complexity_pruning_path 找到最佳 α

实证上看:后剪枝(post-pruning)通常表现更好,尤其在数据集比较大、特征多时。

决策树 vs KNN:相似性与差异

维度 决策树 K近邻(KNN)
本质 基于特征空间的划分 基于距离度量找"邻居"
拟合策略 构建树模型,递归分割特征 训练时存储所有数据,预测时才计算
可解释性 高(树路径可解释) 低(难以解释距离关系)
推理速度 快(树结构查询) 慢(每次都要遍历训练集)
是否可外推 ❌ 不能(只能在训练数据的分割区域内预测) ❌ 不能(只能在训练点附近预测)
预测方式 每个叶子里取平均 / 投票 根据最近的 K 个邻居取平均 / 投票

集成模型

用多个不同模型(甚至是相同模型的不同版本),将它们的预测结果进行简单平均(regression)或投票(classification),从而构建一个更强的集成模型。

实现方式

  1. 不同随机种子训练多个模型(如 XGBoost/LightGBM)
    • 同一个模型,多次训练 → 得到略有不同的模型结构 → 模型间误差不完全相关
    • 例:xgb_1 = XGBClassifier(random_state=0), xgb_2 = XGBClassifier(random_state=42) ...
  2. 模型预测平均
    • 分类问题:
      • Hard Voting(投票多数);
      • Soft Voting(平均预测概率,再投票)。
    • 回归问题:直接取预测结果的平均值。

在 Scikit-learn 中使用 VotingClassifier

python 复制代码
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC

# 初始化各基模型
clf1 = LogisticRegression()
clf2 = DecisionTreeClassifier()
clf3 = SVC(probability=True)  # soft voting 必须支持 predict_proba

# 构建 soft voting 的 VotingClassifier
eclf = VotingClassifier(estimators=[
        ('lr', clf1), ('dt', clf2), ('svc', clf3)],
        voting='soft')  # or 'hard'

# 拟合训练
eclf.fit(X_train, y_train)

# 预测准确率
print(eclf.score(X_test, y_test))

Bagging

Bagging(Bootstrap Aggregation)是一种常见的集成学习方法,主要用于 减少模型的方差(variance),提升模型的泛化能力,尤其适用于高方差模型(如决策树)。

Bagging 的核心思想

将训练数据通过 有放回地抽样(bootstrap sampling) 生成多个不同的数据子集,然后在这些子集上训练多个基模型,最后将预测结果进行平均(回归)或投票(分类)。

流程

重复 B 次以下操作

  • 从训练集 D 中有放回抽样,生成子集 D1,D2,...,DBD_1, D_2, ..., D_BD1,D2,...,DB
  • 在每个子集上训练一个弱学习器 f1,f2,...,fBf_1, f_2, ..., f_Bf1,f2,...,fB

最终预测

  • 分类任务:采用多数投票(majority vote)
  • 回归任务:取平均值
python 复制代码
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

# 加载数据
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

# 创建 Bagging 模型,基学习器为决策树
bagging = BaggingClassifier(
    base_estimator=DecisionTreeClassifier(),
    n_estimators=100,   # 子模型数量
    max_samples=0.8,    # 每个子模型使用80%的训练数据
    bootstrap=True,     # 有放回抽样
    random_state=42
)

# 拟合模型并评估
bagging.fit(X_train, y_train)
print("Test Accuracy:", bagging.score(X_test, y_test))

优点

  • 降低方差:多个子模型平均后降低了过拟合风险。
  • 稳定性更强:对数据扰动更不敏感。
  • 并行训练:子模型间相互独立,可加速训练

随机深林

随机森林(Random Forest)是一种基于决策树的集成学习算法,广泛应用于分类和回归问题

核心原理

  1. Bagging(Bootstrap Aggregating)
    • 从原始数据集中有放回地随机抽样,生成多个子数据集(每个子集大小与原始数据集相同)。
    • 每个子数据集独立训练一棵决策树,确保树之间的多样性。
  2. 随机特征选择
    • 在每棵树的节点分裂时,随机选择部分特征(通常为总特征数的平方根或对数)进行最优分裂。
    • 避免单棵树过度依赖某些特征,进一步增强多样性。
  3. 集成预测
    • 分类问题:通过多数投票确定最终类别。
    • 回归问题:取所有树预测值的平均作为最终结果。

调优随机森林(Tuning Random Forests)是提升性能的关键

参数 说明
n_estimators 森林中的树木数量,越多越稳定,建议 > 100
max_features 每棵树分裂节点时考虑的特征数:

Warm Start 示例(逐步增加树的数量)

python 复制代码
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# 数据准备
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 启用 warm_start
train_scores = []
test_scores = []
estimator_range = range(1, 100, 5)
rf = RandomForestClassifier(warm_start=True, random_state=0)

# 逐步增加树的数量
for n in estimator_range:
    rf.set_params(n_estimators=n)
    rf.fit(X_train, y_train)
    train_scores.append(rf.score(X_train, y_train))
    test_scores.append(rf.score(X_test, y_test))

# 可视化模型性能随树数量的变化
plt.plot(estimator_range, train_scores, label='Train')
plt.plot(estimator_range, test_scores, label='Test')
plt.xlabel("Number of Estimators")
plt.ylabel("Accuracy")
plt.title("Warm-starting Random Forest")
plt.legend()
plt.show()

Out-of-Bag (OOB) 估计 与特征

在训练每棵树时,随机森林采用有放回抽样(bootstrap sampling) ,即每棵树只使用原始数据集的约66%进行训练(因为 1 - (1 - 1/n)^n ≈ 1 - 1/e ≈ 0.632)。其余约34%的数据未被这棵树使用,这部分数据称为Out-of-Bag 数据

作用

我们可以:

  • 使用这些 OOB 样本对相应的树做预测
  • 计算模型对未见样本的性能 → 类似交叉验证,但无需额外数据划分
python 复制代码
train_scores = []
test_scores = []
oob_scores = []
feature_range = range(1, 64, 5)  # 尝试不同 max_features 参数

for max_features in feature_range:
    rf = RandomForestClassifier(max_features=max_features, 
                                oob_score=True, 
                                n_estimators=200, 
                                random_state=0)
    rf.fit(X_train, y_train)

    train_scores.append(rf.score(X_train, y_train))      # 训练集准确率
    test_scores.append(rf.score(X_test, y_test))         # 测试集准确率
    oob_scores.append(rf.oob_score_)                     # OOB 分数(更像验证集分数)

基于平均不纯度减少(Mean Decrease in Impurity, MDI)

每次树分裂时选择能最大化信息增益的特征(如 Gini 或 Entropy 减少最多),统计每个特征对不纯度减少的累计贡献作为其"重要性"。

python 复制代码
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
import matplotlib.pyplot as plt

iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, stratify=iris.target, random_state=1)

rf = RandomForestClassifier(random_state=1)
rf.fit(X_train, y_train)

importances = rf.feature_importances_
plt.barh(range(len(importances)), importances)
plt.yticks(range(len(importances)), iris.feature_names)
plt.xlabel("Feature Importance (MDI)")
plt.title("Random Forest Feature Importances")
plt.show()

模型评估

分类任务

指标 公式 适用场景 示例
准确率(Accuracy) (TP +TN ) / (FP +FN +TP +TN) 类别均衡的数据集 垃圾邮件检测中,准确率90%表示10%邮件被误判。
精确率(Precision) (TP ) / ( FP +TP) 关注误报成本高的场景(如疾病筛查) 癌症检测中,精确率95%表示5%健康人被误诊为患者。
召回率(Recall) TP / (FN +TP) 关注漏报成本高的场景(如欺诈交易) 信用卡欺诈检测中,召回率90%表示10%欺诈交易未被识别。
F1值(F1-Score) Prec is ion +Rec all Prec is ion ×Reca**ll 类别不平衡且需平衡精确率和召回率 新闻分类中,F1值综合衡量分类的全面性和准确性。
ROC-AUC 曲线下的面积(0.5~1) 类别不平衡或阈值敏感的场景 贷款违约预测中,AUC=0.8表示模型区分违约和非违约用户的能力较强。
  • 关键术语:
    • TP(True Positive):正类被正确预测为正类。
    • FP(False Positive):负类被错误预测为正类。
    • TN(True Negative):负类被正确预测为负类。
    • FN(False Negative):正类被错误预测为负类。

回归任务

指标 公式 适用场景 示例
均方误差(MSE) n 1∑i =1n (y**iy ^i)2 惩罚大误差更敏感(如房价预测) MSE=1000表示预测值与真实值的平方差平均为1000。
均方根误差(RMSE) MSE 与MSE单位一致,更直观 RMSE=30表示预测误差平均为30(单位与目标变量相同)。
平均绝对误差(MAE) $ \frac{1}{n}\sum_{i=1}^n y_i - \hat{y}_i $
R²分数 1−∑(y**iy ˉ)2∑(y**iy ^i)2 衡量模型解释方差的比例 R²=0.8表示模型解释了80%的目标变量方差。

评价方法

  1. 训练集与测试集划分
    • 简单划分 :70%-80%训练,20%-30%测试(train_test_split)。
    • 分层抽样 :保持类别比例(stratify=y)。
    • 时间序列数据:按时间顺序划分(避免数据泄露)。
  2. 交叉验证(Cross-Validation)
    • K折交叉验证:将数据分为K份,轮流作为验证集,取平均性能。
    • 留一法(LOO):K=N(样本数),计算成本高但无偏差。
    • 优点:减少数据划分随机性,更稳定地评估模型。

二分类评估

Confusion Matrix(混淆矩阵)

复制代码
[[48  5]
 [ 4 86]]

假设:正类为 1(肿瘤为恶性)

预测为 0(良性) 预测为 1(恶性)
实际为 0(良性) TN = 48 FP = 5
实际为 1(恶性) FN = 4 TP = 86

Accuracy(准确率)

复制代码
Accuracy = (TP + TN) / (TP + TN + FP + FN)  
         = (86 + 48) / (86 + 48 + 5 + 4)  
         = 134 / 143 ≈ 0.94

Accuracy 的局限性

你提到的数据集中"90%是负类(多数类)",这在现实中很常见,如:

  • 欺诈检测中"非欺诈"远多于"欺诈"
  • 医疗诊断中"健康"远多于"患病"

这时,即使模型全部预测为负类(non-fraud/健康) ,准确率也能高达 90%,但完全没用!

更可靠的指标:Precision、Recall、F1-Score

Precision(精确率)

预测为正的样本中有多少是真的

Recall(召回率)

所有真正的正类中,有多少被模型找到了:

F1 Score(调和平均)

你可以直接调用:

python 复制代码
from sklearn.metrics import classification_report

print(classification_report(y_test, y_pred))

Mammography Data 分类示例

数据情况

  • 样本数:11183
  • 特征数:6
  • 正负类比例非常不平衡:
    • 负类 (正常):10923
    • 正类 (异常):260
  • 将目标转换为布尔类型,方便模型识别正类:
python 复制代码
y = (y == '1')  # True表示异常,False表示正常

模型训练与准确率

  • 使用支持向量机(SVC)和随机森林(RandomForestClassifier)分别训练:
python 复制代码
from sklearn.datasets import fetch_openml
# mammography https://www.openml.org/d/310
data = fetch_openml('mammography', as_frame=True)
X, y = data.data, data.target

X_train, X_test, y_train, y_test = train_test_split(X, y == '1', random_state=0)

svc = make_pipeline(StandardScaler(), SVC(C=100, gamma=0.1))
svc.fit(X_train, y_train)
svc.score(X_test, y_test)   # 约0.986

rf = RandomForestClassifier()
rf.fit(X_train, y_train)
rf.score(X_test, y_test)    # 约0.989

准确率很高(98.6%和98.9%),但在高度不平衡数据中,准确率容易误导,因为只预测负类也能得到很高的准确率。

关键点总结

  • 不平衡样本使得准确率指标偏高,不足以反映模型检测异常的能力。
  • 正类(异常)的 Recall(召回率)偏低,表明模型漏检了一些异常样本,尤其是SVC的Recall只有0.53,随机森林稍好一点但仍只有0.56。
  • 正类的 Precision较高(0.81,0.90),表示预测为异常的样本中大部分是正确的,但低Recall意味着不能只靠高Precision。
  • 需综合用 Precision、Recall、F1-scoremacro avg 指标来判断模型对少数类的性能。

改进方向

  • 采用 调整分类阈值调参提高正类召回率
  • 试用 重采样方法(欠采样或过采样) 平衡训练数据。
  • 使用 基于成本的学习专门针对不平衡的模型 (如 BalancedRandomForestEasyEnsemble 等)。
  • 通过 ROC曲线、PR曲线 评估模型在不同阈值下的表现。

通过调整随机森林模型的预测阈值对分类性能,尤其是对少数类(True类)召回率和精度的影响

默认阈值(0.5)下预测结果

python 复制代码
y_pred = rf.predict(X_test)
print(classification_report(y_test, y_pred))

输出(重点):

类别 Precision Recall F1-score Support
False 0.99 1.00 0.99 2732
True 0.90 0.56 0.69 64

召回率只有0.56,漏掉了44%的正类(异常)。

准确率很高(0.99),但容易误导。

修改阈值为0.3后的结果

python 复制代码
y_pred = rf.predict_proba(X_test)[:, 1] > 0.30

print(classification_report(y_test, y_pred))

输出(重点):

类别 Precision Recall F1-score Support
False 0.99 0.99 0.99 2732
True 0.71 0.64 0.67 64

召回率提高到了0.64,漏检减少。

Precision 下降到0.71,少数类的假阳性稍有增加。

F1-score 基本持平,略微下降,但整体模型更倾向于捕捉异常。

总结

阈值调整是应对类别不平衡时的常用技巧:降低阈值能增加正类召回率,但通常会牺牲部分精度。

这对于异常检测类问题尤其重要,通常更重视召回率,避免漏检风险。

可以根据业务需求平衡Precision和Recall。

案例

某广告系统,从第三方拿到一部分奢侈品用户数据,并以此为训练集和测试 集, 训练和测试奢侈品用户的分类模型。该模型的分类准确率超过了 95% ,但 在实际 广告投放过程中,该模型还是把大部分广告投放给了非奢侈品用户,这 可能是 什么原因造成?

类别严重不平衡

奢侈品用户(正类)可能只占极少数,比如只有5%,非奢侈品用户(负类)占95%。

在这种情况下,一个"全部预测为负类"的模型就能有95%的准确率,但没有任何实际价值。

评估指标选择不合理

你用的是accuracy (准确率),而在样本极度不平衡时,应该用:

  • Precision(精度)
  • Recall(召回率)
  • F1-score
  • PR曲线/Average Precision
  • 或者至少使用Balanced Accuracy

测试集划分不合理

可能训练和测试数据都来自这部分"奢侈品用户"子集,导致模型只学到了非泛化的、特定的规律。

线上线下样本分布不一致

也称为 "训练-测试分布漂移(data shift)"。模型在测试集上效果好,但在真实线上环境中面对的是不同分布,导致性能急剧下降

多分类评估

python 复制代码
from sklearn.datasets import load_digits
from sklearn.metrics import accuracy_score

digits = load_digits()
# data is between 0 and 16
X_train, X_test, y_train, y_test = train_test_split(
   digits.data / 16., digits.target, random_state=0)

lr = LogisticRegression().fit(X_train, y_train)
pred = lr.predict(X_test)

print("Accuracy: {:.3f}".format(accuracy_score(y_test, pred)))
plot_confusion_matrix(lr, X_test, y_test, cmap='gray_r')
 
print(classification_report(y_test, pred))

阈值类指标

指标 说明 备注
准确率(Accuracy) 正确预测的样本数占总样本数的比例 最常见的分类指标,但在类别不平衡时可能误导
精确率(Precision) 每个类别的:TP / (TP + FP),表示预测为该类的样本中实际为该类的比例 适合关注"预测结果质量"场景
召回率(Recall) 每个类别的:TP / (TP + FN),表示实际为该类的样本中被成功预测的比例 适合关注"漏报问题"场景
F1 值(F1-score) 精确率与召回率的调和平均:F1 = 2 × (Precision × Recall) / (Precision + Recall) 适用于精确率和召回率都重要的场景
宏平均(Macro average) 对每一类单独计算指标后再取平均(不考虑样本量差异) 强调每个类别同等重要
加权平均(Weighted average) 对每一类按其样本数加权平均 更能反映整体模型性能,适合类别不平衡

排序类指标

这些指标基于每类的概率/得分来衡量模型排序质量,适用于模型输出为概率或分数的情况:

指标 说明 备注
ROC AUC (OVR) One-vs-Rest 方法,对每个类别与其他类别分别计算 ROC AUC,再平均 来自 Provost & Domingos (2000),适合衡量模型排序能力
ROC AUC (OVO) One-vs-One 方法,对任意两个类别组合分别计算 ROC AUC,再平均 来自 Hand & Till (2001),适合类别较少的情况
Average Precision (AP) 精确率-召回曲线下的面积,等价于 PR AUC 在稀有类别检测中更敏感(例如异常检测)

回归模型评估

指标名 英文名称 公式或含义 说明
MSE Mean Squared Error MSE = 1/n ∑(yᵢ − ŷᵢ)² 对误差 平方 后求平均,对大误差敏感
RMSE Root Mean Squared Error RMSE = √MSE 与 MSE 类似,但与原始单位一致
MAE Mean Absolute Error MAE = 1/n ∑(yᵢ − ŷᵢ) / yᵢ yᵢ − ŷᵢ
Coefficient of Determination R² = 1 − SS_res / SS_tot 衡量模型解释总变异的能力,值越接近 1 越好
Adjusted R² Adjusted R-squared 调整后的 R²,考虑模型的特征数量 避免过拟合,适合多变量模型比较
MAPE Mean Absolute Percentage Error MAPE = (1/n) ∑(yᵢ − ŷᵢ) / yᵢ (yᵢ − ŷᵢ) / yᵢ
MSLE Mean Squared Logarithmic Error MSE applied to log(y + 1) 适用于预测值跨多个数量级的数据
MedianAE Median Absolute Error 误差的中位数 比 MAE 更稳健,适合异常值存在时

交叉验证评估模型性能

python 复制代码
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier

cancer = load_breast_cancer()
X, y = cancer.data, cancer.target
rf = RandomForestClassifier(random_state=0)

explicit_accuracy = cross_val_score(rf, X, y, scoring="accuracy")
print("explicit accuracy scoring ", explicit_accuracy)

ap = cross_val_score(rf, X, y, scoring="average_precision")
print("average precision", ap)
# 输出:[0.992 0.973 0.999 0.995 0.999]

cross_val_score 中,如果不显式指定 scoring 参数,对于分类任务,默认使用的是 accuracy

上面的结果表示 5 折交叉验证中,随机森林模型的准确率在 93% 到 99.1% 之间,平均表现良好。

average_precision 是衡量排序质量的指标,常用于类别不平衡的问题中,比 accuracy 更敏感。

结果表明该模型不仅准确率高,而且在关注"正类"排序表现(如阳性乳腺癌识别)时也非常出色。

多指标评估

python 复制代码
from sklearn.model_selection import cross_validate
from sklearn.ensemble import RandomForestClassifier
import pandas as pd

cancer = load_breast_cancer()
X, y = cancer.data, cancer.target

res = cross_validate(
    RandomForestClassifier(),
    X, y,
    scoring=["accuracy", "average_precision", "recall_macro"],
    return_train_score=True,
    cv=5
)
pd.DataFrame(res)

scoring=["accuracy", "average_precision", "recall_macro"]

→ 同时评估三个指标:

  • accuracy:准确率
  • average_precision:平均精度,适合处理正负样本不平衡问题
  • recall_macro:宏平均召回率(即对每一类单独计算召回率再取平均,忽略类别不平衡)

return_train_score=True

→ 返回训练集上的指标,有助于评估是否过拟合。

cv=5

→ 进行 5 折交叉验证。

使用 cross_validatecross_val_score 更强大,适用于你希望:

  • 比较多个指标;
  • 同时观察训练集与测试集的表现;
  • 控制模型评估过程的更多细节。

评分器

Scikit-learn 可用的评分器(Scorers)

python 复制代码
from sklearn.metrics import SCORERS
print("\n".join(sorted(SCORERS.keys())))

这将列出 所有可用于模型评估或交叉验证(如 GridSearchCV)中的评分器名称 ,你可以直接通过字符串使用它们,例如 "f1""roc_auc""r2" 等。

评分器(Scorer)接口 vs 指标(Metric)函数接口

  • Metric 函数接口(适合手动评估)

    python 复制代码
    from sklearn.metrics import average_precision_score, balanced_accuracy_score
    
    # 直接用 metric 函数计算得分(需要明确输入为预测结果/概率)
    y_probs = rf.predict_proba(X_test)
    ap_rf = average_precision_score(y_test, y_probs[:, 1])
    
    y_pred = rf.predict(X_test)
    ba_rf = balanced_accuracy_score(y_test, y_pred)
  • Scorer 接口(适合交叉验证、网格搜索)

    python 复制代码
    from sklearn.metrics import get_scorer
    
    ap_scorer = get_scorer('average_precision')
    ap_rf = ap_scorer(rf, X_test, y_test)
    
    ba_scorer = get_scorer('balanced_accuracy')
    ba_rf = ba_scorer(rf, X_test, y_test)

自定义评分器(Custom Scorer)

python 复制代码
def accuracy_scoring(est, X, y):
    return (est.predict(X) == y).mean()

这个函数完全可以作为评分器传给 GridSearchCVcross_validate,关键要求:

  • 参数为 (estimator, X, y)
  • 返回值越大越好(越大表示越优秀)

假设你在 Lasso 模型中,除了 还想监控非零系数个数(模型稀疏性):

python 复制代码
def nonzero(est, X, y):
    return np.sum(est.coef_ != 0)

你可以将多个评分器用于网格搜索:

python 复制代码
param_grid = {'alpha': np.logspace(-5, 0, 10)}
grid = GridSearchCV(
    Lasso(), param_grid,
    return_train_score=True,
    scoring={'r2': 'r2', 'num_nonzero': nonzero},
    refit='r2'
)
grid.fit(X_train, y_train)

注意:

  • refit='r2' 表示最终选择模型以 为依据;
    accuracy scoring ", explicit_accuracy)

ap = cross_val_score(rf, X, y, scoring="average_precision")

print("average precision", ap)

输出:[0.992 0.973 0.999 0.995 0.999]

复制代码
在 `cross_val_score` 中,如果不显式指定 `scoring` 参数,对于分类任务,默认使用的是 `accuracy`。

上面的结果表示 5 折交叉验证中,随机森林模型的准确率在 93% 到 99.1% 之间,平均表现良好。



`average_precision` 是衡量排序质量的指标,常用于**类别不平衡**的问题中,比 accuracy 更敏感。

结果表明该模型不仅准确率高,而且在**关注"正类"排序表现(如阳性乳腺癌识别)时**也非常出色。





**多指标评估**

```python
from sklearn.model_selection import cross_validate
from sklearn.ensemble import RandomForestClassifier
import pandas as pd

cancer = load_breast_cancer()
X, y = cancer.data, cancer.target

res = cross_validate(
    RandomForestClassifier(),
    X, y,
    scoring=["accuracy", "average_precision", "recall_macro"],
    return_train_score=True,
    cv=5
)
pd.DataFrame(res)

scoring=["accuracy", "average_precision", "recall_macro"]

→ 同时评估三个指标:

  • accuracy:准确率
  • average_precision:平均精度,适合处理正负样本不平衡问题
  • recall_macro:宏平均召回率(即对每一类单独计算召回率再取平均,忽略类别不平衡)

return_train_score=True

→ 返回训练集上的指标,有助于评估是否过拟合。

cv=5

→ 进行 5 折交叉验证。

使用 cross_validatecross_val_score 更强大,适用于你希望:

  • 比较多个指标;
  • 同时观察训练集与测试集的表现;
  • 控制模型评估过程的更多细节。

评分器

Scikit-learn 可用的评分器(Scorers)

python 复制代码
from sklearn.metrics import SCORERS
print("\n".join(sorted(SCORERS.keys())))

这将列出 所有可用于模型评估或交叉验证(如 GridSearchCV)中的评分器名称 ,你可以直接通过字符串使用它们,例如 "f1""roc_auc""r2" 等。

评分器(Scorer)接口 vs 指标(Metric)函数接口

  • Metric 函数接口(适合手动评估)

    python 复制代码
    from sklearn.metrics import average_precision_score, balanced_accuracy_score
    
    # 直接用 metric 函数计算得分(需要明确输入为预测结果/概率)
    y_probs = rf.predict_proba(X_test)
    ap_rf = average_precision_score(y_test, y_probs[:, 1])
    
    y_pred = rf.predict(X_test)
    ba_rf = balanced_accuracy_score(y_test, y_pred)
  • Scorer 接口(适合交叉验证、网格搜索)

    python 复制代码
    from sklearn.metrics import get_scorer
    
    ap_scorer = get_scorer('average_precision')
    ap_rf = ap_scorer(rf, X_test, y_test)
    
    ba_scorer = get_scorer('balanced_accuracy')
    ba_rf = ba_scorer(rf, X_test, y_test)

自定义评分器(Custom Scorer)

python 复制代码
def accuracy_scoring(est, X, y):
    return (est.predict(X) == y).mean()

这个函数完全可以作为评分器传给 GridSearchCVcross_validate,关键要求:

  • 参数为 (estimator, X, y)
  • 返回值越大越好(越大表示越优秀)

假设你在 Lasso 模型中,除了 还想监控非零系数个数(模型稀疏性):

python 复制代码
def nonzero(est, X, y):
    return np.sum(est.coef_ != 0)

你可以将多个评分器用于网格搜索:

python 复制代码
param_grid = {'alpha': np.logspace(-5, 0, 10)}
grid = GridSearchCV(
    Lasso(), param_grid,
    return_train_score=True,
    scoring={'r2': 'r2', 'num_nonzero': nonzero},
    refit='r2'
)
grid.fit(X_train, y_train)

注意:

  • refit='r2' 表示最终选择模型以 为依据;
  • 你仍可以分析 num_nonzero 的趋势。
相关推荐
m0_625686555 分钟前
Day39
python
站大爷IP8 分钟前
用 Python 制作简单小游戏教程:手把手教你开发猜数字游戏
python
小高求学之路14 分钟前
MinIO centos 7 离线(内网) 一键部署安装
python·centos·numpy
天才测试猿14 分钟前
2025最新软件测试面试题总结【附文档】
自动化测试·软件测试·python·测试工具·面试·职场和发展·测试用例
Dxy123931021627 分钟前
python如何做实时资讯分析
开发语言·python
IMPYLH31 分钟前
Python 的内置函数 help
笔记·python
小张在编程43 分钟前
Python 深度学习基础:TensorFlow 入门——从张量到神经网络的实战指南
python·深度学习·tensorflow
胖墩会武术43 分钟前
【PyTorch项目实战】CycleGAN:无需成对训练样本,支持跨领域图像风格迁移
人工智能·pytorch·python
Jooolin1 小时前
【Python】什么?Python 可以用来写 Galgame?
python·游戏·ai编程
里探1 小时前
Django中为api自定义一些装饰器:如参数校验等
python·django·装饰器模式