参考李航《统计学习方法》
1.信息论
信息论的基本想法是一个不太可能的事件居然发生了,要比一个非常可能的事 件发生,能提供更多的信息。消息说:‘‘今天早上太阳升起’’ 信息量是如此之少以至 于没有必要发送,但一条消息说:‘‘今天早上有日食’’ 信息量就很丰富。
我们想要通过这种基本想法来量化信息。特别地,
• 非常可能发生的事件信息量要比较少,并且极端情况下,确保能够发生的事件 应该没有信息量
• 较不可能发生的事件具有更高的信息量。
• 独立事件应具有增量的信息。
例如,投掷的硬币两次正面朝上传递的信息量, 应该是投掷一次硬币正面朝上的信息量的两倍。 为了满足上述三个性质,我们定义一个事件 x = x 的 自信息(self-information) 为
I(x) = − log P(x)
自信息只处理单个的输出。我们可以用 香农熵(Shannon entropy)来对整个概 率分布中的不确定性总量进行量化:
H(x) = Ex∼p [I(x)] = −Ex∼p [log P(x)]
也记作 H(P)。
如果我们对于同一个随机变量 x 有两个单独的概率分布 P(x) 和 Q(x),我们可 以使用 KL 散度(Kullback-Leibler (KL) divergence)来衡量这两个分布的差异:
DKL(P||Q) = Ex∼p [ log P(x) Q(x) ] = Ex∼p [log P(x) − log Q(x)].
一个和 KL 散度密切联系的量是 交叉熵(cross-entropy)H(P, Q) = H(P) + DKL(P||Q),它和 KL 散度很像但是缺少左边一项:
H(P, Q) = −Ex∼p log Q(x)
互信息(Mutual Information)是信息论里一种有用的信息度量,它可以看成是一个随机变量中包含的关于另一个随机变量的信息量,或者说是一个随机变量由于已知另一个随机变量而减少的不确定性 ,记为I(P, Q)
I(P, Q)=H(P) - H(P|Q)
或 I(P, Q)=H(P)+H(Q) - H(PQ)
也记作g(P,Q)。
2.特征选择准则
(1)样本集合D对特征A的信息增益(ID3)
(2)样本集合D对特征A的信息增益比(C4.5)
(3)样本集合D的基尼指数(CART)
(4)特征A条件下集合D的基尼指数:
4.决策树的生成
通常使用信息增益最大、信息增益比最大或基尼指数最小作为特征选择的准则。决策树的生成往往通过计算信息增益或其他指标,从根结点开始,递归地产生决策树。这相当于用信息增益或其他准则不断地选取局部最优的特征,或将训练集分割为能够基本正确分类的子集。
5.决策树的剪枝
由于生成的决策树存在过拟合问题,需要对它进行剪枝,以简化学到的决策树。决策树的剪枝,往往从已生成的树上剪掉一些叶结点或叶结点以上的子树,并将其父结点或根结点作为新的叶结点,从而简化生成的决策树。
6.示例
(1)简单地,使用sklearn实现,样本选自鸢尾花
from sklearn.tree import DecisionTreeClassifier
from sklearn.tree import export_graphviz
# data
def create_data():
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target
df.columns = [
'sepal length', 'sepal width', 'petal length', 'petal width', 'label'
]
data = np.array(df.iloc[:100, [0, 1, -1]])
# print(data)
return data[:, :2], data[:, -1]
if __name__ == '__main__':
#get data
X, y = create_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
#classify
clf = DecisionTreeClassifier()
clf.fit(X_train, y_train,)
clf.score(X_test, y_test)
(2)具体地,GITHUB链接lihang-code/第05章 决策树 at master · yaoyansum/lihang-code (github.com)
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):
# 节点的字符串表示,用于打印
return '{}'.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 calc_ent(datasets):
# 计算给定数据集的熵
data_length = len(datasets)
label_count = {}
for i in range(data_length):
label = datasets[i][-1]
if label not in label_count:
label_count[label] = 0
label_count[label] += 1
ent = -sum([(p / data_length) * log(p / data_length, 2)
for p in label_count.values()])
return ent
# 经验条件熵的计算方法
def cond_ent(self, datasets, axis=0):
data_length = len(datasets)
feature_sets = {}
for i in range(data_length):
feature = datasets[i][axis]
if feature not in feature_sets:
feature_sets[feature] = []
feature_sets[feature].append(datasets[i])
cond_ent = sum([(len(p) / data_length) * self.calc_ent(p)
for p in feature_sets.values()])
return cond_ent
# 信息增益
@staticmethod
def info_gain(ent, cond_ent):
# 根据熵和条件熵计算信息增益
return ent - cond_ent
def info_gain_train(self, datasets):
# 计算所有特征的信息增益,并返回最佳特征
count = len(datasets[0]) - 1
ent = self.calc_ent(datasets)
best_feature = []
for c in range(count):
c_info_gain = self.info_gain(ent, self.cond_ent(datasets, axis=c))
best_feature.append((c, c_info_gain))
# 比较大小
best_ = max(best_feature, key=lambda x: x[-1])
return best_
# 使用提供的训练数据递归训练决策树的方法
def train(self, train_data):
"""
input:数据集D(DataFrame格式),特征集A,阈值eta
output:决策树T
"""
_, y_train, features = train_data.iloc[:, :-1],train_data.iloc[:,-1],
train_data.columns[:-1]
# 1,若D中实例属于同一类Ck,则T为单节点树,并将类Ck作为结点的类标记,返回T
if len(y_train.value_counts()) == 1:
return Node(root=True, label=y_train.iloc[0])
# 2, 若A为空,则T为单节点树,将D中实例树最大的类Ck作为该节点的类标记,返回T
if len(features) == 0:
return Node(
root=True,
label=y_train.value_counts().sort_values(
ascending=False).index[0])
# 3,计算最大信息增益 同5.1,Ag为信息增益最大的特征
max_feature, max_info_gain = self.info_gain_train(np.array(train_data))
max_feature_name = features[max_feature]
# 4,Ag的信息增益小于阈值eta,则置T为单节点树,并将D中是实例数最大的类Ck作为该节点的类标记,返回T
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)
# pprint.pprint(node_tree.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)