0
点赞
收藏
分享

微信扫一扫

决策树(DT)

西曲风 2022-04-23 阅读 94
python

目录

一、什么是决策树

二、决策树学习

1、特征选择

1、ID3:样本集合D对特征A的信息增益

 2、样本集D对特征A 的信息增益比(C4.5)

3、样本集合D的 基尼指数(CART)

4、python代码展示: 

2、决策树生成

3、决策树的剪枝


一、什么是决策树

决策 + 树,我们的决策过程就像树的成长一样。由一个根(节点)不断的发散。

决策树是一种树形结构,其中每个内部节点表示一个属性上的测试,每个分支代表一个测试输出,每个叶节点代表一种类别。

叶节点:存放决策结果

内部节点:内部属性及其对应输出,按照输出选择分支

决策过程:从根节点出发,根据数据的各个属性,计算结果,选择对应的输出分支,直到达到叶子节点,得到结果

下面给出一个比较形象的例子:

当一个男生追求你的时候,总不能随随便便答应他吧!需要选择一下:

相信看到这,已经对于决策树有了初步的了解。

二、决策树学习

一般由三个步骤组成:特征选择、决策树生成、决策树修剪

Quinlan在1986年提出的ID3算法、1993年提出的C4.5算法
Breiman等人在1984年提出的CART算法

决策树学习本质:从训练数据集中归纳出一组分类规则。

  • 需要一个与训练数据矛盾较小的决策树,同时具有很好的泛化能力。
  • 决策树学习的损失函数:通常是正则化的极大似然函数。
  • 决策树学习的策略:损失函数为目标函数的最小化。

1、特征选择

决策树泛化能力差,需要剪枝。

上述三个准则:

1、ID3:样本集合D对特征A的信息增益

  • 熵越大,随机变量的不确定性就越大
  • 信息增益(information gain)表示得知特征X的信息而使得类Y的信息的不确定性减少的程度。
  • 选择信息增益大的

 2、样本集D对特征A 的信息增益比(C4.5)


 

3、样本集合D的 基尼指数(CART)

4、python代码展示: 

def get_data():
    datasets = [['青年', '否', '否', '一般', '否'],
                ['青年', '否', '否', '好', '否'],
                ['青年', '是', '否', '好', '是'],
                ['青年', '是', '是', '一般', '是'],
                ['青年', '否', '否', '一般', '否'],
                ['中年', '否', '否', '一般', '否'],
                ['中年', '否', '否', '好', '否'],
                ['中年', '是', '是', '好', '是'],
                ['中年', '否', '是', '非常好', '是'],
                ['中年', '否', '是', '非常好', '是'],
                ['老年', '否', '是', '非常好', '是'],
                ['老年', '否', '是', '好', '是'],
                ['老年', '是', '否', '好', '是'],
                ['老年', '是', '否', '非常好', '是'],
                ['老年', '否', '否', '一般', '否'],
                ]
    labels = [u'年龄', u'有工作', u'有自己的房子', u'信贷情况', u'分类']
    # 字符串前加 u, 后面字符串以 Unicode 格式 进行编码,一般用在中文字符串前面,防止乱码
    return datasets, labels;
# ---------书上贷款例子-----------------
datasets, labels = get_data()

def cal_entropy(datasets):  # 经验熵H(D)
    data_len = len(datasets)
    label_count = {}
    for i in range(data_len):
        label = datasets[i][-1]
        if label not in label_count:
            label_count[label] = 0
        label_count[label] += 1
    entropy = -sum([(p / data_len) * log(p / data_len, 2) for p in label_count.values()])
    return entropy


def cond_entropy(datasets, axis=0):  # 经验条件熵H(D|A)
    data_len = len(datasets)
    feature_set = {}
    for i in range(data_len):
        feature = datasets[i][axis]
        if feature not in feature_set:
            feature_set[feature] = []
        feature_set[feature].append(datasets[i])
    cond_ent = sum([(len(p) / data_len) * cal_entropy(p) for p in feature_set.values()])
    return cond_ent


def info_gain(entropy, cond_ent):  # 信息增益
    return entropy - cond_ent


def info_gain_train(datasets):	# 基于特征信息增益的特征选择
    count = len(datasets[0]) - 1
    entropy = cal_entropy(datasets)
    best_feature = []
    for i in range(count):
        info_gain_i = info_gain(entropy, cond_entropy(datasets, axis=i))
        best_feature.append((i, info_gain_i))
        print("特征({})- info_gain - {:.3f}".format(labels[i], info_gain_i))
    best_feature_i = max(best_feature, key=lambda x: x[-1])
    print("特征({})的信息增益最大,选为根节点的特征".format(labels[best_feature_i[0]]))

info_gain_train(np.array(datasets))

2、决策树生成

准则使用说明:使用信息增益最大、信息增益比最大或基尼指数最小作为特征选择的准则。

从根节点开始,递归地形成决策树。

  • ID3算法只有树的生成,所以该算法生成的树容易产生过拟合
  • C4.5算法与ID3算法相似,进行了改进。C4.5在生成的过程中,用信息增益比来选择特征。

python代码:

class Node():
    def __init__(self, root=True, label=None, feature_name=None, feature=None):
        self.root = root
        self.label = label
        self.feature_name = feature_name
        self.feature = feature
        self.tree = {}
        self.result = {
            'label:': self.label,
            'feature:': self.feature,
            'tree:': self.tree
        }

    def __repr__(self):  # 类似str方法,更侧重程序员调试
        print('{}'.format(self.result))

    def add_node(self, val, node):
        self.tree[val] = node

    def predict(self, features):
        if self.root is True:
            return self.label
        return self.tree[features[self.feature]].predict(features)


class DTree():
    def __init__(self, epsilon=0.1):  # 信息增益阈值, < epsilon 时,结束决策树展开
        self.epsilon = epsilon
        self._tree = {}

    @staticmethod
    def cal_entropy(datasets):  # 经验熵H(D)
        data_len = len(datasets)
        label_count = {}
        for i in range(data_len):
            label = datasets[i][-1]
            if label not in label_count:
                label_count[label] = 0
            label_count[label] += 1
        entropy = -sum([(p / data_len) * log(p / data_len, 2) for p in label_count.values()])
        return entropy

    def cond_entropy(self, datasets, axis=0):  # 经验条件熵H(D|A)
        data_len = len(datasets)
        feature_set = {}
        for i in range(data_len):
            feature = datasets[i][axis]
            if feature not in feature_set:
                feature_set[feature] = []
            feature_set[feature].append(datasets[i])
        cond_ent = sum([(len(p) / data_len) * self.cal_entropy(p) for p in feature_set.values()])
        return cond_ent

    @staticmethod
    def info_gain(entropy, cond_ent):  # 信息增益
        return entropy - cond_ent

    def info_gain_train(self, datasets):  # 基于特征信息增益的特征选择
        count = len(datasets[0]) - 1
        entropy = self.cal_entropy(datasets)
        best_feature = []
        for i in range(count):
            info_gain_i = info_gain(entropy, cond_entropy(datasets, axis=i))
            best_feature.append((i, info_gain_i))
            print("特征({})- info_gain - {:.3f}".format(labels[i], info_gain_i))
        best_feature_i = max(best_feature, key=lambda x: x[-1])
        return best_feature_i

    def train(self, train_data):
        '''
        :input: 数据集D(DataFrame格式),特征集A,阈值eta
        :return: 决策树DT
        '''
        _, y_train, features = train_data.iloc[:, :-1], train_data.iloc[:, -1], train_data.columns[:-1]

        # 1. 若所有D实例都属于同一分类,不用分了,直接返回那个类
        if len(y_train.value_counts()) == 1:
            return Node(root=True, label=y_train.iloc[0])
        # 2. 若没有特征A,返回D中数量最多的分类
        if len(features) == 0:
            return Node(root=True, label=y_train.value_counts().sort_values(
                ascending=False).index[0])
        # 3. 计算最大信息增益,取为特征
        max_feature, max_info_gain = self.info_gain_train(np.array(train_data))
        max_feature_name = features[max_feature]

        # 4. 如果信息增益小于阈值epsilon,置为单节点,将实例数最大的类作为节点标记
        if max_info_gain < self.epsilon:
            return Node(root=True, label=y_train.value_counts().sort_values(
                ascending=False).index[0])
        # 5. 构建Ag子集
        node_tree = Node(root=False, feature_name=max_feature_name, feature=max_feature)

        feature_list = train_data[max_feature_name].value_counts().index
        for f in feature_list:
            sub_train_df = train_data.loc[train_data[max_feature_name] == f].drop([max_feature_name], axis=1)

            # 6. 递归生成树
            sub_tree = self.train(sub_train_df)
            node_tree.add_node(f, sub_tree)
        return node_tree

    def fit(self, train_data):
        self._tree = self.train(train_data)
        return self._tree

    def predict(self, X_test):
        return self._tree.predict(X_test)


train_data = pd.DataFrame(datasets, columns=labels)
dt = DTree()
tree = dt.fit(train_data)
print(dt.predict(['老年', '否', '否', '一般']))
print(dt.predict(['青年', '否', '是', '一般']))
print(dt.predict(['中年', '是', '否', '好']))
print(dt.predict(['老年', '否', '是', '一般']))

3、决策树的剪枝

学习时,考虑准确性,树复杂,过拟合,泛化能力差,需要剪枝。

方法:极小化决策树整体损失函数

举报

相关推荐

0 条评论