决策树(Decision Tree)是一种基于树形结构的分类与回归方法。
在分类问题中,决策树的每个叶子节点代表一种分类结果,中间的非叶子节点则代表一个特征属性及其取值,通过不断地询问数据的属性值,就可以一步步地走到叶子节点,得到分类结果。
在回归问题中,决策树的每个叶子节点代表一个预测值,中间的非叶子节点则代表一个特征属性及其取值,通过不断地询问数据的属性值,就可以一步步地走到叶子节点,得到预测结果。
from sklearn import tree
X = [[0, 0], [1, 1]]
y = [0, 1]
clf = tree.DecisionTreeClassifier()
clf = clf.fit(X, y)
决策树算法有很多种,其中最常用的是ID3算法和C4.5算法。这两种算法都是基于信息熵的原理来进行特征选择的。
信息熵是用来衡量一个数据集的混乱程度,它越小说明数据集越有序,越大说明数据集越混乱。
ID3算法每次选择信息增益最大的特征作为节点,C4.5算法则是选择信息增益比最大的特征作为节点。
决策树算法的优点是易于理解、易于实现、可解释性强。缺点是容易过拟合,需要进行剪枝处理;对于连续型数据和缺失值的处理比较困难。
下面是决策树算法的基本步骤:
特征选择:选择最好的特征进行划分。
决策树生成:根据选择的特征建立决策树。
决策树剪枝:对决策树进行剪枝,防止过拟合。
具体地,可以按照以下步骤来进行决策树的构建:
初始化:将所有数据的类别设为同一类别,将数据集分为训练集和测试集。
特征选择:对于每个节点,选择一个最好的特征来进行划分。
决策树生成:根据选择的特征建立子节点,并继续对子节点进行划分。
决策树剪枝:对决策树进行剪枝,防止过拟合。
测试:用测试集来验证决策树的准确性。
使用:使用决策树进行分类或预测。
需要注意的是,决策树算法通常要求特征具有离散值,如果特征是连续型的,需要进行离散化处理。此外,决策树算法还可以用于多分类和回归问题。
ID3算法和C4.5算法都是基于信息熵的原理来进行特征选择的决策树算法。
ID3算法是最早的决策树算法之一,它是由Ross Quinlan于1986年提出的。ID3算法的核心思想是在每个节点上选择信息增益最大的特征进行划分。信息增益是用来衡量一个特征对数据集的分类能力的大小。信息增益越大,表示特征对数据集的分类能力越强。
具体地,ID3算法的特征选择过程如下:
计算数据集的信息熵,作为初始的不确定度。
对于每个特征,计算其信息增益,并选择信息增益最大的特征作为节点。
以所选特征的每个取值创建一个子节点,并按照该取值将数据集分成多个子集,继续递归构建决策树。
直到所有的子节点都属于同一类别或者没有更多特征可用时停止递归构建。
ID3算法的缺点是容易过拟合,因为它倾向于选择取值比较多的特征作为节点,这会导致决策树过于复杂,对训练集过拟合。
import math
def entropy(data):
entropy = 0
total = len(data)
counts = {}
for item in data:
label = item[-1]
if label not in counts:
counts[label] = 0
counts[label] += 1
for label in counts:
prob = counts[label] / total
entropy -= prob * math.log(prob, 2)
return entropy
def split_data(data, column):
splits = {}
for item in data:
value = item[column]
if value not in splits:
splits[value] = []
splits[value].append(item)
return splits
def choose_best_feature(data):
best_feature = None
best_gain = 0
total_entropy = entropy(data)
for i in range(len(data[0]) - 1):
feature_entropy = 0
feature_splits = split_data(data, i)
for value in feature_splits:
prob = len(feature_splits[value]) / len(data)
feature_entropy += prob * entropy(feature_splits[value])
gain = total_entropy - feature_entropy
if gain > best_gain:
best_gain = gain
best_feature = i
return best_feature
def majority_label(labels):
counts = {}
for label in labels:
if label not in counts:
counts[label] = 0
counts[label] += 1
max_count = 0
majority = None
for label in counts:
if counts[label] > max_count:
max_count = counts[label]
majority = label
return majority
def id3(data, features):
labels = [item[-1] for item in data]
if len(set(labels)) == 1:
return labels[0]
if len(data[0]) == 1:
return majority_label(labels)
best_feature = choose_best_feature(data)
if best_feature is None:
return majority_label(labels)
best_feature_name = features[best_feature]
tree = {best_feature_name: {}}
feature_splits = split_data(data, best_feature)
for value in feature_splits:
sub_features = features[:best_feature] + features[best_feature+1:]
subtree = id3(feature_splits[value], sub_features)
tree[best_feature_name][value] = subtree
return tree
定义一些辅助函数,包括计算熵entropy()、根据特征分割数据split_data()、选择最佳特征choose_best_feature()、计算多数票标签majority_label()等。
然后,定义主函数id3(),该函数使用递归方式构建决策树。
在id3()函数中,首先检查数据是否属于同一类别,如果是,则返回该类别的标签。
然后,检查是否还有剩余特征可用于分割数据,如果没有,则返回数据中出现最多的标签。
接下来,选择最佳特征进行分割,并根据分割后的数据递归构建子树。
最后,将构建的子树添加到当前节点的字典中,形成完整的决策树。
C4.5算法是ID3算法的改进版,它由Ross Quinlan于1993年提出。C4.5算法的核心思想是在每个节点上选择信息增益比最大的特征进行划分。信息增益比是用来衡量一个特征对数据集的分类能力的大小,它除以特征的固有值可以解决ID3算法偏向于取值较多的特征的问题。
具体地,C4.5算法的特征选择过程如下:
计算数据集的信息熵,作为初始的不确定度。
对于每个特征,计算其信息增益比,并选择信息增益比最大的特征作为节点。
以所选特征的每个取值创建一个子节点,并按照该取值将数据集分成多个子集,继续递归构建决策树。
直到所有的子节点都属于同一类别或者没有更多特征可用时停止递归构建。
C4.5算法的优点是可以解决ID3算法的偏向问题,得到更好的特征选择结果,缺点是计算信息增益比时需要进行除法操作,影响算法的效率。
import math
def entropy(data):
entropy = 0
total = len(data)
counts = {}
for item in data:
label = item[-1]
if label not in counts:
counts[label] = 0
counts[label] += 1
for label in counts:
prob = counts[label] / total
entropy -= prob * math.log(prob, 2)
return entropy
def split_data(data, column):
splits = {}
for item in data:
value = item[column]
if value not in splits:
splits[value] = []
splits[value].append(item)
return splits
def choose_best_feature(data):
best_feature = None
best_gain_ratio = 0
total_entropy = entropy(data)
for i in range(len(data[0]) - 1):
feature_entropy = 0
feature_splits = split_data(data, i)
feature_iv = 0
for value in feature_splits:
prob = len(feature_splits[value]) / len(data)
feature_entropy += prob * entropy(feature_splits[value])
feature_iv -= prob * math.log(prob, 2)
gain = total_entropy - feature_entropy
gain_ratio = gain / feature_iv
if gain_ratio > best_gain_ratio:
best_gain_ratio = gain_ratio
best_feature = i
return best_feature
def majority_label(labels):
counts = {}
for label in labels:
if label not in counts:
counts[label] = 0
counts[label] += 1
max_count = 0
majority = None
for label in counts:
if counts[label] > max_count:
max_count = counts[label]
majority = label
return majority
def c45(data, features):
labels = [item[-1] for item in data]
if len(set(labels)) == 1:
return labels[0]
if len(data[0]) == 1:
return majority_label(labels)
best_feature = choose_best_feature(data)
if best_feature is None:
return majority_label(labels)
best_feature_name = features[best_feature]
tree = {best_feature_name: {}}
feature_splits = split_data(data, best_feature)
for value in feature_splits:
sub_features = features[:best_feature] + features[best_feature+1:]
subtree = c45(feature_splits[value], sub_features)
tree[best_feature_name][value] = subtree
return tree
定义了一些辅助函数,包括计算熵entropy()、根据特征分割数据split_data()、选择最佳特征choose_best_feature()、计算多数票标签majority_label()等。
然后,定义了主函数c45(),该函数使用递归方式构建决策树。
在c45()函数中,首先检查数据是否属于同一类别,如果是,则返回该类别的标签。
然后,检查是否还有剩余特征可用于分割数据,如果没有,则返回数据中出现最多的标签。
接下来,选择最佳特征进行分割,并根据分割后的数据递归构建子树。
最后,将构建的子树添加到当前节点的字典中,形成完整的决策树。