06 案例 - 赌球神器

理论部分先告一段落,如果还有不清晰的地方可以选择留言或我私下沟通。今天起正式进入代码阶段,入门项目是赌球神器。

一、建模步骤

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》文件里是样本集,如果需要请留邮箱,我单独发给大家。


final_dataset的部分特征值

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))

从图片可以发现,对角线上的图片是直方图,这是该特征和自己所画出的图,描述了其分布状况

散点图表示了一个特征和另一个特征之间的关系。这种关系可以用相关系数来表达。

scatter matrix
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的超参设置:
image
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))
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容