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 " 通常指的是 将数据集划分为三部分,即把数据分成三份,用于不同的目的,常见的划分包括:
- 训练集(Training set)
- 用来训练模型,让模型学习数据中的规律。
- 验证集(Validation set)
- 用来调参和选择模型,帮助判断模型在未见数据上的表现,防止过拟合。
- 测试集(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:将每个类别变为一个新的二进制列
pythonfrom sklearn.preprocessing import OneHotEncoder enc = OneHotEncoder() enc.fit_transform(data)
1.3 特征缩放(Feature Scaling)
-
标准化(Standardization):将数据转换为均值为 0,标准差为 1
pythonfrom sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_scaled = scaler.fit_transform(X)
-
归一化(Normalization):将值压缩到 [0, 1] 区间
pythonfrom 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())
-
自动创建一个两步的流水线:
- 对数据进行 标准化(StandardScaler)
- 应用 KNN 回归模型
param_grid = {'kneighborsregressor__n_neighbors': range(1, 10)}
kneighborsregressor__n_neighbors
:- 注意双下划线
__
:用于指定流水线中某一步的参数 kneighborsregressor
是Pipeline
自动生成的名字(小写类名)n_neighbors
是KNeighborsRegressor
的超参数(近邻个数)
- 注意双下划线
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
- 原有 4 个类别,生成了 4 个变量:
- 对于
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
会在每一折中应用 fit
和 transform
;
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
):pythoncat_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
):pythoncont_preprocessing = make_pipeline( SimpleImputer(), StandardScaler())
SimpleImputer()
: 对连续特征的缺失值进行插补,默认采用均值填充。StandardScaler()
: 标准化连续特征,使其均值为0,标准差为1,确保不同特征的量纲相同,有助于模型收敛。
-
-
列转换器(
preprocess
):pythonpreprocess = 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
把预处理流程 preprocess
和 LinearRegression()
串联起来。
用 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
返回每一折交叉验证的得分,通常是 R²,表示模型的拟合优度。
交叉验证: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_depth 、min_samples_split 等 |
ccp_alpha (成本复杂度剪枝) |
优点 | 快速、节省计算资源 | 更灵活、更有可能找到最优结构 |
缺点 | 可能提前剪掉有用信息 | 初期构建大树可能过拟合 & 计算耗时 |
最佳策略 | 通常用网格搜索调优参数 | 用 cost_complexity_pruning_path 找到最佳 α |
实证上看:后剪枝(post-pruning)通常表现更好,尤其在数据集比较大、特征多时。
决策树 vs KNN:相似性与差异
维度 | 决策树 | K近邻(KNN) |
---|---|---|
本质 | 基于特征空间的划分 | 基于距离度量找"邻居" |
拟合策略 | 构建树模型,递归分割特征 | 训练时存储所有数据,预测时才计算 |
可解释性 | 高(树路径可解释) | 低(难以解释距离关系) |
推理速度 | 快(树结构查询) | 慢(每次都要遍历训练集) |
是否可外推 | ❌ 不能(只能在训练数据的分割区域内预测) | ❌ 不能(只能在训练点附近预测) |
预测方式 | 每个叶子里取平均 / 投票 | 根据最近的 K 个邻居取平均 / 投票 |
集成模型
用多个不同模型(甚至是相同模型的不同版本),将它们的预测结果进行简单平均(regression)或投票(classification),从而构建一个更强的集成模型。
实现方式
- 不同随机种子训练多个模型(如 XGBoost/LightGBM) :
- 同一个模型,多次训练 → 得到略有不同的模型结构 → 模型间误差不完全相关。
- 例:
xgb_1 = XGBClassifier(random_state=0)
,xgb_2 = XGBClassifier(random_state=42)
...
- 模型预测平均 :
- 分类问题:
- 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)是一种基于决策树的集成学习算法,广泛应用于分类和回归问题
核心原理
- Bagging(Bootstrap Aggregating)
- 从原始数据集中有放回地随机抽样,生成多个子数据集(每个子集大小与原始数据集相同)。
- 每个子数据集独立训练一棵决策树,确保树之间的多样性。
- 随机特征选择
- 在每棵树的节点分裂时,随机选择部分特征(通常为总特征数的平方根或对数)进行最优分裂。
- 避免单棵树过度依赖某些特征,进一步增强多样性。
- 集成预测
- 分类问题:通过多数投票确定最终类别。
- 回归问题:取所有树预测值的平均作为最终结果。
调优随机森林(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) | 2×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**i −y ^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**i −y ˉ)2∑(y**i −y ^i)2 | 衡量模型解释方差的比例 | R²=0.8表示模型解释了80%的目标变量方差。 |
评价方法
- 训练集与测试集划分
- 简单划分 :70%-80%训练,20%-30%测试(
train_test_split
)。 - 分层抽样 :保持类别比例(
stratify=y
)。 - 时间序列数据:按时间顺序划分(避免数据泄露)。
- 简单划分 :70%-80%训练,20%-30%测试(
- 交叉验证(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-score 和 macro avg 指标来判断模型对少数类的性能。
改进方向
- 采用 调整分类阈值 或 调参提高正类召回率。
- 试用 重采样方法(欠采样或过采样) 平衡训练数据。
- 使用 基于成本的学习 或 专门针对不平衡的模型 (如
BalancedRandomForest
、EasyEnsemble
等)。 - 通过 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ᵢ − ŷᵢ |
R² | 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_validate
比 cross_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 函数接口(适合手动评估)
pythonfrom 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 接口(适合交叉验证、网格搜索)
pythonfrom 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()
这个函数完全可以作为评分器传给 GridSearchCV
或 cross_validate
,关键要求:
- 参数为
(estimator, X, y)
- 返回值越大越好(越大表示越优秀)
假设你在 Lasso 模型中,除了 r²
还想监控非零系数个数(模型稀疏性):
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'
表示最终选择模型以r²
为依据;
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_validate
比 cross_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 函数接口(适合手动评估)
pythonfrom 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 接口(适合交叉验证、网格搜索)
pythonfrom 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()
这个函数完全可以作为评分器传给 GridSearchCV
或 cross_validate
,关键要求:
- 参数为
(estimator, X, y)
- 返回值越大越好(越大表示越优秀)
假设你在 Lasso 模型中,除了 r²
还想监控非零系数个数(模型稀疏性):
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'
表示最终选择模型以r²
为依据;- 你仍可以分析
num_nonzero
的趋势。