决策树先验知识
熵
- 熵表示随机变量的不确定性,熵越大,随机变量的不确定性越大,数学表达式为:
H ( p ) = − ∑ i = 1 n p i l o g ( p i ) H(p) = -\sum^n_{i=1}p_i log (p_i) H(p)=−i=1∑npilog(pi)
- 假设 n = 2 n=2 n=2,则 H ( p ) H(p) H(p)变为:
H ( p ) = − p l o g ( p ) − ( 1 − p ) l o g ( 1 − p ) H(p) = -plog(p) - (1-p)log(1-p) H(p)=−plog(p)−(1−p)log(1−p)
- 在机器学习中,为了表达的统一性,将熵的数学公式变为:
H ( D ) = − ∑ k = 1 k ∣ C k ∣ ∣ D ∣ l o g 2 ∣ C k ∣ ∣ D ∣ H(D) = -\sum_{k=1}^{k}\frac{|C_k|}{|D|}log_2\frac{|C_k|}{|D|} H(D)=−k=1∑k∣D∣∣Ck∣log2∣D∣∣Ck∣
其中, C k C_k Ck表示列 C C C(标签列)的第 k k k个取值, ∣ C k ∣ |C_k| ∣Ck∣表示列 C C C的第 k k k个取值的样本数,|D|则表示全部样本数量。
- 假设要计算下面这个数据集的熵:
年龄 | 有工作 | 有房子 | 信贷情况 | 类别 |
---|---|---|---|---|
青年 | 否 | 否 | 一般 | 否 |
青年 | 否 | 否 | 好 | 否 |
青年 | 是 | 否 | 好 | 是 |
青年 | 是 | 是 | 一般 | 是 |
青年 | 否 | 否 | 一般 | 否 |
中年 | 否 | 否 | 一般 | 否 |
中年 | 否 | 否 | 好 | 否 |
中年 | 是 | 是 | 好 | 是 |
中年 | 否 | 是 | 非常好 | 是 |
中年 | 否 | 是 | 非常好 | 是 |
老年 | 否 | 是 | 非常好 | 是 |
老年 | 否 | 是 | 好 | 是 |
老年 | 是 | 否 | 好 | 是 |
老年 | 是 | 否 | 非常好 | 是 |
老年 | 否 | 否 | 一般 | 否 |
- 可以看到,类别列就是标签列,一共有2种取值,假设 C 1 = 是 C_1 = \text{是} C1=是, C 2 = 否 C_2 = \text{否} C2=否,则 H ( D ) H(D) H(D)为:
H ( D ) = − 9 15 l o g 2 ( 9 15 ) − 6 15 l o g 2 ( 6 15 ) H(D) = -\frac{9}{15}log_2(\frac{9}{15}) - \frac{6}{15}log_2(\frac{6}{15}) H(D)=−159log2(159)−156log2(156)
条件熵
- 条件熵数学表达式:
H ( Y ∣ X ) = ∑ i = 1 n p i H ( Y ∣ X = x i ) H(Y|X) = \sum_{i=1}^np_iH(Y|X=x_i) H(Y∣X)=i=1∑npiH(Y∣X=xi)
其中:
p
i
=
P
(
X
=
x
i
)
p_i = P(X=x_i)
pi=P(X=xi)
- 变为机器学习中的通用表达式:
H ( D ∣ A ) = ∑ i = 1 n ∣ D i ∣ ∣ D ∣ H ( D i ) = − ∑ i = 1 n ∣ D i ∣ ∣ D ∣ ∑ k = 1 k ∣ D i k ∣ ∣ D i ∣ l o g 2 ∣ D i k ∣ ∣ D i ∣ H(D|A) = \sum^n_{i=1}\frac{|D_i|}{|D|}H(D_i) = -\sum_{i=1}^{n}\frac{|D_i|}{|D|}\sum_{k=1}^k\frac{|D_{ik}|}{|D_i|}log_2\frac{|D_{ik}|}{|D_i|} H(D∣A)=i=1∑n∣D∣∣Di∣H(Di)=−i=1∑n∣D∣∣Di∣k=1∑k∣Di∣∣Dik∣log2∣Di∣∣Dik∣
其中, n n n表示特征 A A A可能取值的个数, ∣ D i ∣ |D_i| ∣Di∣则表示特征A取第 i i i个值时的样本个数, ∣ D ∣ |D| ∣D∣表示全部数据的样本数。 k k k表示标签列的可能取值的个数, ∣ D i k ∣ |D_{ik}| ∣Dik∣表示特征A取第i个值时,标签列取第k个值时的样本个数。
- 还是以上面的数据集为例
年龄 | 有工作 | 有房子 | 信贷情况 | 类别 |
---|---|---|---|---|
青年 | 否 | 否 | 一般 | 否 |
青年 | 否 | 否 | 好 | 否 |
青年 | 是 | 否 | 好 | 是 |
青年 | 是 | 是 | 一般 | 是 |
青年 | 否 | 否 | 一般 | 否 |
中年 | 否 | 否 | 一般 | 否 |
中年 | 否 | 否 | 好 | 否 |
中年 | 是 | 是 | 好 | 是 |
中年 | 否 | 是 | 非常好 | 是 |
中年 | 否 | 是 | 非常好 | 是 |
老年 | 否 | 是 | 非常好 | 是 |
老年 | 否 | 是 | 好 | 是 |
老年 | 是 | 否 | 好 | 是 |
老年 | 是 | 否 | 非常好 | 是 |
老年 | 否 | 否 | 一般 | 否 |
- 假设现在特征 A A A代表年龄, i = 1 i=1 i=1表示青年, i = 2 i=2 i=2表示中年, i = 3 i=3 i=3表示老年;对于标签列, k = 1 k=1 k=1表示是, k = 2 k=2 k=2表示否,则 H ( D ∣ A ) H(D|A) H(D∣A)为:
H ( D ∣ A ) = − [ 5 15 × ( 2 5 l o g 2 2 5 + 3 5 l o g 2 3 5 ) + 5 15 × ( 3 5 l o g 2 3 5 + 2 5 l o g 2 2 5 ) + 5 15 × ( 4 5 l o g 2 4 5 + 1 5 l o g 2 1 5 ) ] H(D|A) = -[\frac{5}{15} \times (\frac{2}{5}log_2\frac{2}{5} + \frac{3}{5}log_2\frac{3}{5}) + \frac{5}{15} \times (\frac{3}{5}log_2\frac{3}{5} + \frac{2}{5}log_2\frac{2}{5}) + \frac{5}{15} \times (\frac{4}{5}log_2\frac{4}{5} + \frac{1}{5}log_2\frac{1}{5})] H(D∣A)=−[155×(52log252+53log253)+155×(53log253+52log252)+155×(54log254+51log251)]
信息增益
- 信息增益(互信息)定义为:
g ( D , A ) = H ( D ) − H ( D ∣ A ) g(D,A) = H(D) - H(D|A) g(D,A)=H(D)−H(D∣A)
其中 D D D是数据集, A A A是数据集中的某个特征
- 根据信息增益准则,特征选择方法是:
- 对训练数据集D,计算其每个特征的信息增益
- 选择信息增益最大的特征
- 信息增益算法一般步骤:
- 计算数据集 D D D的经验熵 H ( D ) H(D) H(D)
H ( D ) = − ∑ k = 1 k ∣ C k ∣ ∣ D ∣ l o g 2 C k D H(D) = -\sum_{k=1}^k\frac{|C_k|}{|D|}log_2\frac{C_k}{D} H(D)=−k=1∑k∣D∣∣Ck∣log2DCk
- 计算特征 A A A对数据集 D D D的经验条件熵 H ( D ∣ A ) H(D|A) H(D∣A)
H ( D ∣ A ) = ∑ i = 1 n ∣ D i ∣ ∣ D ∣ H ( D i ) = − ∑ i = 1 n ∣ D i ∣ ∣ D ∣ ∑ k = 1 k ∣ D i k ∣ ∣ D i ∣ l o g 2 ∣ D i k ∣ ∣ D i ∣ H(D|A) = \sum^n_{i=1}\frac{|D_i|}{|D|}H(D_i) = -\sum_{i=1}^{n}\frac{|D_i|}{|D|}\sum_{k=1}^k\frac{|D_{ik}|}{|D_i|}log_2\frac{|D_{ik}|}{|D_i|} H(D∣A)=i=1∑n∣D∣∣Di∣H(Di)=−i=1∑n∣D∣∣Di∣k=1∑k∣Di∣∣Dik∣log2∣Di∣∣Dik∣
- 计算信息增益
g ( D , A ) = H ( D ) − H ( D ∣ A ) g(D,A) = H(D) - H(D|A) g(D,A)=H(D)−H(D∣A)
ID3算法
- 在决策树递归构建过程中,使用信息增益的方式进行特征选择
- 决策树生成过程:
- 从根节点开始计算所有特征的信息增益,选择信息增益最大的特征作为特征作为节点特征。
- 对子节点递归调用上述方法,构建决策树
- 特征信息增益很小或没有特征可以选择时递归结束得到决策树
C4.5算法
- C4.5算法是对ID3算法的改进,包括
- 处理连续值属性:ID3算法主要用于处理离散属性,而C4.5能够直接处理连续值属性,通过寻找最佳切分点来将连续值属性转换为二元特征。
- 缺失值处理:ID3算法无法处理包含缺失值的数据集。C4.5算法提供了一种方法来估算缺失值,并且可以忽略那些特定实例中缺失值的属性。
- 剪枝技术:ID3算法没有提供对过拟合问题的有效解决方案。C4.5引入了预剪枝和后剪枝技术以减少过拟合的风险,从而提高模型的泛化能力。
- 多输出类别:C4.5可以处理具有多个类别的分类任务,而不仅仅局限于两个类别。
- 信息增益比:ID3使用信息增益作为选择属性的标准,但在某些情况下,信息增益可能偏向于选择具有许多不同值的属性。C4.5使用信息增益比(Gain Ratio)作为分裂标准,它考虑了属性纯度改善与该属性值数量之间的关系,以避免偏好具有大量唯一值的属性。
- C4.5算法在树生成过程中,使用信息增益比来选择特征
- C4.5信息增益比计算公式:
g r ( D , A ) = g ( D , A ) H A ( D ) H A ( D ) = − ∑ i = 1 n ∣ D i ∣ ∣ D ∣ l o g 2 D i D g_r(D,A) = \frac{g(D,A)}{H_A(D)}\\ H_A(D) = -\sum^n_{i=1}\frac{|D_i|}{|D|}log_2\frac{D_i}{D} gr(D,A)=HA(D)g(D,A)HA(D)=−i=1∑n∣D∣∣Di∣log2DDi
其中 n n n是特征 A A A可能取值的个数
CART算法
- CART算法相较于C4.5算法有一些不同:
- 连续值处理:CART算法也能够处理连续值属性,它通过寻找最优分割点来将连续值属性转化为二元分割。
- 二叉树结构:CART算法生成的是二叉树,每个非叶子节点都有两个子节点。这与C4.5不同,后者允许非二叉树结构(即一个节点可以有多个子节点)。
- 分裂标准:对于分类任务,CART使用基尼不纯度(Gini Impurity)作为分裂标准,而不是C4.5所使用的增益比。对于回归任务,CART使用平方误差作为分裂标准。
- 剪枝策略:CART算法同样支持剪枝以减少过拟合,但它通常采用成本复杂度剪枝(Cost Complexity Pruning),这是一种系统性的后剪枝方法。
- 可扩展性和效率:CART算法在处理大型数据集时表现良好,并且在计算效率上有所优化。
- CART算法在树生成过程中,使用基尼指数选择最优特征。生成的是二叉树。
- 设样本点属于第 k k k类的概率为 p k p_k pk,则:
G i n i ( p ) = ∑ k = 1 k p k ( 1 − p k ) = 1 − ∑ k = 1 k p k 2 Gini(p) = \sum^k_{k=1}p_k(1-p_k) = 1- \sum_{k=1}^kp_k^2 Gini(p)=k=1∑kpk(1−pk)=1−k=1∑kpk2
- 给定样本集合 D D D,其基尼指数为:
G i n i ( D ) = 1 − ∑ k = 1 k ( ∣ C k ∣ D ) 2 Gini(D) = 1-\sum_{k=1}^k\left(\frac{|C_k|}{D}\right)^2 Gini(D)=1−k=1∑k(D∣Ck∣)2
- 在特征A的条件下,集合D的基尼指数为:
G i n i ( D , A ) = ∣ D 1 ∣ ∣ D ∣ G i n i ( D 1 ) + ∣ D 2 ∣ ∣ D ∣ G i n i ( D 2 ) Gini(D,A) = \frac{|D_1|}{|D|}Gini(D_1) + \frac{|D_2|}{|D|}Gini(D_2) Gini(D,A)=∣D∣∣D1∣Gini(D1)+∣D∣∣D2∣Gini(D2)
- 基尼指数越大,样本的不确定性越大,这点与熵相似
ID3算法python代码实现
- 导入必要库
import numpy as np
import pandas as pd
import statsmodels.api as sm
from tqdm.notebook import tqdm
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_california_housing, make_regression
- 计算 H ( D ) H(D) H(D),即整个数据集的熵
# 计算整个数据集的熵
def entropy(data, label):
row_num, col_num = data.shape
target = np.sort(data[label].unique())
h = 0
for i in target:
p = data[data[label] == i].shape[0]/row_num
h = h + p * np.log2(p)
return -1 * h
- 根据特征以及特征值分割数据集
def split_data_set(data, feature, value):
data = data.copy(deep=True)
return data[data[feature] == value].drop(feature,axis=1)
- 选择最优划分特征
def choose_feature(data, label):
base_entropy = entropy(data, label)
info_gain = []
for col in data.columns:
if col == label:
continue
else:
# 取出当前特征的唯一值
col_unique = np.sort(data[col].unique())
temp_entropy = 0
for u in col_unique:
sd = split_data_set(data, col, u)
p = sd.shape[0]/data.shape[0]
temp_entropy = temp_entropy + p * entropy(sd, label)
info_gain.append({col:base_entropy - temp_entropy})
info_gain = sorted(info_gain, key=lambda d: list(d.values())[0], reverse=True)
return list(info_gain[0].keys())[0]
- 投票结果
def class_vote(data_list):
data = pd.DataFrame(data_list, columns=['vote'])
temp = data['vote'].value_counts()
return temp.index[0]
- 训练决策树
def train_tree(data, label):
# 数据样本数为1
if data.shape[0] ==1:
return class_vote(data[label].tolist())
# lable列只剩下1个类别
if data[label].unique().shape[0] == 1:
return data[label].unique()[0]
feat = choose_feature(data, label)
tree = {feat:{}}
for value in data[feat].unique():
# 持续划分特征递归构建树
tree[feat][value] = train_tree(split_data_set(data, feat, value), label)
return tree
- 决策树预测
def predict(decision_tree, data):
predictions = []
for index, row in data.iterrows():
node = decision_tree
while isinstance(node, dict):
feature, children = next(iter(node.items()))
value = row[feature]
if value in children:
node = children[value]
predictions.append(node)
return predictions
- 函数调用
data = pd.read_csv('/kaggle/input/studyml/loan_data.csv')
tree = train_tree(data, '类别')
print(tree)
# 打印输出:{'有房子': {'否': {'有工作': {'否': '否', '是': '是'}}, '是': '是'}}
- 预测结果
res = predict(tree, data)
print(res)
# 打印输出:['否', '否', '是', '是', '否', '否', '否', '是', '是', '是', '是', '是', '是', '是', '否']