一、实验目的

深入理解决策树、预剪枝和后剪枝的算法原理,能够使用 Python 语言实现带有预剪枝 和后剪枝的决策树算法 C4.5 算法的训练与测试,并且使用五折交叉验证算法进行模型训练 与评估。

二、实验内容

1)从 scikit-learn 库中加载 iris 数据集,使用留出法留出 1/3 的样本作为测试集(注意同分布取样);

2)使用训练集训练分类带有预剪枝和后剪枝的 C4.5 算法;

3)使用五折交叉验证对模型性能(准确度、精度、召回率和 F1 值)进行评估和选 择;

4)使用测试集,测试模型的性能,对测试结果进行分析,完成实验报告中实验三的部分。

 

三、算法步骤、代码、及结果

   1. 算法伪代码

输入: 数据集 D, 属性集 A, 剪枝策略 P(预剪枝或后剪枝)

输出: 决策树 T

 

1. D 中所有样本同属一个类别,返回叶节点;

2. A 为空集,返回叶节点,其类别为 D 中样本最多的类别;

3. A 中每个属性,计算其信息增益率,选择增益率最高的属性作为分裂节点;

4. 根据分裂属性将 D 分成子集,递归调用构建子树;

5. 预剪枝策略:若分裂导致验证集性能下降,则停止分裂;

6. 后剪枝策略:

   a. 对生成的树进行修剪;

   b. 比较剪枝前后的性能,若剪枝后性能无显著下降,则保留剪枝;

7. 返回构建的决策树 T

 

   2. 算法主要代码

完整源代码\调用库方法(函数参数说明)

import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import classification_report, make_scorer, precision_score, recall_score, f1_score

# 数据加载与分割
def load_and_split_data():
    iris = load_iris()
    X, y = iris.data, iris.target
    # 留出法:1/3 的数据作为测试集
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=1/3, random_state=42, stratify=y
    )
    return X_train, X_test, y_train, y_test


# 带预剪枝的决策树
def train_pre_pruned_tree(X_train, y_train, max_depth=3, min_samples_split=5):
    model = DecisionTreeClassifier(
        criterion='entropy',  # 基于信息增益率
        max_depth=max_depth,  # 最大深度限制
        min_samples_split=min_samples_split,  # 节点最小样本数
        random_state=42  # 随机种子
    )
    model.fit(X_train, y_train)
    return model


# 后剪枝实现
def post_prune_tree(model, X_val, y_val):
    tree = model.tree_

    # 递归修剪函数
    def prune(node_id):
        # 如果是叶子节点,直接返回
        if tree.children_left[node_id] == -1 and tree.children_right[node_id] == -1:
            return

        # 递归修剪子节点
        if tree.children_left[node_id] != -1:
            prune(tree.children_left[node_id])
        if tree.children_right[node_id] != -1:
            prune(tree.children_right[node_id])

        # 检查是否可以合并子节点
        if (
            tree.children_left[node_id] != -1
            and tree.children_right[node_id] != -1
        ):
            # 模拟剪枝:移除子节点
            left_child = tree.children_left[node_id]
            right_child = tree.children_right[node_id]

            # 保存当前节点的类别和样本权重
            original_class = tree.value[node_id]
            tree.children_left[node_id] = -1
            tree.children_right[node_id] = -1

            # 重新评估性能
            y_pred = model.predict(X_val)
            acc_after_prune = np.mean(y_pred == y_val)

            # 如果剪枝后性能下降,恢复子节点
            if acc_after_prune < np.mean(model.predict(X_val) == y_val):
                tree.children_left[node_id] = left_child
                tree.children_right[node_id] = right_child

    # 从根节点开始修剪
    prune(0)
    return model


# 五折交叉验证
def cross_validate_model(model, X_train, y_train):
    # 定义评分指标
    scorers = {
        'accuracy': 'accuracy',
        'precision': make_scorer(precision_score, average='macro'),
        'recall': make_scorer(recall_score, average='macro'),
        'f1': make_scorer(f1_score, average='macro')
    }

    # 交叉验证
    results = {}
    for metric, scorer in scorers.items():
        scores = cross_val_score(model, X_train, y_train, cv=5, scoring=scorer)
        results[metric] = scores.mean()
        print(f"{metric.capitalize()} 平均值: {scores.mean():.4f}")
    return results


# 测试模型性能
def evaluate_model(model, X_test, y_test):
    y_pred = model.predict(X_test)
    report = classification_report(y_test, y_pred, output_dict=True, zero_division=0)
    print("测试集性能:")
    print(classification_report(y_test, y_pred, zero_division=0))
    return report


# 主函数
if __name__ == "__main__":
    # 数据加载与分割
    X_train, X_test, y_train, y_test = load_and_split_data()

    # 带预剪枝的决策树训练
    pre_pruned_model = train_pre_pruned_tree(X_train, y_train)

    # 五折交叉验证评估
    print("\n五折交叉验证评估:")
    cross_validate_model(pre_pruned_model, X_train, y_train)

    # 后剪枝
    print("\n开始后剪枝:")
    pruned_model = post_prune_tree(pre_pruned_model, X_test, y_test)

    # 测试集评估
    print("\n测试集评估:")
    evaluate_model(pruned_model, X_test, y_test)

 

 

 

1.train_test_split

来源: sklearn.model_selection.train_test_split

功能: 将数据集划分为训练集和测试集。

参数说明:

X: 特征数据集。

y: 目标标签数据集。

test_size: 测试集所占比例(0-1 之间的浮点数)。

train_size: 训练集所占比例(可选,与 test_size 互斥)。

random_state: 随机种子,用于结果复现。

stratify: 如果为 y,则按目标变量比例分层抽样。

 


 

2. DecisionTreeClassifier

来源: sklearn.tree.DecisionTreeClassifier

功能: 使用决策树对数据进行分类。

参数说明:

criterion: 划分标准,默认值为 "gini";本代码中使用 "entropy"(基于信息增益率)。

max_depth: 决策树的最大深度,用于防止过拟合。

min_samples_split: 每个节点划分所需的最小样本数。

class_weight: 类别权重,可以设置为 "balanced",根据数据集自动调整类别权重。

random_state: 随机种子,用于结果复现。

 


 

3. cross_val_score

来源: sklearn.model_selection.cross_val_score

功能: 使用交叉验证评估模型性能。

参数说明:

estimator: 需要评估的模型。

X: 特征数据集。

y: 目标标签数据集。

cv: 交叉验证的折数,默认为 5

scoring: 评分方法,可选 "accuracy", "precision", "recall", "f1" 等。

 


 

4. classification_report

来源: sklearn.metrics.classification_report

功能: 生成分类任务的详细评估报告,包括准确率、精度、召回率和 F1 值。

参数说明:

y_true: 真实标签。

y_pred: 模型预测结果。

output_dict: 如果为 True,返回字典格式的报告。

zero_division: 默认值为 "warn"。当分母为零时,控制返回的值(0 1)。

 


 

5. make_scorer

来源: sklearn.metrics.make_scorer

功能: 将自定义的评分方法转换为可用于 cross_val_score 的格式。

参数说明:

score_func: 自定义评分函数,如 precision_score, recall_score, f1_score 等。

greater_is_better: 是否更高的分数更好,默认 True

average: 当用于多分类问题时,控制指标计算方式,例如 "macro" "weighted"

 


 

6. np.unique

来源: numpy.unique

功能: 查找数组中的唯一值,并返回每个值的计数。

参数说明:

return_counts: 如果为 True,同时返回每个唯一值的计数。

 


 

7. model.tree_ 属性

来源: sklearn.tree.DecisionTreeClassifier.tree_

功能: 获取决策树的底层数据结构。

主要属性:

children_left: 每个节点的左子节点索引,叶节点为 -1

children_right: 每个节点的右子节点索引,叶节点为 -1

feature: 每个节点分裂时使用的特征索引。

threshold: 每个节点分裂时使用的阈值。

value: 每个节点的类别分布(数量)。

posted on 2024-11-20 11:12  许七安gyg  阅读(6)  评论(0编辑  收藏  举报
$(document).ready(function() { // 禁止右键 $(document).bind("contextmenu", function(){return false;}); // 禁止选择 $(document).bind("selectstart", function(){return false;}); // 禁止Ctrl+C 和Ctrl+A $(document).keydown(function(event) { if ((event.ctrlKey&&event.which==67) || (event.ctrlKey&&event.which==86)) { //alert("对不起,版权所有,禁止复制"); return false; } }); });