理论部分先告一段落,如果还有不清晰的地方可以选择留言或我私下沟通。今天起正式进入代码阶段,入门项目是赌球神器。
一、建模步骤
1、清洗数据集 。
2、划分训练集和测试集:12个特征;1个目标 - 获胜队伍:主队、客队、平局;
3、训练三种不同的分类器:Logistic Regression、Support Vector Machine、XGBoost 。
4、给定主队客队情况下,使用最佳分类器预测是哪支队伍获取胜利 。
----- 在做项目之前回答一个问题 ----
有很多人问我到底哪个分类器最好?
一个较为严谨的回答是:当你没有真正用数据跑出一个模型之前,你永远不知道哪个分类器的效果最好。
但事实上,个人感觉很多分类器预测出的准确率始终在75%~78%之间徘徊,可是一旦使用了XGBoost分类器进行分类,预测效果一下子可以达到80%以上。
所以如果实在想知道哪个模型更好,不负责任的回答是:
那你就用XGBoost吧,XGBoost不仅效果好,而且运算速度也比较快。至于为什么?请先观大略不求甚解,将来有机会再说。
二、数据来源
Kaggle每年都会举办 March Madness的预测竞赛
https://www.kaggle.com/c/march-machine-learning-mania-2017/kernels
在2014年世界杯的时候,Bing正确得预测了所有15场淘汰赛阶段比赛的结果。
获取到数据后要思考两个问题:
1、我们应该使用什么模型?
2、要预测哪支球队获胜,什么样的特征才能起到重要的作用?
这不仅是编程前需要考虑的问题,同时也是最后写报告时需要总结回报的内容。
三、编程
1、导入库
数据处理用到的库:《Python - 科学计算库》笔者在该文集对pandas、numpy等常用数据处理方法做了介绍。
XGBoost 安装请参考文集:《AI - 开发工具》中的《安装 xgboost 库》
如果XGBoost 安装没有问题,那么import xgboost as xgb
不会报错。
linear线性方法包中导入逻辑回归:from sklearn.linear_model import LogisticRegression
ensemble集成方法包中导入随机森林:from sklearn.ensemble import RandomForestClassifier
支撑向量分类器:from sklearn.svm import SVC
逻辑回归(LogisticRegression)、随机森林(RandomForestClassifier)都是属于sklearn中的库,在安装Anaconda时就已经自动包含了,所以无需安装插件,直接导入即可。
%matplotlib inline
作用参考该文章:
《01 matplotlib - 折线图、绘图属性、Web安全色、子图、保存画板》
import warnings warnings.filterwarnings("ignore")
忽略一些警告
完整的头文件:
#数据处理
import pandas as pd
import numpy as np
#XGBoost 生成一个由弱分类器集成的强分类器,一般来说这个弱分类器是决策树
import xgboost as xgb
#Logistic Regression 其输出只有有限个可能的值,在目标变量为分类属性时可以使用。
from sklearn.linear_model import LogisticRegression
# 随机森林同样是一个集成算法,通过独特的抽样方式获得不同的子数据集,
# 通过这些子数据集来建立决策树模型,最终通过求平均的方式来提升结果的准确性,同时来控制过拟合。
from sklearn.ensemble import RandomForestClassifier
# 由一个分隔超平面定义的判别式学习器
from sklearn.svm import SVC
%matplotlib inline
import warnings
warnings.filterwarnings("ignore")
2、读取数据
《final_dataset.csv》文件里是样本集,如果需要请留邮箱,我单独发给大家。
data = pd.read_csv('./Datasets/final_dataset.csv')
读取数据,算法在如下文件中已介绍:
《04 pandas DataFrame_创建、文件读取、编码》
《05 pandas DataFrame_删改查、索引器、读写文件》
在处理特征前,首先要对数据的所有特征进行深入了解,比如查看数据描述:
HomeTeam:主队
AwayTeam:客队
FTR: (H: 主队胜 NH:不是主队胜)
HTGD:Home team goal difference(主队平均进球差)
ATGD:away team goal difference(客队平均进球差)
HTP:Home team points(主队平均每轮得分)
ATP:Away team points(客队平均每轮得分)
DiffFormPts:Diff in points(主队平均得分-客队平均得分)
DiffLP:Differnece in last years position(上一年度主客队排名差)
inplace参数的理解:
修改一个对象时:
inplace=True:不创建新的对象,直接对原始对象进行修改;
inplace=False:对数据进行修改,创建并返回新的对象承载其修改结果。
完整的代码:
#读取数据
data = pd.read_csv('./Datasets/final_dataset.csv')
#移除前三周比赛并移除多余特征
data = data[data.MW > 3]
data.drop(['Unnamed: 0','HomeTeam', 'AwayTeam', 'Date', 'MW', 'HTFormPtsStr', 'ATFormPtsStr', 'FTHG', 'FTAG',
'HTGS', 'ATGS', 'HTGC', 'ATGC','HomeTeamLP', 'AwayTeamLP','DiffPts','HTFormPts','ATFormPts',
'HM4','HM5','AM4','AM5','HTLossStreak5','ATLossStreak5','HTWinStreak5','ATWinStreak5',
'HTWinStreak3','HTLossStreak3','ATWinStreak3','ATLossStreak3'],1, inplace=True)
data.head()
输出 - 读取数据的结果:
3、探索性分析
上述直接读取的数据机器能不能认识?或者说算法能不能直接使用这些数据跑出模型来?显然不行。我们还要做一步探索性分析的工作。
探索性分析首先要考虑的是:1、我们的研究是否有价值,2、能够从数据中分析出哪些信息。
a、总比赛场数
n_matches = data.shape[0]
b、特征数量,需要用总个数减去目标变量个数。
n_features = data.shape[1] - 1
c、主队获胜场数
n_homewins = len(data[data.FTR == 'H'])
d、计算主队胜率
win_rate = (float(n_homewins) / (n_matches)) * 100
打印结果
print("比赛总场数: {}".format(n_matches))
print("特征总数: %d" %n_features)
print("主场球队获胜场数: {}".format(n_homewins))
print("主队胜率: {:.2f}%".format(win_rate))
比赛总场数: 5600
特征总数: 12
主场球队获胜场数: 2603
主队胜率: 46.48%
4、数据可视化
增加一个头文件:from pandas.tools.plotting import scatter_matrix
scatter matrix会画出某一特征和其余特征之间所建立的二维图像
scatter_matrix(data[['HTGD','ATGD','HTP','ATP','DiffFormPts','DiffLP']], figsize=(10,10))
从图片可以发现,对角线上的图片是直方图,这是该特征和自己所画出的图,描述了其分布状况
散点图表示了一个特征和另一个特征之间的关系。这种关系可以用相关系数来表达。
5、数据预处理
1、将数据划分为特征集和目标集
X_all = data.drop(['FTR'],1)
y_all = data['FTR']
2、标准化数据
from sklearn.preprocessing import scale
cols = [['HTGD','ATGD','HTP','ATP','DiffLP']]
for col in cols:
X_all[col] = scale(X_all[col])
3、将数据转换为str型
X_all.HM1 = X_all.HM1.astype('str')
X_all.HM2 = X_all.HM2.astype('str')
X_all.HM3 = X_all.HM3.astype('str')
X_all.AM1 = X_all.AM1.astype('str')
X_all.AM2 = X_all.AM2.astype('str')
X_all.AM3 = X_all.AM3.astype('str')
4、我们希望使用连续性变量,所以移除分类型变量
def preprocess_features(X):
''' 将比赛数据中的分类变量转化为哑变量 '''
# 初始化新的输出数据集
output = pd.DataFrame(index = X.index)
# 检索数据的每个特征
for col, col_data in X.iteritems():
# 如果数据是分类型变量,将他们转化为哑变量
if col_data.dtype == object:
col_data = pd.get_dummies(col_data, prefix = col)
# 收集修正过的数据
output = output.join(col_data)
return output
X_all = preprocess_features(X_all)
运行结果:
print("Processed feature columns ({} total features):\n{}"
.format(len(X_all.columns), list(X_all.columns)))
输出结果:处理完后剩下24个特征
Processed feature columns (24 total features):
['HTP', 'ATP', 'HM1_D', 'HM1_L', 'HM1_W', 'HM2_D', 'HM2_L', 'HM2_W', 'HM3_D', 'HM3_L', 'HM3_W', 'AM1_D', 'AM1_L', 'AM1_W', 'AM2_D', 'AM2_L', 'AM2_W', 'AM3_D', 'AM3_L', 'AM3_W', 'HTGD', 'ATGD', 'DiffFormPts', 'DiffLP']
5、打印前五行数据
print("\nFeature values:")
X_all.head()
from sklearn.model_selection import train_test_split
6、训练集、测试集划分
stratify参数是在数据不均衡情况下,保证训练集和测试集正例负例之间的比例保持不变
X_train, X_test, y_train, y_test = train_test_split(X_all, y_all,
test_size = 50,
random_state = 2,
stratify = y_all)
6、训练模型
from time import time
from sklearn.metrics import f1_score
def train_classifier(clf, X_train, y_train):
''' 使用训练集数据拟合分类器 '''
# 开启计时、训练分类器、停止计时
start = time()
clf.fit(X_train, y_train)
end = time()
print("Trained model in {:.4f} seconds".format(end - start))
def predict_labels(clf, features, target):
''' 基于分类器进行预测,并给出F1得分 '''
start = time()
y_pred = clf.predict(features)
end = time()
print("Made predictions in {:.4f} seconds.".format(end - start))
return f1_score(target, y_pred, pos_label='H'), sum(target == y_pred) / float(len(y_pred))
def train_predict(clf, X_train, y_train, X_test, y_test):
''' 训练和预测的模型整合 '''
# 声明分类器和训练集规模
print("Training a {} using a training set size of {}. . .".format(clf.__class__.__name__, len(X_train)))
# 训练分类器
train_classifier(clf, X_train, y_train)
# 分别打印训练集和分类集的预测结果
f1, acc = predict_labels(clf, X_train, y_train)
print(f1, acc)
print("F1 score and accuracy score for training set: {:.4f} , {:.4f}.".format(f1 , acc))
f1, acc = predict_labels(clf, X_test, y_test)
print("F1 score and accuracy score for test set: {:.4f} , {:.4f}.".format(f1 , acc))
# 初始化三个模型
clf_A = LogisticRegression(random_state = 42)
clf_B = SVC(random_state = 912, kernel='rbf')
clf_C = xgb.XGBClassifier(seed = 82)
train_predict(clf_A, X_train, y_train, X_test, y_test)
print('')
train_predict(clf_B, X_train, y_train, X_test, y_test)
print('')
train_predict(clf_C, X_train, y_train, X_test, y_test)
print('')
显然,XGBoost看上去是我们的最佳模型,其F1和准确度是三个模型当中最高的。
XGBoost的超参设置:
from sklearn.grid_search import GridSearchCV
from sklearn.metrics import make_scorer
# 设置想要调整的参数
parameters = { 'learning_rate' : [0.1, 0.2],
'n_estimators' : [40, 50],
'max_depth': [3],
'min_child_weight': [3],
'gamma':[0.4],
'subsample' : [0.8],
'colsample_bytree' : [0.8],
'scale_pos_weight' : [1],
'reg_alpha':[1e-5]
}
# 初始化分类器
clf = xgb.XGBClassifier(seed=2)
# 得到F1得分
f1_scorer = make_scorer(f1_score,pos_label='H')
# 使用网格搜索寻找最优模型
grid_obj = GridSearchCV(clf,
scoring=f1_scorer,
param_grid=parameters,
cv=5)
# 将数据带入模型进行训练
grid_obj = grid_obj.fit(X_train,y_train)
# Get the estimator获取最优超参
clf = grid_obj.best_estimator_
print(clf)
# 给出最优f1得分和准确率
f1, acc = predict_labels(clf, X_train, y_train)
print("F1 score and accuracy score for training set: {:.4f} , {:.4f}.".format(f1 , acc))
f1, acc = predict_labels(clf, X_test, y_test)
print("F1 score and accuracy score for test set: {:.4f} , {:.4f}.".format(f1 , acc))