You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在sklearn的LeaveOneOut+GridSearchCV/cross_validate中用混淆矩阵自定义评分?

解决方案:在LeaveOneOut交叉验证中计算整体灵敏度、特异度等指标

针对你遇到的问题——LeaveOneOut单fold仅1个样本无法计算有效指标,需汇总所有fold结果后计算整体指标,以下分两种场景给出具体实现方案:


一、GridSearchCV调参(用整体LOO指标选择最优参数)

GridSearchCV默认按fold平均评分,但我们需要基于所有LOO样本的预测结果计算单一指标来选参。最可靠的方式是手动遍历所有参数组合,对每个组合单独执行LOO交叉验证,收集全部预测结果后计算整体指标。

示例代码:

import numpy as np
from sklearn.model_selection import LeaveOneOut
from sklearn.svm import SVC  # 替换为你使用的模型

# 定义参数网格和数据集
param_grid = [
    {'C': [0.1, 1, 10], 'kernel': ['linear']},
    {'C': [0.1, 1, 10], 'kernel': ['rbf'], 'gamma': [0.01, 0.1, 1]}
]
X = ...  # 你的特征矩阵
y = ...  # 二分类标签(0/1)

loo = LeaveOneOut()
best_f1 = -1.0
best_params = None
best_metrics = {}

# 遍历每个参数组合
for params in param_grid:
    model = SVC(**params)
    y_pred_list = []
    y_true_list = []
    
    # 执行LeaveOneOut交叉验证
    for train_idx, test_idx in loo.split(X):
        X_train, X_test = X[train_idx], X[test_idx]
        y_train, y_test = y[train_idx], y[test_idx]
        
        model.fit(X_train, y_train)
        y_pred = model.predict(X_test)
        
        y_pred_list.append(y_pred[0])
        y_true_list.append(y_test[0])
    
    # 转换为数组计算混淆矩阵组件
    y_pred = np.array(y_pred_list)
    y_true = np.array(y_true_list)
    
    tp = np.sum((y_true == 1) & (y_pred == 1))
    tn = np.sum((y_true == 0) & (y_pred == 0))
    fp = np.sum((y_true == 0) & (y_pred == 1))
    fn = np.sum((y_true == 1) & (y_pred == 0))
    
    # 计算整体指标
    sensitivity = tp / (tp + fn) if (tp + fn) != 0 else 0.0
    specificity = tn / (tn + fp) if (tn + fp) != 0 else 0.0
    f1 = 2 * tp / (2 * tp + fp + fn) if (2 * tp + fp + fn) != 0 else 0.0
    
    # 更新最优参数
    if f1 > best_f1:
        best_f1 = f1
        best_params = params
        best_metrics = {
            'sensitivity': sensitivity,
            'specificity': specificity,
            'f1': f1
        }

print(f"最优参数组合: {best_params}")
print(f"整体LOO交叉验证指标: {best_metrics}")

二、cross_validate评估单一模型(计算整体LOO指标)

直接收集所有LOO测试样本的预测结果,再计算整体指标,有两种实现方式:

方法1:用cross_val_predict快速获取所有预测

import numpy as np
from sklearn.model_selection import cross_val_predict, LeaveOneOut
from sklearn.svm import SVC

X = ...
y = ...
model = SVC(C=1, kernel='rbf', gamma=0.1)  # 待评估模型

# 获取所有LOO测试样本的预测结果
y_pred = cross_val_predict(model, X, y, cv=LeaveOneOut())

# 计算混淆矩阵组件
tp = np.sum((y == 1) & (y_pred == 1))
tn = np.sum((y == 0) & (y_pred == 0))
fp = np.sum((y == 0) & (y_pred == 1))
fn = np.sum((y == 1) & (y_pred == 0))

# 计算整体指标
sensitivity = tp / (tp + fn) if (tp + fn) != 0 else 0.0
specificity = tn / (tn + fp) if (tn + fp) != 0 else 0.0
f1 = 2 * tp / (2 * tp + fp + fn) if (2 * tp + fp + fn) != 0 else 0.0

print(f"灵敏度: {sensitivity:.4f}")
print(f"特异度: {specificity:.4f}")
print(f"F1分数: {f1:.4f}")

方法2:手动遍历fold(更灵活,适合自定义逻辑)

import numpy as np
from sklearn.model_selection import LeaveOneOut
from sklearn.svm import SVC

X = ...
y = ...
model = SVC(C=1, kernel='rbf', gamma=0.1)

loo = LeaveOneOut()
tp = tn = fp = fn = 0

for train_idx, test_idx in loo.split(X):
    X_train, X_test = X[train_idx], X[test_idx]
    y_train, y_test = y[train_idx], y[test_idx]
    
    model.fit(X_train, y_train)
    y_pred = model.predict(X_test)[0]
    y_true = y_test[0]
    
    if y_true == 1 and y_pred == 1:
        tp += 1
    elif y_true == 0 and y_pred == 0:
        tn += 1
    elif y_true == 0 and y_pred == 1:
        fp += 1
    else:
        fn += 1

# 计算指标
sensitivity = tp / (tp + fn) if (tp + fn) != 0 else 0.0
specificity = tn / (tn + fp) if (tn + fp) != 0 else 0.0
f1 = 2 * tp / (2 * tp + fp + fn) if (2 * tp + fp + fn) != 0 else 0.0

print(f"灵敏度: {sensitivity:.4f}")
print(f"特异度: {specificity:.4f}")
print(f"F1分数: {f1:.4f}")

注意事项

不推荐用全局变量/类累积的方式给GridSearchCV传自定义scorer,因为GridSearchCV会为每个参数组合独立运行交叉验证,共享计数器会导致不同参数组合的结果混淆,手动遍历参数组合的方式更直观可控。

内容的提问来源于stack exchange,提问作者Thao Nguyen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 01:45:54