基于Python实现的决策树模型 & w( r% W+ j1 `7 I; p" d- V + U" Q, ?/ f+ m g决策树模型 - q+ M; t3 T( y- h0 l; f$ c; m8 B$ D目录$ q* J( b3 z8 b z* `6 R
人工智能第五次实验报告 11 B' C4 f5 l- [" Z: {
决策树模型 1 6 |+ z, k1 O! M, Q一 、问题背景 1 / H1 X: v- b1 z. ^1.1 监督学习简介 1 0 g" F: r6 f# `+ L1.2 决策树简介 12 d8 y3 }% @* e9 N a& X1 G. p
二 、程序说明 3$ P' u7 w0 G2 ]4 D0 ?/ T
2.1 数据载入 3 4 R; ^/ \7 {2 A- c! L" m4 e2.2 功能函数 3 5 C- q" y) S, E) q/ L5 e2.3 决策树模型 4" t3 }6 E9 ^- L2 z% M
三 、程序测试 5& ^% n2 C, B0 U$ T6 ^5 d9 T
3.1 数据集说明 5 + p; }5 \" W1 m' l' g2 y/ P3.2 决策树生成和测试 6. ?0 I/ ` V8 M: e7 P* c: u1 _
3.3 学习曲线评估算法精度 7 3 ~+ x1 a0 Y- A6 b% |- x8 R5 `3 k四 、实验总结 8 n& @9 q+ f9 b; n8 v附 录 - 程序代码 82 R' Z1 t3 z- {0 w' ~3 t1 p( Y
一 、问题背景( H7 Q7 ~9 f; G6 m
1.1监督学习简介 , `0 S+ Z- Y( r; G2 _+ m机器学习的形式包括无监督学习,强化学习,监督学习和半监督学习;学习任务有分类、聚类和回 归等。3 k+ O% i) o& D6 h7 f( j
监督学习通过观察“输入—输出”对,学习从输入到输出的映射函数。分类监督学习的训练集为标记 数据,本文转载自http://www.biyezuopin.vip/onews.asp?id=16720每一条数据有对应的”标签“,根据标签可以将数据集分为若干个类别。分类监督学习经训练集生 成一个学习模型,可以用来预测一条新数据的标签。 3 T- g" O. t+ \常见的监督学习模型有决策树、KNN算法、朴素贝叶斯和随机森林等。( o& C$ `8 c' w
1.2决策树简介 1 a7 K* ^& t' O+ \5 Q决策树归纳是一类简单的机器学习形式,它表示为一个函数,以属性值向量作为输入,返回一个决策。0 n) X9 N3 A" }( Y* I! A
决策树的组成 ; j1 S3 E0 R* V0 }% S: H, j决策树由内节点上的属性值测试、分支上的属性值和叶子节点上的输出值组成。 ) M! Z, r. ]. z/ ^! @ ( Z' n. B8 z+ M; S5 a: jimport numpy as np , g0 o4 \9 a$ r; I" P+ e# b; bfrom matplotlib import pyplot as plt 9 X/ L& K% _3 F( z3 pfrom math import log 7 ~5 l% r9 J% Z/ U$ Iimport pandas as pd ( [& q, d7 X2 \import pydotplus as pdp* I2 x, n* q. _7 G4 ~
3 {" P5 I/ @1 F# L! I
"""3 q# c. g) D- @8 ]3 G) r- x
19335286 郑有为2 k& H" a0 d3 F& A ^4 d
人工智能作业 - 实现ID3决策树6 ~+ P, ?% T9 N: m) y% E
"""+ C; r& y) A1 w: I7 Z( u; f
, A1 x0 t* d9 O I
nonce = 0 # 用来给节点一个全局ID! p5 K. A- [5 s+ u* a, P
color_i = 03 v+ j/ a8 h& s9 P) j, I' d) r ~
# 绘图时节点可选的颜色, 非叶子节点是蓝色的, 叶子节点根据分类被赋予不同的颜色; {1 i3 q. g4 d3 ]/ ?* X% S2 Y
color_set = ["#AAFFDD", "#DDAAFF", "#DDFFAA", "#FFAADD", "#FFDDAA"]5 w+ |) R; s6 o8 q/ P/ W0 l1 \
j' y, h2 f# @. K1 f
# 载入汽车数据, 判断顾客要不要买' |5 Z* _5 B$ r4 o& L4 }: }7 n0 X' v
class load_car:* g6 d$ m" x; ]/ [1 ?" v6 ]* s+ Y
# 在表格中,最后一列是分类结果( g& C2 l2 d: b7 n! |( i
# feature_names: 属性名列表 / }5 C& E) B h# p, V& ]! ~# O. F5 G # target_names: 标签(分类)名 8 I5 ?: S# J4 ^! F; k& [ # data: 属性数据矩阵, 每行是一个数据, 每个数据是每个属性的对应值的列表/ ~3 y$ {( }) U
# target: 目标分类值列表 / B7 \4 W1 @9 \6 ~* s+ b( w5 a | def __init__(self): ! s9 u, f7 S( n& u$ a/ B7 r; i- O6 x df = pd.read_csv('../dataset/car/car_train.csv')6 j y! s# z6 o6 G
labels = df.columns.values ; M/ G. h; A% n. L0 u% Z y data_array = np.array(df[1:]) ) Z9 f9 H* `" [7 S" S" A3 v self.feature_names = labels[0:-1] 2 }( O; I9 o1 C. V( {6 W3 D% t/ t self.target_names = labels[-1] % @9 W+ `7 ~/ c" e8 n% Q7 I self.data = data_array[0:,0:-1]8 N; o5 {3 F/ y+ i- y
self.target = data_array[0:,-1] : e) Z9 r1 \. y7 t6 W0 ]0 U7 u5 {7 u! M' S( R( ^) T) G& t5 G
# 载入蘑菇数据, 鉴别蘑菇是否有毒 & F; W! x1 x! v! x5 g/ P2 c1 Z) Yclass load_mushroom: 5 V7 d7 Y5 R/ D; m7 ^; m" g # 在表格中, 第一列是分类结果: e 可食用; p 有毒., f# x/ K; D) z
# feature_names: 属性名列表& [* \+ B( l2 [ n! _6 b( H) j, x; \+ c
# target_names: 标签(分类)名 1 m" I. ?; @( f2 n4 ] # data: 属性数据矩阵, 每行是一个数据, 每个数据是每个属性的对应值的列表 # T( l5 l* {' V7 |9 J # target: 目标分类值列表2 j6 I E y* `' e4 i* r" H6 \5 O. C
def __init__(self):& `& H7 ^* V8 ?, R# \* I
df = pd.read_csv('../dataset/mushroom/agaricus-lepiota.data') / a0 p- z/ D% j data_array = np.array(df)8 ]4 `2 e( M5 O; r
labels = ["edible/poisonous", "cap-shape", "cap-surface", "cap-color", "bruises", "odor", "gill-attachment",! S3 a- C" M/ b2 c3 q
"gill-spacing", "gill-size", "gill-color", "stalk-shape", "stalk-root", "stalk-surface-above-ring", 0 C( U" `' ]; x+ N3 q% y "stalk-surface-below-ring", "stalk-color-above-ring", "stalk-color-below-ring", 3 N! m2 |% h5 d "veil-type", "veil-color", "ring-number", "ring-type", "spore-print-color", "population", "habitat"] . \4 r) V/ a- Z4 w- \ self.feature_names = labels[1:] \& Z6 E3 r$ U; ?
self.target_names = labels[0] $ ]( j) D4 z+ B' P" @ self.data = data_array[0:,1:]) d, k1 u& T: K1 x# u" K
self.target = data_array[0:,0] 0 i) e2 X& I% B1 x: A( D/ B # `# K, u1 C( @8 f8 N+ w# 创建一个临时的子数据集, 在划分测试集和训练集时使用' u4 I. m' r* H1 U5 n& {/ ~
class new_dataset:7 U; Q2 N+ o4 _( @6 i+ C( w2 x1 ^
# feature_names: 属性名列表 A, a! M. F7 s8 H # target_names: 标签(分类)名 m" ~% _; Z$ h
# data: 属性数据矩阵, 每行是一个数据, 每个数据是每个属性的对应值的列表 4 { d% p; p. i7 r" D9 S # target: 目标分类值列表 - f9 y9 r4 Y1 M6 J- e! q def __init__(self, f_n, t_n, d, t):5 J0 x& |1 @! ^& \( F7 T
self.feature_names = f_n, Q. N# B- H4 d) S- | h
self.target_names = t_n7 \1 [5 X+ K4 K- r/ d, V) O
self.data = d5 ?6 E) Y8 g/ }9 I8 n) U, f; U
self.target = t / t, d5 q. N" k& O. x! w2 r# ?1 W0 y# r: Z
# 计算熵, 熵的数学公式为: $H(V) = - \sum_{k} P(v_k) \log_2 P(v_k)$; Q6 _# _' e+ _3 o
# 其中 P(v_k) 是随机变量 V 具有值 V_k 的概率 6 E$ A+ X7 `7 b) c& B' ]7 [% a# target: 分类结果的列表, return: 信息熵 - J: \5 E$ d$ S4 C8 S/ B E# ^def get_h(target): ; ?4 {: p8 F f" i target_count = {}. Y% m" n! u8 | F/ x
for i in range(len(target)): - X4 e7 g/ h+ u! T, j$ w! w" T, J1 [ label = target 8 s6 w% n6 Z! v& B2 d7 u if label not in target_count.keys():& w m6 ~' v) ~- X9 g2 O/ M
target_count[label] = 1.0 3 s" |% v( |( m! _1 V$ F+ O& m else: ( U3 g/ s- K" `) W target_count[label] += 1.0 ; m: p. V8 a) {* N" a8 K h = 0.05 f7 x( m; G8 N. Y
for k in target_count:; q8 G, b! H9 l; H3 o1 N! A# y: C
p = target_count[k] / len(target) $ b6 x+ S5 R- C7 p h -= p * log(p, 2) ' l. v+ x, e' J& c% q2 A return h 3 O% p8 r1 o6 [$ T - I( E1 Z: z% T$ m S. k; Y# 取数据子集, 选择条件是原数据集中的属性 feature_name 值是否等于 feature_value - f. m4 F8 |. e) j) o$ }0 b; ]# 注: 选择后会从数据子集中删去 feature_name 属性对应的一列 - `$ }* \0 _! r" M( _1 Y+ Wdef get_subset(dataset, feature_name, feature_value): : F) D: ?* ~4 N sub_data = [] ( M! z9 ]) e# R, P0 _ sub_target = []. } T1 g% s0 s" {
f_index = -1 5 ~# R) {; w9 I& O4 w# H for i in range(len(dataset.feature_names)): - \( H5 O9 v3 k2 M. q6 R2 _' Y if dataset.feature_names == feature_name:* |# D3 z+ L R' ?7 o
f_index = i 8 ]0 C2 s/ X' k0 R4 y! T5 \ break : P' G1 X0 S: s$ ]' r7 j& n: @ F# M: H% F9 y1 X
for i in range(len(dataset.data)): 3 V3 j; D$ M Q5 K) ? if dataset.data[f_index] == feature_value: - e4 o& \( z% q l = list(dataset.data[:f_index]) " ^3 U& {% i) a. R) V9 E; O l.extend(dataset.data[f_index+1:]) 6 i1 Z; E1 F2 f8 R3 t" o" i sub_data.append(l) , v. |0 |: V( C: m sub_target.append(dataset.target) 9 }! H' B. y, h( ?& K9 _+ s) m 0 N' x- J4 E- v8 V# I3 v) i9 T; U! _ sub_feature_names = list(dataset.feature_names[:f_index]), l5 o/ W! b, i$ m
sub_feature_names.extend(dataset.feature_names[f_index+1:]) " P$ E* i2 Z8 O" `9 [ return new_dataset(sub_feature_names, dataset.target_names, sub_data, sub_target)1 T0 ]5 Z- z9 d; b* w0 I5 i
0 K5 j A# x4 g/ u
# 寻找并返回信息收益最大的属性划分 " Y: }9 G, a! P# 信息收益值划分该数据集前后的熵减3 k% s+ R6 K" g- P! Z
# 计算公式为: Gain(A) = get_h(ori_target) - sum(|sub_target| / |ori_target| * get_h(sub_target))$ , i8 \0 l0 Z( x9 h* Wdef best_spilt(dataset): % @4 g2 |6 F& r% Y6 ?/ W' n# K' i' l" r: C
base_h = get_h(dataset.target) , w- @% m2 R! Y, q8 s5 y' n |. @ best_gain = 0.08 {0 ~2 |: `9 M( e
best_feature = None ; O( q. L' t* n8 M. ~6 B( f for i in range(len(dataset.feature_names)): S& O8 D% U4 a1 W
feature_range = [] ( ]4 ] G) Q6 p0 P2 t/ O" u7 ]3 g" I for j in range(len(dataset.data)):! h. z5 ^6 \0 p
if dataset.data[j] not in feature_range:$ @; K* R3 o y4 C, v
feature_range.append(dataset.data[j]) 9 G/ i7 k' @- c( R- A9 D2 s$ D$ l& Y2 H( _( j0 \- t
spilt_h = 0.0 $ v1 [- J9 m, T2 p# T$ i for feature_value in feature_range:' H( d2 v2 y- b/ }% d7 F# U/ K1 y
subset = get_subset(dataset, dataset.feature_names, feature_value) ^- `( `( [* g; q8 v
spilt_h += len(subset.target) / len(dataset.target) * get_h(subset.target)( A/ _8 D, _6 ^6 l: @# [( E
: |* _& j# Z# N1 M; u
if best_gain <= base_h - spilt_h:3 t# F/ H/ Q* i
best_gain = base_h - spilt_h1 s! j+ O1 V; ]* P6 \3 m5 m
best_feature = dataset.feature_names( O" {0 i6 \- S. ^# }
: a. z0 }! ]& T" |% v return best_feature # X+ S6 |- N- g% U1 D) u i9 K: j1 A* v/ g" x' J
# 返回数据集中一个数据最可能的标签/ K( I( I" E1 M$ K( z8 F _0 J. q# u
def vote_most(dataset): 5 e; @0 ]0 o0 P b target_range = {} 5 {' \5 Z9 q/ O- G best_target = None% v0 @: X# n0 ~5 k
best_vote = 0 # R" i. r, u! r4 W; G# w) O# c d. x! K4 F! `- c
for t in dataset.target: - U2 v: P7 s! e$ v8 m if t not in target_range.keys(): # M y- d. C$ j" f8 E9 q$ l& P target_range[t] = 1* I% y1 @+ Y" \# u* ]: J0 D
else: + O% F/ u1 X2 m; r8 {) t2 p, E target_range[t] += 1* N( x! X3 c0 l' N7 u/ t
+ T0 G+ p. O* B for t in target_range.keys():3 R2 @+ E: b2 \: D
if target_range[t] > best_vote:5 p" z3 }4 g) G
best_vote = target_range[t] 8 K5 D$ { ?/ r. E/ p best_target = t # ]8 Q) l: H( m! W! ^, ?- _ m! [& T8 b2 r% q return best_target : g% K# c/ ]/ N+ z# y; d ' D8 ]3 l& s! q: s/ E/ \# 返回测试的正确率5 i, Y6 U. h, o, Q* V$ n. q; L# D
# predict_result: 预测标签列表, target_result: 实际标签列表 ) A7 Q2 ]* W* _: L7 N) F/ Adef accuracy_rate(predict_result, target_result): + U( X3 L& Y0 ^1 g2 o8 r- R5 r # print("Predict Result: ", predict_result)) _# S$ h7 O& I e0 Z5 B' p$ u% i
# print("Target Result: ", target_result)" d) ~: t& {2 T+ L& }6 ]+ R
accuracy_score = 0- E" c* C1 X) P7 ~. f
for i in range(len(predict_result)):, a) T( j6 H6 ~# A
if predict_result == target_result:7 w6 R- ^$ ?0 `2 Q# q& |4 h4 F
accuracy_score += 1% \3 q/ ~3 u; ]/ z0 @4 L7 n$ q
return accuracy_score / len(predict_result) l. |$ A0 R0 v3 @
; f1 m6 h, {+ ?5 }3 u8 I7 B0 H# 决策树的节点结构 7 E1 m& W5 x7 n* Iclass dt_node: 9 b0 z- T% l; r; \$ q) ~ y& |( w2 K' { V, z
def __init__(self, content, is_leaf=False, parent=None): , @ F% O5 a. F9 \; r+ ^ global nonce $ a- R( K4 i! T5 x# K; Z* w self.id = nonce # 为节点赋予一个全局ID, 目的是方便画图 ( w9 F9 Q8 _5 n0 N. n0 H6 Z% W9 K nonce += 1 1 u* W, ~/ q$ D; {0 J! {7 U self.feature_name = None - Y3 N/ z2 ?3 {/ N; M* o self.target_value = None ' V8 m8 o* V3 \! Z3 G% K( l self.vote_most = None # 记录当前节点最可能的标签, g8 t4 C7 j: F0 ]8 t/ s( s
if not is_leaf: " z) ?" Z3 c$ i; }( { P6 A% @ self.feature_name = content # 非叶子节点的属性名# _; k& t* ?; Y2 b
else:0 _* h6 a$ {7 j6 b2 l' Z# R; \
self.target_value = content # 叶子节点的标签 & U* p6 Y' J+ N5 T/ ]; R- ~5 O6 u) i' e& I5 g
self.parent = parent8 v) j" F' A: ?$ _5 \7 D0 |8 Z
self.child = {} # 以当前节点的属性对应的属性值作为键值 + e* z! O. F5 l0 E0 @ 3 y; x$ s. @" t. r7 T# 决策树模型 ) Y# U& ^! x, l4 g6 A( [5 q* jclass dt_tree:! }( J: V- b2 d' h1 F& K
+ m! G/ R0 ^2 a/ q8 Z
def __init__(self): ! s3 N. w! N8 X- W4 i self.tree = None # 决策树的根节点 # C. A' ?6 l2 q$ s self.map_str = """- Z% P# Q8 Z$ K4 y8 X+ o* P
digraph demo{ + x X: L: o3 H0 C& t1 O5 a node [shape=box, style="rounded", color="black", fontname="Microsoft YaHei"]; 1 \. Z" `; V! J3 ~& @6 Z6 { edge [fontname="Microsoft YaHei"]; ; y$ k! z4 ^3 a- Q! I """ # 用于作图: pydotplus 格式的树图生成代码结构 % B9 z* ~( g) F* ~' b self.color_dir = {} # 用于作图: 叶子节点可选颜色, 以标签值为键值" J1 N9 H" }: G+ H! C! W& _- k
1 y& Q# F5 ]6 W # 训练模型, train_set: 训练集 $ r: h. k/ ]; Z4 P3 } def fit(self, train_set): # ~: A1 _4 H ~" H4 |4 f& [# y9 |# U, k* f0 G
if len(train_set.target) <= 0: # 如果测试集数据为空, 则返回空节点, 结束递归# [) u$ q4 n; c# ?# h. ?5 |. ~
return None 0 e' u$ ^: p! [& I2 X7 B. K: s4 I . _7 S% Z: N6 k5 ]% q% G9 @ target_all_same = True 5 w; h) z. ]/ b I- \) X for i in train_set.target: }0 V. t' S- l5 `1 U9 E if i != train_set.target[0]:7 ]- f. |8 B9 B, E' [8 W
target_all_same = False# a' h8 O2 Y& }9 ]' A
break & t' w' i$ I( x# l- @9 X6 i! x& Q. i+ H$ D
if target_all_same: # 如果测试集数据中所有数据的标签相同, 则构造叶子节点, 结束递归, `3 G7 ^9 }% D: ?( F! x$ {% k6 t: Q
node = dt_node(train_set.target[0], is_leaf=True)& @: r4 R1 W: I i1 H! m) t n+ m8 V
if self.tree == None: # 如果根节点为空,则让该节点成为根节点 8 ?' p% z) Y1 V# o! j8 L% Y0 r self.tree = node * d$ |: m. \* v' G. P 3 v# K& K, `; ? # 用于作图, 更新 map_str 内容, 为树图增加一个内容为标签值的叶子节点, m! @9 K8 c2 \3 s" K2 d; y8 Z
node_content = "标签:" + str(node.target_value) & U4 l3 b* H& N) r j5 K2 H9 r2 @ self.map_str += "id" + str(node.id) + "[label=\"" + node_content + "\", fillcolor=\"" + self.color_dir[node.target_value] + "\", style=filled]\n" 6 a! h# q/ ]# B; M/ M4 h, E' G5 e7 h" y9 z
return node 1 _- G B& R3 {" V/ i5 u5 K elif len(train_set.feature_names) == 0: # 如果测试集待考虑属性为空, 则构造叶子节点, 结束递归 ' ~' u. @( M6 ?7 d: s5 P- o" ~ node = dt_node(vote_most(train_set), is_leaf=True) # 这里让叶子结点的标签为概率上最可能的标签/ O5 V" X. |7 x1 d( z# d- L5 S
if self.tree == None: # 如果根节点为空,则让该节点成为根节点! X3 ~: D, Z0 J% E/ |
self.color_dir[vote_most(train_set)] = color_set[0]' ?: J) V8 l8 G7 d
self.tree = node" S# R, Z* a* ]8 k3 b
, J+ a. B% l) ^+ {6 R! y # 用于作图, 更新 map_str 内容, 为树图增加一个内容为标签值的叶子节点7 o4 Y* Y9 a6 G, d
node_content = "标签:" + str(node.target_value) & G& m7 K' ~/ {' W; p, v self.map_str += "id" + str(node.id) + "[label=\"" + node_content + "\", fillcolor=\"" + self.color_dir[node.target_value] + "\", style=filled]\n"6 j- U# I8 o7 ~$ k; E) [% X
- K$ E1 `( Z- P& J
return node7 \ o* `- I# ]9 a8 D
else: # 普通情况, 构建一个内容为属性的非叶子节点9 H2 J( Z- e( ]; \) t' X9 f2 {1 N
best_feature = best_spilt(train_set) # 寻找最优划分属性, 作为该结点的值 6 W* q, u. }- z best_feature_index = -1 ) d0 g5 ?' G }) _* ] for i in range(len(train_set.feature_names)): t4 r- B/ u6 ^0 i7 p2 `) i
if train_set.feature_names == best_feature:/ l, `+ n6 O- P6 x6 c+ j
best_feature_index = i ( F: Y- V8 H1 n& [# T2 a break 0 @3 k- _) q4 j! U) i7 X8 y8 \' t7 q( G3 O- v, T O
node = dt_node(best_feature)/ X& S$ M* t3 l- X5 P$ j
node.vote_most = vote_most(train_set) 1 @0 V) ^7 e5 T5 q6 s) H9 D. F if self.tree == None: # 如果根节点为空,则让该节点成为根节点. v8 ?, i, R$ m$ f2 n/ n M3 t a
self.tree = node * J9 @& x1 d" V) ]* Q! G # 用于作图, 初始化叶子节点可选颜色) t( L4 K! f ?/ A+ M
for i in range(len(train_set.target)):+ `3 Y5 I8 L/ P) o3 d
if train_set.target not in self.color_dir: 7 B; Y. a* ~' G6 @7 r7 m global color_i# {0 K9 U. h- V m. T9 Y5 Y- E
self.color_dir[train_set.target] = color_set[color_i] $ R; g0 L* c' {( \; { color_i += 1 ) M7 z9 `( o5 i2 d color_i %= len(color_set)8 ^ ~+ J% T, a8 g$ `
v) w% L0 b5 ?+ b' `" y( ~ feature_range = [] # 获取该属性出现在数据集中的可选属性值 - o# c9 [& h+ _! ~) A0 y9 c for t in train_set.data:1 Q' H) f0 c6 Y. I- q' z: Q: n
if t[best_feature_index] not in feature_range: 2 X) [- C$ C. Y5 p feature_range.append(t[best_feature_index]) 6 D7 ]3 j+ F# y( Y( ?% S6 s+ x0 W8 k, h' C" b) r9 O7 d
# 用于做图, 创建一个内容为属性的非叶子节点; k p. q7 w7 }9 h! o B' R9 }
node_content = "属性:" + node.feature_name" V4 {/ n( {8 ~% y& H6 c' k
self.map_str += "id" + str(node.id) + "[label=\"" + node_content + "\", fillcolor=\"#AADDFF\", style=filled]\n" 7 @3 j& a: O6 V - |4 O+ Z0 e, V7 L* z for feature_value in feature_range:% }. M& i o) f9 t2 p
subset = get_subset(train_set, best_feature, feature_value) # 获取每一个子集 # }& i' n5 i+ X. [7 n node.child[feature_value] = self.fit(subset) # 递归调用 fit 函数生成子节点% ] k% [2 k4 O5 m
if node.child[feature_value] == None: 2 e% u. L7 l2 l8 l! T # 如果创建的子节点为空, 则创建一个叶子节点作为其子节点, 其中标签值为概率上最可能的标签5 A R. d* B9 f
node.child[feature_value] = dt_node(vote_most(train_set), is_leaf=True) 0 J& U' E9 r. r: R1 R+ t# e node.child[feature_value].parent = node6 b! l7 s) i3 b: ?
" \# D# j; Y, h2 i o) N. G # 用于做图, 创建当前节点到所有子节点的连线 8 x0 \% w0 b0 n5 } self.map_str += "id" + str(node.id) + " -> " + "id" + str(node.child[feature_value].id) + "[label=\"" + str(feature_value) + "\"]\n" m& z; n9 M. W! R8 n! m: _' J7 h9 G% S* M6 s8 z# y
# print("Rest Festure: ", train_set.feature_names) ?! }# g4 R) L$ ~ N" r
# print("Best Feature: ", best_feature_index, best_feature, "Feature Range: ", feature_range)" \0 g. z1 u+ N% Z6 o
# for feature_value in feature_range:5 Q% Z/ d6 i; n3 P3 e
# print("Child[", feature_value, "]: ", node.child[feature_value].feature_name, node.child[feature_value].target_value) ) E9 \, ]4 R# A- O n4 s! ?. s+ o9 I return node# L* b8 u: y% i7 U( Z
/ e. i8 D: W* Y# X$ }3 ~ # 测试模型, 对测试集 test_set 进行预测9 I# K: L7 g) [" G* l/ C+ e
def predict(self, test_set): 8 o! r2 F4 ^& |+ j5 Z$ f* v test_result = [] ; q- D' N. |: Z for test in test_set.data: 7 k. ]9 h0 |2 S7 o node = self.tree # 从根节点一只往下找, 知道到达叶子节点 : h# P! Q" V9 j$ `9 E V% c while node.target_value == None:7 V7 p) J, H; N
feature_name_index = -1) ^- k7 m8 N( T. L; p. j) C
for i in range(len(test_set.feature_names)): 2 M1 A: d J& k if test_set.feature_names == node.feature_name: / {' e" s* Z# ]; u0 F feature_name_index = i : N( a. w4 |. }+ }5 v9 b; [ break3 d4 G) V4 X+ k
if test[feature_name_index] not in node.child.keys(): " b( K1 m) c( F, T; O break ' H9 k* a z. g1 ^' {- r5 ` else:: E4 r+ ?8 h7 w. c& w% H
node = node.child[test[feature_name_index]]. }3 ^. d) x# X; U. l3 T7 P- B
& n: x, s: P, U if node.target_value == None: % _- o; m! ^" H test_result.append(node.vote_most)+ T3 k# t# o5 h U( J
else: # 如果没有到达叶子节点, 则取最后到达节点概率上最可能的标签为目标值6 o+ j" B& P6 w
test_result.append(node.target_value) ' T! c' p) f+ N; v* i0 m 6 ~6 {0 U& z' Z: P [ return test_result5 ?4 W1 }/ j0 y& t7 Q6 Z6 R
0 w( `* X- [- s9 d2 y
# 输出树, 生成图片, path: 图片的位置 - {4 b6 D: ^2 I9 v) ]. s' p def show_tree(self, path="demo.png"):7 e: w+ C j4 J- a
map = self.map_str + "}"1 I6 Y( M8 o r- S }9 _3 L; ^
print(map) & T7 O1 t" B5 A) f3 v; Y' X' m graph = pdp.graph_from_dot_data(map)* ]1 ?$ k" I" K$ f
graph.write_png(path)4 n" d) ?, {1 T+ y+ w, J
8 {1 B# A1 J! J7 d* l9 Y" r0 f$ S# 学习曲线评估算法精度 dataset: 数据练集, label: 纵轴的标签, interval: 测试规模递增的间隔- o7 b3 F! D# M- \: x
def incremental_train_scale_test(dataset, label, interval=1): t6 I. |. D; w j, E1 `$ u
c = dataset+ {, Z$ o1 n s7 g
r = range(5, len(c.data) - 1, interval) # @( F g% q( t. G& I rates = [] `0 t( b3 [ F% v. ` H5 d; n for train_num in r: $ Q4 |( o$ M6 T* M' N. t print(train_num)9 K& m" b* J0 d/ k4 i
train_set = new_dataset(c.feature_names, c.target_names, c.data[:train_num], c.target[:train_num]), Z9 D; ^' W4 N: E; t7 U
test_set = new_dataset(c.feature_names, c.target_names, c.data[train_num:], c.target[train_num:])0 \- l {$ m$ O8 p o( f# e
dt = dt_tree(): f' F; ]+ x, D
dt.fit(train_set)/ V$ M: ?# W( ]9 g! p. B4 L2 D
rates.append(accuracy_rate(dt.predict(test_set), list(test_set.target))) ; X$ N5 a! G1 _; q * q' U/ _$ l- t$ `/ D- R3 |, J print(rates) * N! w1 }6 e+ V+ [4 P7 D8 B. c( d plt.plot(r, rates) ! X1 }/ s8 D3 ?9 p# x/ V plt.ylabel(label) , T2 H2 _" \' T% X5 v plt.show()9 E; C7 t8 O; A% j8 {
, J9 w* A1 {& ?5 `$ {if __name__ == '__main__':5 p( O3 W' E6 }' n, u
7 \2 }! s2 h, d) U c = load_car() # 载入汽车数据集 1 N. E$ C6 q: F" b$ u # c = load_mushroom() # 载入蘑菇数据集, h1 e [3 F6 V0 z; |) _
train_num = 1000 # 训练集规模(剩下的数据就放到测试集)3 W2 f! c) Y- G# F* n
train_set = new_dataset(c.feature_names, c.target_names, c.data[:train_num], c.target[:train_num]) q, U, L# @% ]$ l test_set = new_dataset(c.feature_names, c.target_names, c.data[train_num:], c.target[train_num:])0 f ?1 l# u' V0 d$ J& O2 h
2 F3 I, }% a+ y b0 k dt = dt_tree() # 初始化决策树模型 8 A5 M u* Z1 _& y- e dt.fit(train_set) # 训练 0 X8 _8 R# j; U( x& ? dt.show_tree("../image/demo.png") # 输出决策树图片 $ m6 i% g5 y9 Z: y print(accuracy_rate(dt.predict(test_set), list(test_set.target))) # 进行测试, 并计算准确率吧8 w+ r9 M# `' F1 p8 }- |6 _
3 W$ G' h0 r0 K3 r$ A
# incremental_train_scale_test(load_car(), "car")6 X6 \3 D* j8 D! H n0 A. d
# incremental_train_scale_test(load_mushroom(), "mushroom", interval=20) " i- H( M2 c) O6 a5 h& B' l! o; E$ F6 }- E, Z7 u' s: t7 Q
4 x/ v* `" v1 E# b' k% R
+ k' u% [" @+ G' z7 X0 m; t1 $ G3 T9 R/ k0 `) }. W* [2 ' z% O7 n' Z. D9 {' z7 J3 . v3 d2 c. B0 N49 }% r* u5 v% ]- _: W3 N6 F# m
5 ) d) P4 `' G1 ~ n1 s5 B" ?6! E$ L% J$ v+ g3 s
79 F- P% `) m0 q- d% e: u
8% W5 O) s/ Y8 S' d) @% O) e
9+ g' b* n3 K q
10 ! a M$ F( _' d9 E' Z3 X11" s, p9 Z1 E! B5 W6 d' b
12 ) [( w* f- {0 |" R13 ( ~1 s9 h; o" q14" V( k. k# @: |+ I* e: ~2 [ z1 D& o9 w
15. o* C0 M8 C4 i3 z" B
16 $ I* d0 X! Z# j$ a17 ( l- O3 z8 u) O18 ) `6 u% V$ Y6 `, s; D19 ! t: y6 n9 {; w" y, n0 W206 n5 l" j1 F" W) z: P
21 X* }0 q* L8 [$ w. i, Z V22; i4 E" ?+ X) `6 O- ]
23 8 {- D4 v+ g) D" B7 I N242 P- w8 U N$ G c/ Q
25 & v- ]8 Q( s4 C# }26& H' ]1 C7 t' d7 E, d1 Z
274 |- d* S# V0 k( v3 z" R1 j, u( U
28; P3 j/ q" R% h' u9 F& { d, m5 ?
29( z4 s$ ?: X7 x. K8 j9 a
309 ^5 c) z. h& `5 M" d, @$ t
31 5 K9 k+ {, ^ T6 z32) p8 H' `! n" C7 V& E9 V
33% b0 m0 L$ B; Y0 O& V: X
349 D7 j/ U% Y+ P/ G+ @5 a
35- g$ B" m0 k' ?6 O0 v2 M
361 u/ Q5 o1 u2 A1 D0 o, W
37: z& f- s! r! @/ Q% q$ ]
38 / N/ C# \* q0 y% ]0 y ]0 s" Y393 L: w6 Y- I/ A& ^, {- r3 G& |
40 8 A: @6 o% j6 `# ~* A& Z. [1 K) x0 V417 A" k, L+ E' N( X# M$ Y# F; w
42 # }& b# a$ H9 F9 }43 , N. T' Y8 h- s ?; f$ a44% F0 R4 J+ Y2 P% | z0 e
45 . k/ X1 h2 d' }) b [46 0 e5 b4 S! C2 c8 g47 0 Z/ A2 e9 A/ z: S" E) S, f48- C. H/ e) R3 n* r4 ~' a* {
49, R; J( V, Y% Z1 \/ O! |
50 6 O& i; ?) P& Z$ u51+ H2 ^: [( |- b9 c) L! ~
52 5 C# q+ Y" s! S- j53 * A, z4 m; u, ~9 G3 ?( R54 + z3 N% C% r% D; j! G55 : v" k3 X" j# Q56 7 o% X7 q: M4 Y" j7 n57 + O% E5 |4 q$ v9 S; q583 o; d9 `1 w* x+ i q1 @
592 P4 o5 z! R! N# H+ k
60 4 d6 h1 o9 e" |0 ?, X2 @6 g61 3 E8 Q0 X# e+ R& S, T629 w: \4 o- f1 p
63 ' M$ |7 Q/ Z$ S2 }& b" Z64) J& P) i9 I, Z0 q2 t2 D
65 2 l0 F3 R" }+ k669 { R. k& ?3 e# }' k
67) S. |+ u9 ]+ O8 W' W7 k+ M
68 , q) a: O# R8 _" ^69 - z" d' X: a+ ?, M; @$ |70 9 O( G: d; L* [7 E4 p& D71 : O. m5 ~! ^7 }9 h) S+ A724 ]3 k v/ h6 l7 O
73 5 O. s3 J+ E S4 k) E8 W6 Y74 . g9 z8 ~8 u9 |1 I751 B4 b5 Q8 U; t \' c
76 3 t |7 `6 @1 X" i( G77' t2 D( n/ k) s
78& U! e# c. _7 }- }5 m" s$ f
797 \! k/ Y0 b4 Y7 {; o! T3 S g0 C( U
80 6 m5 O; x% M1 e* E813 v' ?/ P0 V1 `: b) k
82 $ {7 s- v) |4 s: Y% ?& h+ C83 7 W, }! o! J* V84 8 h+ y* T' l9 p7 d& R$ O3 R6 K85 & v3 t# H) B% k/ Q7 t/ K; X* Q86 4 E) Z9 _& c6 F0 c87 9 R8 B `1 G; }! v3 m9 O88 * s7 ?: ?5 x( S4 v: s1 l* a89 , E/ B: _! q4 O: A; G$ j8 x" d2 Q90 R" J5 g( I" W# l
91) r7 a/ ]! I( }& h
92. F1 G- d7 Q# F3 t- s3 r
93- {' P5 s- S- V- `
94 ' z7 t+ {3 |! P7 M/ |- F952 G$ g1 ~" n* I8 J' L( K
964 y9 U( I, U9 p5 s T! t; s
97 2 R7 Q* J& G, Y' \98; [& `: A7 v, | e$ ]# @
992 S, M( A4 V" _) k; C2 B
100; D5 E' w) M6 l. r3 ?
101 + L! M, o8 R, g e& O' A102$ n# r7 { l; P+ N* q
103: T, L. J" \5 p# H3 s8 a! f
104 6 l: E, F H4 W9 c6 i: h5 Z105 . ?2 f3 Y% @$ d0 H106 ) f5 r% B5 l, W5 a" ^0 U107 * B; w4 a; R; o; u2 V; P$ y4 f108 ; J8 h/ h% G, \" Z6 }" O109 % u+ r4 E$ f& y$ n) K: a+ K110 6 {; @+ v F/ D- ]111 * s1 d. A4 e' D+ F* J4 k$ s4 N112 6 t% Y/ a& ?8 f+ H( V1130 {* k" ]+ @. x/ \
1145 W$ _- k% L4 G' B' m7 v
115" D- k2 ?3 v `) ^- G0 a% P. `
116 , ]+ y( Y, A5 B# o) N( U117 * a0 j8 h( Y2 [5 V+ D118 " `4 J) ^* ~6 {119% Z3 S7 l0 P2 p7 ] A# Z
120 0 M9 v% d, `- Z0 x. f6 T" Y121: A- p2 g% Q+ O& f9 F
122 : n# ? G" E ^$ X9 p$ B123 ! I% w5 X$ `% }1 ?' r1246 o6 k& F3 @4 i. q. h* ~: U
1251 V$ B+ z: x1 Q5 E3 l+ N) m1 u! {
126, R' L+ B0 N# U
127 ( o1 i6 o2 A0 e: X4 m128 % `+ m* n* L+ q0 |3 i+ ]5 c A129 ! a8 {; L, |; ~" O, }0 H130) b0 C( z7 Y9 t6 U5 T3 ^4 s
1312 T6 l. d2 @7 G
132 1 j% _# e: k5 z; k133/ U2 z; f- t3 X z5 |5 v
134) D9 c5 D6 j8 q
135' L8 W6 A A, g7 P, `8 ?
136 & ^2 ^2 W6 j5 N7 s137: \5 p f2 F. [: ]& @- B
138% c1 ? {. G0 K* x9 N" c6 E/ ?
139; C$ v$ H" l i2 T" o9 d
140 % Y: w# c- d# {% l+ e* F) K141 , N2 u" R- M S+ x142 # e N0 e- N6 ]* k. M6 D7 f9 n143 ; h. H0 [# [2 ]3 T( m144 2 x* q3 M6 f$ r6 T145 - F/ `! M. Z6 F' F' P# L/ j146; u/ H) L9 m" a1 ]4 w1 r. B
147- C- H$ B9 P9 n
148% ]0 e* j G! v& Y5 X
1495 y& }! Z& ^ I. K# [: o$ C7 s
150 ; u! x7 h' L+ v! F) `1513 b8 O( A9 \+ U& N; `) L9 Z) @
152$ }5 _7 L$ t. b9 l
153* W8 i( D9 n" x9 c1 G# \* l9 @
154/ _ x/ B% d3 y! u& O
155 8 n% a! l1 E9 s- ]156% y) u5 Y# o" H2 O# h$ z
157 0 b- M5 z+ u2 n2 d( Y9 g; u1 l3 }6 ~1581 ]# P# Y! p2 s+ t2 `8 V8 i
159+ G8 G g! w! Y# `" j ^ |6 p
160: K9 L6 m1 I( v
161 t- {) A3 e' P* T7 k
162 ~0 y# J% P# ?' t; x
163 9 ^- R1 h4 r& g K& b9 P164 % P. h) @: B! w1 d3 |165 Y7 C2 ?* |- W2 U: J5 p+ Q% O. }8 f1665 p0 D1 c' P7 _% S; e4 w& i
167 ; N6 I }( s7 b* X! P/ }! G' v1684 h, o/ N4 k8 l9 P
1699 y j3 l @% V2 {2 f& u% X" m# t' F
170) G- z; N# y; v; E1 ?* C4 G
171$ l; I4 `8 H* A4 _+ G! W S
172 : x# R. g/ c( G- `- d1 Y0 M& x1 s173$ r# Y) O8 s1 D8 d) `' K
1747 r) b# c; c/ b; d$ h$ Z+ v$ G
175 2 N% v3 M1 H6 f. v. P) G1 ^: E! j; }6 ~5 ]176 ! w1 h3 F/ W. k2 L3 U177 $ B3 I/ A l) b: z178: ^( ]* ?' \+ }/ b
179. `5 m0 r7 F, t3 m
180) E( m0 ?( d( _* \% X! ]( Y$ N0 p+ s# ?
1819 k4 {2 _; j. `8 w0 `$ e0 X
182 8 ^- {# C! \7 H' b; F. s183# x2 X3 c: ^/ P$ }& j
184) f4 t5 l; S( ?2 W( i2 ^
185 9 A2 W3 g0 J) r- j1 z186 & K2 c* i. U. l6 }! B2 g% W187. Z% {, I6 ^- M4 W
188 8 _* O1 I5 V. M7 b( Q6 e. j( S& u/ ^189+ j) B) D7 T4 ]2 L: n
1906 e3 Q) Q# m; {! i! R+ Z' r7 s
191. P& j$ T R ^4 F$ b: L
192/ Y6 k7 i1 E j) d' x
193 % X1 @- @. e7 M' o2 Q+ v194 & v! j6 ^: l5 M% W7 b2 ?195 5 `; v6 I& `* ~1 L0 `% N196 % Q. p; Q; y8 C9 u197 . E( \9 W) l. s3 [9 ^1 \5 U198 . |% J3 w) j: A" `5 N) S" F( ?/ k199% q3 r% T5 ^2 X5 z
200 Q) d' \# y7 l4 L$ X8 i( |
2015 z" k0 g- }2 m7 i
202 + X3 l |8 W* p. r' B" T203 # {" c3 }7 V5 m: b( D( m204, Y: v* X- d! q# S* Q
205! z& n' U6 c4 u; I6 V+ ^
2061 w: @: r. h+ x. g$ O% o! ]/ I
207 4 b, J$ N2 [- `7 R2 v, b4 u208 / q, J! a( o4 V( B209 2 l1 s6 e) r9 b; d1 f7 Y: q210 0 B% D2 W& c! s) m1 C6 b, y211 . Y# F! u1 p: R a+ A212* n- l! }- X2 F8 @4 w
213' C& a# o! f' ^: p3 }8 A
214. v! o4 T& @" Y q- S
215 4 g1 v& t9 m; G5 E3 H; t216$ Z4 H# H' B* r& t9 |# h
217. a9 ^) I( Z4 ]( x: q
218 # Q5 `: b$ v# v: n& m$ @2198 p7 x/ A) J) H8 H9 z
2202 ]/ s% b. n7 ?6 t7 t* S
221 + {, D& }& l3 C, U222% c0 A3 h8 G, m! }3 j% C y
223 % q6 w ]5 a Q# G/ ^224 - G) g0 q r1 O( F8 m" {225 + E" j) q: f- {* Y226 7 H+ _* Y: ]: A, ]4 F2 g4 M227* q+ m1 C- `1 c% N W
2287 ~9 {$ d4 f- g& D5 i) X
229 0 \# N/ Y# ]) W3 ?230 ' S! q P h, e$ a2 N/ ?( y) }231( c0 u' D; m3 O: M+ e
232 7 Z$ Q+ W9 `# W9 [; `+ X+ [( Z# M233 . B% T# W& M, r9 o# g$ e4 h9 [# t7 }234 0 ?! S1 F4 x% i235 & S! e1 \6 [2 u5 l0 Y" r4 w7 u236 7 |6 v8 k0 n. P5 J& C+ b. P237 ! t$ i/ Y3 f! V+ B& C238* I/ p1 t9 K9 f5 A+ I# y: n
239 % q: r+ j2 n/ B* N" L, p2407 ?0 G' z, i9 \% i& ]
241, Z( Z3 k$ n, e9 O" h6 [
242 # C2 T0 y4 o0 V" K243 F1 ^* |- [0 v7 J& k( e
244 7 D+ L& {4 J! e% L/ v! o245. k2 i. v- D3 u# _- @& Z6 b
246+ V3 M: G% E* S, e7 l$ s; {1 r# \
247 : j: H/ h. d+ M0 W! i5 b248 ; D! }( T$ M8 T$ h6 ^$ V249, t9 T! N1 o7 M5 x
2501 |+ |/ Y1 T' u9 {- h# _
251 $ n' h+ ]/ W0 a, `252 ! I; R4 l) e+ W( L! p253 * _$ ?# D3 S- w! p2542 ~& R: J+ ~7 |/ l( T
255 5 U2 A/ x% w+ u1 {5 ?- {256 ~6 M0 n6 B, T) v. a: N
2573 Z- G$ L ~ r; j- k+ u% p4 \
258 ( [' B! Z9 R4 B$ f3 z! l3 Y259 - u/ C# k u& V5 ~/ \! T u0 e260 5 s4 r# R9 ^+ v261: N+ I B1 q7 E+ a" B
262; m r7 E% {7 \% E7 s7 i
2637 O% |5 h5 W) s
264* x/ \9 E6 q' k7 h- ]; N5 ]' F3 ? O- c
2651 o( ]3 \) d) V( p7 m2 o5 q, [
266 3 e( I4 R* V7 P- s; l2672 p, q* A) u. u: r8 I
268 4 m5 O \' ^2 p5 M% h' h269/ z1 |- t0 {8 t4 Z; ^4 f4 D
2708 n2 z h$ D" v" m5 n
271 ) `' U4 E& h. E( T9 ]1 X272) Q s& j5 e7 z- Z0 e. `' d
273 + x9 I% c/ \4 `7 s; H274 8 w. K4 }7 V+ A6 H275 + b, s$ }. F+ A6 ?" o276- a3 k6 i) j: t
277 1 D" D- h. }- F9 h# F8 `5 f2781 j4 h% l; L( L6 X, g% V. A, n5 }; Q
279 ! ~1 J$ \$ p$ ?: W f# V280 5 f. L5 x; v; o! w# M5 @281' X E, D" O/ [, O
282 3 K: k! E2 T8 A: s6 g$ d3 {8 l283 & @ C$ \) m' ^* p7 l8 u284. m$ O2 F& F* |/ G$ w) b( }# |
285 * O. Z( \- v: Q0 O6 l286 & R. C, A) M$ g287+ z ~; l3 }3 s/ z
288 $ W- T* ?$ z, D" |3 F2 L1 u& H289% I0 B. P7 v" O U2 o9 V
290 0 [: C/ g/ a+ V& h291 8 s( D# k; C/ J' a; v292 6 n: h0 B9 u" s5 n! i% n293( D$ M% |+ D5 x: P$ b' z8 {' }
294 ) A9 a, j& w7 d; `9 b4 x295 : t2 w% s- v2 w! R8 ^# q( U' ]296 ( ?1 n* v$ E' {% |& q7 A297 ' y& K% G: I8 J298 9 {9 X3 t1 h- t) b& {* I3 e2 j2994 L: p5 e) v4 C8 H8 u% y: T+ s* r3 A
300 * H& B" m, [8 c3 ~& c3 I/ W301. v; d& j* g8 T& `# e
302) r' |1 L- ^: O2 K
3038 N* V; [+ Y* l- k% V. d+ x5 J
304- J5 _' L/ ^$ w) |7 C6 P2 A
305 ( s8 o+ L2 A' E306 : W0 T: }9 V% L1 n$ }& O( E0 Y307* V6 y$ p4 F& _9 Y7 ?; c. h& q
308! ~! C2 W: u6 g- \
309 4 K/ q( [! |: I) `1 s: K; K" K! E310 * w2 `) Q8 F, M2 P' Y+ `311' ]# s9 f1 K2 f- s T
312& U+ F+ A2 a R1 u; R* v, b& Q" G) ~
313 9 }# G, P1 R$ U9 ]# [+ Y3148 `0 A4 q: D0 u4 C) Y
315 . I3 |, o* ^6 K316 o) ?3 j4 @) ~+ X) H5 c; U- r
3175 M) G$ Y4 P. w5 H7 ?5 x8 k [
318 ; N$ Q) i. x' c# i1 M, A319( m) {7 b! y8 a" j: Z
320 4 h& O3 x# }2 A/ e# h321. I9 _* [+ f# } T4 M
322 - U3 R7 b1 Z3 n) r9 X4 E4 m4 _3234 o6 N9 J9 I. U( P- E5 h
324 9 \+ }! C, l% w! k5 p6 c, Z325 K1 @ K a/ V* ` W# V+ S, |3269 N7 Y$ C O6 {* @- q# l' j, q
327/ ?9 y$ R% s. V( g% m9 a& K& w
328 V h; H. |# c! p) Z
329 ' m }, {" E% J. y7 L/ W330# Y' Z6 z: k1 u: x J
331 0 i" c. o9 ~$ \- z6 y+ f9 j. V! `3 k" u# |; x
/ r$ D+ d5 {: d. @7 B3 o- f* }9 W# s @$ D0 E
; O! E( b/ ?0 S$ W5 W ) e$ T+ [! p M; @4 A9 C0 V2 H2 M0 s" \, f; Z" T