DAY17 多分类问题

知识点复盘清单【涵盖 6 个核心要点】

1. sklearn 自带的三类数据集

小数据(load_前缀)、大数据(fetch_前缀)、数据集构造(make_前缀)

2. 原生多分类和 OVR 策略

  • 原生多分类模型(如随机森林、带 Softmax 的神经网络)直接支持多类别输出,ROC 图基于类别概率的 “竞争关系” 绘制。
  • OVR(One-vs-Rest)策略将多分类拆分为多个二分类任务,需对标签二值化,其 ROC 图是多个二分类 ROC 的组合,与原生多分类的 ROC 图逻辑存在本质差异。

3. 多分类的工业应用

实际业务中可不为所有类别共用同一阈值,而是为每个类别单独选取 “最优阈值”,以此提升不同类别预测的精准度,适配多场景需求(如电商不同品类的分类决策阈值差异)。

4. 多分类的评估指标

指标核心逻辑 & 适用场景
宏平均各类别地位平等,直接平均各分类指标;适用于 ** 类别重要性一致(如罕见病筛查)** 的场景。
微平均将所有类别视为整体计算指标,结果近似 Accuracy;适用于关注整体表现(如电商多品类推荐)的场景。
加权平均按样本数量给各分类指标加权;适用于 业务导向、类别样本量差异大(如抖音热门类目推荐) 的场景。

5. KS 指标和 MCC 指标

指标取值范围 & 核心用途
KS 指标取值 0 到 1,衡量正、负样本累积分布的最大垂直距离;用于金融风控等领域确定最优决策阈值(如信贷分数线)
MCC 指标取值 - 1 到 1,综合混淆矩阵四元素的相关系数;在类别极不平衡场景(如欺诈识别) 下能客观评估模型,避免 Accuracy、F1-Score 的偏差。

6. 环境和环境变量的本质

  • 环境:指模型运行的外部条件(如硬件资源、系统配置、数据分布)。
  • 环境变量:影响模型性能、稳定性的可调节 / 观测因素(如学习率、batch size、数据噪声等),理解它们有助于优化模型在不同场景的表现。

sklearn.datasets中数据集的分类说明,可分为三类前缀数据集五大任务类数据集,具体信息如下:

一、三类前缀数据集的特点

前缀特点典型用途
load_自带小数据集,安装包内存在,秒加载快速测试算法、入门级练习
fetch_真实大数据集,需从网络下载,首次加载慢模拟真实业务场景、深度调优
make_生成定制化假数据(如特殊形状数据)测试算法对复杂 / 特殊数据的适配性

二、分任务的具体数据集说明

1. 分类任务
  • load_iris(鸢尾花):3 分类,150 条数据,机器学习入门经典多分类数据集
  • load_digits(手写数字):10 分类,1797 条数据,64 维特征,强烈推荐多分类练习
  • load_wine(红酒):3 分类,178 条数据,13 维特征,难度略高于鸢尾花。
  • load_breast_cancer(乳腺癌):2 分类,569 条数据,30 维特征,适合医疗诊断类二分类任务练习
2. 回归任务
  • load_diabetes(糖尿病):预测疾病进展指数,回归任务入门数据集
  • fetch_california_housing(加州房价):预测房价,替代旧版有争议的波士顿房价数据集,更贴合真实房价预测场景
3. 图片 / 人脸识别
  • fetch_lfw_people(LFW 人脸数据集):包含几千张名人人脸图片,用于人脸识别算法测试
  • fetch_olivetti_faces:另一组人脸数据,可用于人脸特征提取、身份识别等研究。
4. 文本分类
  • fetch_20newsgroups:20 个主题的新闻数据,适合自然语言处理(NLP)入门,用于文本分类任务。
5. 生成 “定制化” 数据
  • make_classification:生成任意维度的分类数据,可指定噪声、冗余特征,用于测试分类模型的鲁棒性
  • make_blobs:生成聚成多堆的数据,适合K-Means 聚类算法练习
  • make_moons/make_circles:生成月牙形、同心圆数据,用于 测试非线性模型(如带核函数的 SVM) 的性能。

这些数据集覆盖了机器学习从入门到进阶的各类任务,是学习算法、验证模型效果的重要工具。

多分类问题

第一步:先搞懂「分类」到底是什么?

首先问你:生活里你有没有做过「分类」的事?比如把东西分成几类。你能举 1 个自己经历过的例子吗?(比如把衣服分上衣 / 裤子 / 裙子,或者把零食分甜 / 咸 / 辣)

(等你回答后,继续提问)—— 好,那在你举的这个例子里:

  • 被分类的「东西」是什么?(比如 “衣服”)
  • 你最终分成了「几类」?(比如 “3 类:上衣、裤子、裙子”)
  • 你是根据什么「判断标准」来分的?(比如 “衣服的穿着部位和款式”)

思考一下:如果把这个过程交给计算机做,计算机需要知道哪 3 件事,才能像你一样分类?(提示:对应上面的 3 个问题)

第二步:区分「二分类」和「多分类」

现在我们聚焦:刚才你举的例子里,分类的结果是「2 类」还是「多于 2 类」?(比如 “上衣 / 裤子” 是 2 类,“上衣 / 裤子 / 裙子” 是 3 类)

再问你:你能再举 1 个「只有 2 类」的分类例子吗?(比如 “考试及格 / 不及格”“天气下雨 / 不下雨”)

对比这两个例子:

  • 「二分类」的核心是:最终结果只有____个选项?(让你填空)
  • 「多分类」的核心是:最终结果有____个选项?(填空,提示:≥3)

那你觉得:“把水果分成苹果、香蕉、橙子、葡萄” 属于二分类还是多分类?为什么?

第三步:Python 里的「多分类问题」本质是什么?

现在把场景转到 Python:计算机本身不会 “凭感觉” 分类,它需要我们给它「规则」或「数据」。

提问 1:如果我们想让 Python 帮我们 “把水果分成苹果、香蕉、橙子”(3 类),首先得给 Python 什么?(提示:参考第一步里你分类时用的 “判断标准”,比如苹果是 “红色、圆形、直径 5-8cm”,香蕉是 “黄色、长条形、长度 10-15cm”)—— 这些 “红色、圆形、长度” 之类的信息,我们叫它「特征」,你能理解为什么叫 “特征” 吗?(比如 “特征” 就是一个东西区别于其他东西的 “特点”)

提问 2:假设我们给了 Python100 个水果的数据:每个水果都有「特征」(颜色、形状、大小)和「正确类别」(比如 “这个红色圆形的是苹果”),你觉得 Python 会做什么?(提示:就像你看了很多例子后总结规律,计算机也会从这些数据里 “学” 规律)

提问 3:等 Python “学会” 规律后,我们再给它一个「新水果」的数据(比如 “黄色、长条形、长度 12cm”),Python 的任务是什么?(提示:从 “苹果、香蕉、橙子” 里选一个最匹配的类别)

所以,你能试着用一句话总结:Python 里的多分类问题,本质是让计算机做什么事吗?(提示:结合 “学规律” 和 “选类别”)

第四步:多分类问题的「核心逻辑」拆解

我们再深入一点:比如我们要做 “手写数字识别”(把手写的 0-9 分成 10 类),这是典型的多分类问题。

提问 1:这个问题里,「被分类的东西」是什么?(提示:手写的数字图片)提问 2:「最终要分的类别」有多少个?(提示:0、1、2...9)提问 3:计算机判断的「特征」可能是什么?(提示:图片里的像素点 —— 比如某个位置是黑色还是白色,黑色像素的分布形状等,不用懂像素,只要知道是 “图片的特点” 就行)提问 4:如果计算机学完数据后,遇到一个新的手写图片,它输出 “7”,这意味着什么?(提示:它认为这个图片最匹配 “7” 的特征)

思考:如果计算机输出的不是 “7”,而是 “属于 0 的概率 3%、属于 1 的概率 5%、属于 7 的概率 92%”,你觉得这和直接输出 “7” 有什么关系?(提示:选概率最高的那个类别)

如何用Python实现简单的多分类模型?

我们还是从「生活类比 + 步骤拆解」入手,用提问引导你一步步想清楚 “实现” 的逻辑 —— 毕竟代码只是工具,先懂 “为什么这么做”,再看 “怎么用代码做”,零基础也能跟上~

首先,我们先回顾一个生活场景:假设你想教朋友 “区分苹果、香蕉、橙子”(3 分类),你会按什么步骤教?你可以先试着回答这个问题(比如:先找一些苹果 / 香蕉 / 橙子的样本给朋友看,告诉朋友每个样本的特征;等朋友学会后,拿新水果让他试分)。

(基于这个生活步骤,我们对应到 Python 实现)

第一步:先想清楚「实现多分类的核心步骤」

结合你刚才想的生活步骤,提问 1:用 Python 实现多分类,是不是也应该有类似的 “先后顺序”?你觉得最核心的 3 个步骤是什么?(提示:对应 “准备样本→教规律→试分类”)(如果没想全,我帮你补充:① 准备数据(特征 + 正确类别);② 用模型 “学” 数据里的规律;③ 用学完的模型预测新数据)

提问 2:这 3 个步骤里,你觉得最基础的是哪一步?(答案:准备数据 —— 没有数据,模型就没东西可学,就像教朋友分类却没给水果样本一样)

第二步:选择「简单的工具和数据」(零基础友好)

零基础不用自己找复杂数据、写复杂模型,Python 有现成的 “工具包”(比如sklearn,可以理解为 “分类工具箱”),里面有:

  • 现成的简单多分类数据集(比如「鸢尾花数据集」:包含 3 种鸢尾花,每种花有 4 个特征,比如花瓣长度、花萼长度);
  • 现成的简单模型(比如「逻辑回归」:虽然名字叫 “回归”,但可以做分类,而且逻辑简单,适合入门)。

提问 1:为什么我们不自己造数据,而是用sklearn里的现成数据集?(提示:自己造数据要写很多代码,现成数据集可以直接调用,省时间,而且特征和类别都整理好了,不用我们额外处理)提问 2:为什么选 “逻辑回归” 这种简单模型,而不是复杂模型(比如神经网络)?(提示:复杂模型原理难、代码多,入门先掌握 “流程”,再学复杂模型 —— 就像先学骑自行车,再学开汽车)

第三步:拆解「每一步的代码逻辑」(对应核心步骤)

我们现在把 “3 个核心步骤” 翻译成 “代码动作”,每一步都先想 “要做什么”,再看 “代码怎么写”。

步骤 1:准备数据(从工具箱里拿数据 + 拆分数据)

首先,我们需要把数据分成两部分:

  • 训练集:给模型 “学规律” 用的(就像教朋友时给的 “样本水果”);
  • 测试集:用来检验模型学得好不好(就像教完朋友后,拿新水果让他试分,看他分对多少)。

提问 1:为什么要把数据分成 “训练集” 和 “测试集”?能不能用所有数据都给模型学,然后直接用新数据预测?(提示:如果所有数据都用来学,你不知道模型是不是 “真的学会了”,还是 “死记硬背” 了样本 —— 比如朋友把你给的样本都记下来了,但换个没见过的水果还是不会分)

代码动作(先不用记语法,先懂意义):① 从sklearn里导入鸢尾花数据集;② 把数据拆成 “特征”(比如花瓣长度)和 “类别标签”(比如 “苹果”);③ 把特征和标签分成训练集(比如 80% 的数据)和测试集(20% 的数据)。

提问 2:你觉得 “特征” 和 “类别标签” 在代码里是分开存储的吗?(答案:是 —— 就像你给朋友的样本,要告诉他 “这个水果的特点是 XXX(特征),它是苹果(标签)”,特征和标签要对应,但不能混在一起)

步骤 2:用模型 “学” 规律(训练模型)

我们已经有了训练集(特征 + 标签),现在要让模型从里面 “找规律”(比如 “花瓣长度 > 5cm、花萼长度 > 3cm 的是 A 类鸢尾花”)。

提问 1:模型 “学习” 的过程,在代码里用什么动作表示?(提示:sklearn的模型都有一个统一的方法叫fit(),可以理解为 “开始学习”—— 你把训练集的特征和标签传给它,它就自动找规律了)

提问 2:如果模型学完后,我们想知道它 “学了什么”,需要深入理解原理吗?(答案:不需要!零基础入门,你只需要知道 “调用fit()方法,模型就学会了”—— 就像你不用懂朋友的大脑怎么记规律,只要知道他看完样本就学会了一样)

步骤 3:用模型预测 + 检验效果

模型学完后,我们用 “测试集”(模型没见过的数据)让它预测,然后看它预测对了多少。

提问 1:预测的过程,代码里用什么动作表示?(提示:和fit()对应,模型有predict()方法,意思是 “开始预测”—— 你把测试集的特征传给它,它就输出每个样本的预测类别)

提问 2:怎么判断模型学得好不好?(提示:对比 “模型的预测类别” 和 “测试集的正确类别”,比如测试集里有 20 个样本,模型预测对了 18 个,那正确率就是 90%)

代码动作:① 用模型预测测试集的特征,得到预测标签;② 计算 “预测标签” 和 “正确标签” 的匹配度(正确率)。

第四步:看「简化版代码」(对应上面的步骤)

现在我们把上面的逻辑写成代码,每一行都加通俗解释,你先试着 “猜每行代码的作用”,再看解释:

# 1. 从工具箱里拿出需要的工具(导入库)
from sklearn.datasets import load_iris  # 导入鸢尾花数据集
from sklearn.linear_model import LogisticRegression  # 导入逻辑回归模型
from sklearn.model_selection import train_test_split  # 导入拆分训练集/测试集的工具
from sklearn.metrics import accuracy_score  # 导入计算正确率的工具

# 2. 准备数据:拿数据+拆分
iris = load_iris()  # 加载鸢尾花数据集(相当于“拿到一堆苹果、香蕉、橙子样本”)
X = iris.data  # X = 特征(比如花瓣长度、花萼长度,相当于“水果的特点”)
y = iris.target  # y = 类别标签(0、1、2分别代表3种鸢尾花,相当于“水果的正确名字”)
# 拆分数据:80%训练集,20%测试集(random_state=42是为了结果一致,不用纠结)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 3. 训练模型:让模型学规律
model = LogisticRegression()  # 创建一个逻辑回归模型(相当于“请来一个学生”)
model.fit(X_train, y_train)  # 模型学习训练集(相当于“学生看样本学规律”)

# 4. 预测+检验效果
y_pred = model.predict(X_test)  # 模型预测测试集(相当于“学生试分新水果”)
accuracy = accuracy_score(y_test, y_pred)  # 计算正确率(相当于“看学生分对多少”)
print("模型正确率:", accuracy)  # 打印结果(通常能到90%以上,说明模型学得不错)

现在来做几个小提问,帮你确认理解:

  1. 代码里的Xy分别对应我们之前说的 “特征” 和 “类别标签”,对吗?
  2. model.fit(X_train, y_train)这行代码,是让模型学 “训练集的特征和对应标签”,对吗?
  3. 最后打印的accuracy如果是 0.95,意思是模型在测试集上预测对了 95% 的样本,对吗?

第五步:思考「为什么这样就能实现?」(巩固逻辑)

提问 1:如果我们把 “鸢尾花分类” 换成 “手写数字分类”(0-9,10 分类),核心步骤会变吗?(提示:不会!还是 “准备数据→训练模型→预测→检验”,只是换个数据集和调整模型参数而已)提问 2:如果我们想让模型正确率更高,除了换复杂模型,还能做什么?(提示:优化数据 —— 比如增加更多特征、清洗错误数据,就像教朋友分类时,给更多样本、更准确的特征描述)

现在,你可以试着回答:用 Python 实现简单多分类模型,最关键的 4 个代码动作是什么?(提示:导入工具→加载拆分数据→训练模型→预测 + 算正确率)

第一步:从二分类过渡到多分类的「评估逻辑」

先回忆二分类:二分类里我们用精确率(Precision)、召回率(Recall)这些指标,公式是 TP/(TP+FP) 之类的。那如果是多分类(比如猫、狗、猪三类),要计算 “猫” 类的精确率,你觉得应该怎么处理?(提示:把 “猫” 当成正类,“狗 + 猪” 当成负类,用 One-vs-Rest 的思路)

第二步:理解「宏平均」的逻辑

现在有猫、狗、猪三类,每一类都能算出一个精确率(比如猫 0%、狗 0%、猪 100%)。那怎么把这三个指标综合成一个 “整体指标”?

  • 宏平均的 “宏” 是什么意思?(宏观、每个类别地位平等)
  • 那宏平均的计算方式是?(把每个类别的指标相加,除以类别数)
  • 例子中 (0% + 0% + 100%)/3 ≈ 33.3%,这种计算方式的优缺点是什么?

第三步:理解「加权平均」的逻辑

加权平均里的 “权” 指的是样本数量的占比(比如猫占 1%、狗 1%、猪 98%)。

  • 加权平均的计算方式是?(每个类别的指标 × 其样本权重,再相加)
  • 例子中 0%×0.01 + 0%×0.01 + 100%×0.98 = 98%,这种方式适合什么场景?(比如抖音推荐,热门类别样本多,权重高)

第四步:理解「微平均」的逻辑

微平均直接理解为准确率(Accuracy)。

  • 它是怎么计算的?(把所有类别当成整体,总正确预测数 ÷ 总样本数)
  • 假设 “猫” 类别样本很少,模型全预测错了,但 “狗”“猪” 预测全对,微平均的准确率会很低吗?(不会,因为大类别预测对了,总正确数多)这说明微平均有什么不足?

第五步:理解「多分类模型的选择逻辑」

有些模型(如 SVM、逻辑回归)原生是二分类模型,有些(如随机森林、神经网络)原生支持多分类

  • 为什么 SVM 要做多分类时,需要用 OneVsRestClassifier?(因为它只能分两类,所以把多分类拆成 “每一类 vs 其他类” 的多个二分类任务)
  • 原生多分类模型(如神经网络)的优势是什么?(比如用 softmax 维持多个类别概率的竞争关系)
  • 为什么原生多分类模型和 OneVsRest 的 ROC 图不一致?(OneVsRest 是两两比较,丢失了概率间的竞争信息;原生多分类用 softmax 保留了这种信息)

多分类问题的评估指标中,微平均和宏平均的区别是什么?

要理解多分类问题中微平均(Micro Average)宏平均(Macro Average)的区别,我们可以从计算逻辑、适用场景、对类别不平衡的敏感度三个维度拆解,用通俗的例子一步步梳理~

一、先想「计算逻辑」:它们是怎么算出来的?

假设我们有一个 3 分类问题(猫、狗、猪),每个类别都能算出精确率(Precision)

  • 猫的精确率:猫的TP / (猫的TP + 猫的FP)
  • 狗的精确率:狗的TP / (狗的TP + 狗的FP)
  • 猪的精确率:猪的TP / (猪的TP + 猪的FP)
1. 宏平均(Macro Average):“每个类别平等投票”

计算方式:把每个类别的指标(如精确率)直接相加,再除以类别数。比如例子中猫 0%、狗 0%、猪 100%,宏平均精确率就是 (0% + 0% + 100%) / 3 ≈ 33.3%。逻辑:每个类别不管样本多少,权重完全一样,相当于 “每个类别都有一票,平均决定最终分数”。

2. 微平均(Micro Average):“把所有类别揉成一个整体”

计算方式:先把所有类别的 TP、FP、TN、FN 汇总成 “全局的 TP、FP、TN、FN”,再计算指标。比如全局 TP = 猫 TP + 狗 TP + 猪 TP,全局 FP = 猫 FP + 狗 FP + 猪 FP,然后用 全局TP / (全局TP + 全局FP) 算精确率。逻辑:完全不区分类别,把所有样本当成一个二分类问题(正类 vs 负类)来算,结果和 “整体准确率(Accuracy)” 逻辑一致。

二、再看「适用场景」:什么时候用哪个?

1. 宏平均:“每个类别都很重要,不能偏袒”

适合类别地位平等、需要关注小类别表现的场景,比如:

  • 罕见病筛查:“患病” 类别样本很少,但如果模型因为 “健康人多” 就忽略了患者,宏平均会把分数拉低,避免模型 “偷懒”。
  • 多类别质检:产品的 “瑕疵 A”“瑕疵 B”“无瑕疵” 三类,每类都要严格区分,不能因为某类样本多就放松要求。
2. 微平均:“只要整体表现好,个别类别可以妥协”

适合业务更关注 “整体正确率”,对小类别容错率高的场景,比如:

  • 抖音视频推荐:“舞蹈”“美食”“科普” 三类,“舞蹈” 占 90% 样本,只要 “舞蹈” 推得准,即使 “科普” 偶尔推错,用户整体体验也不错,微平均能反映这种 “抓大放小” 的效果。
  • 电商商品分类:“服饰”“数码”“家居” 三类,“服饰” 订单量极大,微平均更关注整体订单的分类正确率。

三、最后看「对类别不平衡的敏感度」

  • 宏平均完全不 care 样本多少,哪怕某类只有 1 个样本,它的指标也和有 1000 个样本的类别权重一样。所以能暴露模型对小类别的 “偏心”。
  • 微平均被大类别牵着走,如果某类样本占 90%,它的 TP/FP 会主导全局计算,小类别的表现很容易被 “淹没”。

总结:一句话区分

  • 宏平均:类别平等,关注每一类的表现,像 “民主投票”。
  • 微平均:类别抱团,关注整体的表现,像 “少数服从多数”。

可以结合自己的业务场景选:想公平对待每一类→用宏平均;想优先保证整体效果→用微平均~

原生多分类模型和 OneVsRest 模型的本质区别是什么?

要理解原生多分类模型OneVsRest 模型的本质区别,我们可以从处理逻辑、概率关系、适用场景三个维度拆解,用通俗的类比一步步梳理~

一、先想「处理逻辑」:它们是怎么 “看待” 多分类问题的?

假设我们要解决一个 3 分类问题(区分猫、狗、猪):

1. 原生多分类模型:“一站式解决所有类别”

典型代表:随机森林、神经网络(带 softmax 输出层)。逻辑:模型从设计上就支持同时处理多个类别,直接输出 “属于猫、狗、猪的概率”。类比:你请了一个 “全能裁判”,他能直接对比猫、狗、猪的特征,一次性告诉你 “这个样本更像猫(70%)、狗(20%)还是猪(10%)”。

2. OneVsRest 模型:“拆成多个二分类,逐个对比”

典型代表:基于 SVM、逻辑回归的多分类扩展。逻辑:把多分类拆成 k 个二分类问题(k 是类别数),每个二分类器负责 “某一类 vs 其他所有类”。比如:

  • 分类器 1:判断 “是猫 vs 不是猫(狗 / 猪)”
  • 分类器 2:判断 “是狗 vs 不是狗(猫 / 猪)”
  • 分类器 3:判断 “是猪 vs 不是猪(猫 / 狗)”最后,选所有分类器中 “预测为正类概率最高” 的那个类别。类比:你请了 3 个 “专项裁判”,分别负责 “猫 vs 非猫”“狗 vs 非狗”“猪 vs 非猪”,最后看哪个裁判给出的 “是某类” 的概率最高,就选哪个类别。

二、再看「概率输出的本质区别」

1. 原生多分类模型:“概率间有竞争关系”

以神经网络的 softmax 为例,输出的 “猫、狗、猪概率” 满足 “总和为 1”,比如猫 70%、狗 20%、猪 10%—— 这意味着 “模型认为是猫的概率高,就会挤压狗和猪的概率”,是一种“此消彼长” 的竞争关系。

2. OneVsRest 模型:“概率间无直接关联”

每个二分类器的概率是独立计算的,比如:

  • 分类器 1(猫 vs 非猫):输出 “是猫” 的概率 80%
  • 分类器 2(狗 vs 非狗):输出 “是狗” 的概率 70%
  • 分类器 3(猪 vs 非猪):输出 “是猪” 的概率 60%这些概率之间没有 “总和为 1” 的约束,也不存在 “此消彼长” 的关系 ——“是猫的概率高,不影响是狗的概率高低”。

三、最后看「适用场景和优缺点」

1. 原生多分类模型
  • 适合:类别间有内在关联、需要保留概率竞争关系的场景(比如图像分类中,“猫” 和 “狗” 的特征有一定重叠,模型需要直接对比)。
  • 优点:逻辑统一,计算效率可能更高(不用训练 k 个分类器)。
  • 缺点:复杂模型(如深度神经网络)的理解和调参门槛较高。
2. OneVsRest 模型
  • 适合:原生是二分类的模型(如 SVM、逻辑回归),需要扩展到多分类的场景;或类别间相对独立的场景(比如 “苹果、香蕉、汽车”,类别间特征差异大,拆成二分类影响小)。
  • 优点:实现简单,能复用二分类模型的成熟方案。
  • 缺点:需要训练 k 个分类器,计算成本高;丢失了类别间的概率竞争信息,在类别重叠严重时表现可能不如原生多分类模型。

总结:本质区别一句话说清

  • 原生多分类模型:“一站式” 直接处理多类别,概率间有竞争关系,像 “全能裁判直接比出结果”。
  • OneVsRest 模型:“拆分式” 把多分类拆成多个二分类,概率间无直接竞争,像 “多个专项裁判各自打分,选最高分”。

可以根据你的模型类型(原生支持多分类 vs 只有二分类版本)和业务场景(类别是否关联紧密)来选择~

模型类型优点缺点
原生多分类模型1. 逻辑自然,保留类别间 “此消彼长” 的概率竞争关系(如 Softmax 输出总和为 1)2. 仅需训练一个模型,类别数 k 大时计算效率更高3. 适配类别内在关联强的场景(如手写数字 0-9 分类)1. 模型复杂度高,调参 / 理解门槛高(如深度神经网络)2. 对小数据集易过拟合,泛化能力差
OneVsRest 模型1. 实现简单,直接复用成熟二分类方案(如 SVM、逻辑回归)2. 适配类别相对独立的场景(如 “苹果、香蕉、汽车” 分类)3. 可针对每个二分类器单独优化类别不平衡问题1. 需训练 k 个二分类器(k 为类别数),类别数大时计算成本高2. 丢失类别概率的竞争关系,类别重叠场景易误判3. 负类是 “其他所有类的总和”,易被多数类碾压,忽略小类别

聚焦金融风控、医学科研等领域的两个核心评估指标:KS 统计量和 MCC 马修斯相关系数。这两个指标在 “需要精准决策阈值”“类别分布极不均衡” 的场景中具有不可替代的价值,是从模型效果评估到业务落地的关键桥梁。

一、KS 统计量

import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, matthews_corrcoef
from scipy.stats import ks_2samp

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 1. 模拟数据 (还是之前的逻辑)
np.random.seed(42)
n_samples = 10000
y_true = np.array([0]*n_samples + [1]*n_samples)
# 模拟一个效果还不错的模型
y_scores_neg = np.random.normal(0.3, 0.15, n_samples)
y_scores_pos = np.random.normal(0.6, 0.15, n_samples)
y_scores = np.concatenate([y_scores_neg, y_scores_pos])
y_scores = np.clip(y_scores, 0, 1) # 限制在0-1

# --- 核心 1: 计算 MCC ---
# MCC 需要具体的预测类别(0或1),所以我们需要先定一个阈值(比如0.5)
y_pred = (y_scores > 0.5).astype(int)
mcc = matthews_corrcoef(y_true, y_pred)
print(f"MCC (马修斯相关系数): {mcc:.4f} (越接近1越好)")

# --- 核心 2: 计算 KS 统计量 ---
# 方法A: 使用 scipy 直接通过原始分布计算 (最准确)
ks_stat, p_value = ks_2samp(y_scores[y_true==1], y_scores[y_true==0])
print(f"KS 统计量 (scipy计算): {ks_stat:.4f}")

# 方法B: 通过 ROC 曲线数据手动计算 (方便画图)
fpr, tpr, thresholds = roc_curve(y_true, y_scores)
# KS 就是 TPR 和 FPR 差值的最大值
ks_diff = tpr - fpr
max_ks_idx = np.argmax(ks_diff) # 找到最大差值对应的索引
max_ks = ks_diff[max_ks_idx]
best_threshold = thresholds[max_ks_idx]

print(f"KS 统计量 (ROC推导): {max_ks:.4f}, 最佳切分阈值: {best_threshold:.4f}")

# --- 绘图: KS 曲线 ---
plt.figure(figsize=(10, 6))

# 绘制 TPR (覆盖率) 和 FPR (误伤率) 随阈值变化的曲线
plt.plot(thresholds, tpr, label='TPR (正样本累积/捕获率)', color='red')
plt.plot(thresholds, fpr, label='FPR (负样本累积/误伤率)', color='blue')

# 绘制 KS 最大距离线
plt.plot([best_threshold, best_threshold], [fpr[max_ks_idx], tpr[max_ks_idx]], 
         color='green', linestyle='--', lw=3, label=f'KS={max_ks:.3f}')

# 装饰
plt.title('KS 曲线 (K-S Curve) - 金融风控标准', fontsize=14)
plt.xlabel('阈值 (Threshold)', fontsize=12)
plt.ylabel('累积比例', fontsize=12)
plt.xlim([0, 1])
plt.ylim([0, 1.05])
plt.legend(loc='best')
plt.grid(True)
plt.text(best_threshold+0.02, (fpr[max_ks_idx] + tpr[max_ks_idx])/2, 
         '← 最大差距 (KS)', color='green', fontweight='bold')

# 注意:x轴通常是阈值从1到0逆序,或者按分位数排序。
# 这里为了直观,直接展示随阈值变化的走势。
# 可以看到在 KS 线那里,红线和蓝线分得最开。

plt.show()

维度说明
定义衡量正样本累积分布(TPR)与负样本累积分布(FPR)的最大垂直距离,公式为 KS = max(TPR - FPR)
核心用途寻找 “最佳阈值”,例如银行可通过 KS 确定拒贷 / 放贷的分数线,最大化 “抓坏人(坏样本)” 同时最小化 “误伤好人(好样本)”。
与 AUC 对比AUC 仅反映模型整体优劣,KS 则明确 “阈值设在哪里最有效”,是落地业务时的关键决策依据。

KS 统计量的优缺点

维度说明
优点

1. 直接衡量 “最佳阈值”:通过计算 TPR 与 FPR 的最大垂直距离,明确模型区分正负样本的最优决策点(如银行拒贷 / 放贷的分数线),对业务落地指导性强

2. 直观反映分离程度:KS 值越大,说明正负样本的累积分布分离越明显,模型区分能力越强,结果易于理解

3. 适配金融风控等场景:在信贷、欺诈识别等需要精准阈值决策的领域,KS 是核心评估指标之一

缺点

1. 仅关注 “局部最优”:只聚焦 TPR 和 FPR 的最大差距,未考虑整个 ROC 曲线的表现,无法全面反映模型的整体区分能力(相比之下,AUC 更能体现模型的全局性能)

2. 对分箱 / 阈值敏感:分箱方式、阈值选择的差异可能导致 KS 结果波动,存在一定的主观性3. 多分类适配性弱:原生设计用于二分类,若应用于多分类场景(如 OneVsRest 策略),解释性和准确性会大打折扣

二、MCC(马修斯相关系数)

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import label_binarize
from sklearn.multiclass import OneVsRestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_curve, matthews_corrcoef

# --- 1. 设置画图风格 ---
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False   # 用来正常显示负号

# --- 2. 加载数据 & 预处理 ---
iris = datasets.load_iris()
X = iris.data
y = iris.target
class_names = iris.target_names  # ['setosa', 'versicolor', 'virginica']

# 【为了演示效果添加噪声】
# 鸢尾花太简单,不加噪声 MCC 很容易全是 1.0,加噪声为了模拟真实困难场景
random_state = np.random.RandomState(0)
n_samples, n_features = X.shape
X = np.c_[X, random_state.randn(n_samples, 200 * n_features)]

# 【标签二值化】
y = label_binarize(y, classes=[0, 1, 2])
n_classes = y.shape[1]

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)

# --- 3. 训练模型 (改为输出概率) ---
# 使用 OneVsRest 策略训练逻辑回归
classifier = OneVsRestClassifier(LogisticRegression(random_state=0, solver='liblinear'))

# 【核心修改点】使用 predict_proba 获取 [0, 1] 之间的概率
# y_score 的形状是 (n_samples, n_classes),每一列对应一个类别的正例概率
y_score = classifier.fit(X_train, y_train).predict_proba(X_test)

# --- 4. 遍历阈值计算 MCC 并寻找最佳点 ---
plt.figure(figsize=(10, 8))
colors = ['aqua', 'darkorange', 'cornflowerblue']

print("="*20 + " 各类别最佳 MCC 分析 (基于概率) " + "="*20)

for i in range(n_classes):
    # 1. 获取当前类别的真实标签 (0或1) 和 预测概率 (0.0~1.0)
    y_true_cls = y_test[:, i]
    y_prob_cls = y_score[:, i]
    
    # 2. 利用 ROC 得到的阈值 (这些是数据中实际存在的关键切分点)
    # roc_curve 返回的 thresholds 是从大到小排序的
    current_fpr, current_tpr, current_thresholds = roc_curve(y_true_cls, y_prob_cls)
    
    # 3. 阈值筛选
    # roc_curve 通常会在第一个位置加一个 > 1 的阈值 (比如 max_prob + 1),
    # 对于概率来说这是无效的,我们需要过滤掉 > 1.0 的阈值
    valid_thresholds = current_thresholds[current_thresholds <= 1.0]
    
    mcc_list = []
    
    # 4. 遍历这些概率阈值,计算 MCC
    for t in valid_thresholds:
        # 大于等于阈值 t 的预测为 1,否则为 0
        y_pred_cls = (y_prob_cls >= t).astype(int)
        
        # 计算 MCC
        mcc = matthews_corrcoef(y_true_cls, y_pred_cls)
        mcc_list.append(mcc)
    
    # 5. 找到 MCC 最大的位置
    best_idx = np.argmax(mcc_list)
    best_mcc = mcc_list[best_idx]
    best_thresh = valid_thresholds[best_idx]
    
    print(f"【类别 {class_names[i]}】")
    print(f"  最佳 MCC: {best_mcc:.4f}")
    print(f"  最佳阈值: {best_thresh:.4f} (概率值)")
    print("-" * 30)
    
    # 6. 绘制 MCC 随阈值变化的曲线
    plt.plot(valid_thresholds, mcc_list, color=colors[i], lw=2, 
             label=f'{class_names[i]} (Max MCC={best_mcc:.2f})')
    
    # 标记最高点
    plt.scatter(best_thresh, best_mcc, s=100, color=colors[i], edgecolors='k', zorder=10)

# --- 5. 完善图表 ---
plt.xlabel('概率阈值 (Probability Threshold)', fontsize=12)
plt.ylabel('MCC 值', fontsize=12)
plt.title('各类别 MCC 随概率阈值变化曲线', fontsize=16)
plt.legend(loc="lower center") # 图例放下面,避免挡住曲线
plt.grid(True, alpha=0.3)

# 【核心修改点】限制 X 轴为 0 到 1,因为现在是概率
plt.xlim([0.0, 1.0])
plt.ylim([-0.2, 1.05]) 

plt.show()

维度说明
定义预测结果与真实标签的相关系数,综合混淆矩阵全部四个元素(TP、TN、FP、FN) 计算。
特点仅当 TP、TN、FP、FN 都表现优异时才会给出高分,取值范围为 - 1(极差)到 + 1(完美)。
优势对正负样本一视同仁,在类别极不平衡场景(如正负样本比例 1:100)下,Accuracy 会失真、F1-Score 会偏科,只有 MCC 能给出客观评价。
与 F1-Score 对比F1-Score 仅关注正样本(TP),完全忽略真负样本(TN);MCC 则全面考虑四类样本,评估更均衡。

MCC 的优缺点

维度说明
优点1. 综合考虑混淆矩阵全部四个元素(TP、TN、FP、FN),评估全面均衡,避免 F1-Score 仅关注正样本的局限2. 对正负样本一视同仁,在类别极不平衡场景(如 1:100)下,Accuracy 和 F1 易失真,MCC 能给出客观评价3. 取值范围为 - 1(极差)到 + 1(完美),数值直观,便于不同模型间横向对比
缺点1. 计算相对复杂,需先构建完整的混淆矩阵,当数据量极大时,计算过程可能耗时2. 依赖阈值选择,需遍历阈值才能找到最优 MCC,增加了分析流程的复杂度3. 样本量极小时,四个元素的微小波动会对 MCC 结果产生较大影响,稳定性不足
指标本质逻辑取值范围核心应用场景特点总结
KS 指标衡量正样本累积分布(TPR)与负样本累积分布(FPR)的最大垂直距离,公式为 KS = max(TPR - FPR)0~1金融风控(如信贷、欺诈识别)中确定 “最优决策阈值”,例如银行通过 KS 找到既能最大程度抓 “坏人” 又能最小化误伤 “好人” 的分数线聚焦 “局部最优阈值”,对业务落地的决策指导性强;但仅关注二分类,且未考虑整个 ROC 曲线的全局表现
MCC 指标综合混淆矩阵四个元素(TP、TN、FP、FN)的相关系数,本质是预测结果与真实标签的相关性度量-1~1类别极不平衡的场景(如癌症筛查、罕见病识别),或多分类任务中需要均衡评估四类样本表现的场景对正负样本一视同仁,仅当 TP、TN、FP、FN 都表现优异时才给高分;在 Accuracy 和 F1-Score 失真的场景下,能给出客观评价

补充说明

  • KS 的局限性:仅针对二分类任务,且只关注 “TPR 与 FPR 的最大差距”,无法全面反映模型的全局区分能力(需结合 AUC 等指标)。
  • MCC 的优势:是唯一同时考虑 “真阳、真阴、假阳、假阴” 的指标,在正负样本比例 1:100 甚至更悬殊时,仍能客观评估模型性能,避免 Accuracy “被多数类带偏”、F1-Score “偏袒正样本” 的问题。

环境和环境变量的本质

要理解环境环境变量的本质,我们可以从 “模型运行的外部条件” 和 “条件中的可调节因素” 两个维度拆解:

一、环境的本质:模型的 “生存舞台”

环境是模型运行时的外部条件总和,包含以下层次:

  • 硬件环境:比如用 CPU 还是 GPU 训练模型、服务器的内存大小等(类比 “舞台的物理空间和设备”)。
  • 软件环境:比如 Python 版本、依赖库(如 sklearn、TensorFlow)的版本、操作系统等(类比 “舞台的规则和工具”)。
  • 数据环境:比如数据的分布(是否平衡)、数据质量(是否有噪声)、数据规模等(类比 “舞台上的‘剧本’质量”)。

简单来说,环境是模型 “赖以生存” 的外部基础,它决定了模型能在什么条件下运行,也会直接影响模型的性能上限。

二、环境变量的本质:舞台上的 “可调节道具”

环境变量是环境中可以被调整、观测的因素,它们能直接影响模型的表现,包括:

  • 超参数类:比如机器学习模型的学习率、决策树的深度、神经网络的 batch size 等(类比 “舞台上的灯光亮度、道具位置”,调整它们能改变模型的 “表演效果”)。
  • 数据处理类:比如是否对数据归一化、是否做特征筛选、是否增加数据噪声等(类比 “剧本的修改方式”,调整数据能改变模型的学习对象)。
  • 运行配置类:比如训练时的迭代次数、是否开启早停机制、日志输出的详细程度等(类比 “舞台的运行规则”,调整它们能改变模型的训练过程)。

本质上,环境变量是 “环境中可被人为干预的细节”,通过调整这些变量,我们可以优化模型在特定环境下的性能。

三、两者的关系:舞台与道具的协同

  • 环境是基础框架,决定了模型的运行边界(比如在低配电脑上,复杂模型可能跑不起来)。
  • 环境变量是优化手段,在现有环境下,通过调整这些变量(如调大学习率、清洗数据噪声),可以让模型在这个 “舞台” 上表现得更好。

举个例子:

  • 环境:你用 “低配笔记本 + 小样本数据” 训练模型(舞台小且剧本简单)。
  • 环境变量:你调整 “学习率变小 + 增加数据增强”(把灯光调暗、给剧本加些细节),让模型在这个受限环境下也能学得不错。

总结来说,环境是模型的外部运行条件,环境变量是这些条件中可调节的细节,两者共同决定了模型的最终表现

作业:现尝试对sklean自带的红酒数据集完成机器学习的多分类流程

遵循机器学习通用流程:数据集认知 → 数据加载与预处理 → 模型训练(多分类/二分类) → 性能评估(基础指标+进阶指标) → 可视化呈现

核心目标:用红酒数据集(3 类)演示多分类任务的常用评估方法,同时覆盖工程中实用的进阶指标(MCC 阈值选择、KS 曲线)。

模块练手重点思考问题
数据集认知load_*/fetch_*/make_*的区别如果要做 “数据量更大的分类任务”,该选哪种数据集?
数据预处理分层抽样的作用不做分层抽样,若某类样本在测试集占比极低,会导致什么问题?
模型训练随机森林(原生多分类)vs 逻辑回归(二分类)除了随机森林,还有哪些模型原生支持多分类?
性能评估三种 AUC 平均的差异若红酒数据集类别分布不均衡(如 class_0 占 80%),该用哪种 AUC 平均?
可视化ROC 曲线的 “越靠近左上角越好”若某类红酒的 ROC 曲线接近随机猜测线,说明什么问题?
# ================================
# 前置:一次性导入所有依赖库(避免重复导入)
# ================================
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, label_binarize
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import (
    roc_curve, auc, classification_report,
    matthews_corrcoef, roc_auc_score
)
from scipy.stats import ks_2samp

# ================================
# 模块1:数据集认知(知道“用什么、为什么用”)
# 核心:理解sklearn数据集体系,快速定位适合的数据集
# ================================
print("="*80)
print("【模块1:数据集认知 - sklearn数据集体系】")
print("="*80)

# 1. 获取datasets模块下所有数据集相关属性
all_dataset_attrs = dir(datasets)

# 2. 按用途分类数据集(3类核心场景)
load_datasets = [name for name in all_dataset_attrs if name.startswith("load_")]  # 自带小数据集
fetch_datasets = [name for name in all_dataset_attrs if name.startswith("fetch_")]  # 在线大数据集
make_datasets = [name for name in all_dataset_attrs if name.startswith("make_")]  # 构造假数据

# 3. 重点介绍本次练手的数据集
print("1. 三类核心数据集场景:")
print(f"   - 自带小数据集(load_*):无需下载,练手首选 → 例:{load_datasets[:4]}...")
print(f"   - 在线大数据集(fetch_*):需下载,真实项目用 → 例:{fetch_datasets[:3]}...")
print(f"   - 构造假数据(make_*):自定义参数,实验用 → 例:{make_datasets[:3]}...")

print("\n2. 本次练手目标:load_wine(红酒数据集)")
print("   - 特点:13个特征(酒精含量、黄酮类等)、3类红酒标签(0/1/2)")
print("   - 优势:数据量适中(178样本)、无缺失值,适合多分类入门练手")

# ================================
# 模块2:数据加载与预处理(建模“地基”,避免踩坑)
# 核心:让数据符合模型输入要求,保留关键信息
# ================================
print("\n" + "="*80)
print("【模块2:数据加载与预处理】")
print("="*80)

# 1. 加载红酒数据集(Bunch对象,类似字典)
wine = datasets.load_wine()
X = wine.data  # 特征矩阵:(178, 13) → 178个样本,13个特征
y = wine.target  # 标签:(178,) → 0=class_0,1=class_1,2=class_2
feature_names = wine.feature_names  # 特征名称(中文友好,如'alcohol'→酒精含量)
target_names = wine.target_names  # 类别名称:['class_0', 'class_1', 'class_2']

# 2. 探索数据基本信息(练手必做:了解数据结构)
print(f"3. 数据基本信息:")
print(f"   - 样本数:{X.shape[0]},特征数:{X.shape[1]},类别数:{len(np.unique(y))}")
print(f"   - 类别分布:{np.bincount(y)} → 对应类别:{target_names}(分布均衡,适合建模)")
print(f"   - 特征示例:{feature_names[:5]}...(共13个特征)")

# 3. 格式化显示数据(用pandas更直观,练手时快速检查数据)
df = pd.DataFrame(X, columns=feature_names)
df["类别标签"] = y
df["类别名称"] = [target_names[i] for i in y]
print("\n4. 前5行数据预览(特征+标签):")
print(df.head())

# 4. 核心预处理步骤(建模关键!)
print("\n5. 预处理流程(避免模型报错/效果差):")
# 4.1 分层抽样划分训练集/测试集(保证类别分布一致)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y
)
print(f"   - 训练集:{X_train.shape[0]}样本,测试集:{X_test.shape[0]}样本(7:3划分)")

# 4.2 特征标准化(消除尺度影响,逻辑回归必需,随机森林可选但统一处理)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)  # 训练集:拟合+转换(学习均值/方差)
X_test_scaled = scaler.transform(X_test)  # 测试集:只转换(避免数据泄露)
print("   - 特征标准化:完成(消除不同特征的尺度差异)")

# 4.3 标签二值化(适配多分类评估:ROC/MCC需要二分类标签)
n_classes = len(target_names)
y_train_bin = label_binarize(y_train, classes=np.arange(n_classes))  # 训练集:(124, 3)
y_test_bin = label_binarize(y_test, classes=np.arange(n_classes))    # 测试集:(54, 3)
print(f"   - 标签二值化:完成(原标签0→[1,0,0],1→[0,1,0],2→[0,0,1])")

# ================================
# 模块3:模型训练(多分类+二分类,对比学习)
# 核心:掌握不同任务的建模逻辑,适配不同模型特性
# ================================
print("\n" + "="*80)
print("【模块3:模型训练(多分类+二分类)】")
print("="*80)

# 3.1 任务1:多分类(核心任务:区分3类红酒)
print("6. 多分类任务(原生支持,无需拆分):")
# 选用随机森林(原生支持多分类,效果稳定,抗过拟合,适合练手)
rf_multi = RandomForestClassifier(
    n_estimators=50,  # 50棵决策树(平衡效果和速度)
    max_depth=4,      # 限制树深度,避免过拟合
    random_state=42   # 固定随机种子,结果可复现
)
rf_multi.fit(X_train_scaled, y_train)  # 直接用整数标签训练(原生多分类特性)

# 预测输出(用于后续评估)
y_prob_multi = rf_multi.predict_proba(X_test_scaled)  # 每个类别的概率(54, 3)
y_pred_multi = rf_multi.predict(X_test_scaled)        # 预测类别(54,)→ 0/1/2
print(f"   - 模型:随机森林(原生多分类)")
print(f"   - 预测输出:概率矩阵{y_prob_multi.shape},类别矩阵{y_pred_multi.shape}")

# 3.2 任务2:二分类(衍生任务:class_0 vs 其他类,适配KS指标)
print("\n7. 二分类任务(多分类转二分类):")
# 转换标签:class_0为正例(1),其他两类为负例(0)
y_train_binary = (y_train == 0).astype(int)
y_test_binary = (y_test == 0).astype(int)

# 选用逻辑回归(二分类经典模型,概率输出连续,适合KS/MCC评估)
lr_binary = LogisticRegression(
    solver="liblinear",  # 适合小数据集的求解器
    random_state=42
)
lr_binary.fit(X_train_scaled, y_train_binary)

# 预测输出(用于后续评估)
y_prob_binary = lr_binary.predict_proba(X_test_scaled)[:, 1]  # 正例(class_0)的概率
y_pred_binary = lr_binary.predict(X_test_scaled)              # 预测类别(0/1)
print(f"   - 模型:逻辑回归(二分类经典模型)")
print(f"   - 任务:class_0(正例)vs 其他类(负例)")
print(f"   - 预测输出:正例概率{y_prob_binary.shape},类别矩阵{y_pred_binary.shape}")

# ================================
# 模块4:性能评估(基础+进阶,全面了解模型)
# 核心:不仅看“准确率”,更要懂工程实用指标
# ================================
print("\n" + "="*80)
print("【模块4:性能评估(基础指标+进阶指标)】")
print("="*80)

# 4.1 多分类基础指标(分类报告:精确率/召回率/F1)
print("8. 多分类基础指标(分类报告):")
print(classification_report(
    y_test, y_pred_multi,
    target_names=target_names,
    digits=3  # 保留3位小数,更精准
))

# 4.2 多分类进阶指标:AUC(Micro/Macro/Weighted)
print("9. 多分类进阶指标:AUC汇总(衡量区分能力)")
# 计算每个类别的AUC
class_auc = []
for i in range(n_classes):
    fpr, tpr, _ = roc_curve(y_test_bin[:, i], y_prob_multi[:, i])
    class_auc.append(auc(fpr, tpr))

# 计算三种平均AUC(适配不同场景)
micro_auc = roc_auc_score(y_test_bin, y_prob_multi, average="micro", multi_class="ovr")
macro_auc = roc_auc_score(y_test_bin, y_prob_multi, average="macro", multi_class="ovr")
weighted_auc = roc_auc_score(y_test_bin, y_prob_multi, average="weighted", multi_class="ovr")

# 打印结果
for i, cls_name in enumerate(target_names):
    print(f"   - {cls_name} AUC:{class_auc[i]:.3f}")
print(f"   - Micro平均AUC:{micro_auc:.3f}(样本层面整体性能)")
print(f"   - Macro平均AUC:{macro_auc:.3f}(类别均衡性能,不考虑样本数)")
print(f"   - Weighted平均AUC:{weighted_auc:.3f}(样本加权性能,适配不均衡数据)")

# 4.3 多分类进阶指标:MCC(马修斯相关系数,选最佳阈值)
print("\n10. 多分类进阶指标:MCC(综合分类效果+阈值选择)")
thresholds = np.linspace(0, 1, 200)  # 密集阈值(200个点,保证曲线连续)
multi_mcc_best = []  # 存储每个类别的最佳MCC和阈值

for i in range(n_classes):
    y_true = y_test_bin[:, i]  # 当前类的二值真实标签
    y_prob = y_prob_multi[:, i]  # 当前类的预测概率
    mcc_scores = []
    
    # 遍历阈值计算MCC
    for thresh in thresholds:
        y_pred = (y_prob > thresh).astype(int)
        mcc = matthews_corrcoef(y_true, y_pred)
        mcc_scores.append(mcc)
    
    # 找到最佳MCC和对应阈值
    best_idx = np.argmax(mcc_scores)
    best_mcc = mcc_scores[best_idx]
    best_thresh = thresholds[best_idx]
    multi_mcc_best.append((best_mcc, best_thresh))
    print(f"   - {target_names[i]}:最佳MCC={best_mcc:.3f},最佳阈值={best_thresh:.2f}")

# 4.4 二分类指标(基础+进阶KS,风控常用)
print("\n11. 二分类指标(基础+KS曲线)")
# 二分类基础指标
print("   二分类分类报告(正例=class_0):")
print(classification_report(y_test_binary, y_pred_binary, digits=3))

# 二分类进阶指标:MCC(默认阈值0.5)
binary_mcc = matthews_corrcoef(y_test_binary, y_pred_binary)
print(f"   - 二分类MCC(阈值0.5):{binary_mcc:.3f}")

# 二分类进阶指标:KS统计量(区分正负样本能力,风控核心)
# 方法1:scipy直接计算(正负样本分布差异)
ks_stat_scipy, ks_p = ks_2samp(
    y_prob_binary[y_test_binary == 1],  # 正例概率分布
    y_prob_binary[y_test_binary == 0]   # 负例概率分布
)
# 方法2:ROC推导(获取最佳阈值)
fpr_bin, tpr_bin, thresh_bin = roc_curve(y_test_binary, y_prob_binary)
ks_diff = tpr_bin - fpr_bin
ks_stat_roc = np.max(ks_diff)
ks_best_thresh = thresh_bin[np.argmax(ks_diff)]

print(f"   - KS统计量(scipy):{ks_stat_scipy:.3f}")
print(f"   - KS统计量(ROC推导):{ks_stat_roc:.3f},最佳阈值={ks_best_thresh:.2f}")

# ================================
# 模块5:可视化呈现(把抽象指标变直观)
# 核心:通过图表快速判断模型性能,定位问题
# ================================
print("\n" + "="*80)
print("【模块5:可视化呈现(3张核心图)】")
print("="*80)

# Mac中文显示配置(彻底解决乱码)
plt.rcParams['font.sans-serif'] = ['PingFang SC', 'Arial Unicode MS', 'Heiti TC']
plt.rcParams['axes.unicode_minus'] = False

# 定义曲线颜色(鲜明区分,避免混淆)
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1']  # 珊瑚红、青绿色、天蓝色

# 5.1 图1:多分类ROC曲线(含Micro/Macro平均)
plt.figure(figsize=(12, 8))
# 绘制每个类别的ROC曲线
for i, (color, cls_name) in enumerate(zip(colors, target_names)):
    fpr, tpr, _ = roc_curve(y_test_bin[:, i], y_prob_multi[:, i])
    plt.plot(
        fpr, tpr, color=color, linewidth=2.5,
        label=f'{cls_name} (AUC={class_auc[i]:.3f})'
    )
# 绘制Micro平均ROC(样本层面)
fpr_micro, tpr_micro, _ = roc_curve(y_test_bin.ravel(), y_prob_multi.ravel())
plt.plot(
    fpr_micro, tpr_micro, 'r--', linewidth=3, alpha=0.8,
    label=f'Micro平均 (AUC={micro_auc:.3f})'
)
# 绘制Macro平均ROC(类别层面,插值处理避免形状不匹配)
all_fpr = np.unique(np.concatenate([roc_curve(y_test_bin[:, i], y_prob_multi[:, i])[0] for i in range(n_classes)]))
mean_tpr = np.zeros_like(all_fpr)
for i in range(n_classes):
    mean_tpr += np.interp(all_fpr, roc_curve(y_test_bin[:, i], y_prob_multi[:, i])[0], roc_curve(y_test_bin[:, i], y_prob_multi[:, i])[1])
mean_tpr /= n_classes
plt.plot(
    all_fpr, mean_tpr, 'b--', linewidth=3, alpha=0.8,
    label=f'Macro平均 (AUC={macro_auc:.3f})'
)
# 随机猜测基准线
plt.plot([0, 1], [0, 1], 'k:', linewidth=2, label='随机猜测 (AUC=0.5)')
# 图表装饰(中文标注,小白易懂)
plt.xlabel('假正率(FPR):误判为当前类红酒的概率', fontsize=14)
plt.ylabel('真正率(TPR):正确识别当前类红酒的概率', fontsize=14)
plt.title('红酒数据集多分类ROC曲线(含Micro/Macro平均)', fontsize=16, pad=20)
plt.legend(loc='lower right', fontsize=12, framealpha=0.9)
plt.grid(alpha=0.3, linewidth=0.8)
plt.xlim([-0.01, 1.01])
plt.ylim([-0.01, 1.01])
plt.tight_layout()
plt.savefig('红酒多分类ROC曲线.png', dpi=300, bbox_inches='tight')
print("→ 图1:红酒多分类ROC曲线已保存")

# 5.2 图2:多分类MCC随阈值变化曲线(选最佳阈值)
plt.figure(figsize=(12, 8))
for i, (color, cls_name, (best_mcc, best_thresh)) in enumerate(zip(colors, target_names, multi_mcc_best)):
    y_true = y_test_bin[:, i]
    y_prob = y_prob_multi[:, i]
    mcc_scores = []
    # 重新计算MCC(确保曲线数据对应)
    for thresh in thresholds:
        y_pred = (y_prob > thresh).astype(int)
        mcc_scores.append(matthews_corrcoef(y_true, y_pred))
    # 绘制曲线
    plt.plot(
        thresholds, mcc_scores, color=color, linewidth=2.5,
        label=f'{cls_name} (最佳MCC={best_mcc:.3f}, 阈值={best_thresh:.2f})'
    )
    # 标记最佳点(红色散点+文本框,不遮挡)
    plt.scatter(best_thresh, best_mcc, color='red', s=100, zorder=5, edgecolors='black')
    plt.text(
        best_thresh + 0.02, best_mcc + 0.02,
        f'{cls_name}\nMCC={best_mcc:.3f}\n阈值={best_thresh:.2f}',
        fontsize=10, bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.9)
    )
# 图表装饰
plt.xlabel('概率阈值:判断为当前类红酒的最低概率标准', fontsize=14)
plt.ylabel('MCC值:综合分类评分(-1=最差,0=随机,1=完美)', fontsize=14)
plt.title('红酒数据集多分类MCC随阈值变化曲线', fontsize=16, pad=20)
plt.legend(loc='lower center', bbox_to_anchor=(0.5, -0.15), ncol=3, fontsize=11)
plt.grid(alpha=0.3, linewidth=0.8)
plt.xlim([0, 1])
plt.ylim([-0.2, 1.05])
plt.tight_layout()
plt.savefig('红酒多分类MCC阈值曲线.png', dpi=300, bbox_inches='tight')
print("→ 图2:红酒多分类MCC阈值曲线已保存")

# 5.3 图3:二分类KS曲线(金融风控常用)
plt.figure(figsize=(12, 8))
# 绘制TPR(正例捕获率)和FPR(负例误伤率)
plt.plot(thresh_bin, tpr_bin, color='red', linewidth=3, label='TPR(正例捕获率)')
plt.plot(thresh_bin, fpr_bin, color='blue', linewidth=3, label='FPR(负例误伤率)')
# 标记KS最大点(绿色虚线)
ks_max_idx = np.argmax(ks_diff)
plt.plot(
    [ks_best_thresh, ks_best_thresh],
    [fpr_bin[ks_max_idx], tpr_bin[ks_max_idx]],
    color='green', linestyle='--', linewidth=3,
    label=f'KS={ks_stat_roc:.3f}(最佳阈值={ks_best_thresh:.2f})'
)
# 图表装饰
plt.xlabel('阈值:判断为正例(class_0)的最低概率', fontsize=14)
plt.ylabel('累积比例', fontsize=14)
plt.title('红酒数据集二分类KS曲线(金融风控常用评估)', fontsize=16, pad=20)
plt.legend(loc='best', fontsize=12, framealpha=0.9)
plt.grid(alpha=0.3, linewidth=0.8)
plt.xlim([0, 1])
plt.ylim([0, 1.05])
# 添加文字标注(突出KS核心意义)
plt.text(
    ks_best_thresh + 0.03, (fpr_bin[ks_max_idx] + tpr_bin[ks_max_idx])/2,
    '最大区分度\n(KS核心:正例多捕获,负例少误伤)',
    color='green', fontweight='bold', fontsize=11
)
plt.tight_layout()
plt.savefig('红酒二分类KS曲线.png', dpi=300, bbox_inches='tight')
print("→ 图3:红酒二分类KS曲线已保存")

# ================================
# 练手总结(帮助回顾核心知识点)
# ================================
print("\n" + "="*80)
print("【练手完成!核心知识点回顾】")
print("="*80)
print("1. 数据集:load_wine是多分类入门首选,无缺失值、分布均衡;")
print("2. 预处理:分层抽样(保证类别分布)、标准化(消除尺度)、标签二值化(适配评估);")
print("3. 模型:随机森林(原生多分类)、逻辑回归(二分类经典);")
print("4. 指标:")
print("   - 基础:精确率/召回率/F1(看单类性能);")
print("   - 进阶:AUC(区分能力)、MCC(综合评分+阈值选择)、KS(风控区分能力);")
print("5. 可视化:ROC(看区分能力)、MCC-阈值(选决策阈值)、KS(风控场景)。")
print("\n→ 生成的3张图片在代码所在文件夹,可结合终端指标对比分析!")

这是一张红酒数据集多分类 ROC 曲线(含 Micro/Macro 平均),用于评估多分类模型在红酒数据集上的区分能力,以下是详细解读:

1. 图表核心用途

ROC 曲线(受试者工作特征曲线)是分类模型的核心评估工具,通过 “真正率(TPR)” 和 “假正率(FPR)” 的权衡,展示模型在不同阈值下的分类性能。在多分类场景中,通过 “One-vs-Rest” 策略将其拆解为多个二分类任务,再结合Micro 平均(样本层面的整体性能)和Macro 平均(类别层面的均衡性能),全面评估模型。

2. 关键元素解读

元素含义与分析
坐标轴横轴(FPR):假正率,即 “误判为当前类红酒的概率”;纵轴(TPR):真正率,即 “正确识别当前类红酒的概率”。
三类红酒曲线class_0/class_1/class_2的 ROC 曲线,AUC 均为 1.00,说明模型对每类红酒的区分能力 “近乎完美”。
Micro 平均曲线将所有样本的标签和概率 “展平” 为二分类后计算的 ROC,AUC=1.00,反映样本层面的整体分类性能
Macro 平均曲线对每个类别 AUC 取算术平均后计算的 ROC,AUC=1.00,反映类别层面的均衡分类性能
随机猜测线AUC=0.5 的虚线,模型曲线越远离它,说明性能远超随机猜测,区分能力越强。

3. 背景与拓展

  • 数据集:基于 sklearn 的load_wine数据集(178 个样本、13 个特征、3 类红酒标签),该数据集特征区分度高,是多分类入门的 “标杆数据”,常用于验证模型基础能力。
  • 性能解读:图中 AUC=1.00 属于 “理想状态”,实际业务数据(如金融风控、医疗诊断)通常更复杂,需关注 AUC 的实际区间(0.7~0.9 为较好,0.5 以下则模型无效)。
  • 实用价值:Micro 平均适合类别分布均衡的场景,Macro 平均适合关注 “每类性能均衡性” 的场景;实际项目中需结合业务目标(如 “优先保证某类识别率” 或 “整体样本分类准确”)选择评估维度。

这是一张红酒数据集多分类 MCC(马修斯相关系数)随阈值变化曲线,用于评估多分类模型在不同概率阈值下的综合分类性能,以下是详细解读:

1. 图表核心用途

MCC 是综合衡量分类性能的指标(范围 - 1~1,1 = 完美分类、0 = 随机分类、-1 = 完全错误)。该图通过遍历 “概率阈值”(判断为某类红酒的最低概率标准),展示每个红酒类别在不同阈值下的 MCC 变化,帮助找到最佳分类阈值(使 MCC 最大的阈值)。

2. 关键元素解读

元素含义与分析
坐标轴横轴:概率阈值(判断为当前类红酒的最低概率标准);纵轴:MCC 值(综合分类评分,-1 最差、0 随机、1 完美)。
三类红酒曲线class_0/class_1/class_2的 MCC - 阈值曲线,每条曲线的 “峰值点” 对应该类的最佳 MCC 和阈值(如 class_0 最佳 MCC=1.000,阈值 = 0.38)。
最佳点标记红色圆点标注了每类的最佳 MCC 对应的阈值,直观体现 “在该阈值下,模型对该类红酒的分类综合性能最优”。

3. 背景与实用价值

  • 数据集:基于 sklearn 的load_wine数据集(3 类红酒、13 个特征),该数据集特征区分度高,模型容易达到 “完美分类”(MCC=1.000),是多分类阈值选择的 “理想演示数据”。
  • 阈值选择意义:在实际业务中(如医疗诊断、金融风控),需根据 “误判成本” 选择阈值(例如:优先降低假阳性,则提高阈值;优先提高召回率,则降低阈值)。该图通过 MCC 量化了 “阈值 - 性能” 的权衡,为决策提供数据支撑。
  • 性能解读:图中三类红酒的最佳 MCC 均为 1.000,属于 “理想场景”。实际业务中,若 MCC 在 0.7~0.9 区间,说明模型综合性能较好;若低于 0.5,则需优化模型或特征。

这张图的核心价值是将 “抽象的分类性能” 转化为 “可操作的阈值选择”,帮助在 “分类准确率” 和 “业务风险” 之间找到平衡。

这是一张红酒数据集二分类 KS 曲线(金融风控常用评估),用于衡量模型对 “class_0 红酒(正例)” 和 “其他类红酒(负例)” 的区分能力,以下是详细解读:

1. 图表核心用途

KS 曲线(Kolmogorov-Smirnov 曲线)是金融风控、信用评分等领域的核心评估工具,通过对比正例捕获率(TPR)负例误伤率(FPR)最大差值(KS 值),量化模型对正负样本的区分能力 ——KS 值越大,模型区分能力越强。

2. 关键元素解读

元素含义与分析
坐标轴横轴:阈值(判断为正例 class_0 的最低概率),即模型判定 “是 class_0 红酒” 的概率门槛;纵轴:累积比例(0~1),表示在该阈值下,正例 / 负例被捕获 / 误伤的比例。
红色曲线(TPR)正例捕获率(True Positive Rate),即 “正确识别 class_0 红酒的比例”。曲线越高,正例识别能力越强。
蓝色曲线(FPR)负例误伤率(False Positive Rate),即 “错误将其他类红酒判定为 class_0 的比例”。曲线越低,误判越少。
绿色虚线(KS=1.000,最佳阈值 = 0.83)KS 值的最大值点,此时 “正例捕获率” 与 “负例误伤率” 的差距最大,是模型区分能力最强的阈值。

3. 背景与实用价值

  • 数据集:基于 sklearn 的load_wine数据集(3 类红酒),此处被转化为 “class_0 vs 其他类” 的二分类任务,用于演示 KS 曲线的逻辑。
  • KS 值意义:KS 值范围 0~1,实际业务中(如金融风控),KS≥0.4 即认为模型区分能力较好;图中 KS=1.000 属于 “理想场景”,因红酒数据集特征区分度极高。
  • 阈值选择:最佳阈值(0.83)是业务决策的关键参考 —— 若需 “少误判”(如金融风控避免错拒优质客户),可降低阈值;若需 “多捕获正例”(如医疗诊断避免漏诊),可提高阈值。

这张图的核心价值是将 “模型区分能力” 转化为 “可操作的阈值决策”,在 “识别准确率” 和 “业务风险” 之间找到平衡,是金融、医疗等领域模型落地的关键工具。

浙大疏锦行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值