这一章是介绍决策树的,由于在之前一篇文章里已经大致学习过了,所以这里就只记录一些补充的知识,以及实例。
数据预处理
这里采用的是NBA2013-2014的数据集
数据集清洗
用pandas的read_csv()读入数据后,查看数据,可以看到数据是有问题的,例如表头不完整,日期是字符串格式而不是日期对象等问题。1
2
3
4
5
6import pandas as pd
data_path = '/Users/apple/Desktop/python_study/Python数据挖掘入门与实践Code/Chapter 3/89644NBA 2013-14赛季比赛数据资料 csv格式 可用于机器学习和数据挖掘.csv'
data = pd.read_csv(data_path,parse_dates=['Date']) # parse_dates 转换日期格式
#print(data)
data.columns = ['Date','Visitor/Neutral','PTS','Home/Neutral','PTS.1','Score Type']
#print(data)
提取新特征
1表示主场球队获胜,0表示客场球队获胜
创建HomeLastWin和VisitorLastWin两个特征来记录上场比赛的结果1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#创建特征记录两支球队上一场比赛的胜负
from collections import defaultdict
won_last = defaultdict(int) #创建字典,存放上次比赛结果
data['HomeLastWin'] = None
data['VisitorLastWin'] = None
for index,row in data.sort_values('Date').iterrows():
#循环获得球队名称
home_team = row["Home/Neutral"]
visitor_team = row['Visitor/Neutral']
row['HomeLastWin'] = won_last[home_team]
row['VisitorLastWin'] = won_last[visitor_team]
data.ix[index] = row #更新行数
#判断上一场是否获胜
won_last[home_team] = row['HomeWin']
won_last[visitor_team] = not row['HomeWin']
决策树
训练+预测
scikit-learn中实现了CART算法,该算法支持连续型和类别型特征。
在构建决策树时,最后几步决策仅依赖于少数个体,随意性很大,使用特定节点作出推测容易导致过拟合,使用退出原则可以放置决策精度过高。另外,还可以先创建一颗完整的树,然后进行剪枝,去掉对整个过程没有提供太多信息的节点。
退出原则:
min_samples_split:指定创建一个新节点至少需要的个体数量【控制决策节点的建立】
min_samples_leaf:指定为了保留节点,每个节点至少应该包含的个体数量【控制该节点能否被保留】
特征选择准则:
基尼不纯度:决策点错误预测新个体类别的比例
信息增益:用熵表示决策节点提供多少信息
实例:用决策树预测NBA球队比赛结果
首先建立决策树模型并看一下训练完成后的精确度1
2
3
4
5
6
7
8from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(random_state=14)
x = data[['HomeLastWin','VisitorLastWin']].values
from sklearn.model_selection import cross_val_score
import numpy as np
scores = cross_val_score(clf,x,y_true,scoring='accuracy')
print('the accuracy is {0:.1f}%'.format(np.mean(scores)*100))
#the accuracy is 57.5%
这个精确度比随机预测胜负的50%要好一点,但是还是不是很高,所以我们应当构建有效特征,来提高精确度。
思考这样两个问题:
1.一般而言,什么样的球队水平更高?
2.两支球队上一次相遇时,谁赢了?
对于第一个问题,我们创建一个“主场队是否通常比对手水平高”的特征,使用2013年的成绩作为特征取值来源,如果一支球队2013年排名在对手前面,就认为它的水平更高。
下图是2013年的成绩部分截图,也就是要创建特征的数据来源:
那我们接下来就是要创建上面提到的特征。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
37from collections import defaultdict
won_last = defaultdict(int)
data['HomeLastWin'] = 0
data['VisitorLastWin'] = 0
##创建一个新特征值, 主场球队是否比对手排名高
new_datapath = '/Users/apple/Desktop/python_study/Python数据挖掘入门与实践Code/Chapter 3/standing.csv'
new_data = pd.read_csv(new_datapath)
data['HomeRankHigher'] = 0
for index,row in data.iterrows():
home_team = row['Home/Neutral']
visitor_team = row['Visitor/Neutral']
row['HomeLastWin'] = won_last[home_team]
row['VisitorLastWin'] = won_last[visitor_team]
if home_team == 'New Orleans Pelicans': # 更换了名字的球队
home_team = 'New Orleans Hornets'
elif visitor_team == 'New Orleans Pelicans':
visitor_team = 'New Orleans Hornets'
# 得到两支球队的排名,更新特征值
home_rank = new_data[new_data['team'] == home_team]['RK'].values[0]
visitor_rank = new_data[new_data['team'] == visitor_team]['RK'].values[0]
row['HomeRankHigher'] = int(home_rank>visitor_rank)
data.ix[index] = row
won_last[home_team] = row['HomeWin']
won_last[visitor_team] = not row['HomeWin']
from sklearn.tree import DecisionTreeClassifier
dtc = DecisionTreeClassifier(random_state=14)
from sklearn.model_selection import cross_val_score
data = data[['HomeLastWin','VisitorLastWin','HomeRankHigher']]
#print(data)
scores = cross_val_score(dtc,data,y_true,scoring='accuracy')
import numpy as np
print('the accuracy is {0:.1f}%'.format(np.mean(scores)*100))
#the accuracy is 61.0%
可以看到,增加了这一项特征后,模型准确度变高了。
随机森林
一颗决策树可以学习到很复杂的规则,但有时可能会导致过拟合,其中一个解决方法就是限制它学习到的规则数量,比如说限制决策树的深度。这样一来,决策树只学习从全局角度拆分数据集的最佳规则,没有学习使用面更窄的特定规则,这些特定规则会将数据集进一步拆分为更加细致的群组。这样,决策树的泛化能力更强,但是整体表现略弱。
这时候我们引入随机森林的概念,就是创建多棵决策树,分别进行预测,根据少数服从多数的原则从多个预测结果中选择最终的预测结果。
但也存在两个问题,一是创建的多棵决策树很大程度上是一样的,解决方法是每次随机从数据集中选取部分数据作为训练集,该过程叫袋装bagging;二是用于前几个决策节点的特征非常突出,最后的决策树相似度仍然很大,解决方法是随机选取部分特征作为决策依据。
决策树的集成效果
随机森林的一个策略就是对多棵随机创建的决策树对预测效果取均值,降低预测结果的不一致性,我们用方差表示这种不一致
随机森林算法的参数
scikit.learn中的RandomForestClassifier就是对随机森林算法的实现。
决策标准(gini/entropy)、max_features、min_samples_split
n_eatimators:指创建决策树的数量。该值越大,所花时间越长,正确率可能越高。
oob_score:True,测试时将不使用训练模型时用过的数据
n_jobs: 采用并行计算方法训练决策树时所用到的内核数量。默认为1,-1代表开动全部。
使用随机森林
1 | from sklearn.ensemble import RandomForestClassifier |
在实际使用的时候,我们可以用GridSearchCV类搜索最佳参数:1
2
3
4
5
6
7
8
9
10
11
12from sklearn.model_selection import GridSearchCV
parameter_space = {
'max_features':[1,3,'auto'], #不要超过特征的最大数量值
'n_estimators':[10,20,30],
'criterion':['gini','entropy'],
'min_samples_leaf':[2,4,6],
}
clf = RandomForestClassifier(random_state=14)
grid = GridSearchCV(clf,parameter_space)
grid.fit(data,y_true)
print('the accuracy is {0:.1f}%'.format(grid.best_score_ * 100))
#the accuracy is 61.0%
可以看到,通过网格搜索找到的最佳模型,准确度比之前默认参数的随机森林高一点。
查看给出正确率最大的模型所用到的参数:1
2
3
4
5
6
7print(grid.best_estimator_)
#RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=None, max_features=1, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=2, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=20, n_jobs=None,
oob_score=False, random_state=14, verbose=0, warm_start=False)
创建新特征
从这一章的例子可以看出,创建新的特征有时候可以大大提高模型的准确度。
用pandas提供的函数创建特征:1
2
3
4
5
6
7
8
9
10
11
12
13data['new feature'] = feature_creator()
#frature_creator函数返回数据集中每条数据的各个特征值,常用数据集作参数:
data['new feature'] = feature_creator(dataset)
#最直接的做法是一开始为新的特征设置默认值,例如0:
data['new feature'] = 0
#遍历数据集,计算新的特征:
#本章多次使用该方法
for index,row in data.iterrows():
home_team = row['Home Team'] #获取队名
visitor_team = row['Visitor Team']
#Some calculation here to alter row
data.ix[index] = row #更新数据
#上述方法效率不高,如果要使用,一次性处理所有的特征。