决策树2

1.信息熵的概念

某个离散随机变量的概率分布为

(1.1)P(X=xi)=pi,i=1,2,...,n

则随机变量X的熵定义为

(1.2)H(X)=i=1npilogpi

点击查看代码

# 熵的计算
def cal_entropy(pk):
    '''
    the entropy is calculated as S = -sum(pk * log(pk), axis=axis).

    :math:`S=-\sum_{i=1}^{n}(p(x)\log p(x))`

    :param pk:概率分布
    :return:
    '''
    return entropy(pk, base=2)


pk = [1 / 2, 1 / 2]
print(cal_entropy(pk))

点击查看代码

def calc_entropy(sub_labels):
    """
    计算机信息熵
    :param sub_labels:当前选择的数据集对应的类别值,一列
    :return: 信息熵
    """
    x_label_array = np.unique(sub_labels)  # 获取不同类别值:好瓜、坏瓜
    entropy = 0.0  # 信息熵
    for x_label in x_label_array:
        sub_y = sub_labels[sub_labels == x_label]  # 布尔索引取每个类别的样本
        p = len(sub_y) / len(sub_labels)  # 各类别所占样本比例p
        entropy -= p * np.log2(p)  # 信息熵累加
    return entropy


2.条件熵的概念

条件熵H(Y|X)表示在已知随机变量X的条件下随机变量Y的不确定性
随机变量X在给定条件下随机变量Y的条件熵H(Y|X),定义为X在给定Y的条件概率分布的熵对X的数学期望

H(Y|X)=i=1npiH(Y|X=xi),pi=P(X=xi),i=1,2,...,n


H(Y|X=xi),

3.信息增益

g(D,A)=H(D)H(D|A)

4.信息增益算法

输入:训练数据集D和特征A
输出:特征A对训练数据集D的信息增益g(D,A)

  • 1.计算数据集D的信息熵H(D)

H(D)=k=1K|Ck||D|log2|Ck||D|


,K=,A3,B4K=12,

  • 2.计算特征A对数据集D的条件熵H(D|A)

H(D|A)=i=1n|Di||D|H(Di)=i=1n|Di||D|k=1K|Cik||Di|log2|Cik||Di|

点击查看代码

def calc_condition_entropy(feature_data, y_labels):
    """
    计算以特征属性feature_data为划分节点的条件熵
    :param feature_data: 递归划分后的样本集,计算某个特征的条件熵,一列
    :param y_labels: 对应某个特征的类别值,一列
    :return: 条件熵
    """
    feature_label_array = np.unique(feature_data)  # 某特征不同的取值,[青绿,乌黑,浅白]
    cond_entropy = 0.0
    for feature_label in feature_label_array:
        sub_y = y_labels[feature_data == feature_label]  # 某特征某个取值所对应的类别值
        feature_ent = calc_entropy(sub_y)  # 某特征某个取值所对应的信息熵
        cond_entropy += len(sub_y) / len(y_labels) * feature_ent
    return cond_entropy

  • 3.计算信息增益

g(D,A)=H(D)H(D|A)

5.信息增益比

gR(D,A)=D,AHA(D)

HA(D)=k=1K|Di||D|log2|Di||D|,nA

6.连续值处理

二分法

将一连续值A从小到大排序好后进行二分法,比如序列{A1,A2,...,An},令切分点依次从小到大迭代

(6.1)TA={Ai+Ai+12|1in1}

迭代计算信息增益,挑出信息增益最大的那个划分

(6.2)g(D,A)=maxtTAg(D,A,t)=maxtTA H(D)λ{,+}|Dtλ||D|H(Dtλ)


7.ID3算法

ID3算法的核心是在决策树各个节点上应用信息增益准则选择特征
输入:训练数据集D,特征集A,阈值ϵ
输出:决策树T

  • 1.按照公式1计算A中各特征对D的信息增益,选择信息增益最大的特征Ag
  • 2.若Ag的信息增益小于阈值ϵ,则返回T,并将D中实例数最大的类Ck作为该节点的类标记
  • 3.否则,对Ag的每一可能值ai,依照Ag=aiD分割为若干非空子集Di,将Di中实例数最大的类作为标记,构建子节点,由节点及其子节点构成T
  • 4.对i个子节点,以Di作为训练集,以AAg,

案例

性别 学历 年龄 风险级别
M 本科 11
M 大专 29
F 本科 18
F 大专 34
M 大专 19
M 本科 25
F 大专 10
M 本科 33
M 大专 40
  • 1.按照公式1计算A中各特征对D的信息增益,选择信息增益最大的特征Ag

H()=i=1npilogpi=49log24929log22939log239=1.5304930567574826

H(|)=[49(24log224+24log224)+29(12log212+12log212)+39(33log233)]=0.6666666666666666

H(|)=[49(24log224+24log224)+29(12log212+12log212)+39(13log213+23log223)]=0.9727652780181631

,{(10,),(11,),(18,),(19,),(25,),(29,),(33,),(34,),(40,)}

1,8

性别 学历 年龄 年龄分类 风险级别
M 本科 11 +
M 大专 29 +
F 本科 18 +
F 大专 34 +
M 大专 19 +
M 本科 25 +
F 大专 10 -
M 本科 33 +
M 大专 40 +

H(|1/8)=[49(34log234+14log214)+29(22log222)+39(33log233)]=0.3605680553151701

以此类推

H(|2/7)=[49(24log224+24log224)+29(22log222)+39(33log233)]=0.4444444444444444

H(|3/6)=[49(24log224+24log224)+29(12log212+12log212)+39(33log233)]=0.6666666666666666

H(|4/5)=[49(24log224+24log224)+29(12log212+12log212)+39(23log223+13log213)]=0.9727652780181631

H(5/4|)=[49(24log224+24log224)+29(12log212+12log212)+39(23log223+13log213)]=0.9727652780181631

H(|6/3)=[49(24log224+24log224)+29(22log222)+39(23log223+13log213)]=0.7505430557959409

H(|7/2)=[49(34log234+14log214)+29(22log222)+39(23log223+13log213)]=0.6666666666666666

H(|8/1)=[49(44log244)+29(22log222)+39(23log223+13log213)]=0.3060986113514965

即最优划分是H(8/1|)=0.3060986113514965

综上3个维度,信息熵增益最大的是H(8/1|)

,40+342=37,<37

{37<37

继续迭代第二个维度

性别 学历 风险级别
M 本科
M 大专
F 本科
F 大专
M 大专
M 本科
F 大专
M 本科

注意这里连续值是还可以继续迭代的,这里为了演示方便,不做深入迭代了

H(|)=[48(24log224+24log224)+28(12log212+12log212)+28(22log222)]=0.75

H(|)=[48(24log224+24log224)+28(12log212+12log212)+28(12log212+12log212)]=1

所以选择性别维度,分支1 性别M 迭代,分支2 性别 F 迭代

{37<37{MF

性别 学历 风险级别
M 本科
M 大专
M 大专
M 本科
M 本科

M
67%33%50%50%

性别 学历 风险级别
F 本科
F 大专
F 大专

F


{37<37{M{67%33%50%50%F{

性别 学历 年龄 风险                 
M 本科 16 67%33%

点击查看代码


每个target_label 对应的样本数量
class_len [(0, 3024), (1, 2831), (2, 230), (3, 2986), (4, 1)]

# 按照数量排序,取含数据量最大的那个类别
max_class, max_len = max(class_len, key=lambda x: x[1])


If # 特征集为空
	投票法,取样本量最大所对应的类别,即叶子节点取  max_class
	Node(LEAF, Class=max_class)
	# 叶子节点,return
Else 特征集不为空
	计算每个特征的信息增益,并选择信息增益最大的特征作为划分节点依据
	if max_gain < epsilon  收益太低
		还是按照投票法,即叶子节点取  max_class
		Node(LEAF, Class=max_class)
		# 叶子节点,return
	else 按照信息增益生成叶子节点
		max_feature, max_gain = 0, 0s
		for feature in features:  # 循环每个特征
			feature_data = train_set.iloc[:, feature] # t
			feature_gain = calc_ent_gain(feature_data, train_lable)
		        if feature_gain > max_gain:
            			max_feature, max_gain = feature, feature_gain
		Node(INTERNAL, best_feature=max_feature)
		# 内部节点,继续递归迭代
		# 递归前先除去已经被选择的特征属性
    		sub_features = list(filter(lambda x: x != max_feature, features)) # 原来可能有n个特征,现在是n-1个特征
		max_feature_col = train_set.iloc[:, max_feature]  # 取出最优特征那一列的所有数据	
		max_feature_unique_values = np.unique(max_feature_col)  # 取出特征所有的离散特征值
		
		#继续循环所有的特征值,添加到当前节点的dict中
		for feature_value in max_feature_unique_values:
        		sub_train_set = train_set[train_set.iloc[:, max_feature] == feature_value]
        		sub_train_label = train_lable[train_set.iloc[:, max_feature] == feature_value]
        		sub_tree = id3_tree_fit(sub_train_set, sub_train_label, sub_features, class_num)
        		tree.add_tree(feature_value, sub_tree)



点击查看代码

import numpy as np
import scipy as sp
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report

epsilon = 1e-3  # 信息增益阀值,小于该阀值,即为叶子节点


def calc_entropy(sub_labels):
    """
    计算机信息熵
    :param sub_labels:当前选择的数据集对应的类别值,一列
    :return: 信息熵
    """
    x_label_array = np.unique(sub_labels)  # 获取不同类别值:好瓜、坏瓜
    entropy = 0.0  # 信息熵
    for x_label in x_label_array:
        sub_y = sub_labels[sub_labels == x_label]  # 布尔索引取每个类别的样本
        p = len(sub_y) / len(sub_labels)  # 各类别所占样本比例p
        entropy -= p * np.log2(p)  # 信息熵累加
    return entropy


def calc_condition_entropy(feature_data, y_labels):
    """
    计算以特征属性feature_data为划分节点的条件熵
    :param feature_data: 递归划分后的样本集,计算某个特征的条件熵,一列
    :param y_labels: 对应某个特征的类别值,一列
    :return: 条件熵
    """
    feature_label_array = np.unique(feature_data)  # 某特征不同的取值,[青绿,乌黑,浅白]
    cond_entropy = 0.0
    for feature_label in feature_label_array:
        sub_y = y_labels[feature_data == feature_label]  # 某特征某个取值所对应的类别值
        feature_ent = calc_entropy(sub_y)  # 某特征某个取值所对应的信息熵
        cond_entropy += len(sub_y) / len(y_labels) * feature_ent
    return cond_entropy


def calc_ent_gain(feature_data, y_labels):
    # 计算某个特征作为划分节点的信息增益
    return calc_entropy(y_labels) - calc_condition_entropy(feature_data, y_labels)


class Node:
    def __init__(self, node_type, Class=None, best_feature=None):
        self.node_type = node_type  # 决策树节点类型(内部节点,叶子节点)
        # 键:最佳信息增益所对应的特征,值:以最佳特征的某个取值为根节点的子树
        self.child_node_dict = dict()
        self.Class = Class  # 叶子节点所对应的列表值,若内部节点则值为None
        self.best_feature = best_feature  # 最佳划分特征

    def add_node(self, key, tree):
        """
        键:最佳信息增益所对应的特征,值:以最佳特征的某个取值为根节点的子树
        :param key: 最佳划分特征 特征值 feature_value
        :param tree: 决策子树
        :return:
        """
        self.child_node_dict[key] = tree

    def predict(self, test_sample):
        """
        递归,根据构建的决策树进行预测
        :param test_sample: 测试样本,单个
        :return: 类别
        """
        if self.node_type == "leaf" or (test_sample[self.best_feature] not in self.child_node_dict):
            return self.Class
        tree = self.child_node_dict.get(test_sample[self.best_feature])
        return tree.predict(test_sample)


def target_encode(y_target):
    """
    对非数值类别进行数值化,不是one-hot编码
    :param y_target: n*1 矩阵,内容是类别值
    :return: n*1 矩阵,类别值对应的数字编码,如5类,则0-4编码
    """
    y_labels = list(set(y_target))
    y_labels_dict = dict()
    for i, label in enumerate(y_labels):
        y_labels_dict[str(float(i))] = label
    y_encode = np.zeros(len(y_target))
    for i, y in enumerate(y_target):
        y_encode[i] = y_labels.index(y)
    return y_encode, len(y_labels), y_labels_dict


def id3_tree_fit(train_set, train_lable, features, class_num):
    """
    递归实现ID3算法,以特征最大的信息增益为划分节点的标准
    1、递归出口是什么?
    2、递归T是什么?
    :param train_set: 递归产生的样本数据集
    :param train_lable: 递归产生的,对应样本数据的类别标签值
    :param features: 特征索引列表
    :return:
    """
    LEAF = "leaf"  # 叶子节点类型
    INTERNAL = "internal"  # 内部节点类型

    label_unique = np.unique(train_lable)  # 当前类别不同的取值
    # 当前节点包含的样本全属于同一类别,无需划分
    if len(label_unique) == 1:
        print("类别:", y_target_dict[str(label_unique[0])])
        return Node(LEAF, Class=label_unique[0])

    # (是,)、(否,) =  (0,9)、(1,8)
    class_len = [(i, len(list(filter(lambda x: x == i, train_lable)))) for i in range(class_num)]
    max_class, max_len = max(class_len, key=lambda x: x[1])
    # 特征集为空,投票法,取样本量最大所对应的类别
    if len(features) == 0:
        print("类别:", y_target_dict[str(max_class)])
        return Node(LEAF, Class=max_class)

    # 计算每个特征的信息增益,并选择信息增益最大的特征作为划分节点依据
    max_feature, max_gain = 0, 0  # 定义最佳划分节点的特征索引和最大信息增益
    for feature in features:  # 针对每个特征计算信息增益
        feature_data = train_set.iloc[:, feature]
        feature_gain = calc_ent_gain(feature_data, train_lable)  # 信息增益
        print("特征:%s,信息增益是:%f" % (feature_names[feature], feature_gain))
        if feature_gain > max_gain:
            max_feature, max_gain = feature, feature_gain

    if max_gain < epsilon:
        print("类别:", y_target_dict[str(max_class)])
        return Node(LEAF, Class=max_class)

    tree = Node(INTERNAL, best_feature=max_feature)  # 构造决策树内部节点

    print("-" * 60)
    print("最佳划分特征:%s,最大信息增益:%f" % (feature_names[max_feature], max_gain))
    print("=" * 60)

    # 递归时,除去已经被选择的特征属性
    sub_features = list(filter(lambda x: x != max_feature, features))
    max_feature_col = train_set.iloc[:, max_feature]  # 当前最佳划分节点的样本值
    max_feature_unique_values = np.unique(max_feature_col)  # [清晰,稍糊,模糊]

    # 递归循环
    for feature_value in max_feature_unique_values:
        print("当特征【%s】,值为【%s】时:" % (feature_names[max_feature], feature_value))
        sub_train_set = train_set[train_set.iloc[:, max_feature] == feature_value]
        sub_train_label = train_lable[train_set.iloc[:, max_feature] == feature_value]
        sub_tree = id3_tree_fit(sub_train_set, sub_train_label, sub_features, class_num)
        tree.add_node(feature_value, sub_tree)

    return tree


def predict(test_set, tree):
    """
    循环对每个测试样本进行预测
    :param test_set: 测试集
    :param tree: 已构建的决策树
    :return:
    """
    predict_result = []  # 预测结果列表
    for features_value in test_set.values:
        y_pred = tree.predict(features_value)
        predict_result.append(y_pred)
    return np.array(predict_result)


if __name__ == "__main__":
    # 读取数据
    raw_data = pd.read_csv('./datasets/nursery.csv').dropna()  # 读取csv数据

    # 数据预处理
    feature_names = raw_data.columns
    X_sample_data = raw_data.iloc[:, :-1]  # 特征样本数据
    y_target = raw_data.iloc[:, -1]  # 目标值
    target_names = np.unique(y_target)
    y_target, class_num, y_target_dict = target_encode(y_target)  # 对非数值类别进行数值化编码,数据集类别数
    feature_len = X_sample_data.shape[1]  # 数据集特征数
    X_train, X_test, y_train, y_test = train_test_split(X_sample_data, y_target, train_size=0.7,
                                                        random_state=0, stratify=y_target)

    print('Start training...')
    tree = id3_tree_fit(X_train, y_train, list(range(feature_len)), class_num)
    print('End training...')

    test_predict = predict(X_test, tree)
    # 效果比对
    for i in range(len(test_predict)):
        if test_predict[i] is None:
            test_predict[i] = epsilon
    y_pred = [int(y) for y in test_predict]  # 类别整数,防止float类别数值
    score = accuracy_score(y_test, y_pred)
    print("The accuracy score is %f\n" % score)
    print(classification_report(y_test, y_pred, target_names=target_names))


8.C4.5算法

C4.5与ID3算法相似,C4.5在生成过程中,用信息增益比来选择特征,其他一样

案例

性别 学历 年龄 风险级别
M 本科 11
M 大专 29
F 本科 18
F 大专 34
M 大专 19
M 本科 25
F 大专 10
M 本科 33
M 大专 40
  • 1.按照公式1计算A中各特征对D的信息增益,选择信息增益最大的特征Ag

H()=i=1npilogpi=49log24929log22939log239=1.5304930567574826

H()=i=1npilogpi=39log23969log269=0.9182958340544896

H()=i=1npilogpi=49log24959log259=0.9910760598382222

H(|)=[49(24log224+24log224)+29(12log212+12log212)+39(33log233)]=0.6666666666666666

gR(|)=1.53049305675748260.66666666666666660.9182958340544896=0.9406842087879477

H(|)=[49(24log224+24log224)+29(12log212+12log212)+39(13log213+23log223)]=0.9727652780181631

gR(|)=1.53049305675748260.97276527801816310.9910760598382222=0.5627497235987718

,{(10,),(11,),(18,),(19,),(25,),(29,),(33,),(34,),(40,)}

1,8

性别 学历 年龄 年龄分类 风险级别
M 本科 11 +
M 大专 29 +
F 本科 18 +
F 大专 34 +
M 大专 19 +
M 本科 25 +
F 大专 10 -
M 本科 33 +
M 大专 40 +

H(|1/8)=0.3605680553151701

H(1/8)=(19log219+89log289)=0.5032583347756457

gR(1/8)=1.53049305675748260.36056805531517010.5032583347756457=2.324700696638972

H(|2/7)=0.4444444444444444

H(2/7)=0.7642045065086203

gR(2/7)=1.53049305675748260.44444444444444440.7642045065086203=1.4211491859355943

H(|3/6)=0.6666666666666666

H(3/6)=0.9182958340544896

gR(3/6)=1.53049305675748260.66666666666666660.9182958340544896=0.9406842087879477

H(|4/5)=0.9727652780181631

H(4/5)=0.9910760598382222

gR(4/5)=1.53049305675748260.97276527801816310.9910760598382222=0.5627497235987718

H(|5/4)=0.9727652780181631

H(5/4)=0.9910760598382222

gR(5/4)=1.53049305675748260.97276527801816310.9910760598382222=0.5627497235987718

H(|6/3)=0.7505430557959409

H(6/3)=0.9182958340544896

gR(6/3)=1.53049305675748260.75054305579594090.9182958340544896=0.849345028080854

H(|7/2)=0.6666666666666666

H(7/2)=0.7642045065086203

gR(7/2)=1.53049305675748260.66666666666666660.7642045065086203=1.1303602409220705

H(|8/1)=0.3060986113514965

H(8/1)=0.5032583347756457

gR(8/1)=1.53049305675748260.30609861135149650.5032583347756457=2.4329342621852956

gR(8/1)

{37<37

继续迭代第二个维度

性别 学历 风险级别
M 本科
M 大专
F 本科
F 大专
M 大专
M 本科
F 大专
M 本科

H()=i=1npilogpi=48log24828log22828log228=1.4843777790598331

H()=i=1npilogpi=38log23858log258=0.954434002924965

H(|)=[48(24log224+24log224)+28(12log212+12log212)+28(22log222)]=0.75

gR(|)=1.48437777905983310.750.954434002924965=0.7694379881786

H()=i=1npilogpi=48log24848log248=1

H(|)=[48(24log224+24log224)+28(12log212+12log212)+38(12log212+12log212)]=1

gR(|)=1.484377779059833111=0.48437777905983315

ID3

{37<37


{37<37{M{67%33%50%50%F{

posted @   筷点雪糕侠  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示