人工智能
第五章 车辆评级分类与cart分类树
一、决策树分类
1. 算法核心原理
2. 决策树的关键问题
3. CART 分类树
CART 分类树算法对每个特征进行二分,寻找分割点时使用基尼系数来表达数据集的不纯度,基尼系数越小,不纯度越低,数据集划分的效果越好。 CART 分类树划分子表的过程:
针对每个特征,基于基尼系数计算最优分割值。在计算出来的各个特征的每个分割值对数据集
D
D
D 的基尼系数中,选择基尼系数最小的特征A和对应的分割值a。根据这个最优特征和最优分割值,把数据集划分成两部分
D
1
D_1
D 1 和
D
2
D_2
D 2 ,同时建立当前节点的左右节点,左节点的数据集
D
D
D 为
D
1
D_1
D 1 ,右节点的数据集
D
D
D 为
D
2
D_2
D 2 .对左右节点的子节点递归调用这个过程,生成决策树。 而 CART 分类树也基于基尼系数来决定子表划分所选特征的次序 。
4. 基尼系数
对于样本D,个数为|D|,假设K个类别,第k个类别的数量为
C
k
C_k
C k ,则样本D的基尼系数表达式:
G
i
n
i
(
D
)
=
1
−
∑
i
=
1
K
(
∣
C
k
∣
∣
D
∣
)
2
Gini(D) = 1 - \sum_{i=1}^K \left(\frac{|C_k|}{|D|}\right)^2
G i n i ( D ) = 1 − i = 1 ∑ K ( ∣ D ∣ ∣ C k ∣ ) 2 有100个样本(
D
1
D_1
D 1 )包含A与B两个类别,数量分别为40与60,Gini(
D
1
D_1
D 1 ) = ?
1
−
(
(
∣
40
∣
∣
100
∣
)
2
+
(
∣
60
∣
∣
100
∣
)
2
)
=
1
−
(
0.16
+
0.36
)
=
0.48
1 - \left( \left(\frac{|40|}{|100|}\right)^2 + \left( \frac{|60|}{|100|}\right)^2 \right) = 1 - (0.16 + 0.36) = 0.48
1 − ( ( ∣ 1 0 0 ∣ ∣ 4 0 ∣ ) 2 + ( ∣ 1 0 0 ∣ ∣ 6 0 ∣ ) 2 ) = 1 − ( 0 . 1 6 + 0 . 3 6 ) = 0 . 4 8 有100个样本(
D
2
D_2
D 2 )包含A与B两个类别,数量分别为10与90,Gini(
D
2
D_2
D 2 ) = ?
1
−
(
(
∣
10
∣
∣
100
∣
)
2
+
(
∣
90
∣
∣
100
∣
)
2
)
=
1
−
(
0.01
+
0.81
)
=
0.18
1 - \left( \left( \frac{|10|}{|100|}\right)^2 + \left( \frac{|90|}{|100|}\right)^2 \right) = 1 - (0.01 + 0.81) = 0.18
1 − ( ( ∣ 1 0 0 ∣ ∣ 1 0 ∣ ) 2 + ( ∣ 1 0 0 ∣ ∣ 9 0 ∣ ) 2 ) = 1 − ( 0 . 0 1 + 0 . 8 1 ) = 0 . 1 8 对于样本D,个数为|D|,根据特征A的某个值a,把D分成
D
1
D_1
D 1 和
D
2
D_2
D 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)
G i n i ( D , A ) = ∣ D ∣ ∣ D 1 ∣ G i n i ( D 1 ) + ∣ D ∣ ∣ D 2 ∣ G i n i ( D 2 )
5. 决策树的生成过程
算法输入训练集D,基尼系数的阈值,样本个数阈值。输出决策树T。
对于当前节点的数据集为D,如果样本个数小于阈值,则返回决策子树,当前节点停止递归。 计算样本集D的基尼系数,如果基尼系数小于阈值,则返回决策树子树,当前节点停止递归。 计算当前节点现有的各个特征的各个特征值对数据集D的基尼系数。 在计算出来的各个特征的各个特征值对数据集D的基尼系数中,选择基尼系数最小的特征A和对应的特征值a。根据这个最优特征和最优特征值,把数据集划分成两部分
D
1
D_1
D 1 和
D
2
D_2
D 2 ,同时建立当前节点的左右节点,左节点的数据集D为
D
1
D_1
D 1 ,右节点的数据集D为
D
2
D_2
D 2 。 对左右的子节点递归的调用1-4步,生成决策树。 预测过程:对生成的决策树做预测的时候,假如测试集里的样本A落到了某个叶子节点,而节点里有多个训练样本。则对于A的类别预测采用的是这个叶子节点里概率最大的类别 。
6. 决策树分类实现
import sklearn. tree as st
model = st. DecisionTreeClassifier(
max_depth= 6 , min_samples_split= 3 , random_state= 7 )
model. fit( train_x, train_y)
7. 鸢尾花案例
import sklearn. tree as st
model = st. DecisionTreeClassifier( max_depth= 4 , min_samples_split= 3 )
model. fit( train_x, train_y)
pred_test_y = model. predict( test_x)
print ( ( pred_test_y== test_y) . sum ( ) / test_y. size)
print ( test_y. values)
"""
0.8666666666666667
[2 0 0 1 0 2 2 2 1 1 2 1 1 0 0]
"""
8. 集合模型分类实现
import sklearn. ensemble as se
model = se. RandomForestClassifier( . . . )
model = se. AdaBoostClassifier( . . . )
model = se. GridientBoostingClassifier( . . . )
二、预测小汽车等级
car.txt 样本文件中统计了小汽车的常见特征信息及小汽车的分类,使用这些数据基于决策树分类算法训练模型预测小汽车等级。 汽车价格 维修费用 车门数量 载客数 后备箱 安全性 汽车级别 分析实现思路:
加载数据。 特征分析与特征工程。 数据预处理(标签编码)。 训练模型。 模型测试。
import numpy as np
import pandas as pd
data = pd. read_csv( 'car.txt' , header= None )
data. head( )
data[ 0 ] . value_counts( )
"""
vhigh 432
low 432
med 432
high 432
Name: 0, dtype: int64
"""
确定:是分类问题,还是回归问题?答:分类 选哪一个分类模型:逻辑回归、决策树、RF、GBDT、AdaBoost?答:RF (当然也可以尝试其他模型)
import sklearn. preprocessing as sp
train_data = pd. DataFrame( [ ] )
encoders = { }
for col_ind, col_val in data. items( ) :
encoder = sp. LabelEncoder( )
train_data[ col_ind] = encoder. fit_transform( col_val)
encoders[ col_ind] = encoder
train_data
x, y = train_data. iloc[ : , : - 1 ] , train_data[ 6 ]
x. shape, y. shape
"""
((1728, 6), (1728,))
"""
model = se. RandomForestClassifier( max_depth= 6 , n_estimators= 400 , random_state= 7 )
scores = ms. cross_val_score( model, x, y, cv= 5 , scoring= 'f1_weighted' )
print ( scores. mean( ) )
"""
0.7537201013972693
"""
model. fit( x, y)
pred_y = model. predict( x)
print ( sm. classification_report( y, pred_y) )
print ( sm. confusion_matrix( y, pred_y) )
"""
precision recall f1-score support
0 0.77 0.82 0.79 384
1 0.00 0.00 0.00 69
2 0.95 0.99 0.97 1210
3 1.00 0.77 0.87 65
accuracy 0.91 1728
macro avg 0.68 0.65 0.66 1728
weighted avg 0.87 0.91 0.89 1728
[[ 315 0 69 0]
[ 69 0 0 0]
[ 11 0 1199 0]
[ 15 0 0 50]]
"""
data = [
[ 'high' , 'med' , '5more' , '4' , 'big' , 'low' , 'unacc' ] ,
[ 'high' , 'high' , '4' , '4' , 'med' , 'med' , 'acc' ] ,
[ 'low' , 'low' , '2' , '4' , 'small' , 'high' , 'good' ] ,
[ 'low' , 'med' , '3' , '4' , 'med' , 'high' , 'vgood' ]
]
test_data = pd. DataFrame( data)
for col_ind, col_val in test_data. items( ) :
encoder = encoders[ col_ind]
encoded_col = encoder. transform( col_val)
test_data[ col_ind] = encoded_col
test_x, test_y = test_data. iloc[ : , : - 1 ] , test_data[ 6 ]
pred_test_y = model. predict( test_x)
pred_test_y
"""
array([2, 0, 0, 0])
"""
encoders[ 6 ] . inverse_transform( pred_test_y)
"""
array(['unacc', 'acc', 'acc', 'acc'], dtype=object)
"""
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
三、验证曲线与学习曲线
1. 验证曲线
验证曲线描述的关系即是模型性能关于模型超参数的函数关系:
2. 验证曲线实现
train_scores, test_scores = ms. validation_curve(
model,
输入集, 输出集,
'n_estimators' ,
np. arange( 50 , 550 , 50 ) ,
cv= 5
)
返回train_scores与test_scores为每个超参数取值下的每次交叉验证结果组成的得分矩阵。
3. 案例:预测小汽车等级调整参数
import numpy as np
import pandas as pd
data = pd. read_csv( 'car.txt' , header= None )
import sklearn. preprocessing as sp
import sklearn. ensemble as se
import sklearn. model_selection as ms
import sklearn. metrics as sm
train_data = pd. DataFrame( [ ] )
encoders = { }
for col_ind, col_val in data. items( ) :
encoder = sp. LabelEncoder( )
train_data[ col_ind] = encoder. fit_transform( col_val)
encoders[ col_ind] = encoder
x, y = train_data. iloc[ : , : - 1 ] , train_data[ 6 ]
model = se. RandomForestClassifier( max_depth= 9 , n_estimators= 140 , random_state= 7 )
import matplotlib. pyplot as plt
params = np. arange( 100 , 200 , 10 )
train_scores, test_scores = ms. validation_curve( model, x, y, param_name= 'n_estimators' , param_range= params, cv= 5 )
scores = test_scores. mean( axis= 1 )
plt. grid( linestyle= ':' )
plt. plot( params, scores, 'o-' , color= 'dodgerblue' , label= 'n_estimators VC' )
plt. legend( )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
params = np. arange( 1 , 12 )
train_scores, test_scores = ms. validation_curve( model, x, y, param_name= 'max_depth' , param_range= params, cv= 5 )
scores = test_scores. mean( axis= 1 )
plt. grid( linestyle= ':' )
plt. plot( params, scores, 'o-' , color= 'orangered' , label= 'max_depth VC' )
plt. legend( )
model. fit( x, y)
pred_y = model. predict( x)
print ( sm. classification_report( y, pred_y) )
print ( sm. confusion_matrix( y, pred_y) )
"""
precision recall f1-score support
0 0.96 1.00 0.98 384
1 1.00 0.75 0.86 69
2 1.00 1.00 1.00 1210
3 0.94 1.00 0.97 65
accuracy 0.99 1728
macro avg 0.97 0.94 0.95 1728
weighted avg 0.99 0.99 0.99 1728
[[ 383 0 0 1]
[ 14 52 0 3]
[ 3 0 1207 0]
[ 0 0 0 65]]
"""
data = [
[ 'high' , 'med' , '5more' , '4' , 'big' , 'low' , 'unacc' ] ,
[ 'high' , 'high' , '4' , '4' , 'med' , 'med' , 'acc' ] ,
[ 'low' , 'low' , '2' , '4' , 'small' , 'high' , 'good' ] ,
[ 'low' , 'med' , '3' , '4' , 'med' , 'high' , 'vgood' ]
]
test_data = pd. DataFrame( data)
for col_ind, col_val in test_data. items( ) :
encoder = encoders[ col_ind]
encoded_col = encoder. transform( col_val)
test_data[ col_ind] = encoded_col
test_x, test_y = test_data. iloc[ : , : - 1 ] , test_data[ 6 ]
pred_test_y = model. predict( test_x)
encoders[ 6 ] . inverse_transform( pred_test_y)
"""
array(['unacc', 'acc', 'good', 'vgood'], dtype=object)
"""
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
4. 学习曲线
学习曲线描述的关系即是模型性能关于训练样本量大小的函数关系:
5. 学习曲线实现
train_scores, test_scores = ms. learning_curve(
model,
输入集, 输出集,
train_sizes= [ 0.9 , 0.8 , 0.7 ] ,
cv= 5
)
返回train_scores与test_scores为每个超参数取值下的每次交叉验证结果组成的得分矩阵。
6. 案例:预测小汽车等级调整参数
params = np. arange( 0.1 , 1.1 , 0.1 )
_, train_scores, test_scores = ms. learning_curve( model, x, y, train_sizes= params, cv= 5 )
scores = test_scores. mean( axis = 1 )
plt. grid( linestyle= ':' )
plt. plot( params, scores, 'o-' , color= 'green' , label= 'Learning Curve' )
plt. legend( )
四、完整案例
import numpy as np
import pandas as pd
data = pd. read_csv( 'car.txt' , header= None )
data. head( )
"""
0 1 2 3 4 5 6
0 vhigh vhigh 2 2 small low unacc
1 vhigh vhigh 2 2 small med unacc
2 vhigh vhigh 2 2 small high unacc
3 vhigh vhigh 2 2 med low unacc
4 vhigh vhigh 2 2 med med unacc
"""
import sklearn. preprocessing as sp
import sklearn. ensemble as se
import sklearn. model_selection as ms
import sklearn. metrics as sm
train_data = pd. DataFrame( [ ] )
encoders = { }
for col_ind, col_val in data. items( ) :
encoder = sp. LabelEncoder( )
train_data[ col_ind] = encoder. fit_transform( col_val)
encoders[ col_ind] = encoder
x, y = train_data. iloc[ : , : - 1 ] , train_data[ 6 ]
model = se. RandomForestClassifier( max_depth= 9 , n_estimators= 140 , random_state= 7 )
import matplotlib. pyplot as plt
params = np. arange( 100 , 200 , 10 )
train_scores, test_scores = ms. validation_curve( model, x, y, param_name= 'n_estimators' , param_range= params, cv= 5 )
scores = test_scores. mean( axis= 1 )
plt. grid( linestyle= ':' )
plt. plot( params, scores, 'o-' , color= 'dodgerblue' , label= 'n_estimators VC' )
plt. legend( )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
params = np. arange( 1 , 12 )
train_scores, test_scores = ms. validation_curve( model, x, y, param_name= 'max_depth' , param_range= params, cv= 5 )
scores = test_scores. mean( axis= 1 )
plt. grid( linestyle= ':' )
plt. plot( params, scores, 'o-' , color= 'orangered' , label= 'max_depth VC' )
plt. legend( )
params = np. arange( 0.1 , 1.1 , 0.1 )
_, train_scores, test_scores = ms. learning_curve( model, x, y, train_sizes= params, cv= 5 )
scores = test_scores. mean( axis = 1 )
plt. grid( linestyle= ':' )
plt. plot( params, scores, 'o-' , color= 'green' , label= 'Learning Curve' )
plt. legend( )
model. fit( x, y)
pred_y = model. predict( x)
print ( sm. classification_report( y, pred_y) )
print ( sm. confusion_matrix( y, pred_y) )
"""
precision recall f1-score support
0 0.96 1.00 0.98 384
1 1.00 0.75 0.86 69
2 1.00 1.00 1.00 1210
3 0.94 1.00 0.97 65
accuracy 0.99 1728
macro avg 0.97 0.94 0.95 1728
weighted avg 0.99 0.99 0.99 1728
[[ 383 0 0 1]
[ 14 52 0 3]
[ 3 0 1207 0]
[ 0 0 0 65]]
"""
data = [
[ 'high' , 'med' , '5more' , '4' , 'big' , 'low' , 'unacc' ] ,
[ 'high' , 'high' , '4' , '4' , 'med' , 'med' , 'acc' ] ,
[ 'low' , 'low' , '2' , '4' , 'small' , 'high' , 'good' ] ,
[ 'low' , 'med' , '3' , '4' , 'med' , 'high' , 'vgood' ]
]
test_data = pd. DataFrame( data)
for col_ind, col_val in test_data. items( ) :
encoder = encoders[ col_ind]
encoded_col = encoder. transform( col_val)
test_data[ col_ind] = encoded_col
test_x, test_y = test_data. iloc[ : , : - 1 ] , test_data[ 6 ]
pred_test_y = model. predict( test_x)
encoders[ 6 ] . inverse_transform( pred_test_y)
"""
array(['unacc', 'acc', 'good', 'vgood'], dtype=object)
"""
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41