赞
踩
决策树属于非参数监督学习方法,在学习数据特征推断出简单的决策规划创建一个预测目标变量值的模型。
优点
缺点
在计算机中,决策树更多的时用于分类。有点类似于用 if else 语句去穷举每一种可能,但是面对多个判断的情况时,每个判断出现的位置还不确定,我们需要通过某种方法确定各个条件出现顺序(谁在树根谁在下面),这个方法是决策树的一个难点。此外,面对大量数据时,建立的决策树模型可能会特别特别大,深度深,叶子节点多且节点中的样例少等可能性,优化这个问题需要的方法也是一个难题。
所有的决策树的步骤的最终目的都是希望通过学习产生一个泛化能力强的决策树,即处理未见示例能力强的决策树。
根据西瓜书的定义:⼀棵决策树包含⼀个根节点,若⼲个内部节点和若⼲个叶⼦节点。叶结点对应决策树决策结果,其他每个结点则对应于⼀个属性测试。
决策树这里用到了信息论中间很基础的知识,我们希望决策树的分支节点所包含的样本尽可能都属于一个类别,那么这么衡量这个?这需要用到纯度。直观上讲就是分类纯度越低,里面的标签就越杂 。
那么纯度怎么表示?
可以用信息熵表示。假定当前样本集合 D 中第 k 类样本所占的比例为
P
k
P_k
Pk (k =1, 2,. . . ,
∣
γ
∣
|\gamma|
∣γ∣) ,则D的信息熵定义为:
E
n
t
(
D
)
=
−
∑
k
=
1
∣
γ
∣
p
k
l
o
g
2
p
k
Ent(D)=-\sum^{|\gamma|}_{k=1}{p_klog_2p_k}
Ent(D)=−k=1∑∣γ∣pklog2pk
Ent(D)的值越小,则D的纯度越⾼。
Ent(D)的值越大,则D的不纯度越大,则D的不确定性越大。
决策树模型构建一般分为三步,
特征选择更多的是取决于数据集,在生活中的问题里可能更加重要,一般来说没有什么特定的标准,需要注意的点比较琐碎,这边不细讲。
决策树生成和修剪的难度更大,这里做出总结。
如何构造决策树其实就是如何选取每层节点的问题,西瓜书上介绍了三种方法,这边也按照这三种方法总结,其他的方法我将在面试题中提到。
靠信息增益区分。
我们知道信息熵可以确定经过一个节点划分后每个分支节点的纯度,但是我们这里需要确定节点的顺序,所以单单知道划分后每个分支节点的纯度还不够,应该通过某种方法利用分支节点的纯度反映到划分节点的优劣。
ID3就是使用信息增益来判断每个节点的划分能力的。信息增益公式如下:
G a i n ( D , α ) = E n t ( D ) − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ E n t ( D v ) Gain(D, \alpha)=Ent(D)-\sum^{V}_{v=1}{\frac{|D^v|}{|D|}Ent(D^v)} Gain(D,α)=Ent(D)−v=1∑V∣D∣∣Dv∣Ent(Dv)
∣ D v ∣ ∣ D ∣ \frac{|D^v|}{|D|} ∣D∣∣Dv∣就是每个分支节点的划分权重,所以样本数量越多划分权重越大。 G a i n ( D , α ) Gain(D, \alpha) Gain(D,α)就是使用 α \alpha α属性划分对整体纯度提升的能力。因此我们有 ID3 的逻辑顺序:
西瓜数据集
编号 | 色泽 | 根蒂 | 敲声 | 纹理 | 脐部 | 触感 | 密度 | 含糖率 | 好瓜 |
---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0.697 | 0.46 | 1 |
2 | 1 | 0 | 1 | 0 | 0 | 0 | 0.774 | 0.376 | 1 |
3 | 1 | 0 | 0 | 0 | 0 | 0 | 0.634 | 0.264 | 1 |
4 | 0 | 0 | 1 | 0 | 0 | 0 | 0.608 | 0.318 | 1 |
5 | 2 | 0 | 0 | 0 | 0 | 0 | 0.556 | 0.215 | 1 |
6 | 0 | 1 | 0 | 0 | 1 | 1 | 0.403 | 0.237 | 1 |
7 | 1 | 1 | 0 | 1 | 1 | 1 | 0.481 | 0.149 | 1 |
8 | 1 | 1 | 0 | 0 | 1 | 0 | 0.437 | 0.211 | 1 |
9 | 1 | 1 | 1 | 1 | 1 | 0 | 0.666 | 0.091 | 0 |
10 | 0 | 2 | 2 | 0 | 2 | 1 | 0.243 | 0.267 | 0 |
11 | 2 | 2 | 2 | 2 | 2 | 0 | 0.245 | 0.057 | 0 |
12 | 2 | 0 | 0 | 2 | 2 | 1 | 0.343 | 0.099 | 0 |
13 | 0 | 1 | 0 | 1 | 0 | 0 | 0.639 | 0.161 | 0 |
14 | 2 | 1 | 1 | 1 | 0 | 0 | 0.657 | 0.198 | 0 |
15 | 1 | 1 | 0 | 0 | 1 | 1 | 0.36 | 0.37 | 0 |
16 | 2 | 0 | 0 | 2 | 2 | 0 | 0.593 | 0.042 | 0 |
17 | 0 | 0 | 1 | 1 | 1 | 0 | 0.719 | 0.103 | 0 |
计算根蒂的信息增益:
对种类多的属性有偏好,比如编号属性。这个属性的信息增益会特别高,但是实际上它所包含的信息没多少。
此时就需要另一个算法 C4.5
靠增益率区分。
既然考虑到信息增益对种类多的属性的偏好,增益率应该削弱这类属性的权重。于是提出增益的权重,也称为属性的固有值。
对信息增益提出了惩罚,特征可取值数量较多时,惩罚参数大;特征可取值数量较少时,惩罚参数小。
I
V
(
α
)
=
−
∑
v
=
1
V
∣
D
v
∣
∣
D
∣
l
o
g
2
∣
D
v
∣
∣
D
∣
IV(\alpha)=-\sum^{V}_{v=1}{\frac{|D^v|}{|D|}}log_2\frac{|D^v|}{|D|}
IV(α)=−v=1∑V∣D∣∣Dv∣log2∣D∣∣Dv∣
G
a
i
n
_
r
a
t
i
o
=
G
a
i
n
(
D
,
α
)
I
V
(
α
)
Gain\_ratio=\frac{Gain(D,\alpha)}{IV(\alpha)}
Gain_ratio=IV(α)Gain(D,α)
底层思路是讲连续特征离散化,所有也可以用来处理连续型数据。
西瓜数据集
编号 | 色泽 | 根蒂 | 敲声 | 纹理 | 脐部 | 触感 | 密度 | 含糖率 | 好瓜 |
---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0.697 | 0.46 | 1 |
2 | 1 | 0 | 1 | 0 | 0 | 0 | 0.774 | 0.376 | 1 |
3 | 1 | 0 | 0 | 0 | 0 | 0 | 0.634 | 0.264 | 1 |
4 | 0 | 0 | 1 | 0 | 0 | 0 | 0.608 | 0.318 | 1 |
5 | 2 | 0 | 0 | 0 | 0 | 0 | 0.556 | 0.215 | 1 |
6 | 0 | 1 | 0 | 0 | 1 | 1 | 0.403 | 0.237 | 1 |
7 | 1 | 1 | 0 | 1 | 1 | 1 | 0.481 | 0.149 | 1 |
8 | 1 | 1 | 0 | 0 | 1 | 0 | 0.437 | 0.211 | 1 |
9 | 1 | 1 | 1 | 1 | 1 | 0 | 0.666 | 0.091 | 0 |
10 | 0 | 2 | 2 | 0 | 2 | 1 | 0.243 | 0.267 | 0 |
11 | 2 | 2 | 2 | 2 | 2 | 0 | 0.245 | 0.057 | 0 |
12 | 2 | 0 | 0 | 2 | 2 | 1 | 0.343 | 0.099 | 0 |
13 | 0 | 1 | 0 | 1 | 0 | 0 | 0.639 | 0.161 | 0 |
14 | 2 | 1 | 1 | 1 | 0 | 0 | 0.657 | 0.198 | 0 |
15 | 1 | 1 | 0 | 0 | 1 | 1 | 0.36 | 0.37 | 0 |
16 | 2 | 0 | 0 | 2 | 2 | 0 | 0.593 | 0.042 | 0 |
17 | 0 | 0 | 1 | 1 | 1 | 0 | 0.719 | 0.103 | 0 |
计算根蒂的信息增益率:
缺点也很明显,就是矫枉过正。面对可取数值比较少的属性时,增益率存在偏好。因此有了基指系数。
靠基尼指数划分。
我们可以理解为随机抽取两个样本,其类别不一样的概率的加权和。这个权重跟 C4.5 的权重划分方法相同。
这个本质上是一个回归树,对于计算机来说虽然很多时候结果跟信息熵算出来的差不多,但是计算会快很多,因为没有对数。
数据集的基尼指数:
G i n i ( D ) = ∑ k = 1 ∣ γ ∣ ∑ k ′ ≠ k p k p k ′ = 1 − ∑ k = 1 ∣ γ ∣ p k 2 = 1 − ∑ k = 1 ∣ γ ∣ ( ∣ D i ∣ ∣ D ∣ ) 2 Gini(D)=\sum^{|\gamma|}_{k=1}\sum_{k^{'} \ne k}p_kp_{k^{'}}=1-\sum^{|\gamma|}_{k=1}p_{k}^{2}=1-\sum^{|\gamma|}_{k=1}{(\frac{|D_i|}{|D|})^2} Gini(D)=k=1∑∣γ∣k′=k∑pkpk′=1−k=1∑∣γ∣pk2=1−k=1∑∣γ∣(∣D∣∣Di∣)2
数据集纯度越高, ∑ k = 1 ∣ γ ∣ p k 2 \sum^{|\gamma|}_{k=1}p_{k}^{2} ∑k=1∣γ∣pk2 越大,Gini 越小。
属性的基尼指数:
G i n i _ i n d e x ( D , α ) = ∑ v = 1 V ∣ D v ∣ ∣ D ∣ G i n i ( D v ) Gini\_index(D, \alpha )=\sum^{V}_{v=1}{\frac{|D^v|}{|D|}Gini(D^{v})} Gini_index(D,α)=v=1∑V∣D∣∣Dv∣Gini(Dv)
最优划分属性就是 G i n i _ i n d e x ( D , α ) Gini\_index(D, \alpha ) Gini_index(D,α) 最小的属性。
西瓜数据集
编号 | 色泽 | 根蒂 | 敲声 | 纹理 | 脐部 | 触感 | 密度 | 含糖率 | 好瓜 |
---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0.697 | 0.46 | 1 |
2 | 1 | 0 | 1 | 0 | 0 | 0 | 0.774 | 0.376 | 1 |
3 | 1 | 0 | 0 | 0 | 0 | 0 | 0.634 | 0.264 | 1 |
4 | 0 | 0 | 1 | 0 | 0 | 0 | 0.608 | 0.318 | 1 |
5 | 2 | 0 | 0 | 0 | 0 | 0 | 0.556 | 0.215 | 1 |
6 | 0 | 1 | 0 | 0 | 1 | 1 | 0.403 | 0.237 | 1 |
7 | 1 | 1 | 0 | 1 | 1 | 1 | 0.481 | 0.149 | 1 |
8 | 1 | 1 | 0 | 0 | 1 | 0 | 0.437 | 0.211 | 1 |
9 | 1 | 1 | 1 | 1 | 1 | 0 | 0.666 | 0.091 | 0 |
10 | 0 | 2 | 2 | 0 | 2 | 1 | 0.243 | 0.267 | 0 |
11 | 2 | 2 | 2 | 2 | 2 | 0 | 0.245 | 0.057 | 0 |
12 | 2 | 0 | 0 | 2 | 2 | 1 | 0.343 | 0.099 | 0 |
13 | 0 | 1 | 0 | 1 | 0 | 0 | 0.639 | 0.161 | 0 |
14 | 2 | 1 | 1 | 1 | 0 | 0 | 0.657 | 0.198 | 0 |
15 | 1 | 1 | 0 | 0 | 1 | 1 | 0.36 | 0.37 | 0 |
16 | 2 | 0 | 0 | 2 | 2 | 0 | 0.593 | 0.042 | 0 |
17 | 0 | 0 | 1 | 1 | 1 | 0 | 0.719 | 0.103 | 0 |
计算根蒂的基尼指数:
剪枝 (pruning)是决策树学习算法对付"过拟合"的主要⼿段。在决策树学习中,为了尽可能正确分类训练样本,结点划分过程将不断重复,有时会造成决策树分支过多,这时就可能因训练样本学得"太好"了,以致于把训练集自身的⼀些特点当作所有数据都具有的⼀般性质⽽导致过拟合。
决策树剪枝的基本策略有“预剪枝” (prepruning)和“后剪枝 ”(post pruning) [Quinlan, 1993]. 预剪枝是指在决策树⽣成过程中,对每个结点在划分前先进⾏估计,若当前结点的划分不能带来决策树泛化性能提升,则停⽌划分并将当前结点标记为叶结点;后剪枝则是先从训练集⽣成⼀棵完整的决策树, 然后自底向上地对非叶结点进⾏考察,若将该结点对应的⼦树替换为叶结点能带来决策树泛化性能提升,则将该⼦树替换为叶结点。
watermelon.csv
x1,x2,x3,x4,x5,x6,label 0,0,0,0,0,0,1 1,0,1,0,0,0,1 1,0,0,0,0,0,1 0,0,1,0,0,0,1 2,0,0,0,0,0,1 0,1,0,0,1,1,1 1,1,0,1,1,1,1 1,1,0,0,1,0,1 1,1,1,1,1,0,0 0,2,2,0,2,1,0 2,2,2,2,2,0,0 2,0,0,2,2,1,0 0,1,0,1,0,0,0 2,1,1,1,0,0,0 1,1,0,0,1,1,0 2,0,0,2,2,0,0 0,0,1,1,1,0,0
# 计算信息熵 def c_entropy(label): # 每类有几个 count_class = Counter(label) ret = 0 for classes in count_class.keys(): ret += count_class[classes]/len(label)*np.log2(count_class[classes]/len(label)) return -ret # 计算单个属性信息增益 def c_entropy_gain(data, label): ent = c_entropy(label) # 几个值循环几次 count_value = Counter(data) sum_entropy = 0 for value_a in count_value.keys(): sum_entropy += count_value[value_a] / len(label) * c_entropy(label[data == value_a]) return ent - sum_entropy # 计算计算每个属性的信息增益并求最大的 def find_entropy_index(data, label): label = label.flatten() feature_count = data.shape[1] max_list = [] for i in range(feature_count): max_list.append(c_entropy_gain(data[:, i], label)) max_value = max(max_list) # 求列表最大值 max_idx = max_list.index(max_value) # 求最大值对应索引 return max_idx
# 计算信息熵 def c_entropy(label): # 每类有几个 count_class = Counter(label) ret = 0 for classes in count_class.keys(): ret += count_class[classes]/len(label)*np.log2(count_class[classes]/len(label)) return -ret # 计算单个属性信息增益率 def c_entropy_gain_rate(data, label): ent = c_entropy(label) # 几个值循环几次 count_value = Counter(data) sum_entropy = 0 sum_iv = 0 for value_a in count_value.keys(): d = count_value[value_a] / len(label) sum_entropy += d * c_entropy(label[data == value_a]) sum_iv -= d * np.log2(d) return (ent - sum_entropy) / sum_iv # 计算计算每个属性的信息增益率并求最大的 def find_entropy_ratio(data, label): # 返回大于中位数的下标 def get_mid_index(list): mid = int(len(list)/2) b = sorted(enumerate(list), key=lambda x: x[1]) # x[1]是因为在enumerate(a)中,a数值在第1位 c = [x[0] for x in b] # 获取排序好后b坐标,下标在第0位 return c[mid:] label = label.flatten() feature_count = data.shape[1] max_list_e = [] max_list = [] for i in range(feature_count): max_list_e.append(c_entropy_gain(data[:, i], label)) max_list.append(c_entropy_gain_rate(data[:, i], label)) # 只保留信息增益大于平均水平的 max_list_mid = [max_list[i] for i in get_mid_index(max_list_e)] max_value = max(max_list_mid) # 求列表最大值 max_idx = max_list_mid.index(max_value) # 求最大值对应索引 return max_idx
# 计算单个属性基尼指数 def c_gini(data, label): # 几个值循环几次 count_value = Counter(data) gini = 0 for value_a in count_value.keys(): d = count_value[value_a] / len(label) class_gini = 1 # 取出值一样的data的value same_value_class = label[data == value_a] count_labels = Counter(same_value_class) for i in count_labels.values(): class_gini -= (i / count_value[value_a])**2 gini += d * class_gini return gini # 计算计算每个属性的基尼指数并求最小的 def find_gini(data, label): label = label.flatten() feature_count = data.shape[1] min_list = [] for i in range(feature_count): min_list.append(c_gini(data[:, i], label)) min_value = min(min_list) # 求列表最小值 min_idx = min_list.index(min_value) # 求最小值对应索引 return min_idx
def sk(data_train, label_train, data_test, label_test): clf = tree.DecisionTreeClassifier(criterion="entropy") clf = clf.fit(data_train, label_train) score = clf.score(data_test, label_test) # 返回精确度 print("准确率为:", score * 100, "%") # 画决策树 feature_name = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感'] import matplotlib as mpl mpl.rcParams['font.sans-serif'] = ['FangSong'] # 指定中文字体 mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 正常显示负号 fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(4, 4), dpi=300) tree.plot_tree(clf, feature_names=feature_name, class_names=["好瓜", "坏瓜"], filled=True, # 填充颜色,颜色越深,不纯度越低 rounded=True # 框的形状 ) plt.show()
结果如下
可视化一下:
决策树有几个注意点,可能会在面试中被提到,还是比较能体现被面试者对这个算法的理解的。当然我们也不一定就是为了面试,搞清楚这些问题对帮助我们理解这个算法还是很有好处的。其中部分答案是博主自己的理解,如果有问题麻烦路过的大佬评论区指正。
答:
这仨还有一些特点:
ID3 泛化能力弱,C4.5一定程度上惩罚了取值较多的特征,避免ID3的过拟合,提升泛化能力。
ID3 只能处理离散数据,其他俩都可以处理连续型变量,C4.5靠排序后找分割线把连续型变量转化为布尔型,CART靠对特征二值划分。
ID3 C4.5 只能做分类任务,CART可以分类也可以回归。
ID3 对缺失值特别敏感。
ID3 C4.5 每个节点可以产生多分支,CART只会产生二分支。
答:
老生常谈的问题了,前面也有讲。这里就重复讲下。
预剪枝
在树长到一定深度后停止生长,当树的节点样本数小于某个阈值时停止生长,每次分裂树带来的提升小于某个阈值时停止生长。
可能导致欠拟合。不过比较适合大规模的问题,在分类后期准确率会有显著增长。需要有经验的人确定相关参数(节点样本数、最大深度等)
后剪枝
后剪枝是在树建立后,从下往上计算是否需要剪掉一部分子树并用叶子代替。比较难的地方是几个后剪枝的方法(错误率降低后剪枝、悲观剪枝、代价复杂度剪枝、最小误差剪枝、CVP、OPP 等)。这个比较复杂,不详细介绍。
答:
样本 | 颜值(A) | 身材(B) | 性格(C) | 收入(D) | 学历(E) | 交往(R) |
---|---|---|---|---|---|---|
1 | 1 | 0 | 1 | 1 | 1 | 1 |
2 | 1 | 1 | 0 | 0 | 0 | 0 |
3 | 1 | 1 | 1 | 1 | 1 | 1 |
4 | 1 | 0 | 1 | 0 | 1 | 0 |
5 | 1 | 1 | 0 | 1 | 0 | 0 |
6 | 0 | 1 | 0 | 1 | 1 | 0 |
7 | 1 | 1 | 0 | 0 | 0 | 0 |
8 | 1 | 0 | 1 | 1 | 1 | 1 |
9 | 0 | 0 | 1 | 1 | 1 | 0 |
10 | 1 | 1 | 1 | 1 | 1 | 1 |
11 | 1 | 0 | 1 | 0 | 0 | 0 |
12 | 0 | 0 | 1 | 1 | 1 | 0 |
13 | 1 | 1 | 1 | 0 | 1 | 1 |
14 | 1 | 0 | 1 | 1 | 1 | 1 |
15 | 1 | 1 | 1 | 1 | 1 | 1 |
答:
暂时就算一个颜值和身材哈~~
import random from collections import Counter import numpy as np import pandas as pd import warnings from matplotlib import pyplot as plt warnings.filterwarnings("ignore") from sklearn import tree class Node(object): def __init__(self, son=None, data=None, label=None, node_label=None): self.son = son # 节点的子节点 self.data = data # 节点包含的数据 self.label = label # 节点包含的标签 self.node_label = node_label # 按照哪个属性分类 self.if_leaf = False # 默认不是子节点 def build_tree(data, label, choice): mytree = Node() # 全部属于一个类别 if np.unique(label.flatten()).shape[0] == 1: mytree.if_leaf = True mytree.label = label[0] return mytree # 当前属性都属于一个类别 if np.unique(data, axis=0).shape[0] == 1: mytree.if_leaf = True # 听天由命,直接选第一个 mytree.label = label[0] return mytree # 样本或属性集合是空的 if label.shape[0] == 0: mytree.if_leaf = True # 从来没碰到这种情况,默认0 mytree.label = 0 return mytree # 判断 if choice == 1: mytree = Node(data=data, label=label, node_label=find_entropy_index(data, label)) if choice == 2: mytree = Node(data=data, label=label, node_label=find_entropy_ratio(data, label)) if choice == 3: mytree = Node(data=data, label=label, node_label=find_gini(data, label)) mytree.son_label = Counter(data[:, mytree.node_label]).keys() # 递归 # for feature_class in Counter(data[:, mytree.node_label]).keys(): # print(data[data[:, mytree.node_label] == feature_class], "\n", label[data[:, mytree.node_label] == feature_class],"\n\n") mytree.son = {feature_class: build_tree(data[data[:, mytree.node_label] == feature_class], label[data[:, mytree.node_label] == feature_class], choice) for feature_class in Counter(data[:, mytree.node_label]).keys()} return mytree # 计算信息熵 def c_entropy(label): # 每类有几个 count_class = Counter(label) ret = 0 for classes in count_class.keys(): ret += count_class[classes]/len(label)*np.log2(count_class[classes]/len(label)) return -ret # 计算单个属性信息增益 def c_entropy_gain(data, label): ent = c_entropy(label) # 几个值循环几次 count_value = Counter(data) sum_entropy = 0 for value_a in count_value.keys(): sum_entropy += count_value[value_a] / len(label) * c_entropy(label[data == value_a]) return ent - sum_entropy # 计算计算每个属性的信息增益并求最大的 def find_entropy_index(data, label): label = label.flatten() feature_count = data.shape[1] max_list = [] for i in range(feature_count): max_list.append(c_entropy_gain(data[:, i], label)) max_value = max(max_list) # 求列表最大值 max_idx = max_list.index(max_value) # 求最大值对应索引 return max_idx # 计算单个属性信息增益率 def c_entropy_gain_rate(data, label): ent = c_entropy(label) # 几个值循环几次 count_value = Counter(data) sum_entropy = 0 sum_iv = 0 for value_a in count_value.keys(): d = count_value[value_a] / len(label) sum_entropy += d * c_entropy(label[data == value_a]) sum_iv -= d * np.log2(d) return (ent - sum_entropy) / sum_iv # 计算计算每个属性的信息增益率并求最大的 def find_entropy_ratio(data, label): # 返回大于中位数的下标 def get_mid_index(list): mid = int(len(list)/2) b = sorted(enumerate(list), key=lambda x: x[1]) # x[1]是因为在enumerate(a)中,a数值在第1位 c = [x[0] for x in b] # 获取排序好后b坐标,下标在第0位 return c[mid:] label = label.flatten() feature_count = data.shape[1] max_list_e = [] max_list = [] for i in range(feature_count): max_list_e.append(c_entropy_gain(data[:, i], label)) max_list.append(c_entropy_gain_rate(data[:, i], label)) # 只保留信息增益大于平均水平的 max_list_mid = [max_list[i] for i in get_mid_index(max_list_e)] max_value = max(max_list_mid) # 求列表最大值 max_idx = max_list_mid.index(max_value) # 求最大值对应索引 return max_idx # 计算单个属性基尼指数 def c_gini(data, label): # 几个值循环几次 count_value = Counter(data) gini = 0 for value_a in count_value.keys(): d = count_value[value_a] / len(label) class_gini = 1 # 取出值一样的data的value same_value_class = label[data == value_a] count_labels = Counter(same_value_class) for i in count_labels.values(): class_gini -= (i / count_value[value_a])**2 gini += d * class_gini return gini # 计算计算每个属性的基尼指数并求最小的 def find_gini(data, label): label = label.flatten() feature_count = data.shape[1] min_list = [] for i in range(feature_count): min_list.append(c_gini(data[:, i], label)) min_value = min(min_list) # 求列表最小值 min_idx = min_list.index(min_value) # 求最小值对应索引 return min_idx def sk(data_train, label_train, data_test, label_test): clf = tree.DecisionTreeClassifier(criterion="entropy") clf = clf.fit(data_train, label_train) score = clf.score(data_test, label_test) # 返回精确度 print("准确率为:", score * 100, "%") # 画决策树 feature_name = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感'] import matplotlib as mpl mpl.rcParams['font.sans-serif'] = ['FangSong'] # 指定中文字体 mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 正常显示负号 fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(4, 4), dpi=300) tree.plot_tree(clf, feature_names=feature_name, class_names=["好瓜", "坏瓜"], filled=True, # 填充颜色,颜色越深,不纯度越低 rounded=True # 框的形状 ) plt.show() def pridect(data, mytree): # 找下个节点 def find_result(mytree, row): # 如果是叶子节点 if mytree.if_leaf: return mytree.label # 不是叶子节点就继续找 row_the_value = row[mytree.node_label] return find_result(mytree.son[row_the_value], row) ret = np.array([find_result(mytree, row) for row in data]) return ret def eveluate(predict, result): correct = 0 for i in range(predict.shape[0]): correct += (int(predict[i][0]+0.5) == result[0]) print("准确率为:", correct / predict.shape[0] * 100, "%") if __name__ == '__main__': random.seed(1129) data = pd.read_csv("watermelonData.csv").sample(frac=1, random_state=1129) # 因为西瓜数据集每列都是0到2的,所以这里就不进行标准化了 labels = data["label"] data_shuffled = np.array(data[data.columns[:-1]]) # 划分训练集测试集 data_train = data_shuffled[:int(data_shuffled.shape[0]*0.6), :] label_train = np.array(labels[:data_train.shape[0]]).reshape(-1, 1) data_test = data_shuffled[data_train.shape[0]:, :] label_test = np.array(labels[data_train.shape[0]:]).reshape(-1, 1) choice = 0 while choice != 5: print("1. ID3求解\n2. C45求解\n3. CART求解\n4. sklearn求解\n5. 退出") try: choice = int(input()) except: break if choice == 1: print("ID3求解中...") mytree = build_tree(data_train, label_train, choice) eveluate(pridect(data_test, mytree), label_test) # find_entropy_index(data_train, label_train) elif choice == 2: print("C45求解中...") mytree = build_tree(data_train, label_train, choice) eveluate(pridect(data_test, mytree), label_test) # find_entropy_ratio(data_train, label_train) elif choice == 3: print("CART求解中...") mytree = build_tree(data_train, label_train, choice) eveluate(pridect(data_test, mytree), label_test) # find_gini(data_train, label_train) elif choice == 4: print('sklearn yyds') sk(data_train, label_train, data_test, label_test) else: print("退出成功") break
参考:
吴恩达《机器学习》
sklearn官网
《百面机器学习》
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。