分类与回归算法(六)- 随机森林

文章目录

  • 一、随机森林相关理论
    • [1. 随机森林的核心定义](#1. 随机森林的核心定义)
    • [2. 随机森林的底层核心原理](#2. 随机森林的底层核心原理)
      • [2.1 Bootstrap重采样:构建多样的训练集](#2.1 Bootstrap重采样:构建多样的训练集)
      • [2.2 特征随机选择:构建多样的决策树](#2.2 特征随机选择:构建多样的决策树)
      • [2.3 集成组合策略:投票与平均](#2.3 集成组合策略:投票与平均)
    • [3. 随机森林的完整构建步骤](#3. 随机森林的完整构建步骤)
    • [4. 随机森林的关键特性](#4. 随机森林的关键特性)
      • [4.1 强泛化能力,抗过拟合](#4.1 强泛化能力,抗过拟合)
      • [4.2 鲁棒性强,对异常值不敏感](#4.2 鲁棒性强,对异常值不敏感)
      • [4.3 支持并行训练,效率高](#4.3 支持并行训练,效率高)
      • [4.4 无需特征预处理,适用性广](#4.4 无需特征预处理,适用性广)
      • [4.5 可输出特征重要性,可解释性较强](#4.5 可输出特征重要性,可解释性较强)
    • [5. 随机森林的适用场景与局限性](#5. 随机森林的适用场景与局限性)
      • [5.1 适用场景](#5.1 适用场景)
      • [5.2 局限性](#5.2 局限性)
    • [6. 总结](#6. 总结)
  • 二、scikit-learn中随机森林相关方法介绍
    • [1. 随机森林分类构造方法](#1. 随机森林分类构造方法)
      • [1.1 基础构造语法](#1.1 基础构造语法)
      • [1.2 核心构造参数详解](#1.2 核心构造参数详解)
        • [1.2.1 森林规模与基础参数](#1.2.1 森林规模与基础参数)
        • [1.2.2 单棵树结构控制参数](#1.2.2 单棵树结构控制参数)
        • [1.2.3 随机性控制参数(Bagging核心)](#1.2.3 随机性控制参数(Bagging核心))
        • [1.2.4 评估与正则化参数](#1.2.4 评估与正则化参数)
      • [1.3 构造前的环境准备](#1.3 构造前的环境准备)
    • [2. 随机森林分类构造方法返回对象的方法](#2. 随机森林分类构造方法返回对象的方法)
      • [2.1 核心功能方法](#2.1 核心功能方法)
      • [2.2 模型评估与解释方法](#2.2 模型评估与解释方法)
    • [3. 随机森林分类构造方法返回对象的属性](#3. 随机森林分类构造方法返回对象的属性)
    • [4. 随机森林分类模型评估指标](#4. 随机森林分类模型评估指标)
      • [4.1 基础评估指标(混淆矩阵衍生指标)](#4.1 基础评估指标(混淆矩阵衍生指标))
      • [4.2 集成特有评估指标](#4.2 集成特有评估指标)
      • [4.3 类别不平衡专用指标](#4.3 类别不平衡专用指标)
    • [5. 常见问题与解决方案](#5. 常见问题与解决方案)
      • [5.1 问题1:模型过拟合(训练集准确率接近100%,测试集准确率低)](#5.1 问题1:模型过拟合(训练集准确率接近100%,测试集准确率低))
      • [5.2 问题2:训练速度慢(尤其大数据集)](#5.2 问题2:训练速度慢(尤其大数据集))
      • [5.3 问题3:特征重要性误导(高基数特征得分虚高)](#5.3 问题3:特征重要性误导(高基数特征得分虚高))
      • [5.4 问题4:OOB分数与测试集分数差距大](#5.4 问题4:OOB分数与测试集分数差距大)
      • [5.5 问题5:模型欠拟合(训练集与测试集准确率均低)](#5.5 问题5:模型欠拟合(训练集与测试集准确率均低))
    • [6. 总结](#6. 总结)
  • 三、基于随机森林的威斯康星乳腺癌数据分析与挖掘
    • [1. 分析目标与技术路线](#1. 分析目标与技术路线)
      • [1.1 核心目标](#1.1 核心目标)
      • [1.2 技术路线](#1.2 技术路线)
    • [2. 数据收集与加载](#2. 数据收集与加载)
      • [2.1 威斯康星乳腺癌数据集介绍](#2.1 威斯康星乳腺癌数据集介绍)
        • [2.1.1 数据集基本信息](#2.1.1 数据集基本信息)
        • [2.1.2 数据集字段说明](#2.1.2 数据集字段说明)
      • [2.2 数据加载](#2.2 数据加载)
    • [3. 探索性数据分析(EDA)](#3. 探索性数据分析(EDA))
      • [3.1 数据基本信息](#3.1 数据基本信息)
        • [3.1.1 查看数据前10行](#3.1.1 查看数据前10行)
        • [3.1.2 查看数据摘要信息](#3.1.2 查看数据摘要信息)
        • [3.1.3 特征描述性统计](#3.1.3 特征描述性统计)
      • [3.2 目标变量类别分布分析](#3.2 目标变量类别分布分析)
      • [3.3 目标变量类别与特征相关性分析](#3.3 目标变量类别与特征相关性分析)
      • [3.4 关键特征分布分析](#3.4 关键特征分布分析)
      • [3.5 关键特征相关性分析](#3.5 关键特征相关性分析)
      • [3.6 关键特征的类别区分能力验证](#3.6 关键特征的类别区分能力验证)
    • [4. 数据预处理](#4. 数据预处理)
      • [4.1 数据集划分](#4.1 数据集划分)
    • [5. 随机森林模型构建](#5. 随机森林模型构建)
      • [5.1 确定最优参数组合](#5.1 确定最优参数组合)
      • [5.2 模型构建(模型选择、训练、预测)](#5.2 模型构建(模型选择、训练、预测))
    • [6. 随机森林模型评估](#6. 随机森林模型评估)
      • [6.1 核心评估指标计算](#6.1 核心评估指标计算)
      • [6.2 可视化评估](#6.2 可视化评估)
        • [6.2.1 混淆矩阵可视化](#6.2.1 混淆矩阵可视化)
        • [6.2.2 ROC曲线与AUC值可视化](#6.2.2 ROC曲线与AUC值可视化)
        • [6.2.3 预测概率分布可视化](#6.2.3 预测概率分布可视化)
        • [6.2.4 特征重要性可视化](#6.2.4 特征重要性可视化)
  • 四、完整代码
    • [1. 完整代码](#1. 完整代码)

一、随机森林相关理论

作为集成学习中并行式集成(Bagging)的经典代表,随机森林(Random Forest, RF)以其简单易用、泛化能力强、鲁棒性高等优势,成为工业界和学术界最常用的机器学习算法之一。它在决策树的基础上引入了"随机采样"和"特征随机选择"双重机制,有效解决了单一决策树过拟合、稳定性差的问题。本文将从随机森林的核心定义、底层原理、构建步骤、关键特性及适用场景等维度,深入解析其相关理论,为后续实践应用奠定基础。

1. 随机森林的核心定义

随机森林是由多棵独立训练的决策树组成的集成学习模型,其本质是通过Bagging思想将多个决策树的预测结果进行组合,以提升模型的整体性能。具体来说,随机森林通过Bootstrap重采样技术生成多个不同的训练子集,在每个子集上训练一棵决策树,且在决策树的每个分裂节点处,仅从随机选择的部分特征中筛选最优分裂特征。最终,对于分类任务,采用投票(多数表决)的方式确定最终预测结果;对于回归任务,则采用所有决策树预测结果的平均值作为最终输出。

从定义可以看出,随机森林的核心是"随机性"与"集成性"的结合:随机性保证了各棵决策树的多样性,避免了单一决策树对局部数据的过度依赖;集成性则通过组合多棵树的结果,实现了"群体智慧",有效降低了模型的方差,提升了泛化能力。

2. 随机森林的底层核心原理

随机森林的性能优势源于其底层的两大核心原理:Bootstrap重采样带来的训练集多样性,以及特征随机选择带来的决策树视角多样性。这两大原理共同保障了基学习器(决策树)的独立性,为后续的集成优化奠定了基础。

2.1 Bootstrap重采样:构建多样的训练集

Bootstrap重采样是Bagging思想的核心技术,也是随机森林生成多样训练集的关键。其具体过程如下:

  1. 假设原始训练集包含N个样本,对于每一棵决策树的训练,都从原始训练集中随机抽取N个样本(有放回抽样),生成一个新的训练子集。

  2. 由于抽样是有放回的,新训练子集与原始训练集相比,会存在部分样本重复、部分样本缺失的情况。统计理论表明,对于足够大的N,每棵决策树的训练集大约包含原始数据63.2%的独特样本------因为每个样本不被抽取的概率为 ( 1 − 1 / N ) N (1-1/N)^N (1−1/N)N,当N趋近于无穷大时,该概率趋近于 1 / e ≈ 0.368 1/e≈0.368 1/e≈0.368,因此被抽取的独特样本比例约为 1 − 0.368 = 0.632 1-0.368=0.632 1−0.368=0.632。

  3. 未被抽取到的 36.8 % 36.8\% 36.8%的样本被称为"袋外样本(Out-of-Bag, OOB)",这些样本可直接用于随机森林的模型评估,无需额外划分验证集,大大提升了数据利用效率。

通过Bootstrap重采样,每一棵决策树都基于不同的训练子集进行训练,这就使得各棵树的学习视角存在差异------有的树关注数据的这部分特征组合,有的树关注另一部分特征组合,从而避免了所有树都"同质化"学习的问题,为后续的集成优化提供了多样性基础。

2.2 特征随机选择:构建多样的决策树

仅靠训练集的多样性还不足以保证决策树的独立性------若所有决策树都使用全部特征进行分裂,那么即使训练集不同,最终的分裂逻辑也可能趋同。因此,随机森林在决策树的构建过程中引入了"特征随机选择"机制,具体规则如下:

  1. 假设原始数据包含M个特征,在每一棵决策树的每个分裂节点处,从M个特征中随机选择k个特征(k<M)组成特征子集。

  2. 决策树仅基于这个特征子集,通过信息增益(ID3算法)、信息增益比(C4.5算法)或基尼系数(CART算法)等指标,选择最优的特征及分裂阈值进行节点分裂。

  3. 对于k的取值,通常建议设置为 √ M √M √M(分类任务)或 M / 3 M/3 M/3(回归任务),也可通过交叉验证根据具体任务进行调优。

特征随机选择进一步增强了决策树的多样性:即使两棵树的训练集存在重叠,由于分裂时使用的特征子集不同,它们的分裂路径和最终结构也会存在显著差异。这种"训练集随机+特征随机"的双重随机机制,是随机森林区别于普通Bagging集成模型的核心,也是其泛化能力更强的关键原因。

2.3 集成组合策略:投票与平均

随机森林的集成组合策略简洁高效,根据任务类型的不同分为两类:

  • 分类任务:多数投票法 每一棵决策树对样本进行分类预测,得到一个类别结果;统计所有决策树的预测结果,将出现次数最多的类别作为随机森林的最终预测类别。为了进一步优化,也可采用加权投票法------根据决策树的性能(如在OOB样本上的准确率)为每棵树分配不同的权重,性能更优的树拥有更高的投票权重。

  • 回归任务:平均法 每一棵决策树对样本输出一个回归值;将所有决策树的回归值取平均值(或加权平均值),作为随机森林的最终预测结果。加权平均同样可基于决策树的OOB性能分配权重,提升预测精度。

这种简单的组合策略之所以有效,核心在于"大数定律"------当多个独立的弱学习器(决策树)的错误相互独立时,集成后的错误率会随着学习器数量的增加而指数级下降。而随机森林的双重随机机制,恰好保证了基学习器之间的独立性,为组合策略的有效性提供了保障。

3. 随机森林的完整构建步骤

结合上述核心原理,随机森林的完整构建过程可分为以下6个步骤,无论是分类还是回归任务,其流程框架基本一致,仅在组合策略上存在差异:

  1. 确定超参数 提前设定随机森林的核心超参数,包括:决策树的数量(通常设为100-1000,需根据任务调整)、每棵树的最大深度(防止单棵树过拟合)、每个分裂节点的随机特征数k(分类任务默认√M,回归任务默认M/3)、叶子节点的最小样本数等。

  2. 生成训练子集 针对每一棵决策树,使用Bootstrap重采样技术从原始训练集中抽取N个样本(有放回),生成对应的训练子集。

  3. 构建单棵决策树 基于生成的训练子集构建决策树:在每个节点分裂时,先随机选择k个特征组成子集,再从子集中选择最优分裂特征及阈值进行分裂,直至达到预设的停止条件(如树深达到最大值、叶子节点样本数小于阈值等)。需要注意的是,随机森林中的决策树通常不进行剪枝------因为单棵树的过拟合问题会通过集成组合被稀释,剪枝反而可能降低基学习器的多样性。

  4. 重复构建过程 重复步骤2和步骤3,直至生成预设数量的决策树,所有决策树共同构成随机森林的基学习器集合。

  5. 模型集成组合 对于新的测试样本,将其输入所有决策树中得到各自的预测结果,再通过投票(分类)或平均(回归)得到最终预测结果。

  6. OOB样本评估 利用每棵树的袋外样本(未参与该树训练的样本)评估模型性能:对于每个OOB样本,仅使用未将其包含在训练集中的决策树进行预测,再通过组合策略得到预测结果,与真实标签对比计算准确率(分类)或均方误差(回归),作为模型的泛化性能指标。

4. 随机森林的关键特性

随机森林的广泛应用与其独特的特性密不可分,这些特性既包括性能上的优势,也包含工程实现上的便利,使其在各类任务中都能展现出优异的表现。

4.1 强泛化能力,抗过拟合

抗过拟合是随机森林最核心的特性。单一决策树容易因学习过深而拟合训练数据中的噪声,导致泛化能力差;而随机森林通过双重随机机制保证了基学习器的多样性,再通过集成组合稀释了单棵树的过拟合风险。实践表明,随着决策树数量的增加,随机森林的泛化误差会逐渐收敛到一个稳定值,不会出现过拟合现象------这是随机森林区别于许多复杂模型的显著优势。

4.2 鲁棒性强,对异常值不敏感

随机森林对数据中的异常值和噪声具有较强的抵抗能力:一方面,Bootstrap重采样技术使得异常值被重复抽取的概率较低,单棵树受异常值的影响有限;另一方面,集成组合策略通过"多数表决"或"平均",进一步削弱了异常值对最终预测结果的干扰。例如,在金融风控数据中,个别异常的交易记录很难影响随机森林对用户风险等级的判断,而单一决策树则可能被这类异常值误导。

4.3 支持并行训练,效率高

由于随机森林中的每一棵决策树都是基于独立的训练子集和随机特征集进行训练的,各棵树之间不存在依赖关系,因此可以实现完全并行训练。在工程实现中,可通过多线程或分布式计算的方式同时训练多棵决策树,大幅缩短模型的训练时间。这一特性使得随机森林在处理大规模数据时,依然能够保持较高的训练效率,远超串行式集成模型(如GBDT)。

4.4 无需特征预处理,适用性广

随机森林对输入特征的要求极低,无需进行复杂的预处理:

  • 无需特征标准化或归一化------决策树的分裂基于特征的相对大小,不受特征量纲的影响,因此随机森林可直接处理混合量纲的数据(如身高、体重等连续特征与性别、职业等分类特征)。

  • 对缺失值不敏感------决策树在分裂时可通过样本权重调整或特征子集选择避开缺失值,集成后进一步降低了缺失值的影响,无需额外填充缺失值。

这种特性使得随机森林能够快速应用于各类数据场景,降低了模型落地的门槛。

4.5 可输出特征重要性,可解释性较强

虽然随机森林是由多棵决策树组成的集成模型,但它能够通过统计特征在所有决策树中的贡献度,输出每一个特征的重要性得分,为特征选择和业务解释提供依据。特征重要性的计算逻辑如下:

  1. 对于每一棵决策树,计算每个特征在所有分裂节点上的"不纯度降低量"(如基尼系数减少量、信息增益等)。

  2. 将该特征在所有决策树中的不纯度降低量取平均值,得到该特征的重要性得分。得分越高,说明该特征对模型预测结果的影响越大。

通过特征重要性分析,开发者可以筛选出关键特征,简化模型结构;同时,也能向业务方解释"模型基于哪些核心特征做出预测",提升模型的可信度。

5. 随机森林的适用场景与局限性

尽管随机森林性能优异,但并非适用于所有场景。明确其适用范围和局限性,是合理选择模型的关键。

5.1 适用场景

  • 大规模数据分类/回归任务 随机森林的并行训练能力和高效性,使其在处理百万级甚至亿级样本数据时依然表现出色,如用户行为预测、商品销量预测等。

  • 特征维度高、数据类型复杂的场景 当数据包含大量连续特征、分类特征,且存在缺失值或异常值时,随机森林无需复杂预处理即可直接建模,如医疗数据诊断、金融风控等。

  • 快速原型验证场景 在项目初期,需要快速构建模型验证业务可行性时,随机森林的易用性和高效性使其成为首选------无需调优过多超参数即可得到性能尚可的模型,为后续优化提供基准。

  • 特征选择与数据探索场景 利用随机森林输出的特征重要性,可快速识别关键特征,为数据探索和特征工程提供方向,尤其适用于高维数据的特征降维。

5.2 局限性

  • 对极端不平衡数据敏感 在类别不平衡的分类任务中(如欺诈检测,正样本占比仅0.1%),随机森林的多数投票法会倾向于预测占比高的负样本,导致正样本的预测准确率较低。此时需要通过样本加权或数据重采样等方式进行优化。

  • 预测概率的可信度较低 随机森林输出的类别概率是基于投票比例计算的,并非严格意义上的概率估计,其可信度低于逻辑回归等概率模型。在需要精准概率输出的场景(如信用评分中需输出违约概率),随机森林的表现不如专门的概率模型。

  • 对小样本数据可能欠拟合 当训练样本数量极少时,Bootstrap重采样生成的训练子集多样性不足,导致基学习器的独立性差,集成后的模型可能无法充分捕捉数据规律,出现欠拟合现象。

  • 超参数调优成本较高 虽然随机森林的默认参数性能较好,但要达到最优性能,需要调优的超参数较多(如决策树数量、最大深度、特征数k等),且超参数之间存在交互作用,调优过程需要大量的交叉验证,成本较高。

6. 总结

随机森林是集成学习思想的成功实践,其通过"双重随机+并行集成"的核心逻辑,完美平衡了模型性能、训练效率和易用性。双重随机机制(Bootstrap重采样、特征随机选择)保证了基学习器的多样性,并行集成则实现了性能与效率的统一,使其在各类数据场景中都能稳定发挥作用。当然,随机森林也存在对不平衡数据敏感、概率预测可信度低等局限性,在实际应用中需要结合业务场景进行优化。


二、scikit-learn中随机森林相关方法介绍

随机森林(Random Forest)是sklearn中经典的集成学习算法,基于"多个决策树投票"的核心思想,通过引入随机性(随机选特征、随机选样本)降低单棵决策树的过拟合风险,兼具高准确率与强鲁棒性,广泛应用于分类、回归任务(本文聚焦分类场景)。sklearn通过sklearn.ensemble.RandomForestClassifier类封装随机森林分类器,提供了丰富的参数配置、对象方法及评估手段,本文将从构造方法、对象方法与属性、评估指标、常见问题等维度,系统讲解其实战应用。

1. 随机森林分类构造方法

sklearn中随机森林分类的核心构造通过RandomForestClassifier类实现,该类基于Bagging集成框架,对多棵决策树的预测结果进行投票(默认硬投票),初始化时可通过参数控制树的数量、单棵树的结构、随机性强度等核心特性。

1.1 基础构造语法

python 复制代码
from sklearn.ensemble import RandomForestClassifier

# 基础构造(使用默认参数)
rf_clf = RandomForestClassifier()

# 带参数的构造(常用配置)
rf_clf = RandomForestClassifier(
    n_estimators=100,          # 决策树数量(森林规模)
    criterion='gini',          # 单棵树的特征拆分准则
    max_depth=5,               # 单棵树的最大深度
    min_samples_leaf=2,        # 单棵树叶节点最小样本数
    max_features='sqrt',       # 单棵树拆分时的最大特征数
    bootstrap=True,            # 是否使用自助采样(Bagging核心)
    oob_score=True,            # 是否计算袋外分数(OOB)
    class_weight='balanced',   # 类别权重(处理不平衡数据)
    random_state=42            # 随机种子(保证结果可复现)
)

1.2 核心构造参数详解

1.2.1 森林规模与基础参数
  • n_estimators:随机森林中决策树的数量,默认100,是影响模型性能的核心参数。调优建议:从50开始逐步增加,至验证集准确率趋于稳定(通常100-500足够,过多会增加计算成本,收益递减)。
  • criterion :单棵决策树的特征拆分准则,与DecisionTreeClassifier一致,默认'gini',可选'entropy'/'log_loss',无特殊需求保持默认即可。
  • random_state:全局随机种子,同时控制样本抽样、特征抽样的随机性,设为固定整数(如42)可保证模型训练可复现。
1.2.2 单棵树结构控制参数

此类参数与DecisionTreeClassifier完全兼容,用于限制单棵树的复杂度,避免单棵树过拟合:

  • max_depth :单棵树的最大深度,默认None(无限制),调优建议3-10,需结合n_estimators协同调整(树数量越多,单棵树可越简单);
  • min_samples_split:单棵树节点可拆分的最小样本数,默认2,调优建议5-10;
  • min_samples_leaf:单棵树叶节点的最小样本数,默认1,调优建议2-5;
  • max_leaf_nodes :单棵树的最大叶节点数,默认None,可选整数限制树的复杂度。
1.2.3 随机性控制参数(Bagging核心)
  • max_features :单棵决策树拆分时可考虑的最大特征数,默认'sqrt'(根号下总特征数),是随机森林"随机子空间"的核心实现。可选值:
    • 整数:固定选取N个特征;
    • 浮点数:选取总特征数的百分比;
    • 'sqrt'/'log2':分别为根号下/对数下总特征数;
    • None:使用全部特征(退化为普通Bagging)。
      调优建议:分类任务优先选'sqrt'(sklearn默认),回归任务可选'log2'或0.2-0.5。
  • bootstrap :是否对训练集进行自助采样(Bootstrap),默认True(Bagging核心机制)。若设为False,则所有树使用全量训练集(无随机性,易过拟合),仅小数据集可尝试。
  • bootstrap_features :是否对特征进行自助采样,默认False,仅需在"特征维度极高(如>1000)"时开启,进一步增强随机性。
1.2.4 评估与正则化参数
  • oob_score :是否计算袋外(Out-of-Bag)分数,默认False。开启后,模型会用未被采样到的样本(袋外样本)评估泛化能力,无需额外划分验证集,是随机森林特有的高效评估手段,建议开启。
  • oob_score_estimator :计算OOB分数时使用的评估指标,默认'accuracy'(分类)/'r2'(回归),可根据任务调整。
  • class_weight :类别权重,与DecisionTreeClassifier一致,默认None,可选'balanced'或自定义字典,解决类别不平衡问题。
  • n_jobs :并行训练的CPU核心数,默认None(单核心),设为-1使用全部核心,大幅提升训练速度(尤其n_estimators较大时)。

注意事项:随机森林的核心优势是"随机性+集成",无需后剪枝(ccp_alpha参数虽兼容,但极少使用),优先通过max_depthmax_features等预剪枝参数控制复杂度。

1.3 构造前的环境准备

随机森林依赖的环境与决策树完全一致,核心安装命令如下:

bash 复制代码
# 核心依赖(sklearn+数据处理+可视化)
pip install scikit-learn numpy pandas matplotlib -i https://mirrors.aliyun.com/pypi/simple
# 可选:加速训练(大数据集)
pip install joblib -i https://mirrors.aliyun.com/pypi/simple

2. 随机森林分类构造方法返回对象的方法

通过RandomForestClassifier构造的分类器对象(如rf_clf),继承了决策树分类器的核心方法,同时扩展了集成学习特有的功能方法,覆盖训练、预测、评估、解释全流程。

2.1 核心功能方法

方法名称 语法示例 功能说明
fit() rf_clf.fit(X_train, y_train) 核心训练方法,基于自助采样为每棵树生成训练子集,训练多棵决策树,无返回值,更新分类器对象状态。
predict() y_pred = rf_clf.predict(X_test) 预测方法,输入测试集特征矩阵,返回各样本的投票结果(类别标签),数组形式。
predict_proba() proba = rf_clf.predict_proba(X_test) 概率预测方法,返回各样本属于各类别的概率(所有树概率的平均值),形状为[样本数, 类别数]。
score() acc = rf_clf.score(X_test, y_test) 简单评估方法,计算测试集准确率(0-1浮点数),等价于accuracy_score(y_test, rf_clf.predict(X_test))
apply() leaf_indices = rf_clf.apply(X_test) 返回每个样本在每棵树中最终落入的叶节点索引,形状为[样本数, 树数量],用于高级分析(如树间一致性)。
decision_path() path = rf_clf.decision_path(X_test) 返回每个样本在每棵树中的决策路径(节点访问情况),稀疏矩阵格式,用于模型解释。
set_params() rf_clf.set_params(n_estimators=200) 动态修改模型参数,无需重新构造对象,适合调优阶段快速调整。

2.2 模型评估与解释方法

方法/函数 语法示例 功能说明
feature_importances_(属性+方法结合) importances = rf_clf.feature_importances_ 随机森林特有的特征重要性计算(所有树特征重要性的平均值),是特征筛选的核心依据,无需额外函数调用,训练后直接读取属性。
permutation_importance()(辅助函数) from sklearn.inspection import permutation_importance result = permutation_importance(rf_clf, X_test, y_test) 置换特征重要性,更鲁棒的特征解释方法,避免高基数特征的误导,适用于随机森林模型解释。
plot_partial_dependence()(辅助函数) from sklearn.inspection import plot_partial_dependence plot_partial_dependence(rf_clf, X_train, features=['feature1']) 绘制部分依赖图(PDP),展示单个/多个特征对预测结果的影响,直观解释模型逻辑。

3. 随机森林分类构造方法返回对象的属性

随机森林分类器对象在fit()训练后,会生成一系列属性存储模型核心信息,所有属性均为"只读",需训练后才能获取有效值,可用于模型分析、解释与调优。

属性名称 数据类型 功能说明
feature_importances_ 数组(形状为[特征数,]) 每个特征的重要性得分(所有树的平均),得分越高表示该特征对分类结果的贡献越大,是随机森林最核心的解释性属性。
classes_ 数组(形状为[类别数,]) 模型识别的类别标签,按升序排列,与predict_proba()返回的概率顺序对应。
n_classes_ 整数 模型识别的类别数量,如鸢尾花分类任务中该值为3。
n_features_ 整数 训练模型时使用的特征数量,与输入X_train的特征数一致。
n_estimators_ 整数 实际训练的决策树数量,与构造时的n_estimators参数一致。
estimators_ 列表(元素为DecisionTreeClassifier对象) 存储森林中所有训练好的决策树对象,可单独提取某棵树进行分析(如rf_clf.estimators_[0]获取第一棵树)。
oob_score_ 浮点数 袋外分数(OOB分数),仅oob_score=True时有效,是模型泛化能力的无偏估计,值越接近测试集准确率越好。
oob_decision_function_ 数组(形状为[样本数, 类别数]) 袋外样本的概率预测结果,仅oob_score=True时有效,可用于OOB-based评估(如OOB F1分数)。

4. 随机森林分类模型评估指标

随机森林的评估指标与决策树完全兼容,但需结合其"集成特性"选择适配的评估方式,核心分为"基础指标""集成特有指标""类别不平衡指标"三类。

4.1 基础评估指标(混淆矩阵衍生指标)

与决策树一致,基于混淆矩阵计算,适用于所有分类场景:

  • 准确率(Accuracy):正确分类样本数/总样本数,适用于类别均衡场景;
  • 精确率(Precision):预测正类中实际正类的比例,关注"预测可靠性";
  • 召回率(Recall):实际正类中被正确预测的比例,关注"正类覆盖度";
  • F1分数(F1-Score):精确率与召回率的调和平均,平衡两者矛盾。

4.2 集成特有评估指标

这类指标利用随机森林的"集成特性",更高效地评估模型泛化能力:

  • 袋外分数(OOB Score):无需划分验证集,直接用未被采样的样本评估模型,是随机森林特有的高效评估手段,常用OOB准确率、OOB F1分数;
  • 分类报告(Classification Report):输出各类别的精确率、召回率、F1分数,结合随机森林的高稳定性,结果比单棵决策树更可靠;
  • 对数损失(Log Loss) :基于predict_proba()的概率输出计算,随机森林的概率输出(多棵树平均)比单棵决策树更平滑,对数损失通常更低。

4.3 类别不平衡专用指标

随机森林处理不平衡数据的能力优于单棵决策树,但仍需针对性评估:

  • ROC曲线与AUC值:二分类任务中,随机森林的AUC值通常高于单棵决策树,多分类需转化为"一对多"模式;
  • 马修斯相关系数(MCC):极端不平衡数据的核心评估指标,取值[-1,1],1表示完美分类;
  • 平衡准确率(Balanced Accuracy):各类别召回率的平均值,避免多数类主导评估结果。

5. 常见问题与解决方案

随机森林虽鲁棒性强,但实际使用中仍会遇到过拟合、训练慢、特征重要性误导等问题,以下为针对性解决方案:

5.1 问题1:模型过拟合(训练集准确率接近100%,测试集准确率低)

核心原因:树数量过多、单棵树过复杂、随机性不足。

解决方案:

  • 降低单棵树复杂度:增大max_depth(如从10减至5)、min_samples_leaf(如从1增至3);
  • 增强随机性:将max_featuresNone改为'sqrt',或开启bootstrap_features
  • 减少树数量:适当降低n_estimators(如从500减至200),避免过度拟合训练集噪声;
  • 数据层面:增加训练数据,或使用class_weight平衡类别(若数据不平衡)。

5.2 问题2:训练速度慢(尤其大数据集)

核心原因:树数量过多、特征维度高、未开启并行训练。

解决方案:

  • 开启并行训练:设置n_jobs=-1,利用所有CPU核心;
  • 降低计算成本:减少n_estimators(如100)、增大max_depth(简化单棵树)、降低max_features(减少每棵树的特征数);
  • 数据降维:对高维特征进行PCA/筛选,减少输入特征数;
  • 使用轻量级采样:设置max_samples(如0.8),每棵树仅用80%的样本训练(sklearn 1.2+支持)。

5.3 问题3:特征重要性误导(高基数特征得分虚高)

核心原因:随机森林的默认特征重要性对"高基数特征"(如ID、编码值)过度敏感。

解决方案:

  • 使用置换重要性(permutation_importance)替代默认特征重要性,更鲁棒;
  • 对高基数特征进行分箱/编码,降低其基数;
  • 结合业务逻辑筛选特征,排除无意义的高基数特征。

5.4 问题4:OOB分数与测试集分数差距大

核心原因:自助采样的随机性导致袋外样本与测试集分布差异大,或数据量过小。

解决方案:

  • 增大n_estimators:树数量越多,OOB样本越接近整体数据分布;
  • 关闭bootstrap(仅小数据集):改用全量样本训练,放弃OOB评估,手动划分验证集;
  • 数据增强:增加样本量,提升数据分布的代表性。

5.5 问题5:模型欠拟合(训练集与测试集准确率均低)

核心原因:树数量过少、单棵树过简单、特征不足。

解决方案:

  • 增加树数量:提升n_estimators(如从50增至300);
  • 提升单棵树复杂度:减小max_depth(如从3增至8)、减小min_samples_leaf(如从5减至2);
  • 特征工程:增加有效特征,或对现有特征进行组合(如交互特征);
  • 关闭随机性:将max_features设为None,使用全部特征训练。

6. 总结

随机森林是sklearn中"开箱即用"的高性能分类算法,核心优势是鲁棒性强、无需复杂调优、可解释性好(通过特征重要性)。使用时需把握"随机性+集成"的核心逻辑:通过n_estimators控制森林规模,通过max_depth/min_samples_leaf控制单棵树复杂度,通过max_features/bootstrap引入随机性,结合OOB分数高效评估模型。

相较于单棵决策树,随机森林在准确率、稳定性上有显著提升,是工业界分类任务的首选算法之一,尤其适合中小规模数据集、类别不平衡场景,且无需复杂的特征预处理即可取得较好效果。


三、基于随机森林的威斯康星乳腺癌数据分析与挖掘

威斯康星乳腺癌数据集(Wisconsin Breast Cancer Dataset)是医疗数据挖掘领域的经典数据集,包含乳腺肿瘤的细胞核特征指标及病理诊断结果。本文将以该数据集为研究对象,基于scikit-learn的决策树分类器,完成从数据探索、模型构建到性能优化的全流程分析挖掘,最终实现肿瘤良恶性的精准识别,为临床辅助诊断提供参考。

1. 分析目标与技术路线

1.1 核心目标

  • 挖掘乳腺肿瘤细胞核特征与良恶性(目标变量)的关联关系;

  • 构建基于决策树的分类模型,实现肿瘤良恶性的自动判断;

  • 通过参数调优与模型解释,提升模型泛化能力与临床可解释性。

1.2 技术路线

本次分析遵循"数据准备→探索性分析→模型构建(模型选择、参数调优、训练、预测)→模型评估"的核心流程。

2. 数据收集与加载

2.1 威斯康星乳腺癌数据集介绍

2.1.1 数据集基本信息

威斯康星乳腺癌数据集由美国威斯康星大学的研究人员收集,包含乳腺肿块的细针穿刺(FNA)活检数据,主要用于良性/恶性肿瘤的二分类任务

  • 来源:由Dr. William H. Wolberg于1995年公开,现收录于UCI机器学习仓库和Scikit-learn内置数据集;
  • 样本量:569个样本(每个样本代表一个乳腺肿块的活检结果);
  • 类别分布
    • 良性(Benign):357个样本(占比约62.7%);
    • 恶性(Malignant):212个样本(占比约37.3%);
  • 特征类型:全部为连续型数值特征,基于肿块细胞核的图像特征提取;
  • 任务目标:根据细胞核特征预测肿瘤为良性(0)或恶性(1)。
2.1.2 数据集字段说明

威斯康星乳腺癌数据集包含31个字段,其中1个为目标标签,30个为特征字段(按"均值""标准误差""最差值"三类分组),具体如下:

类别 字段英文名称 字段中文说明 描述
目标变量 diagnosis 诊断结果 0=恶性(Malignant), 1=良性(Benign)
均值特征 mean radius 平均半径 细胞核半径的算术平均值
mean texture 平均纹理 细胞核纹理的灰度值标准差
mean perimeter 平均周长 细胞核周长的算术平均值
mean area 平均面积 细胞核面积的算术平均值
mean smoothness 平均平滑度 半径长度的局部变化
mean compactness 平均紧凑度 周长² / 面积 - 1.0
mean concavity 平均凹度 轮廓凹部分的严重程度
mean concave points 平均凹点 轮廓凹部分的数量
mean symmetry 平均对称性 细胞核的对称性
mean fractal dimension 平均分形维数 海岸线近似 - 1.0
标准误差特征 radius error 半径误差 半径测量的标准误差
texture error 纹理误差 纹理测量的标准误差
perimeter error 周长误差 周长测量的标准误差
area error 面积误差 面积测量的标准误差
smoothness error 平滑度误差 平滑度测量的标准误差
compactness error 紧凑度误差 紧凑度测量的标准误差
concavity error 凹度误差 凹度测量的标准误差
concave points error 凹点误差 凹点测量的标准误差
symmetry error 对称性误差 对称性测量的标准误差
fractal dimension error 分形维数误差 分形维数测量的标准误差
最差值特征 worst radius 最差半径 最大的细胞核半径
worst texture 最差纹理 最差的纹理值
worst perimeter 最差周长 最大的细胞核周长
worst area 最差面积 最大的细胞核面积
worst smoothness 最差平滑度 最差的平滑度值
worst compactness 最差紧凑度 最差的紧凑度值
worst concavity 最差凹度 最差的凹度值
worst concave points 最差凹点 最差的凹点值
worst symmetry 最差对称性 最差的对称性值
worst fractal dimension 最差分形维数 最差的分形维数值

特征统计摘要

特征类别 特征数量 测量内容
均值特征 10个 细胞核特征的算术平均值
标准误差特征 10个 特征测量的变异程度
最差值特征 10个 细胞核最异常的特征值

这些特征都是从乳腺肿块的细针穿刺(FNA)数字化图像中提取的细胞核特征,用于帮助医生区分恶性肿瘤和良性肿瘤。

2.2 数据加载

Scikit-learn库内置了威斯康星乳腺癌数据集,可直接通过datasets模块加载,避免手动下载和解析文件。以下是加载代码:

python 复制代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split, GridSearchCV, cross_val_score
from sklearn.tree import DecisionTreeClassifier, plot_tree, export_text
from sklearn.metrics import (confusion_matrix, classification_report, 
                             accuracy_score, precision_score, recall_score, f1_score,
                             roc_auc_score, roc_curve)

# 设置中文正常显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 设置Pandas全局选项
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)
pd.set_option('display.max_colwidth', 50)
pd.set_option('display.expand_frame_repr', False)

# 加载数据集
cancer = load_breast_cancer()
# 转换为DataFrame便于分析(特征矩阵)
X = pd.DataFrame(data=cancer.data, columns=cancer.feature_names)
# 目标变量(诊断结果)
y = pd.Series(data=cancer.target, name='diagnosis')

3. 探索性数据分析(EDA)

探索性数据分析旨在挖掘数据内在规律,为后续模型构建提供依据。重点分析特征分布、特征相关性及良恶性样本的特征差异。

3.1 数据基本信息

3.1.1 查看数据前10行

在数据分析的初始阶段,查看数据集的前几行是了解其结构、字段含义和数据类型的重要步骤。通过观察原始数据的分布与格式,可以初步判断是否存在异常值、缺失值或不一致的命名规范,并为后续的数据清洗、特征工程和建模工作提供直观依据。

该代码将特征矩阵 X(包含所有数值型特征)与标签向量 y(类别标签)沿列方向进行拼接,形成一个完整的 DataFrame,便于统一查看样本的完整信息;随后调用 head(10) 输出前10个样本,快速呈现数据结构和内容。

python 复制代码
# 合并特征和标签,便于同时查看
data = pd.concat([X, y], axis=1)  # 按列合并
print("数据前10行:")
print(data.head(10))

数据集中各字段部分数据如下图所示,从输出结果可见,数据集中包含多个描述肿瘤形态学特征的数值变量,如 mean radius(平均半径)、mean texture(平均纹理)、mean perimeter(平均周长)等,均为连续型数值。此外还包含误差项(如 radius error)和最差情况指标(如 worst radius),反映肿瘤的异质性与恶化程度。

3.1.2 查看数据摘要信息

在数据探索阶段,了解数据集的整体结构和基本属性是确保后续分析顺利进行的关键步骤。info() 方法提供了关于 DataFrame 的综合信息,包括样本数量、特征数量、各列的数据类型以及非空值计数,有助于快速判断是否存在缺失值、数据类型是否一致等问题。

该代码调用 Pandas 的 info() 方法,输出数据框的元信息。它会显示数据集的总行数(样本数)、列数(字段数)、每列的名称、非空值数量(Non-Null Count)以及对应的数据类型(Dtype),从而全面反映数据的结构特征。

python 复制代码
print("\n数据摘要信息:")
print(data.info())

从输出结果可见,该数据集共包含 569 个样本31 个字段 ,其中前 30 列为肿瘤形态学特征,均为 float64 类型,表示连续数值;最后一列为标签列 diagnosis,类型为 int32,代表二分类结果(0 表示恶性,1 表示良性)。所有列的 Non-Null Count 均为 569,表明 无任何缺失值,数据完整性高,无需进行缺失值填充或删除操作。此外,数据类型统一且符合预期,无需转换,极大简化了预处理流程,为后续建模提供了高质量的数据基础。

3.1.3 特征描述性统计

在数据探索过程中,描述性统计分析是理解特征分布特性的核心手段。通过 describe() 方法可以快速获取每个数值型特征的关键统计指标,如均值、标准差、四分位数和极值等,从而评估其集中趋势、离散程度与分布形态。本节对特征矩阵进行描述性统计分析,旨在揭示各特征的数值范围、变异程度及潜在偏态情况,为后续的数据预处理(如标准化)和建模策略提供依据。

该代码调用 Pandas 的 describe() 方法,计算特征矩阵 X 的基本统计量,包括样本数量(count)、均值(mean)、标准差(std)、最小值(min)、25%分位数(25%)、中位数(50%)、75%分位数(75%)和最大值(max)。使用 .round(2) 将结果保留两位小数,提升可读性,便于直观比较不同特征的分布特性。

python 复制代码
print("\n特征描述性统计(保留2位小数):")
print(X.describe().round(2))  # 仅对特征进行统计,保留2位小数

从输出结果可见,30个特征在数值范围和离散程度上存在显著差异。例如,area_worst 最大值达 4254.00,最小值为 135.80,极差超过 4100,而 fractal_dimension_mean 的极差仅为 0.16,表明不同特征的尺度差异极大。这种不一致性会导致距离类算法(如 KNN)在计算样本间相似度时,高尺度特征(如面积)主导距离贡献,而低尺度特征(如纹理误差)被忽略,造成模型偏差。此外,部分特征的标准差较大(如 area_mean 标准差为 351.91),说明数据波动剧烈;而另一些特征(如 smoothness_se 标准差仅 0.01)则高度集中。分布形态方面,多数特征的均值与中位数接近(如 radius_mean 均值 14.13 vs 中位数 13.37),呈近似对称分布;但也有特征如 area_se 均值(56.14)远大于中位数(24.53),呈现明显右偏,提示存在异常值或长尾分布。综上,这些特征需进行标准化处理以消除尺度影响,并结合偏态情况考虑是否进行对数变换等进一步预处理。

3.2 目标变量类别分布分析

类别分布分析是分类任务中至关重要的探索性步骤,用于评估目标变量的样本比例是否均衡。若某一类别样本显著少于其他类别,则可能导致模型偏向多数类,影响少数类的识别能力。本节通过可视化手段展示乳腺癌数据集中良性与恶性肿瘤的样本数量分布,判断是否存在类别不平衡问题,并为后续建模策略(如是否采用过采样、欠采样或调整类别权重)提供依据。

该代码使用 Seaborn 的 countplot 绘制目标变量 y 的频数分布图,其中 x=y 表示以诊断结果为分类变量。通过设置中文字体和样式增强可读性,同时在每个柱子上方添加具体数值标签,直观显示各类别样本数量。最终将图表保存为图像文件,便于报告展示。

python 复制代码
import matplotlib.pyplot as plt
import seaborn as sns

# 设置可视化风格
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
print("\n目标变量类别分布:")
print(y.value_counts())
# 绘制类别分布柱状图
plt.figure(figsize=(8, 5))
ax = sns.countplot(x=y, hue=y, palette='pastel')
ax.set_title('乳腺癌类别分布(0=恶性,1=良性)', fontsize=14)
ax.set_xlabel('诊断结果', fontsize=12)
ax.set_ylabel('样本数量', fontsize=12)

# 添加数值标签
for p in ax.patches:
    ax.text(s=p.get_height(), x=p.get_x() + p.get_width() / 2., y=p.get_height(), ha='center', va='bottom', fontsize=12)

plt.savefig('目标变量类别分布.png')

从生成的柱状图可见,良性肿瘤(标签为 1)样本数量为 357 个,恶性肿瘤(标签为 0)样本数量为 212 个,两类样本比例约为 1.7:1。虽然良性样本略多,但未出现极端不平衡(如 9:1 或更高),属于机器学习中可接受的范围。在此比例下,大多数分类算法(如逻辑回归、随机森林)仍能有效学习两类特征差异,无需额外处理类别不平衡问题。

3.3 目标变量类别与特征相关性分析

在分类任务中,理解各特征与目标变量之间的关联强度有助于识别对预测结果影响最大的关键特征。本节通过计算每个特征与诊断标签(良性/恶性)之间的斯皮尔曼相关系数,评估其单调关系的强弱,从而筛选出具有高判别能力的特征。由于目标变量为二分类整数标签(0/1),使用非参数的斯皮尔曼相关系数更为稳健,能有效捕捉非线性趋势。结合可视化排序,可直观呈现特征的重要性,为后续特征选择和模型优化提供依据。

该代码首先调用 X.corrwith(y, method='spearman') 计算每个特征与标签之间的斯皮尔曼相关系数,并按降序排列;随后绘制水平条形图,正相关系数以绿色表示,负相关以红色表示,便于快速识别关键特征。图形清晰展示各特征对分类任务的贡献方向与强度。

python 复制代码
# 计算每个特征与目标标签的相关系数(斯皮尔曼相关系数)
feature_label_corr = X.corrwith(y, method='spearman').sort_values(ascending=False)
print("特征与目标标签的相关系数(降序):")
print(feature_label_corr.round(2))

# 绘制水平柱状图
plt.figure(figsize=(12, 10))
colors = ['green' if x > 0 else 'red' for x in feature_label_corr.values]
feature_label_corr.plot(kind='barh', color=colors)
plt.title('特征与类别的相关性条形图', fontsize=14)
plt.xlabel('相关系数', fontsize=12)
plt.ylabel('特征名称', fontsize=12)
plt.tight_layout()
plt.grid(axis='x', alpha=0.5)
plt.savefig('特征与类别相关性排序.png')

从输出结果可见,多数特征与诊断标签呈显著负相关,其中 worst perimeter、worst radius、worst area 等"最差"系列特征相关系数接近 -0.8,表明其数值越大,越倾向于恶性肿瘤,是区分良恶性的最强指标。而 mean concavity、mean concave points、worst concavity 等反映边缘不规则性的特征也表现出较强负相关(约 -0.7~ -0.8),进一步验证了恶性肿瘤形态更不规则的医学规律。少数特征如 symmetry error、smoothness error 相关系数接近 0,表明其对分类贡献较小。整体来看,尺寸和凹陷类特征主导分类过程,在KNN建模时应优先保留这些高相关特征,剔除低相关项以提升模型效率与准确性。

3.4 关键特征分布分析

在分类任务中,理解关键特征在不同类别之间的分布差异对于模型构建具有重要意义。本节通过绘制核密度估计图(Kernel Density Estimate, KDE) ,直观展示多个与诊断结果高度相关的特征在良性(diagnosis=1)和恶性(diagnosis=0)两类样本中的概率密度分布情况。KDE是一种非参数方法,能够平滑地估计数据的概率密度函数,从而揭示数据的分布形态、集中趋势及分离程度。

从医学角度出发,乳腺肿瘤的良恶性往往与其细胞核的形态学异常程度密切相关:恶性肿瘤通常表现出更大的尺寸、更高的不规则性(如凹陷、边缘粗糙等)。因此,我们重点关注那些与"最差"(worst)或"凹度"(concavity)相关的特征,这些特征在先前的相关性分析中已显示出较强的判别能力。通过观察这些特征在两类样本中的分布是否明显分离,可以判断其作为分类依据的有效性,并为后续特征选择和模型训练提供支持。

python 复制代码
# 选择关键特征
key_features = feature_label_corr[abs(feature_label_corr) > 0.5].index.tolist()

# 绘制核密度图,KDE:核密度估计
plt.figure(figsize=(18, 12))
feature = key_features
for i in range(len(feature)):
    plt.subplot(3, 5, i + 1)
    sns.kdeplot(
        data=data,
        x=feature[i],
        hue='diagnosis',
        fill=True,
        common_norm=False,  # 每个类别单独归一化
        alpha=0.5
    )
    plt.title(f'{feature[i]} 分布')
    plt.xlabel(feature[i])
plt.tight_layout()
plt.savefig('乳腺癌关键特征KDE分布.png')

从生成的核密度图可以看出,多个关键特征在良性与恶性肿瘤之间呈现出明显的分布差异:

  • worst_perimeterworst_radiusworst_area:这三个"最差"系列的尺寸特征中,恶性肿瘤(蓝色)的密度峰值明显偏向更高数值区间,而良性肿瘤(橙色)集中在较低值区域。这表明恶性肿瘤的细胞核通常更大、更异常,具备更强的区分度。

  • mean_concavityworst_concavitymean_concave_pointsworst_concave_points:反映细胞核边缘凹陷程度的特征,在恶性样本中表现出更高的值和更宽的分布范围,且与良性样本的分布几乎无重叠,说明这些特征对识别恶性肿瘤极为敏感。

  • mean_compactnessworst_compactness:紧凑度越高表示形状越不规则,恶性肿瘤在这两项上的分布也显著偏移至高值区,进一步验证了恶性肿瘤形态异质性强的特点。

  • radius_errorperimeter_errorarea_error:误差项反映了测量的波动性,恶性肿瘤在这些指标上也表现出更大的变异性和更高的均值,可能暗示其结构不稳定。

总体来看,大多数高相关特征在两类样本间具有良好的可分性,尤其是在"最差"系列和"凹度"类特征上,分布几乎不重叠,说明它们能有效帮助KNN算法进行邻近点搜索和分类决策。这些发现不仅增强了我们对数据的理解,也为后续建模提供了明确的方向,应优先保留这些具有强判别能力的特征,以提升KNN模型的预测性能。

3.5 关键特征相关性分析

在构建分类模型之前,理解特征之间的相互关系至关重要。高度相关的特征可能导致多重共线性 问题,影响模型的稳定性和解释性,尤其是在基于距离的算法(如KNN)中,冗余特征可能放大某些维度的影响,降低模型泛化能力。因此,本节通过计算关键特征之间的皮尔逊相关系数,并绘制热力图,直观展示各特征间的线性相关程度。

选取了在前序分析中特征与类别相关性绝对值大于0.5的关键特征,这些特征主要反映肿瘤细胞核的尺寸、形状不规则性和边缘凹陷等重要形态学指标。通过分析它们之间的相关性,可以识别出是否存在信息重叠的特征对,从而为后续的特征选择或降维提供依据。例如,若两个特征高度正相关(如 >0.8),则可考虑保留其中一个以减少冗余,提升模型效率与鲁棒性。

python 复制代码
import numpy as np

# 计算特征间的相关系数(皮尔逊相关系数)
corr_matrix = X[key_features].corr(method='pearson')
# 创建掩码,只保留下三角部分(包括对角线)
mask = np.triu(m=np.ones_like(corr_matrix, dtype=bool), k=1)
# 绘制热力图
plt.figure(figsize=(10, 8))
sns.heatmap(data=corr_matrix, mask=mask, annot=True, cmap='coolwarm', fmt='.2f', linewidths=0.5)
plt.title('关键特征间相关性热力图', fontsize=14)
plt.tight_layout()
plt.savefig('关键特征间相关性热力图.png')

从生成的热力图可以看出,所选关键特征之间普遍存在较强的正相关关系,符合生物学逻辑:

  • 尺寸类特征高度相关

    • worst_radiusworst_perimeterworst_area 三者之间的相关系数均超过 0.97,表明它们在描述肿瘤最大形态时几乎完全一致,属于高度冗余的特征。
    • 同样,mean_radiusmean_perimetermean_area 也表现出较强正相关(>0.7),说明平均尺寸特征也具有一定的信息重叠。
  • 凹陷与不规则性特征群

    • mean_concavitymean_concave_points 相关系数达 0.82worst_concavityworst_concave_points0.91,说明凹陷程度与凹点数量密切相关,二者共同反映了细胞核边缘的异常程度。
    • 此外,mean_compactnessmean_concavity 的相关系数为 0.88,进一步验证了"紧凑度"与"凹度"在几何上的对立关系(越凹则越不紧凑)。
  • 误差项与主特征相关性较弱

    • radius_errorarea_error 等虽与其他特征有一定相关性(约0.3~0.5),但整体偏低,说明测量波动性相对独立于总体形态,可作为补充信息保留。

综上所述,该热力图揭示了多个特征组合之间的强相关性 ,提示我们在后续建模过程中应谨慎处理这些冗余变量。例如,在KNN中,如果同时包含worst_radiusworst_area,由于它们几乎线性相关,可能会导致某一个特征主导距离计算,从而降低模型鲁棒性。

3.6 关键特征的类别区分能力验证

在分类任务中,特征的判别能力(即其在不同类别之间是否存在显著差异)是决定模型性能的关键因素。为了进一步验证前文筛选出的高相关性特征是否具备良好的类别区分效果,本节采用箱线图(Boxplot) 对这些关键特征在良性(diagnosis=1)与恶性(diagnosis=0)两类样本中的分布进行可视化对比。

箱线图能够清晰地展示数据的中位数、四分位距、异常值等统计信息,特别适合用于比较两个或多个组之间的分布差异。通过观察各类别下特征的中心位置(中位数)、离散程度(箱体高度)以及异常值分布,可以直观判断该特征是否能有效将两类样本区分开来。例如,若某一特征在恶性肿瘤中的中位数远高于良性肿瘤,且两组箱体无重叠,则说明该特征具有很强的判别力。

选取了与目标变量斯皮尔曼相关系数绝对值大于0.5的特征作为"关键特征",这些特征已在相关性分析中被确认为对分类结果有较强影响。接下来通过箱线图验证其实际分布上的可分性,从而为后续建模提供更可靠的依据。

python 复制代码
# 选择关键特征
key_features = feature_label_corr[abs(feature_label_corr) > 0.5].index.tolist()

# 绘制箱线图
plt.figure(figsize=(18, 12))
# enumerate() 函数同时返回索引和元素值,参数 1 表示索引从 1 开始(默认从 0 开始)
for i, feature in enumerate(key_features, 1):
    plt.subplot(3, 5, i)
    sns.boxplot(x=y, y=data[feature], hue=y, palette='Set3')
    plt.title(f'{feature}在不同类别中的分布', fontsize=12)
    plt.xlabel('诊断结果(0=恶性,1=良性)', fontsize=10)
    plt.ylabel(feature, fontsize=10)
    plt.grid(axis='y', alpha=0.3)

plt.tight_layout()
plt.savefig('关键特征类别区分箱线图.png')

从箱线图可见,所选关键特征在良性(1)与恶性(0)肿瘤之间均表现出显著的分布差异。例,尤其是在"最差"系列和"凹度"类特征上,箱体几乎没有交集,说明它们对区分良恶性具有极高的判别价值。此外,多数特征的四分位距(IQR)较小,说明数据集中趋势明确,异常值较少。虽然部分特征如 mean radius 存在少量重叠,但总体仍能有效分离两类。这验证了相关性分析的结果,确认这些特征是构建KNN模型的理想输入,可显著提升分类性能。

从图中可见部分特征存在异常值,但这些值源于真实有效的医学观测,反映了肿瘤在形态学上的极端但合理的生物学变异,具有重要的临床意义,因此无需剔除或修正。保留这些样本有助于模型更好地识别高风险恶性病例,提升实际应用中的泛化能力与鲁棒性。

4. 数据预处理

随机森林模型因采用特征随机采样机制,对特征尺度与多重共线性均不敏感,无需强制进行标准化或共线性处理,极大降低了预处理成本。前文相关性分析显示,部分特征间存在极强的线性相关(相关系数>0.97),但随机森林通过每棵树的特征随机采样机制,可自然缓解多重共线性对模型的干扰,无需额外处理。

4.1 数据集划分

为客观评估随机森林模型的泛化能力,需采用分层抽样策略划分训练集与测试集,确保两类样本在划分后的数据集中比例与原始数据集一致,避免因类别分布失衡导致模型评估偏差。考虑到数据集样本量(569个)适中,按8:2比例划分训练集(用于模型训练与参数调优)和测试集(用于独立性能验证),同时设置固定随机种子保证实验可复现性。

python 复制代码
from sklearn.model_selection import train_test_split

X_selected = X[key_features]
# 划分训练集(80%)和测试集(20%)
X_train, X_test, y_train, y_test = train_test_split(
    X_selected, y,
    test_size=0.2,
    random_state=42,
    stratify=y  # 分层抽样,保持类别分布一致
)

# 查看划分后的数据形状
print(f"训练集特征矩阵形状:{X_train.shape}")
print(f"测试集特征矩阵形状:{X_test.shape}")
print(f"训练集类别分布:\n{y_train.value_counts(normalize=True).round(2)}")
print(f"测试集类别分布:\n{y_test.value_counts(normalize=True).round(2)}")

划分结果验证:训练集包含455个样本,测试集包含114个样本;训练集中良性样本占比63%、恶性样本占比37%,测试集中良性样本占比63%、恶性样本占比37%,与原始数据集分布一致,分层抽样有效维持了数据的类别结构,为后续模型训练与评估奠定了可靠基础。

5. 随机森林模型构建

随机森林的性能高度依赖于超参数设置,核心参数包括决策树数量(n_estimators)、每棵树的最大深度(max_depth)、节点拆分的最小样本数(min_samples_split)等。需通过网格搜索结合交叉验证,筛选最优参数组合,平衡模型复杂度与泛化能力。同时,利用随机森林的类别权重调节功能,缓解数据集轻微类别不平衡带来的影响。

5.1 确定最优参数组合

随机森林的性能受多维度超参数共同影响,需通过系统性调优平衡模型的拟合能力与泛化能力。本次参数调优以决策树数量(n_estimators)、单棵树最大深度(max_depth)、叶节点最小样本数(min_samples_leaf)、节点拆分准则(criterion)为核心调优维度,构建包含多组候选值的参数网格。

为保证调优结果的可靠性,采用5折交叉验证策略,以F1分数为优化目标(适配乳腺癌数据的类别不平衡特性),通过网格搜索遍历所有参数组合并筛选最优解。同时在模型初始化时设置类别权重平衡,进一步缓解类别分布差异对训练的影响。

python 复制代码
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV

# 定义超参数网格
param_grid = {
    'n_estimators': [100, 200, 300, 400, 500],
    'max_depth': [3, 5, 7, 9],
    'min_samples_leaf': [2, 3, 5, 7],
    'criterion': ['gini', 'entropy']
}

# 初始化随机森林模型
rf_base = RandomForestClassifier(random_state=42, class_weight='balanced', n_jobs=-1)

# 网格搜索(5折交叉验证,以F1分数为优化目标)
grid_search = GridSearchCV(
    estimator=rf_base,
    param_grid=param_grid,
    cv=5,
    scoring='f1'
)

# 在训练集上执行网格搜索
grid_search.fit(X_train, y_train)

# 输出最优参数与最优交叉验证分数
print("最优参数组合:", grid_search.best_params_)
print("最优交叉验证F1分数:", grid_search.best_score_.round(4))

调优结果显示,最优参数组合为拆分准则选择entropy、单棵树最大深度设为5、叶节点最小样本数取3、决策树数量为400;对应的5折交叉验证F1分数达0.9614,说明该参数组合在训练集上既能通过足够数量的决策树保证集成学习的稳定性,又通过树深度、叶节点样本数的约束避免了过拟合,为后续模型的泛化性能奠定了基础。

5.2 模型构建(模型选择、训练、预测)

基于网格搜索得到的最优参数组合,构建最终的随机森林分类模型,并在独立测试集上完成预测验证。

首先以最优参数初始化随机森林模型,选择entropy作为节点拆分准则(更关注类别信息的不确定性),设置单棵树最大深度为5、叶节点最小样本数为3以控制单棵树的复杂度,同时通过400棵决策树的集成学习保证模型的稳定性;结合类别权重平衡策略,进一步适配数据集的类别分布特性。随后利用训练集对模型进行参数学习,完成模型训练。

模型训练完成后,在测试集上同步输出两类结果:一是直接的类别预测标签,二是样本属于"恶性""良性"的概率值,以此体现模型对预测结果的置信度。为增强结果的可读性,将数值型标签转换为"恶性""良性"的类别名称,并整合为包含"真实标签""预测标签""恶性概率""良性概率"的结构化表格,最终导出为文件留存。

python 复制代码
# 基于最优参数构建最终模型
# dt_best = grid_search.best_estimator_
dt_best = RandomForestClassifier(
    criterion='entropy',
    max_depth=5,
    min_samples_leaf=3,
    n_estimators=400,
    random_state=42,
    class_weight='balanced',
    n_jobs=-1
)
dt_best.fit(X_train, y_train)
# 在测试集上评估最终模型
y_pred_best = dt_best.predict(X_test)
y_pred_proba_best = dt_best.predict_proba(X_test)

# 将类别标签转换为类别名称
y_test_names = y_test.reset_index(drop=True).replace({0: '恶性', 1: '良性'})
y_pred_names = pd.Series(y_pred_best).replace({0: '恶性', 1: '良性'})

# 构建测试集预测结果DataFrame
test_pred_df = pd.DataFrame({
    '真实标签': y_test_names,
    '预测标签': y_pred_names,
    '恶性概率': y_pred_proba_best[:, 0].round(4),
    '良性概率': y_pred_proba_best[:, 1].round(4)
})

test_pred_df.to_csv('最优模型测试集预测结果及概率.csv', index=False)

从部分预测结果可见,多数样本的预测概率具有明显的倾向性(如部分恶性样本的恶性概率达1.0、良性样本的良性概率接近1),说明模型对这类样本的分类决策置信度较高;仅少数样本的两类概率相对接近(如某良性样本的恶性概率为0.6803),对应模型的误判情况,整体呈现出较强的分类区分度,为后续模型性能评估提供了具体的预测依据。

6. 随机森林模型评估

6.1 核心评估指标计算

为全面验证随机森林模型的分类性能,从整体正确性、类别区分度、特征贡献度等维度,选取准确率、混淆矩阵、AUC值、分类报告、特征重要性等指标进行量化分析。

首先通过准确率 反映模型在测试集上的整体预测正确率;借助混淆矩阵 明确恶性、良性样本的真实与预测结果对应关系,直观呈现误判分布;AUC值 用于衡量模型对两类样本的区分能力;分类报告 则细化至每个类别,输出精确率、召回率与F1分数,适配乳腺癌数据的类别不平衡特性;特征重要性则体现各输入特征对模型决策的贡献程度。

python 复制代码
from sklearn.metrics import (accuracy_score, classification_report, confusion_matrix, roc_auc_score)
import pandas as pd

# 准确率
test_accuracy = accuracy_score(y_test, y_pred_best)
print(f"\n模型准确率(Accuracy):{test_accuracy:.4f}")

# 混淆矩阵(带标签)
cm = confusion_matrix(y_test, y_pred_best)
cm_df = pd.DataFrame(cm, index=cancer.target_names, columns=cancer.target_names)
print("\n混淆矩阵(带标签):")
print(cm_df)

# AUC计算(需预测概率)
auc = roc_auc_score(y_test, y_pred_proba_best[:, 1])
print(f"\n模型AUC值(Area Under Curve):{auc:.4f}")

# 输出详细分类报告(按类别展示指标)
cr = classification_report(y_test, y_pred_best, target_names=cancer.target_names, digits=4)
print("\n分类报告:")
print(cr)

# 特征重要性计算与排序
feature_importance = pd.DataFrame({
    '特征名称': key_features,
    '重要性': dt_best.feature_importances_
}).sort_values(by='重要性', ascending=False)
print("\n特征重要性排序:")
print(feature_importance)

从评估结果可见:

  • 整体性能:模型准确率达0.9561,说明超95%的测试样本被正确分类;AUC值接近0.99,表明模型对两类样本的区分能力极强;
  • 类别表现:分类报告显示,恶性类别的F1分数为0.9412、良性类别的F1分数为0.9650,两类性能均衡------其中恶性类别的召回率达0.9524(真实恶性样本中95%被正确识别),良性类别的精确率达0.9718(预测为良性的样本中97%真实为良性),既降低了恶性肿瘤的漏诊风险,也减少了良性样本的过度医疗可能;
  • 混淆矩阵:42个恶性样本仅2例被误判为良性,72个良性样本仅3例被误判为恶性,误判规模极小;
  • 特征重要性:"worst perimeter(最差周长)""worst radius(最差半径)""mean concave points(平均凹点)"等形态学特征是模型决策的核心依据,与医学认知高度契合。

这些指标共同表明,该随机森林模型在威斯康星乳腺癌数据集上具备优异的分类性能与临床实用价值。

6.2 可视化评估

6.2.1 混淆矩阵可视化

为更直观地呈现随机森林模型在测试集上的分类结果分布,将混淆矩阵以热力图形式可视化,通过颜色深浅与数值标注结合的方式,清晰展示不同类别样本的真实与预测标签对应关系。

可视化过程中采用红色系配色(颜色越深代表对应单元格的样本数量越多),并在单元格内标注具体样本数,同时在标题中补充模型整体准确率,增强图表的信息密度与可读性。

python 复制代码
import seaborn as sns
import matplotlib.pyplot as plt

# 绘制混淆矩阵热力图
plt.figure(figsize=(8, 6))
sns.heatmap(
    data=cm,
    annot=True,  # 显示单元格数值
    fmt='d',  # 数值格式为整数
    cmap='Reds',  # 配色方案
    # xticklabels=cancer.target_names,
    # yticklabels=cancer.target_names
    xticklabels=['恶性(0)', '良性(1)'],
    yticklabels=['恶性(0)', '良性(1)']
)
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.title('混淆矩阵(准确率={:.4f})'.format(test_accuracy))
plt.tight_layout()
plt.savefig('混淆矩阵.png')

从热力图结果可直观观察到:

  • 热力图的深色区域集中在对角线位置("真实恶性-预测恶性""真实良性-预测良性"),说明绝大多数样本被正确分类;
  • 非对角线区域(误判样本)的颜色较浅且数值极小,对应仅2例恶性样本被误判为良性、3例良性样本被误判为恶性,误判规模远低于正确分类样本量。

该热力图通过视觉化方式清晰印证了模型的高分类正确率,同时直观呈现了误判的类别分布,为后续模型优化方向提供了清晰的参考依据。

6.2.2 ROC曲线与AUC值可视化

ROC曲线是评估二分类模型区分能力的核心工具,通过横轴"假阳性率(FPR,即恶性样本被误判为良性的比例)"与纵轴"真阳性率(TPR,即良性样本被正确识别的比例)"的变化关系,直观呈现模型在不同分类阈值下的性能表现;而AUC值(ROC曲线下的面积)是对曲线的量化总结,值越接近1代表模型对两类样本的区分能力越强。

本次可视化中,以深红色曲线呈现随机森林模型的ROC变化趋势,并添加"随机猜测"基准线(AUC=0.5)作为参照:若模型性能与随机猜测相当,曲线会贴合基准线;若模型具备有效区分能力,曲线则会向图的左上角(高TPR、低FPR)靠拢。

python 复制代码
from sklearn.metrics import roc_curve, roc_auc_score

# 计算ROC曲线坐标
fpr, tpr, thresholds = roc_curve(y_test, y_pred_proba_best[:, 1])
auc = roc_auc_score(y_test, y_pred_proba_best[:, 1])

# 绘制ROC曲线
plt.figure(figsize=(8, 6))
# 绘制模型ROC曲线
plt.plot(fpr, tpr, color='darkred', lw=2, label=f'KNN模型(AUC = {auc:.4f})')
# 绘制随机猜测基准线(AUC=0.5)
plt.plot([0, 1], [0, 1], color='gray', lw=1, linestyle='--', label='随机猜测(AUC=0.5)')
# 设置标签与格式
plt.xlabel('假阳性率(FPR)')
plt.ylabel('真阳性率(TPR)')
plt.title('模型ROC曲线')
plt.legend(loc='lower right')
plt.grid(alpha=0.3)
plt.savefig('模型ROC曲线.png')

从结果可见:

  • 模型ROC曲线显著偏离随机猜测基准线,在假阳性率极低的区间内(FPR≈0),真阳性率已快速接近1,说明模型能在几乎不产生误判良性样本的前提下,高效识别恶性样本;
  • 对应的AUC值达0.9914,接近理论最优值1,进一步验证了模型对恶性、良性样本的极强区分能力------这意味着在乳腺癌诊断场景中,该模型既能最大限度降低恶性肿瘤的漏诊风险,又能严格控制对良性样本的误判,兼顾了诊断的准确性与合理性。
6.2.3 预测概率分布可视化

预测概率分布可视化通过核密度图,呈现不同真实类别样本的"预测为良性的概率"分布特征,核心作用是直观判断模型对两类样本的概率区分度------若两类分布重叠越少,说明模型对不同类别样本的概率预测越清晰,分类决策的置信度越高。

本次可视化中,以红色填充区域代表"真实恶性"样本的预测概率分布,绿色填充区域代表"真实良性"样本的分布,并添加默认分类阈值(0.5)辅助观察:当样本预测为良性的概率≥0.5时,模型判定为良性;反之则判定为恶性。

python 复制代码
import seaborn as sns
import matplotlib.pyplot as plt

# 提取良性样本(真实标签1)和恶性样本(真实标签0)的预测良性概率
# 真实恶性→预测良性的概率
malignant_proba = test_pred_df[test_pred_df['真实标签'] == '恶性']['良性概率']
# 真实良性→预测良性的概率
benign_proba = test_pred_df[test_pred_df['真实标签'] == '良性']['良性概率']

# 绘制预测概率核密度图
plt.figure(figsize=(10, 6))
# 绘制真实恶性样本的预测概率分布
sns.kdeplot(data=malignant_proba, fill=True, color='red', alpha=0.5, label='真实恶性(0)')
# 绘制真实良性样本的预测概率分布
sns.kdeplot(data=benign_proba, fill=True, color='green', alpha=0.5, label='真实良性(1)')
# 添加分类阈值线(默认0.5)
plt.axvline(x=0.5, color='black', linestyle='--', linewidth=2, label='分类阈值=0.5')
# 设置标签与格式
plt.xlabel('预测为良性(1)的概率')
plt.ylabel('概率密度')
plt.title('决策树模型预测概率分布')
plt.xlim(0, 1)  # 概率范围限定在0-1
plt.grid(alpha=0.3)
plt.legend()
plt.savefig('预测概率分布.png')

从结果可见:

  • 真实恶性样本的预测良性概率集中在0-0.2区间(多数接近0),真实良性样本的预测良性概率集中在0.8~1区间(多数接近1),两类分布的重叠区域极小;
  • 分类阈值(0.5)恰好处于两类分布的间隔区域,说明在默认阈值下,模型能通过概率值明确区分恶性与良性样本,仅极少数样本的概率会接近阈值,对应实际的误判情况。

该图从概率维度进一步印证了模型的分类可靠性:两类样本的预测概率分布边界清晰,意味着模型对绝大多数样本的分类决策都具备明确的概率依据,而非模糊判定------这在医疗场景中可帮助医生快速识别"高置信度样本"与"低置信度样本",对低置信度样本可结合临床信息进一步复核,提升诊断的安全性。

6.2.4 特征重要性可视化

特征重要性是随机森林模型的核心解释性指标之一,它量化了每个输入特征在模型分类决策中的贡献程度(得分越高,代表该特征在决策树的节点拆分过程中对样本纯度提升的作用越大)。通过水平条形图可视化特征重要性,可直观明确哪些医学特征是区分乳腺癌良恶性的关键依据。

python 复制代码
# 获取特征重要性
importances = dt_best.feature_importances_
feature_names = X_selected.columns  # 使用你选择的关键特征列名

# 创建 DataFrame 并按重要性降序排序
feature_importance_df = pd.DataFrame({
    'feature': feature_names,
    'importance': importances
}).sort_values(by='importance', ascending=False)

print("\n特征重要性排序:")
print(feature_importance_df.round(4))

# 绘制水平条形图
plt.figure(figsize=(10, 8))
sns.barplot(
    data=feature_importance_df,
    x='importance',
    y='feature',
    hue='feature',
    palette='viridis'
)
plt.title('决策树模型特征重要性', fontsize=14)
plt.xlabel('重要性得分', fontsize=12)
plt.ylabel('特征名称', fontsize=12)
plt.tight_layout()
plt.grid(axis='x', alpha=0.5)
plt.savefig('特征重要性.png')

本次可视化将特征按重要性得分降序排列,条形长度对应得分高低。从结果可见:

  • 核心特征高度突出:"worst perimeter(最差周长)"的重要性得分远超其他特征,是模型分类决策的主导依据;"worst radius(最差半径)""mean concave points(平均凹点)""worst concave points(最差凹点)"等形态学特征紧随其后,共同构成了模型决策的核心特征集;
  • 次要特征贡献有限:"mean concavity(平均凹度)""mean area(平均面积)"等特征的重要性得分较低,对模型决策的影响较小;部分特征的得分接近0,几乎未参与模型的分类逻辑构建。

这一结果与医学认知高度契合------肿瘤的周长、半径、凹点等形态学特征是临床判断良恶性的经典指标,既验证了模型的特征选择逻辑符合实际诊疗的关注重点,也为后续临床特征采集提供了简化方向:可优先聚焦少数核心特征,在保证诊断准确性的同时降低数据采集成本。


四、完整代码

1. 完整代码

python 复制代码
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.datasets import load_breast_cancer

# 设置中文正常显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 设置Pandas全局选项
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)
pd.set_option('display.max_colwidth', 50)
pd.set_option('display.expand_frame_repr', False)

# 加载数据集
cancer = load_breast_cancer()
# 转换为DataFrame便于分析(特征矩阵)
X = pd.DataFrame(data=cancer.data, columns=cancer.feature_names)
# 目标变量(诊断结果)
y = pd.Series(data=cancer.target, name='diagnosis')
print(cancer.target_names)
# 合并特征和标签,便于同时查看
data = pd.concat([X, y], axis=1)  # 按列合并
print("数据前10行:")
print(data.head(10))

print("\n数据摘要信息:")
print(data.info())

print("\n特征描述性统计(保留2位小数):")
print(X.describe().round(2))

import matplotlib.pyplot as plt
import seaborn as sns

# 设置可视化风格
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
print("\n目标变量类别分布:")
print(y.value_counts())
# 绘制类别分布柱状图
plt.figure(figsize=(8, 5))
ax = sns.countplot(x=y, hue=y, palette='pastel')
ax.set_title('乳腺癌类别分布(0=恶性,1=良性)', fontsize=14)
ax.set_xlabel('诊断结果', fontsize=12)
ax.set_ylabel('样本数量', fontsize=12)

# 添加数值标签
for p in ax.patches:
    ax.text(s=p.get_height(), x=p.get_x() + p.get_width() / 2., y=p.get_height(), ha='center', va='bottom', fontsize=12)

plt.savefig('目标变量类别分布.png')

# 计算每个特征与目标标签的相关系数(斯皮尔曼相关系数)
feature_label_corr = X.corrwith(y, method='spearman').sort_values(ascending=False)
print("特征与目标标签的相关系数(降序):")
print(feature_label_corr.round(2))

# 绘制水平柱状图
plt.figure(figsize=(12, 10))
colors = ['green' if x > 0 else 'red' for x in feature_label_corr.values]
feature_label_corr.plot(kind='barh', color=colors)
plt.title('特征与类别的相关性条形图', fontsize=14)
plt.xlabel('相关系数', fontsize=12)
plt.ylabel('特征名称', fontsize=12)
plt.tight_layout()
plt.grid(axis='x', alpha=0.5)
plt.savefig('特征与类别相关性排序.png')

# 选择关键特征
key_features = feature_label_corr[abs(feature_label_corr) > 0.5].index.tolist()

# 绘制核密度图,KDE:核密度估计
plt.figure(figsize=(18, 12))
feature = key_features
for i in range(len(feature)):
    plt.subplot(3, 5, i + 1)
    sns.kdeplot(
        data=data,
        x=feature[i],
        hue='diagnosis',
        fill=True,
        common_norm=False,  # 每个类别单独归一化
        alpha=0.5
    )
    plt.title(f'{feature[i]} 分布')
    plt.xlabel(feature[i])
plt.tight_layout()
plt.savefig('乳腺癌关键特征KDE分布.png')

import numpy as np

# 计算特征间的相关系数(皮尔逊相关系数)
corr_matrix = X[key_features].corr(method='pearson')
# 创建掩码,只保留下三角部分(包括对角线)
mask = np.triu(m=np.ones_like(corr_matrix, dtype=bool), k=1)
# 绘制热力图
plt.figure(figsize=(10, 8))
sns.heatmap(data=corr_matrix, mask=mask, annot=True, cmap='coolwarm', fmt='.2f', linewidths=0.5)
plt.title('关键特征间相关性热力图', fontsize=14)
plt.tight_layout()
plt.savefig('关键特征间相关性热力图.png')

# 选择关键特征
key_features = feature_label_corr[abs(feature_label_corr) > 0.5].index.tolist()

# 绘制箱线图
plt.figure(figsize=(18, 12))
# enumerate() 函数同时返回索引和元素值,参数 1 表示索引从 1 开始(默认从 0 开始)
for i, feature in enumerate(key_features, 1):
    plt.subplot(3, 5, i)
    sns.boxplot(x=y, y=data[feature], hue=y, palette='Set3')
    plt.title(f'{feature}在不同类别中的分布', fontsize=12)
    plt.xlabel('诊断结果(0=恶性,1=良性)', fontsize=10)
    plt.ylabel(feature, fontsize=10)
    plt.grid(axis='y', alpha=0.3)

plt.tight_layout()
plt.savefig('关键特征类别区分箱线图.png')
# =====================================================
from sklearn.model_selection import train_test_split

X_selected = X[key_features]
# 划分训练集(80%)和测试集(20%)
X_train, X_test, y_train, y_test = train_test_split(
    X_selected, y,
    test_size=0.2,
    random_state=42,
    stratify=y  # 分层抽样,保持类别分布一致
)

# 查看划分后的数据形状
print(f"训练集特征矩阵形状:{X_train.shape}")
print(f"测试集特征矩阵形状:{X_test.shape}")
print(f"训练集类别分布:\n{y_train.value_counts(normalize=True).round(2)}")
print(f"测试集类别分布:\n{y_test.value_counts(normalize=True).round(2)}")
# =====================================================
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV

# 定义超参数网格
param_grid = {
    'n_estimators': [100, 200, 300, 400, 500],
    'max_depth': [3, 5, 7, 9],
    'min_samples_leaf': [2, 3, 5, 7],
    'criterion': ['gini', 'entropy']
}

# 初始化随机森林模型
rf_base = RandomForestClassifier(random_state=42, class_weight='balanced', n_jobs=-1)

# 网格搜索(5折交叉验证,以F1分数为优化目标)
grid_search = GridSearchCV(
    estimator=rf_base,
    param_grid=param_grid,
    cv=5,
    scoring='f1'
)

# 在训练集上执行网格搜索
grid_search.fit(X_train, y_train)

# 输出最优参数与最优交叉验证分数
print("最优参数组合:", grid_search.best_params_)
print("最优交叉验证F1分数:", grid_search.best_score_.round(4))
# =====================================================
# 最优参数组合: {'criterion': 'entropy', 'max_depth': 5, 'min_samples_leaf': 3, 'n_estimators': 400}
# 最优交叉验证F1分数: 0.9614
# =====================================================
# 基于最优参数构建最终模型
# dt_best = grid_search.best_estimator_
dt_best = RandomForestClassifier(
    criterion='entropy',
    max_depth=5,
    min_samples_leaf=3,
    n_estimators=400,
    random_state=42,
    class_weight='balanced',
    n_jobs=-1
)
dt_best.fit(X_train, y_train)
# 在测试集上评估最终模型
y_pred_best = dt_best.predict(X_test)
y_pred_proba_best = dt_best.predict_proba(X_test)

# 将类别标签转换为类别名称
y_test_names = y_test.reset_index(drop=True).replace({0: '恶性', 1: '良性'})
y_pred_names = pd.Series(y_pred_best).replace({0: '恶性', 1: '良性'})

# 构建测试集预测结果DataFrame
test_pred_df = pd.DataFrame({
    '真实标签': y_test_names,
    '预测标签': y_pred_names,
    '恶性概率': y_pred_proba_best[:, 0].round(4),
    '良性概率': y_pred_proba_best[:, 1].round(4)
})

test_pred_df.to_csv('最优模型测试集预测结果及概率.csv', index=False)
# =====================================================
from sklearn.metrics import (accuracy_score, classification_report, confusion_matrix, roc_auc_score)
import pandas as pd

# 准确率
test_accuracy = accuracy_score(y_test, y_pred_best)
print(f"\n模型准确率(Accuracy):{test_accuracy:.4f}")

# 混淆矩阵(带标签)
cm = confusion_matrix(y_test, y_pred_best)
cm_df = pd.DataFrame(cm, index=cancer.target_names, columns=cancer.target_names)
print("\n混淆矩阵(带标签):")
print(cm_df)

# AUC计算(需预测概率)
auc = roc_auc_score(y_test, y_pred_proba_best[:, 1])
print(f"\n模型AUC值(Area Under Curve):{auc:.4f}")

# 输出详细分类报告(按类别展示指标)
cr = classification_report(y_test, y_pred_best, target_names=cancer.target_names, digits=4)
print("\n分类报告:")
print(cr)

# 特征重要性计算与排序
feature_importance = pd.DataFrame({
    '特征名称': key_features,
    '重要性': dt_best.feature_importances_
}).sort_values(by='重要性', ascending=False)
print("\n特征重要性排序:")
print(feature_importance)

# =====================================================
import seaborn as sns
import matplotlib.pyplot as plt

# 绘制混淆矩阵热力图
plt.figure(figsize=(8, 6))
sns.heatmap(
    data=cm,
    annot=True,  # 显示单元格数值
    fmt='d',  # 数值格式为整数
    cmap='Reds',  # 配色方案
    # xticklabels=cancer.target_names,
    # yticklabels=cancer.target_names
    xticklabels=['恶性(0)', '良性(1)'],
    yticklabels=['恶性(0)', '良性(1)']
)
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.title('混淆矩阵(准确率={:.4f})'.format(test_accuracy))
plt.tight_layout()
plt.savefig('混淆矩阵.png')
# =====================================================
from sklearn.metrics import roc_curve, roc_auc_score

# 计算ROC曲线坐标
fpr, tpr, thresholds = roc_curve(y_test, y_pred_proba_best[:, 1])
auc = roc_auc_score(y_test, y_pred_proba_best[:, 1])

# 绘制ROC曲线
plt.figure(figsize=(8, 6))
# 绘制模型ROC曲线
plt.plot(fpr, tpr, color='darkred', lw=2, label=f'KNN模型(AUC = {auc:.4f})')
# 绘制随机猜测基准线(AUC=0.5)
plt.plot([0, 1], [0, 1], color='gray', lw=1, linestyle='--', label='随机猜测(AUC=0.5)')
# 设置标签与格式
plt.xlabel('假阳性率(FPR)')
plt.ylabel('真阳性率(TPR)')
plt.title('模型ROC曲线')
plt.legend(loc='lower right')
plt.grid(alpha=0.3)
plt.savefig('模型ROC曲线.png')
# =====================================================
import seaborn as sns
import matplotlib.pyplot as plt

# 提取良性样本(真实标签1)和恶性样本(真实标签0)的预测良性概率
# 真实恶性→预测良性的概率
malignant_proba = test_pred_df[test_pred_df['真实标签'] == '恶性']['良性概率']
# 真实良性→预测良性的概率
benign_proba = test_pred_df[test_pred_df['真实标签'] == '良性']['良性概率']

# 绘制预测概率核密度图
plt.figure(figsize=(10, 6))
# 绘制真实恶性样本的预测概率分布
sns.kdeplot(data=malignant_proba, fill=True, color='red', alpha=0.5, label='真实恶性(0)')
# 绘制真实良性样本的预测概率分布
sns.kdeplot(data=benign_proba, fill=True, color='green', alpha=0.5, label='真实良性(1)')
# 添加分类阈值线(默认0.5)
plt.axvline(x=0.5, color='black', linestyle='--', linewidth=2, label='分类阈值=0.5')
# 设置标签与格式
plt.xlabel('预测为良性(1)的概率')
plt.ylabel('概率密度')
plt.title('决策树模型预测概率分布')
plt.xlim(0, 1)  # 概率范围限定在0-1
plt.grid(alpha=0.3)
plt.legend()
plt.savefig('预测概率分布.png')
# =====================================================
# 获取特征重要性
importances = dt_best.feature_importances_
feature_names = X_selected.columns  # 使用你选择的关键特征列名

# 创建 DataFrame 并按重要性降序排序
feature_importance_df = pd.DataFrame({
    'feature': feature_names,
    'importance': importances
}).sort_values(by='importance', ascending=False)

print("\n特征重要性排序:")
print(feature_importance_df.round(4))

# 绘制水平条形图
plt.figure(figsize=(10, 8))
sns.barplot(
    data=feature_importance_df,
    x='importance',
    y='feature',
    hue='feature',
    palette='viridis'
)
plt.title('决策树模型特征重要性', fontsize=14)
plt.xlabel('重要性得分', fontsize=12)
plt.ylabel('特征名称', fontsize=12)
plt.tight_layout()
plt.grid(axis='x', alpha=0.5)
plt.savefig('特征重要性.png')
相关推荐
懒麻蛇6 小时前
从矩阵相关到矩阵回归:曼特尔检验与 MRQAP
人工智能·线性代数·矩阵·数据挖掘·回归
一水鉴天8 小时前
整体设计 定稿 之15 chat分类的专题讨论(codebuddy)
大数据·分类·数据挖掘
deng120419 小时前
基于LeNet-5的图像分类小结
人工智能·分类·数据挖掘
大千AI助手1 天前
概率单位回归(Probit Regression)详解
人工智能·机器学习·数据挖掘·回归·大千ai助手·概率单位回归·probit回归
陈辛chenxin2 天前
【大数据技术07】分类和聚类算法
神经网络·决策树·分类·聚类·分类算法
hacker7073 天前
openGauss 在K12教育场景的数据处理测评:CASE WHEN 实现高效分类
人工智能·分类·数据挖掘
Lwcah3 天前
Python | LGBM+SHAP可解释性分析回归预测及可视化算法
python·算法·回归
大数据魔法师4 天前
分类与回归算法(六)- 集成学习(随机森林、梯度提升决策树、Stacking分类)相关理论
分类·回归·集成学习
大数据魔法师4 天前
分类与回归算法(五)- 决策树分类
决策树·分类·回归