3. 分类问题基础:二分类与多分类,混淆矩阵
3.1 二分类 vs 多分类——问题的本质
做RNA-seq模型评估,先得搞清楚你面对的是什么类型的分类问题。其实就两种:二分类和多分类。
二分类,说白了就是“是”还是“不是”。比如判断一个基因是否差异表达、一个样本是否患病。输出只有两个标签,通常记作正类(Positive)和负类(Negative)。我刚开始接触RNA-seq数据分析时,觉得二分类太简单了,不就是0和1吗?但后来发现,真正麻烦的往往就是这种问题——类别极度不平衡。
多分类呢,是三个或更多互斥的类别。比如把肿瘤样本分成亚型A、B、C、D。每个样本只能属于一个亚型,标签之间没有顺序关系。你想想看,如果类别之间有层次关系(比如组织类型→细胞类型),那就是层级分类,不在我们今天讨论范围。多分类和二分类的核心区别在于——评估维度从“2×2”变成了“N×N”。
我个人习惯:拿到一个新问题时,先用二分类思维把最核心的判别任务拎出来。如果本质上是多分类,再扩展。这样不容易被多个类别的混淆矩阵搞晕。
3.2 混淆矩阵:模型预测的“两两对照表”
不管二分类还是多分类,评估模型的第一步都是构建混淆矩阵。它把真实标签和预测标签做交叉统计,形成一个方阵。
先看二分类的混淆矩阵长什么样:
| 真实 \ 预测 | 预测为正类 | 预测为负类 |
|---|---|---|
| 真实为正类 | TP(真正例) | FN(假负例) |
| 真实为负类 | FP(假正例) | TN(真负例) |
TP、FN、FP、TN这四个数字,就是我评估模型的基础原材料。所有后续指标——准确率、精确率、召回率、F1、特异度——全是从它们算出来的。
举个例子,假设我们训练了一个分类器预测基因是否在某种疾病中过表达。测试集有100个样本,其中40个真实过表达(正类),60个真实未过表达(负类)。模型预测出35个正类,其中30个猜对(TP=30),5个是误报(FP=5);剩下的65个预测为负类,其中55个真实负类猜对(TN=55),10个是漏报(FN=10)。你就把这四个数填进矩阵,开始了你的评估之旅。
多分类的混淆矩阵是二分类的扩展:真实类别有K个,预测类别也有K个,矩阵变成K×K。对角线上的值是分类正确的个数,非对角线上的值是误分类到其他类的个数。比如三分类的矩阵:
| 真实 \ 预测 | A | B | C |
|---|---|---|---|
| A | 正确A | A→B | A→C |
| B | B→A | 正确B | B→C |
| C | C→A | C→B | 正确C |
分析多分类矩阵时,我习惯先看每一行的非对角线元素——那些是漏分的样本集中在哪里?再看每一列的非对角线元素——哪些类别容易被误判进来?这个信息能直接指导特征工程。
3.3 从混淆矩阵导出的核心指标
二分类的常用指标,我列个表格对比:
| 指标 | 公式 | 含义 | 什么时候关注 |
|---|---|---|---|
| 准确率 (Accuracy) | (TP+TN)/(TP+TN+FP+FN) | 所有样本中预测正确的比例 | 类别平衡时 |
| 精确率 (Precision) | TP/(TP+FP) | 预测为正类中有多少是真实正类 | FP代价高(如误诊) |
| 召回率 (Recall) | TP/(TP+FN) | 真实正类中有多少被找出来了 | FN代价高(如漏诊) |
| 特异度 (Specificity) | TN/(TN+FP) | 真实负类中有多少被正确排除 | 需要优先保证负类识别 |
| F1分数 | 2 × (P×R)/(P+R) | 精确率与召回率的调和平均 | 需要平衡时 |
我用Python构建混淆矩阵和计算指标,代码一般长这样:
from sklearn.metrics import confusion_matrix, classification_report
y_true = [1, 0, 1, 1, 0, 0, 1, 0]
y_pred = [1, 0, 1, 0, 0, 1, 1, 0]
cm = confusion_matrix(y_true, y_pred)
print("Confusion Matrix:\n", cm)
report = classification_report(y_true, y_pred, target_names=['Negative', 'Positive'])
print(report)
对于多分类,光看全局准确率远远不够。我们需要对每个类别分别计算精确率、召回率,然后做宏平均(macro-average)或加权平均(weighted-average)。宏平均就是把每个类别的指标简单平均,不考虑类别样本数,适合各类别同等重要时。加权平均按各真实类别的样本数加权,更能反映整体性能。
3.4 多分类场景下的混淆矩阵——扩展与聚合
多分类的原始混淆矩阵是K×K的方阵,但我们可以把它“降解”成K个二分类问题:对每个类别,把该类视为正类,其他所有类视为负类。然后计算每个类别的TP、FP、FN、TN,再算指标。这叫“一对多”策略。
比如对类别A:
- 真正类TP_A = 对角线上A对应的值
- 假正类FP_A = 列A中除去对角线的所有值之和(即其他类别被误判为A的个数)
- 假负类FN_A = 行A中除去对角线的所有值之和(即A被误判为其他类别的个数)
- 真负类TN_A = 所有非A行、非A列的元素之和
这样每个类别都能得到一套二分类指标。我个人习惯把这些指标画成雷达图,直观看出哪个类别是薄弱环节。
下面这张SVG图总结了本章的核心概念关系:从预测结果出发,构建混淆矩阵,再引出关键指标,并区分二分类与多分类的评估路径。
3.5 常用API速查
Python的sklearn.metrics包基本覆盖了所有功能。下面是我常用的几个函数:
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
# 二分类示例
y_true = [0,1,0,1,1,0]
y_pred = [0,0,0,1,1,0]
cm = confusion_matrix(y_true, y_pred)
acc = accuracy_score(y_true, y_pred)
prec = precision_score(y_true, y_pred)
rec = recall_score(y_true, y_pred)
f1 = f1_score(y_true, y_pred)
print(f"Confusion Matrix:\n{cm}")
print(f"Acc:{acc:.3f} Prec:{prec:.3f} Rec:{rec:.3f} F1:{f1:.3f}")
# 多分类示例(一对多自动处理)
from sklearn.metrics import classification_report
y_true_multi = [0,1,2,0,1,2]
y_pred_multi = [0,1,1,0,2,2]
report = classification_report(y_true_multi, y_pred_multi, target_names=['Class0','Class1','Class2'])
print(report)
用classification_report一次性输出每个类别的精确率、召回率、F1,以及宏平均和加权平均,非常方便。
pd.crosstab(y_true, y_pred, rownames=['True'], colnames=['Predicted'])
二分类与多分类、混淆矩阵与衍生指标,是整个模型评估的基石。你理解了它们,后续的ROC曲线、交叉验证等高级话题,说白了就是在这个基础上叠加更精细的分析维度。