Python数据挖掘入门与实践 第三章 用决策树预测获胜球队(二) 随机森林(RandomForest)

时间:2024-04-13 10:42:40

紧接上文,我们来看一下,决策树在训练数据量很大的情况下,能否得到有效的分类模型。我们将会为决策树添加球队,以检测它是否能整合新增的信息。
虽然决策树能够处理特征值为类别型的数据,但scikit-learn库所实现的决策树算法要求先对这类特征进行处理。用LabelEncoder转换器就能把字符串类型的球队名转化为整型。代码如下:

from sklearn.preprocessing import LabelEncoder
encoding = LabelEncoder()
encoding.fit(dataset["Home Team"].values) 
##将主场球队名称转化为整型:
home_teams = encoding.transform(dataset["Home Team"].values) 
##将客场球队名称转化为整型:
visitor_teams = encoding.transform(dataset["Visitor Team"].values)
## 将主客队的2个数据进行上下叠加后转置,这样就能获得一个编号代表对阵双方的2列数据了
X_teams = np.vstack([home_teams, visitor_teams]).T
##我们把X_teams作为特征值,进行预测:
clf = DecisionTreeClassifier(random_state=14)
scores = cross_val_score(clf, X_teams, y_true,scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))

结果得到:Accuracy: 57.3%,比之前下降了不少。
一个原因是:决策树可以用这些特征值进行训练,但DecisionTreeClassifier仍把它们当作连续型特征。例如,编号从0到16的17支球队,算法会认为球队1和2相似,而球队4和10不同。但其实这没意义,对于两支球队而言,它们要么是同一支球队,要么不同,没有中间状态!
为了消除这种和实际情况不一致的现象,我们可以使用OneHotEncoder转换器把这些整数转换为二进制数字。每个特征用一个二进制数字来表示。例如,LabelEncoder为芝加哥公牛队分配的数值是7,那么OneHotEncoder为它分配的二进制数字的第七位就是1,其余队伍的第七位就是0。每个可能的特征值都这样处理,而数据集会变得很大。代码如下:

from sklearn.preprocessing import OneHotEncoder
onehot = OneHotEncoder()
X_teams_expanded = onehot.fit_transform(X_teams).todense()

clf = DecisionTreeClassifier(random_state=14)
scores = cross_val_score(clf, X_teams_expanded, y_true,scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))

结果得到:60.3%,比非二进制的时候,进步了3%,但是比之前的算法还是退步了。

于是,我们尝试通过一个森林(多棵树),来进行尝试。
在创建多颗树的情况下,如果每次都用一个数据集,那么就有可能失去森林的价值,于是,进行一个装袋(bagging)的操作,每次从数据集里抽出一部分数据,使用到一棵树上,又抽出另一部分数据,使用到另一棵树上。
尽管这样解决了数据集的问题,但是,万一创建的决策树相似性很大,同样会失去森林的意义。解决方法是,随机选取部分特征作为决策依据。

于是,森林就由,随机选取部分特征的树,与随机选取部分的数据组成。
再根据少数服从多数的原则,我们就能获得森林的结果。
而每棵树的结果的不一致性,我们用方差来表示。

随机森林算法的参数

RandomForestClassifier除了继承了DecisionTreeClassifier的部分参数之外,他自己的参数有如下几项:
①n_estimators:用来指定创建决策树的数量。该值越高,所花时间越长,正确率(可能)也越高。
②oob_score:如果设置为真,测试时将不使用训练模型时用过的数据。
③n_jobs:采用并行计算方法训练决策树时所用到的内核数量。
(默认为1,若设置为-1则开足马力,运行所有的多核CPU)

使用随机森林算法

from sklearn.ensemble import RandomForestClassifier ## 随机森林
clf = RandomForestClassifier(random_state=14)
scores = cross_val_score(clf, X_teams, y_true, scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))
scores = cross_val_score(clf, X_teams_expanded, y_true, scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))

结果分别为:Accuracy: 58.2%,Accuracy: 60.9%
可以看到,仅仅是更换了随即森林,对比决策树的Accuracy: 57.3%,Accuracy: 60.3%,两者都进步了一点。

(这些地方,我看的书估计是翻译的锅,坑不少。。。
略过被坑的地方,直接写对的部分。)

随机森林,增加学习的特征子集,理论上效果会更好。
于是我们把上文的X_teams_expanded,与之前的X_lastwinner两个数据集结合起来:

X_all = np.hstack([X_home_higher, X_teams])
clf = RandomForestClassifier(random_state=14)
scores = cross_val_score(clf, X_all, y_true, scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))

又进步到了61.1%!

上述的过程,有没有自动寻找参数的做法呢?
答案是肯定的:

from sklearn.model_selection import GridSearchCV ##导入最佳参数
parameter_space = {
	"max_features": [2, 10, 'auto'],
	"n_estimators": [100,],
	"criterion": ["gini", "entropy"],
	"min_samples_leaf": [2, 4, 6],
} 
## 设置为搜索范围
clf = RandomForestClassifier(random_state=14)
grid = GridSearchCV(clf, parameter_space)
grid.fit(X_all, y_true)
print(grid.best_estimator_) ##查看使用的模型与参数
print(grid.best_params_)    ##查看设置的参数选择了什么
print("Accuracy: {0:.1f}%".format(grid.best_score_ * 100))

Python数据挖掘入门与实践 第三章 用决策树预测获胜球队(二) 随机森林(RandomForest)
可以看到,自动搜索了最佳参数,得到了最佳结果为64.2%。
但是时间也花的有点久。。。

至此,结果也就比随机的55开,多了14.2%。
显然并不能为我们预测结果带来太多的帮助。
如下的因素也是需要来进行考虑的。
①球队上次打比赛距今有多长时间?短期内连续作战,容易导致球员疲劳。
②两支球队过去五场比赛结果如何?这两个数据要比HomeLastWin和Vi
sitorLastWin更能反映球队的真实水平(抽取特征方法类似)。
③球队是不是跟某支特定球队打比赛时发挥得更好?例如,球队在某个体育场里打比赛,即使是客场作战也能发挥得很好。
④借助球员数据来分析每个队的实力以预测输赢。
其实,赌徒和体育博彩机构每天都在用这些复杂的特征预测赛事结果来牟利。
这本书也仅仅提供了一个入门的方法,主要是为了介绍一下相关的内容。
至此,书本的三章内容终于结束了~

参考文献:
1.https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html#sklearn.model_selection.GridSearchCV