本文目录导读:

我来为你详细讲解如何使用Scikit-learn计算F1分数,包括多个实际案例。
F1分数基础
F1分数是精确率(Precision)和召回率(Recall)的调和平均数:
- F1 = 2 × (Precision × Recall) / (Precision + Recall)
基础案例:二分类问题
from sklearn.metrics import f1_score, precision_score, recall_score
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
import numpy as np
# 创建示例数据
X, y = make_classification(n_samples=1000, n_features=20,
n_classes=2, random_state=42)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 训练模型
model = LogisticRegression(random_state=42)
model.fit(X_train, y_train)
# 预测
y_pred = model.predict(X_test)
# 计算各个指标
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
print(f"精确率 (Precision): {precision:.3f}")
print(f"召回率 (Recall): {recall:.3f}")
print(f"F1分数: {f1:.3f}")
多分类案例:F1的多种计算方式
from sklearn.metrics import classification_report
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
# 加载鸢尾花数据集(3分类)
iris = load_iris()
X, y = iris.data, iris.target
# 划分数据
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)
# 训练随机森林
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(X_train, y_train)
# 预测
y_pred = rf.predict(X_test)
# 1. 微平均 (Micro) - 计算所有样本的总体F1
f1_micro = f1_score(y_test, y_pred, average='micro')
print(f"微平均F1: {f1_micro:.3f}")
# 2. 宏平均 (Macro) - 计算每个类别的F1然后取平均
f1_macro = f1_score(y_test, y_pred, average='macro')
print(f"宏平均F1: {f1_macro:.3f}")
# 3. 加权平均 (Weighted) - 按样本数量加权
f1_weighted = f1_score(y_test, y_pred, average='weighted')
print(f"加权平均F1: {f1_weighted:.3f}")
# 4. 每个类别的F1分数
f1_per_class = f1_score(y_test, y_pred, average=None)
for i, f1_c in enumerate(f1_per_class):
print(f"类别 {i} 的F1分数: {f1_c:.3f}")
# 5. 完整的分类报告
print("\n完整分类报告:")
print(classification_report(y_test, y_pred, target_names=iris.target_names))
实战案例:不平衡分类问题
from sklearn.metrics import f1_score, make_scorer
from sklearn.model_selection import cross_val_score
from sklearn.svm import SVC
from imblearn.datasets import make_imbalance # 需要安装imbalanced-learn
# 创建不平衡数据集
X, y = make_classification(n_samples=1000, n_classes=2,
weights=[0.9, 0.1], # 90%负类,10%正类
random_state=42)
# 划分数据
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
print(f"训练集正类比例: {y_train.mean():.3f}")
print(f"测试集正类比例: {y_test.mean():.3f}")
# 使用不同模型比较F1分数
models = {
'逻辑回归': LogisticRegression(random_state=42),
'随机森林': RandomForestClassifier(n_estimators=100, random_state=42),
'SVM': SVC(random_state=42)
}
for name, model in models.items():
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
f1 = f1_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
print(f"\n{name}:")
print(f" 精确率: {precision:.3f}")
print(f" 召回率: {recall:.3f}")
print(f" F1分数: {f1:.3f}")
交叉验证中优化F1分数
from sklearn.model_selection import GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
# 创建pipeline
pipeline = Pipeline([
('scaler', StandardScaler()),
('svm', SVC(random_state=42))
])
# 参数网格
param_grid = {
'svm__C': [0.1, 1, 10, 100],
'svm__gamma': [0.01, 0.1, 1, 10],
'svm__kernel': ['rbf', 'linear']
}
# 使用F1分数作为评分标准
f1_scorer = make_scorer(f1_score, average='weighted')
# 网格搜索
grid_search = GridSearchCV(
pipeline,
param_grid,
scoring=f1_scorer,
cv=5,
n_jobs=-1,
verbose=1
)
grid_search.fit(X_train, y_train)
print(f"最佳参数: {grid_search.best_params_}")
print(f"最佳交叉验证F1分数: {grid_search.best_score_:.3f}")
# 测试最佳模型
best_model = grid_search.best_estimator_
y_pred = best_model.predict(X_test)
final_f1 = f1_score(y_test, y_pred)
print(f"测试集F1分数: {final_f1:.3f}")
可视化F1分数与阈值的关系
import matplotlib.pyplot as plt
from sklearn.metrics import precision_recall_curve
from sklearn.calibration import CalibratedClassifierCV
# 使用概率预测
model = LogisticRegression(random_state=42)
model.fit(X_train, y_train)
# 获取预测概率
y_prob = model.predict_proba(X_test)[:, 1]
# 计算不同阈值下的精确率和召回率
precisions, recalls, thresholds = precision_recall_curve(y_test, y_prob)
# 计算不同阈值下的F1分数
f1_scores = 2 * (precisions[:-1] * recalls[:-1]) / (precisions[:-1] + recalls[:-1] + 1e-10)
# 找到最优阈值
best_threshold_idx = np.argmax(f1_scores)
best_threshold = thresholds[best_threshold_idx]
best_f1 = f1_scores[best_threshold_idx]
print(f"最优阈值: {best_threshold:.3f}")
print(f"最优F1分数: {best_f1:.3f}")
# 可视化
plt.figure(figsize=(10, 6))
plt.plot(thresholds, f1_scores, 'b-', label='F1 Score')
plt.axvline(x=best_threshold, color='r', linestyle='--',
label=f'Best Threshold: {best_threshold:.2f}')
plt.xlabel('Threshold')
plt.ylabel('F1 Score')'F1 Score vs Threshold')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
实用技巧总结
def evaluate_model_with_f1(y_true, y_pred, model_name="Model"):
"""
完整的模型评估函数
"""
# 计算各类F1分数
f1_micro = f1_score(y_true, y_pred, average='micro')
f1_macro = f1_score(y_true, y_pred, average='macro')
f1_weighted = f1_score(y_true, y_pred, average='weighted')
# 对于二分类,获取正类的F1
if len(np.unique(y_true)) == 2:
f1_binary = f1_score(y_true, y_pred, average='binary')
print(f"{model_name} - 二分类F1: {f1_binary:.3f}")
print(f"{model_name} - 微平均F1: {f1_micro:.3f}")
print(f"{model_name} - 宏平均F1: {f1_macro:.3f}")
print(f"{model_name} - 加权平均F1: {f1_weighted:.3f}")
return {
'micro': f1_micro,
'macro': f1_macro,
'weighted': f1_weighted
}
# 使用示例
y_true = [0, 1, 2, 0, 1, 2]
y_pred = [0, 1, 1, 0, 2, 2]
evaluate_model_with_f1(y_true, y_pred, "示例模型")
关键要点:
-
选择合适的average参数:
'binary': 二分类默认'micro': 适合不平衡数据'macro': 所有类别同等重要'weighted': 考虑类别分布
-
阈值优化:对于概率预测,可以调整阈值优化F1分数
-
交叉验证:使用F1作为评分标准进行模型选择
-
多分类问题:注意选择合适的average策略
这些案例覆盖了F1分数的常见使用场景,你可以根据具体需求选择合适的方法。