混淆矩阵简单化-准确率-精确率-召回率和-F1-分数

混淆矩阵简单化:准确率、精确率、召回率和 F1 分数

原文:towardsdatascience.com/confusion-matrix-made-simple-accuracy-precision-recall-f1-score/

当我们处理机器学习中的分类算法,如逻辑回归、K 最近邻、支持向量分类器等时,我们不使用像平均绝对误差(MAE)、均方误差(MSE)或均方根误差(RMSE)这样的评估指标。

相反,我们生成一个混淆矩阵,并根据混淆矩阵生成一个分类报告。

在这篇博客中,我们旨在了解什么是混淆矩阵,如何使用它来计算准确率、精确率、召回率和 F1 分数,以及如何根据数据的特征选择相关的指标。

为了理解混淆矩阵和分类指标,让我们使用威斯康星乳腺癌数据集

此数据集包含 569 行,每一行提供了关于肿瘤的各种特征及其诊断信息,包括它是恶性(癌症)还是良性(非癌症)。

现在,让我们为这些数据构建一个分类模型,根据肿瘤的特征对其进行分类。

我们现在将逻辑回归应用于在此数据集上训练模型。

代码:

import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix, classification_report
import seaborn as sns
import matplotlib.pyplot as plt

# Load the dataset 
column_names = [
    "id", "diagnosis", "radius_mean", "texture_mean", "perimeter_mean", "area_mean", "smoothness_mean",
    "compactness_mean", "concavity_mean", "concave_points_mean", "symmetry_mean", "fractal_dimension_mean",
    "radius_se", "texture_se", "perimeter_se", "area_se", "smoothness_se", "compactness_se", "concavity_se",
    "concave_points_se", "symmetry_se", "fractal_dimension_se", "radius_worst", "texture_worst",
    "perimeter_worst", "area_worst", "smoothness_worst", "compactness_worst", "concavity_worst",
    "concave_points_worst", "symmetry_worst", "fractal_dimension_worst"
]

df = pd.read_csv("C:/wdbc.data", header=None, names=column_names)

# Drop ID column
df = df.drop(columns=["id"])

# Encode target: M=1 (malignant), B=0 (benign)
df["diagnosis"] = df["diagnosis"].map({"M": 1, "B": 0})

# Split features and target
X = df.drop(columns=["diagnosis"])
y = df["diagnosis"]

# Train-test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, stratify=y, random_state=42)

# Scale the features
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# Train logistic regression
model = LogisticRegression(max_iter=10000)
model.fit(X_train, y_train)

# Predict
y_pred = model.predict(X_test)

# Confusion Matrix and Classification Report
conf_matrix = confusion_matrix(y_test, y_pred, labels=[1, 0])  # 1 = Malignant, 0 = Benign

report = classification_report(y_test, y_pred, labels=[1, 0], target_names=["Malignant", "Benign"])

# Display results
print("Confusion Matrix:\n", conf_matrix)
print("\nClassification Report:\n", report)

# Plot Confusion Matrix
sns.heatmap(conf_matrix, annot=True, fmt="d", cmap="Purples", xticklabels=["Malignant", "Benign"], yticklabels=["Malignant", "Benign"])
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.title("Confusion Matrix")
plt.tight_layout()
plt.show() 

在这里,在将逻辑回归应用于数据后,我们生成了一个混淆矩阵和分类报告来评估模型的性能。

首先,让我们了解混淆矩阵。

作者图片

从上述混淆矩阵中

’60’ 代表正确预测的恶性肿瘤,我们将其称为“真阳性”

‘4’ 代表错误预测的良性肿瘤,实际上为恶性肿瘤,我们将其称为“假阴性”

‘1’ 代表错误预测的恶性肿瘤,实际上为良性肿瘤,我们将其称为“假阳性”

‘106’ 代表正确预测的良性肿瘤,我们将其称为“真阴性”

现在,让我们看看我们可以用这些值做什么。

为了做到这一点,我们考虑分类报告。

作者图片

从上述分类报告中,我们可以得出以下结论:

对于恶性:

– 精确率为 0.98,这意味着当模型预测肿瘤为恶性时,它在 98%的时间内是正确的。

– 召回率为 0.94,这意味着模型正确识别了 94%的所有恶性肿瘤。

– F1 分数为 0.96,它平衡了精确率和召回率。

对于良性:

– 精确率为 0.96,这意味着当模型预测肿瘤为良性时,它在 96%的时间内是正确的。

– 召回率为 0.99,这意味着模型正确识别了 99%的所有良性肿瘤。

– F1 分数为 0.98。

从报告中我们可以观察到模型的准确度为 97%。

我们还有宏观平均和加权平均,让我们看看这些是如何计算的。

宏观平均

宏观平均计算了所有指标(精确度、召回率和 F1 分数)在两个类别中的平均值,每个类别给予相同的权重,无论每个类别包含多少样本。

当我们想要了解模型在所有类别中的性能,而忽略类别不平衡时,我们使用宏观平均。

对于这些数据:

图片由作者提供

加权平均

加权平均也计算所有指标的平均值,但给样本量更多的类别更多的权重。

在上面的代码中,我们使用了test_size = 0.3,这意味着我们留出 30%用于测试,这意味着我们使用 569 个数据中的 171 个样本作为测试集。

混淆矩阵和分类报告是基于这个测试集的。

在测试集的 171 个样本中,我们有 64 个恶性肿瘤和 107 个良性肿瘤。

现在,让我们看看如何计算所有指标的加权平均。

图片由作者提供

加权平均在具有类别不平衡数据集时提供了更现实的性能度量。

我们现在对分类报告中的每个术语以及如何计算宏观和加权平均有了了解。

现在,让我们看看混淆矩阵在生成分类报告中的作用。

在分类报告中,我们有不同的指标,如准确度、精确度等,这些指标是使用混淆矩阵中的值计算的。

从混淆矩阵中我们可以看到

真阳性(TP)= 60

假阴性(FN)= 4

假阳性(FP)= 1

真阴性(TN)= 106

现在,让我们使用这些值来计算分类指标。

图片由作者提供

这就是我们如何使用混淆矩阵来计算分类指标。

但为什么我们有四个不同的分类指标而不是一个像准确度这样的指标呢?这是因为不同的指标根据数据的上下文显示了分类器的不同优势和劣势。

现在,让我们回到这里使用的威斯康星乳腺癌数据集。

当我们将逻辑回归模型应用于这些数据时,我们得到了 97%的准确率,这是一个很高的准确率,可能会让我们认为模型是有效的。

但让我们考虑另一个指标,称为“召回率”,这个模型的召回率为 0.94,这意味着在测试集中所有恶性肿瘤中,模型能够正确识别 94%。

在这里,模型错过了 6%的恶性病例。

在现实世界的场景中,主要是医疗保健应用,如癌症检测,如果我们错过一个阳性病例,可能会延误诊断和治疗。

通过这种方式,我们可以理解即使我们的准确率达到 97%,我们也需要根据数据的上下文(考虑不同的指标)进行更深入的分析。

因此,我们现在可以做的,是否应该将召回率的目标值设为 1.0,这意味着所有恶性肿瘤都被正确识别,但如果我们将召回率推至 1.0,那么精确度会下降,因为模型可能会将更多良性肿瘤错误地分类为恶性。

当模型将更多良性肿瘤错误地分类为恶性时,可能会导致不必要的焦虑,并可能需要额外的测试或治疗。

我们应该旨在通过保持“精确度”合理地高来最大化“召回率”。

我们可以通过更改分类器设置的阈值来对样本进行分类。

大多数分类器将阈值设置为 0.5,如果我们将其改为 0.3,那么我们就是在说即使它有 30%的信心,也将其分类为恶性。

现在,让我们使用一个自定义阈值为 0.3。

代码:

# Train logistic regression
model = LogisticRegression(max_iter=10000)
model.fit(X_train, y_train)

# Predict probabilities
y_probs = model.predict_proba(X_test)[:, 1]

# Apply custom threshold
threshold = 0.3
y_pred_custom = (y_probs >= threshold).astype(int)

# Classification Report
report = classification_report(y_test, y_pred_custom, target_names=["Benign", "Malignant"])

# Confusion Matrix
conf_matrix = confusion_matrix(y_test, y_pred_custom, labels=[1, 0])

# Plot Confusion Matrix
plt.figure(figsize=(6, 4))
sns.heatmap(
    conf_matrix,
    annot=True,
    fmt="d",
    cmap="Purples",
    xticklabels=["Malignant", "Benign"],
    yticklabels=["Malignant", "Benign"]
)
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.title("Confusion Matrix (Threshold = 0.3)")
plt.tight_layout()
plt.show()

在这里,我们应用了一个自定义的阈值为 0.3,并生成了一个混淆矩阵和分类报告。

图片由作者提供

分类报告:

图片由作者提供

在这里,准确度提高到 98%,恶性召回率提高到 97%,而精确度保持不变。

我们之前讨论过,如果我们试图最大化召回率,可能会降低精确度,但在这里精确度保持不变,这取决于数据(是否平衡),预处理步骤以及调整阈值。

对于像这样的医疗数据集,通常更倾向于最大化召回率而不是准确度或精确度。

当我们考虑像垃圾邮件检测或欺诈检测这样的数据集时,我们更倾向于精确度,就像在上面的方法中,我们通过相应地调整阈值以及平衡精确度和召回率之间的权衡来尝试提高精确度。

当数据不平衡时,我们使用 f1-score,当我们既希望精确度又希望召回率时,既不能忽略假阳性也不能忽略假阴性。


数据集来源

威斯康星乳腺癌数据集

Wolberg, W., Mangasarian, O., Street, N., & Street, W. (1993). 威斯康星乳腺癌(诊断)[数据集]. UCI 机器学习库. https://doi.org/10.24432/C5DW2B.

该数据集根据Creative Commons Attribution 4.0 International (CC BY 4.0)许可证授权,可用于商业或教育目的,只要适当注明原始来源。


在这里,我们讨论了什么是混淆矩阵以及它是如何用于计算不同的分类指标,如准确度、精确度、召回率和 f1-score。

我们还探讨了何时优先考虑哪个分类指标,以威斯康星癌症数据集为例,我们更倾向于最大化召回率。

希望您发现这篇博客有助于更清晰地理解混淆矩阵和分类度量。

感谢阅读。

posted @ 2026-03-27 10:30  布客飞龙III  阅读(81)  评论(0)    收藏  举报