採用 Python 機器學習預測足球比賽結果

採用 Python 機器學習預測足球比賽結果

足球是世界上最火爆的運動之一,世界盃期間也每每是球迷們最亢奮的時刻。比賽狂歡季除了炸出了熬夜看球的鐵桿粉絲,也讓足球競猜也成了你們茶餘飯後最熱衷的話題。甚至連原來不怎麼看足球的人,也是暗中努力惡補了不少足球相關知識,想經過賽事競猜先賺一個小目標。今天咱們將介紹如何用機器學習來預測足球比賽結果!php

本 Chat 採用 Python 編程語言,使用 人工智能建模平臺 Mo 做爲在線開發環境進行編程,經過獲取 2000 年到 2018 年共 19 年英超的比賽數據,而後基於監督學習中邏輯迴歸模型、支持向量機模型和 XGBoost 模型,對英超比賽結果進行預測。html

下面咱們一塊兒來看看預測英超比賽結果的機器學習步驟:前端

主要流程步驟

  1. 獲取數據和讀取數據的信息
  2. 數據清洗和預處理
  3. 特徵工程
  4. 創建機器學習模型並進行預測
  5. 總結與展望

1. 獲取數據和讀取數據的信息

首先咱們進入 Mo 工做臺,建立一個空白項目,點擊 開始開發 進入內嵌 JupyterLab 的 Notebook 開發環境。python

enter image description here

enter image description here

接着咱們須要在項目中上傳數據集算法

英超每一年舉辦一個賽季,在每一年的 8 月到第二年的 5 月進行,共有 20 支球隊,實行主客場雙循環賽制,每一個賽季共 38 輪比賽(其中 19 場主場比賽,19 場客場比賽),每輪比賽共計 10 場比賽,因此每一個賽季,英超共有 380 場比賽。apache

若是您已經在 MO 平臺新建項目,能夠在平臺直接導入數據集,流程以下:編程

enter image description here

1.1 讀取 csv 數據接口解釋

讀取 csv 數據通常採用 pandas.read_csv():

pandas.read_csv(filepath_or_buffer, sep =',' , delimiter = None)api

  • filepath_or_buffer:文件路徑
  • sep:指定分隔符,默認是逗號
  • delimiter:定界符,備選分隔符(若是指定改參數,則sep失效)
  • usecols: 指定讀取的列名,列表形式
# 導入必須的包
import warnings
warnings.filterwarnings('ignore')  # 防止警告文件的包
import pandas as pd  # 數據分析包
import os
import matplotlib.pyplot as plt # 可視化包
import matplotlib
%matplotlib inline
import seaborn as sns  # 可視化包
from time import time
from sklearn.preprocessing import scale  # 標準化操做
from sklearn.model_selection import train_test_split  # 將數據集分紅測試集和訓練集
from sklearn.metrics import f1_score  # F1得分
import xgboost as xgb  # XGBoost模型
from sklearn.svm import SVC  ## 支持向量機分類模型
from sklearn.linear_model import LogisticRegression  # 邏輯迴歸模型
from sklearn.model_selection import GridSearchCV  # 超參數調參模塊
from sklearn.metrics import make_scorer  # 模型評估
import joblib  # 模型的保存與加載模塊

下面開始咱們的表演:數組

# 獲取地址中的全部文件
loc = './/football//' # 存放數據的路徑
res_name = []  # 存放數據名的列表
filecsv_list = []  # 獲取數據名後存放的列表
def file_name(file_name):
    # root:當前目錄路徑   dirs:當前目錄下全部子目錄   files:當前路徑下全部非目錄文件
    for root,dirs,files in os.walk(file_name):
        files.sort() # 排序,讓列表裏面的元素有順序
        for i,file in enumerate(files):
            if os.path.splitext(file)[1] == '.csv':
                filecsv_list.append(file)
                res_name.append('raw_data_'+str(i+1))
    print(res_name)
    print(filecsv_list)
file_name(loc)
['raw_data_1', 'raw_data_2', 'raw_data_3', 'raw_data_4', 'raw_data_5', 'raw_data_6', 'raw_data_7', 'raw_data_8', 'raw_data_9', 'raw_data_10', 'raw_data_11', 'raw_data_12', 'raw_data_13', 'raw_data_14', 'raw_data_15', 'raw_data_16', 'raw_data_17', 'raw_data_18', 'raw_data_19']

['2000-01.csv', '2001-02.csv', '2002-03.csv', '2003-04.csv', '2004-05.csv', '2005-06.csv', '2006-07.csv', '2007-08.csv', '2008-09.csv', '2009-10.csv', '2010-11.csv', '2011-12.csv', '2012-13.csv', '2013-14.csv', '2014-15.csv', '2015-16.csv', '2016-17.csv', '2017-18.csv', '2018-19.csv']

1.2 時間列表

獲取每年的數據後,將每年的年份放入到 time_list 列表中:app

time_list = [filecsv_list[i][0:4]  for i in range(len(filecsv_list))]
time_list

['2000','2001','2002','2003','2004','2005','2006','2007','2008','2009','2010','2011','2012','2013','2014','2015','2016','2017','2018']

1.3 用 Pandas.read_csv()  接口讀取數據

讀取時將數據與 res_name 中的元素名一一對應。

for i in range(len(res_name)):
    res_name[i] = pd.read_csv(loc+filecsv_list[i],error_bad_lines=False)
    print('第%2s個文件是%s,數據大小爲%s'%(i+1,filecsv_list[i],res_name[i].shape))
第 1個文件是2000-01.csv,數據大小爲(380, 45)
第 2個文件是2001-02.csv,數據大小爲(380, 48)
第 3個文件是2002-03.csv,數據大小爲(316, 48)
第 4個文件是2003-04.csv,數據大小爲(335, 57)
第 5個文件是2004-05.csv,數據大小爲(335, 57)
第 6個文件是2005-06.csv,數據大小爲(380, 68)
第 7個文件是2006-07.csv,數據大小爲(380, 68)
第 8個文件是2007-08.csv,數據大小爲(380, 71)
第 9個文件是2008-09.csv,數據大小爲(380, 71)
第10個文件是2009-10.csv,數據大小爲(380, 71)
第11個文件是2010-11.csv,數據大小爲(380, 71)
第12個文件是2011-12.csv,數據大小爲(380, 71)
第13個文件是2012-13.csv,數據大小爲(380, 74)
第14個文件是2013-14.csv,數據大小爲(380, 68)
第15個文件是2014-15.csv,數據大小爲(381, 68)
第16個文件是2015-16.csv,數據大小爲(380, 65)
第17個文件是2016-17.csv,數據大小爲(380, 65)
第18個文件是2017-18.csv,數據大小爲(380, 65)
第19個文件是2018-19.csv,數據大小爲(304, 62)

1.4 刪除特定文件的空值

通過查看第 15 個文件讀取的第 381 行爲空值,故採起刪除行空值操做。

1.4.1 刪除空值的接口
  • Pandas.dropna(axis=0,how='any')

    • axis: 0 表示是行;1表示是列
    • how:'all'表示只去掉全部值均缺失的行、列;any表示只去掉有缺失值的行、列
1.4.2 接口運用
res_name[14] = res_name[14].dropna(axis=0,how='all')
res_name[14].tail()
Div Date HomeTeam AwayTeam FTHG FTAG FTR HTHG HTAG HTR ... BbAv<2.5 BbAH BbAHh BbMxAHH BbAvAHH BbMxAHA BbAvAHA PSCH PSCD PSCA
375 E0 24/05/15 Hull Man United 0.0 0.0 D 0.0 0.0 D ... 1.99 25.0 0.50 1.76 1.71 2.27 2.19 3.20 3.76 2.27
376 E0 24/05/15 Leicester QPR 5.0 1.0 H 2.0 0.0 H ... 2.41 28.0 -1.00 1.98 1.93 1.98 1.93 1.53 4.94 6.13
377 E0 24/05/15 Man City Southampton 2.0 0.0 H 1.0 0.0 H ... 2.66 28.0 -1.00 2.00 1.94 2.03 1.93 1.60 4.35 6.00
378 E0 24/05/15 Newcastle West Ham 2.0 0.0 H 0.0 0.0 D ... 2.25 25.0 -0.50 1.82 1.78 2.20 2.10 1.76 4.01 4.98
379 E0 24/05/15 Stoke Liverpool 6.0 1.0 H 5.0 0.0 H ... 1.99 25.0 0.25 2.07 2.02 1.88 1.85 3.56 3.60 2.17

5 rows × 68 columns

1.5 刪除行數不是 380 的文件名

考慮到英超通常是 19 個球隊,每一個球隊須要打 20 場球,故把行數不是 380 的數據刪除掉,並找到器原 CSV 文件一一對應。

for i in range(len(res_name),0,-1): 
    # 採用從大到小的遍歷方式,而後進行刪除不知足條件的。
    if res_name[i-1].shape[0] != 380:
        key = 'res_name[' + str(i) + ']'
        print('刪除的數據是:%s年的數據,文件名:%s大小是:%s'%(time_list[i-1],key,res_name[i-1].shape))
        res_name.pop(i-1)
        time_list.pop(i-1)
        continue
刪除的數據是:2018年的數據,文件名:res_name[19]大小是:(304, 62)
刪除的數據是:2004年的數據,文件名:res_name[5]大小是:(335, 57)
刪除的數據是:2003年的數據,文件名:res_name[4]大小是:(335, 57)
刪除的數據是:2002年的數據,文件名:res_name[3]大小是:(316, 48)

1.6 查看某一個數據集前n行數據

  • 文件名.head(n)

    • n:默認是5,想獲取多少行數據就填寫數字值。

讀取數據前五行操做:

res_name[0].head()
Div Date HomeTeam AwayTeam FTHG FTAG FTR HTHG HTAG HTR ... IWA LBH LBD LBA SBH SBD SBA WHH WHD WHA
0 E0 19/08/00 Charlton Man City 4 0 H 2 0 H ... 2.7 2.20 3.25 2.75 2.20 3.25 2.88 2.10 3.2 3.10
1 E0 19/08/00 Chelsea West Ham 4 2 H 1 0 H ... 4.2 1.50 3.40 6.00 1.50 3.60 6.00 1.44 3.6 6.50
2 E0 19/08/00 Coventry Middlesbrough 1 3 A 1 1 D ... 2.7 2.25 3.20 2.75 2.30 3.20 2.75 2.30 3.2 2.62
3 E0 19/08/00 Derby Southampton 2 2 D 1 2 A ... 3.5 2.20 3.25 2.75 2.05 3.20 3.20 2.00 3.2 3.20
4 E0 19/08/00 Leeds Everton 2 0 H 2 0 H ... 4.5 1.55 3.50 5.00 1.57 3.60 5.00 1.61 3.5 4.50

5 rows × 45 columns

讀取數據前10行:

res_name[0].head(10)
Div Date HomeTeam AwayTeam FTHG FTAG FTR HTHG HTAG HTR ... IWA LBH LBD LBA SBH SBD SBA WHH WHD WHA
0 E0 19/08/00 Charlton Man City 4 0 H 2 0 H ... 2.7 2.20 3.25 2.75 2.20 3.25 2.88 2.10 3.20 3.10
1 E0 19/08/00 Chelsea West Ham 4 2 H 1 0 H ... 4.2 1.50 3.40 6.00 1.50 3.60 6.00 1.44 3.60 6.50
2 E0 19/08/00 Coventry Middlesbrough 1 3 A 1 1 D ... 2.7 2.25 3.20 2.75 2.30 3.20 2.75 2.30 3.20 2.62
3 E0 19/08/00 Derby Southampton 2 2 D 1 2 A ... 3.5 2.20 3.25 2.75 2.05 3.20 3.20 2.00 3.20 3.20
4 E0 19/08/00 Leeds Everton 2 0 H 2 0 H ... 4.5 1.55 3.50 5.00 1.57 3.60 5.00 1.61 3.50 4.50
5 E0 19/08/00 Leicester Aston Villa 0 0 D 0 0 D ... 2.5 2.35 3.20 2.60 2.25 3.25 2.75 2.40 3.25 2.50
6 E0 19/08/00 Liverpool Bradford 1 0 H 0 0 D ... 8.0 1.35 4.00 8.00 1.36 4.00 8.00 1.33 4.00 8.00
7 E0 19/08/00 Sunderland Arsenal 1 0 H 0 0 D ... 2.1 4.30 3.20 1.70 3.30 3.10 2.05 3.75 3.00 1.90
8 E0 19/08/00 Tottenham Ipswich 3 1 H 2 1 H ... 4.7 1.45 3.60 6.50 1.50 3.50 6.50 1.44 3.60 6.50
9 E0 20/08/00 Man United Newcastle 2 0 H 1 0 H ... 5.0 1.40 3.75 7.00 1.40 3.75 7.50 1.40 3.75 7.00

10 rows × 45 columns
讀取最後 5 行操做:

res_name[0].tail()
Div Date HomeTeam AwayTeam FTHG FTAG FTR HTHG HTAG HTR ... IWA LBH LBD LBA SBH SBD SBA WHH WHD WHA
375 E0 19/05/01 Man City Chelsea 1 2 A 1 1 D ... 1.65 4.0 3.60 1.67 4.20 3.40 1.70 4.00 3.1 1.80
376 E0 19/05/01 Middlesbrough West Ham 2 1 H 2 1 H ... 3.20 1.8 3.25 3.75 1.90 3.20 3.50 1.83 3.4 3.50
377 E0 19/05/01 Newcastle Aston Villa 3 0 H 2 0 H ... 2.90 2.4 3.25 2.50 2.38 3.30 2.50 2.25 3.4 2.60
378 E0 19/05/01 Southampton Arsenal 3 2 H 0 1 A ... 2.35 2.5 3.25 2.37 2.63 3.25 2.30 2.62 3.5 2.20
379 E0 19/05/01 Tottenham Man United 3 1 H 1 1 D ... 2.10 2.6 3.20 2.37 2.60 3.25 2.35 2.62 3.3 2.25

5 rows × 45 columns

讀取最後 4 行操做:

res_name[0].tail(4)
Div Date HomeTeam AwayTeam FTHG FTAG FTR HTHG HTAG HTR ... IWA LBH LBD LBA SBH SBD SBA WHH WHD WHA
376 E0 19/05/01 Middlesbrough West Ham 2 1 H 2 1 H ... 3.20 1.8 3.25 3.75 1.90 3.20 3.50 1.83 3.4 3.50
377 E0 19/05/01 Newcastle Aston Villa 3 0 H 2 0 H ... 2.90 2.4 3.25 2.50 2.38 3.30 2.50 2.25 3.4 2.60
378 E0 19/05/01 Southampton Arsenal 3 2 H 0 1 A ... 2.35 2.5 3.25 2.37 2.63 3.25 2.30 2.62 3.5 2.20
379 E0 19/05/01 Tottenham Man United 3 1 H 1 1 D ... 2.10 2.6 3.20 2.37 2.60 3.25 2.35 2.62 3.3 2.25

4 rows × 45 columns

1.8 獲取某一年主場隊伍的名稱

res_name[0]['HomeTeam'].unique()
array(['Charlton', 'Chelsea', 'Coventry', 'Derby', 'Leeds', 'Leicester',
       'Liverpool', 'Sunderland', 'Tottenham', 'Man United', 'Arsenal',
       'Bradford', 'Ipswich', 'Middlesbrough', 'Everton', 'Man City',
       'Newcastle', 'Southampton', 'West Ham', 'Aston Villa'],
      dtype=object)

1.9 解析數據集列表頭含義

數據集行數已經固定,通常都是 380 行,而列數可能每一年統計指標有變化,不必定相等,並且咱們也比較關心列數表表頭。因爲比較小,能夠直接看數據集列數,這樣比較快,也能夠代碼實現,找到最大的列數,而後獲取列數的表頭進行通常性介紹解釋。

# 獲取列表頭最大的列數,而後獲取器參數
shape_list = [res_name[i].shape[1] for i in range(len(res_name))]
for i in range(len(res_name)):
    if res_name[i].shape[1] == max(shape_list):
        print('%s年數據是有最大列數:%s,列元素表頭:\n %s'%(time_list[i],max(shape_list),res_name[i].columns))
2012年數據是有最大列數:74,列元素表頭:
 Index(['Div', 'Date', 'HomeTeam', 'AwayTeam', 'FTHG', 'FTAG', 'FTR', 'HTHG',
       'HTAG', 'HTR', 'Referee', 'HS', 'AS', 'HST', 'AST', 'HF', 'AF', 'HC',
       'AC', 'HY', 'AY', 'HR', 'AR', 'B365H', 'B365D', 'B365A', 'BWH', 'BWD',
       'BWA', 'GBH', 'GBD', 'GBA', 'IWH', 'IWD', 'IWA', 'LBH', 'LBD', 'LBA',
       'PSH', 'PSD', 'PSA', 'WHH', 'WHD', 'WHA', 'SJH', 'SJD', 'SJA', 'VCH',
       'VCD', 'VCA', 'BSH', 'BSD', 'BSA', 'Bb1X2', 'BbMxH', 'BbAvH', 'BbMxD',
       'BbAvD', 'BbMxA', 'BbAvA', 'BbOU', 'BbMx>2.5', 'BbAv>2.5', 'BbMx<2.5',
       'BbAv<2.5', 'BbAH', 'BbAHh', 'BbMxAHH', 'BbAvAHH', 'BbMxAHA', 'BbAvAHA',
       'PSCH', 'PSCD', 'PSCA'],
      dtype='object')

咱們看到數據包括 Date(比賽的時間),Hometeam(主場隊伍名),Awayteam(客場隊伍名),FTHG(主場球隊全場進球數),HTHG(主場球隊半場進球數),FTR(全場比賽結果)等等,更多關於數據集中特徵信息能夠參考數據集特徵說明文檔

2. 數據清洗和預處理

咱們挑選 Hometeam,Awayteam,FTHG,FTAG,FTR 這五列數據,做爲咱們的原始的特徵數據,後面基於這些原始特徵,咱們再構造一些新的特徵。

2.1  挑選信息列

  • HomeTeam: 主場球隊名
  • AwayTeam: 客場球隊名
  • FTHG: 全場 主場球隊進球數
  • FTAG:  全場 客場球隊進球數
  • FTR:  比賽結果 ( H= 主場贏, D= 平局, A= 客場贏)
# 將挑選的信息放在一個新的列表中
columns_req = ['HomeTeam','AwayTeam','FTHG','FTAG','FTR']
playing_statistics = []   # 創造處理後數據名存放處
playing_data = {}  # 鍵值對存儲數據
for i in range(len(res_name)):
    playing_statistics.append('playing_statistics_'+str(i+1))
    playing_statistics[i] = res_name[i][columns_req]
    print(time_list[i],'playing_statistics['+str(i)+']',playing_statistics[i].shape)
2000 playing_statistics[0] (380, 5)
2001 playing_statistics[1] (380, 5)
2005 playing_statistics[2] (380, 5)
2006 playing_statistics[3] (380, 5)
2007 playing_statistics[4] (380, 5)
2008 playing_statistics[5] (380, 5)
2009 playing_statistics[6] (380, 5)
2010 playing_statistics[7] (380, 5)
2011 playing_statistics[8] (380, 5)
2012 playing_statistics[9] (380, 5)
2013 playing_statistics[10] (380, 5)
2014 playing_statistics[11] (380, 5)
2015 playing_statistics[12] (380, 5)
2016 playing_statistics[13] (380, 5)
2017 playing_statistics[14] (380, 5)

2.2 分析原始數據

咱們首先預測全部主場球隊全都勝利,而後預測全部的客場都會勝利,對結果進行對比分析:

2.2.1 統計全部主場球隊都會勝利的準確率
def predictions_0(data):
    """ 
    當咱們統計全部主場球隊都贏,那麼咱們預測的結果是什麼
    返回值是預測值和實際值
    """
    predictions = []
    for _, game in data.iterrows():
        
        if game['FTR']=='H':
            predictions.append(1)
        else:
            predictions.append(0)
    # 返回預測結果
    return pd.Series(predictions)

# 那咱們對19年所有主場球隊都贏的結果進行預測,獲取預測的準確率。
avg_acc_sum = 0
for i in range(len(playing_statistics)):
    predictions = predictions_0(playing_statistics[i])
    acc=sum(predictions)/len(playing_statistics[i])
    avg_acc_sum += acc
    print("%s年數據主場全勝預測的準確率是%s"%(time_list[i],acc))
print('共%s年的平均準確率是:%s'%(len(playing_statistics),avg_acc_sum/len(playing_statistics)))
2000年數據主場全勝預測的準確率是0.4842105263157895
2001年數據主場全勝預測的準確率是0.4342105263157895
2005年數據主場全勝預測的準確率是0.5052631578947369
2006年數據主場全勝預測的準確率是0.4789473684210526
2007年數據主場全勝預測的準確率是0.4631578947368421
2008年數據主場全勝預測的準確率是0.45526315789473687
2009年數據主場全勝預測的準確率是0.5078947368421053
2010年數據主場全勝預測的準確率是0.4710526315789474
2011年數據主場全勝預測的準確率是0.45
2012年數據主場全勝預測的準確率是0.4368421052631579
2013年數據主場全勝預測的準確率是0.4710526315789474
2014年數據主場全勝預測的準確率是0.45263157894736844
2015年數據主場全勝預測的準確率是0.4131578947368421
2016年數據主場全勝預測的準確率是0.4921052631578947
2017年數據主場全勝預測的準確率是0.45526315789473687
共15年的平均準確率是:0.46473684210526317
2.2.2 統計全部客場球隊都會勝利的準確率
def predictions_1(data):
    """ 
    當咱們統計全部客場球隊都贏,那麼咱們預測的結果是什麼
    返回值是預測值和實際值
    """
    predictions = []
    for _, game in data.iterrows():
        
        if game['FTR']=='A':
            predictions.append(1)
        else:
            predictions.append(0)
    # 返回預測結果
    return pd.Series(predictions)

# 那咱們對19年客場球隊都贏的結果進行預測,獲取預測的準確率。
for i in range(len(playing_statistics)):
    predictions = predictions_1(playing_statistics[i])
    acc=sum(predictions)/len(playing_statistics[i])
    print("%s年數據客場全勝預測的準確率是%s"%(time_list[i],acc))
2000年數據客場全勝預測的準確率是0.25
2001年數據客場全勝預測的準確率是0.3
2005年數據客場全勝預測的準確率是0.29210526315789476
2006年數據客場全勝預測的準確率是0.2631578947368421
2007年數據客場全勝預測的準確率是0.2736842105263158
2008年數據客場全勝預測的準確率是0.2894736842105263
2009年數據客場全勝預測的準確率是0.2394736842105263
2010年數據客場全勝預測的準確率是0.23684210526315788
2011年數據客場全勝預測的準確率是0.30526315789473685
2012年數據客場全勝預測的準確率是0.2789473684210526
2013年數據客場全勝預測的準確率是0.3236842105263158
2014年數據客場全勝預測的準確率是0.3026315789473684
2015年數據客場全勝預測的準確率是0.30526315789473685
2016年數據客場全勝預測的準確率是0.2868421052631579
2017年數據客場全勝預測的準確率是0.28421052631578947

綜上比較:咱們能夠看出主場勝利的機率相對於輸和平局來講,確實機率要大。

2.3 咱們想知道 Arsenal 做爲主場隊伍時,他們的表現,如何求出 2005-06 全部比賽累計進球數 ?

咱們知道 2005-06 年數據在 playing_statistics[2] 中:

def score(data):
    """ Arsenal做爲主場隊伍時,累計進球數 """
    scores=[]
    for _,game in data.iterrows():
        if game['HomeTeam']=='Arsenal':
            scores.append(game['FTHG'])
    return np.sum(scores)
Arsenal_score=score(playing_statistics[2])
print("Arsenal做爲主場隊伍在2005年時,累計進球數:%s"%(Arsenal_score))
Arsenal 做爲主場隊伍在2005年時,累計進球數:48

2.4 咱們想知道各個球隊做爲主場隊伍時,他們的表現如何 ?

先試試求 2005-06 全部比賽各個球隊累計進球數。

print(playing_statistics[5].groupby('HomeTeam').sum()['FTHG'])
HomeTeam
Arsenal          31
Aston Villa      27
Blackburn        22
Bolton           21
Chelsea          33
Everton          31
Fulham           28
Hull             18
Liverpool        41
Man City         40
Man United       43
Middlesbrough    17
Newcastle        24
Portsmouth       26
Stoke            22
Sunderland       21
Tottenham        21
West Brom        26
West Ham         23
Wigan            17
Name: FTHG, dtype: int64

3. 特徵工程

特徵工程指的是把原始數據轉變爲模型的訓練數據的過程,它的目的就是獲取更好的訓練數據特徵,獲得更好的訓練模型。特徵工程能使得模型的性能獲得提高,有時甚至在簡單的模型上也能取得不錯的效果。特徵工程在機器學習中佔有很是重要的做用,通常認爲括特徵構建、特徵提取、特徵選擇三大部分。

3.1 構造特徵

由於這個比賽是一年一個賽季,是有前後順序的,那咱們就能夠統計到截止到本場比賽以前,整個賽季內,主客場隊伍的淨勝球的數量。那麼對於每個賽季的每一週,都統計出每一個球隊到本週爲止累計的進球數和丟球數之差,也就是淨勝球的數量。

3.1.1 計算每一個隊周累計淨勝球數量

處理後的數據,咱們能夠經過看某一年的某幾條數據來體現,好比:05-06 年的後五條數據

def get_goals_diff(playing_stat):
    # 建立一個字典,每一個 team 的 name 做爲 key
    teams = {}
    for i in playing_stat.groupby('HomeTeam').mean().T.columns:
        teams[i] = []
    # 對於每一場比賽
    for i in range(len(playing_stat)):
        # 全場比賽,主場隊伍的進球數
        HTGS = playing_stat.iloc[i]['FTHG']
        # 全場比賽,客場隊伍的進球數
        ATGS = playing_stat.iloc[i]['FTAG']

        # 把主場隊伍的淨勝球數添加到 team 這個 字典中對應的主場隊伍下
        teams[playing_stat.iloc[i].HomeTeam].append(HTGS-ATGS)
        # 把客場隊伍的淨勝球數添加到 team 這個 字典中對應的客場隊伍下
        teams[playing_stat.iloc[i].AwayTeam].append(ATGS-HTGS)

    # 建立一個 GoalsDifference 的 dataframe
    # 行是 team 列是 matchweek,
    # 39解釋:19個球隊,每一個球隊分主場客場2次,共38個賽次,可是range取不到最後一個值,故38+1=39
    GoalsDifference = pd.DataFrame(data=teams, index = [i for i in range(1,39)]).T
    GoalsDifference[0] = 0
    # 累加每一個隊的周比賽的淨勝球數
    for i in range(2,39):
        GoalsDifference[i] = GoalsDifference[i] + GoalsDifference[i-1]
    return GoalsDifference

def get_gss(playing_stat):
    # 獲得淨勝球數統計
    GD = get_goals_diff(playing_stat)
    j = 0
    #  主客場的淨勝球數
    HTGD = []
    ATGD = []
    # 整年一共380場比賽
    for i in range(380):
        ht = playing_stat.iloc[i].HomeTeam
        at = playing_stat.iloc[i].AwayTeam
        HTGD.append(GD.loc[ht][j])
        ATGD.append(GD.loc[at][j])
        if ((i + 1)% 10) == 0:
            j = j + 1
    # 把每一個隊的 HTGD ATGD 信息補充到 dataframe 中
    playing_stat.loc[:,'HTGD'] = HTGD
    playing_stat.loc[:,'ATGD'] = ATGD
    return playing_stat

for i in range(len(playing_statistics)):
    playing_statistics[i] = get_gss(playing_statistics[i])

####  查看構造特徵後的05-06年的後五條數據
playing_statistics[2].tail()
HomeTeam AwayTeam FTHG FTAG FTR HTGD ATGD
375 Fulham Middlesbrough 1 0 H -11 -9
376 Man United Charlton 4 0 H 34 -10
377 Newcastle Chelsea 1 0 H 4 51
378 Portsmouth Liverpool 1 3 A -23 30
379 West Ham Tottenham 2 1 H -4 16

經過以上數據:咱們發現 376 行數據的特色, 截止到這一場比賽以前,本賽季主場曼聯隊的淨勝球數是 34 , 客場查爾頓隊的淨勝球數是 -10 。

3.1.2 統計主客場隊伍到當前比賽周的累計得分

統計整個賽季主客場隊伍截止到當前比賽周的累計得分。一場比賽勝利計 3 分, 平局計 1 分,輸了計 0 分。咱們根據本賽季本週以前的比賽結果來統計這個值。咱們繼續觀看  05-06 年的後五條數據:

# 把比賽結果轉換爲得分,贏得三分,平局得一分,輸不得分
def get_points(result):
    if result == 'W':
        return 3
    elif result == 'D':
        return 1
    else:
        return 0
    
def get_cuml_points(matchres):
    matchres_points = matchres.applymap(get_points)
    for i in range(2,39):
        matchres_points[i] = matchres_points[i] + matchres_points[i-1]
    matchres_points.insert(column =0, loc = 0, value = [0*i for i in range(20)])
    return matchres_points

def get_matchres(playing_stat):
    # 建立一個字典,每一個 team 的 name 做爲 key
    teams = {}
    for i in playing_stat.groupby('HomeTeam').mean().T.columns:
        teams[i] = []
    # 把比賽結果分別記錄在主場隊伍和客場隊伍中
    # H:表明 主場 贏
    # A:表明 客場 贏
    # D:表明 平局
    for i in range(len(playing_stat)):
        if playing_stat.iloc[i].FTR == 'H':
            # 主場 贏,則主場記爲贏,客場記爲輸
            teams[playing_stat.iloc[i].HomeTeam].append('W')
            teams[playing_stat.iloc[i].AwayTeam].append('L')
        elif playing_stat.iloc[i].FTR == 'A':
            # 客場 贏,則主場記爲輸,客場記爲贏
            teams[playing_stat.iloc[i].AwayTeam].append('W')
            teams[playing_stat.iloc[i].HomeTeam].append('L')
        else:
            # 平局
            teams[playing_stat.iloc[i].AwayTeam].append('D')
            teams[playing_stat.iloc[i].HomeTeam].append('D')
    return pd.DataFrame(data=teams, index = [i for i in range(1,39)]).T

def get_agg_points(playing_stat):
    matchres = get_matchres(playing_stat)
    cum_pts = get_cuml_points(matchres)
    HTP = []
    ATP = []
    j = 0
    for i in range(380):
        ht = playing_stat.iloc[i].HomeTeam
        at = playing_stat.iloc[i].AwayTeam
        HTP.append(cum_pts.loc[ht][j])
        ATP.append(cum_pts.loc[at][j])

        if ((i + 1)% 10) == 0:
            j = j + 1
    # 主場累計得分
    playing_stat.loc[:,'HTP'] = HTP
    # 客場累計得分
    playing_stat.loc[:,'ATP'] = ATP
    return playing_stat

for i in range(len(playing_statistics)):
    playing_statistics[i] = get_agg_points(playing_statistics[i])
    
#查看構造特徵後的05-06年的後五條數據
playing_statistics[2].tail()
HomeTeam AwayTeam FTHG FTAG FTR HTGD ATGD HTP ATP
375 Fulham Middlesbrough 1 0 H -11 -9 45 45
376 Man United Charlton 4 0 H 34 -10 80 47
377 Newcastle Chelsea 1 0 H 4 51 55 91
378 Portsmouth Liverpool 1 3 A -23 30 38 79
379 West Ham Tottenham 2 1 H -4 16 52 65

咱們處理獲得 HTP (本賽季主場球隊截止到本週的累計得分), ATP (本賽季客場球隊截止到本週的累計得分)。
咱們再看 376 行,截止到這一場比賽,本賽季,曼聯隊一共積了80分, 查爾頓隊積了 47 分。

3.1.3  統計某支隊伍最近三場比賽的表現

前面咱們構造的特徵反映了一隻隊伍本賽季的歷史總表現,咱們看看隊伍在最近三場比賽的表現。
咱們用:

HM1 表明主場球隊上一次比賽的輸贏,

AM1 表明客場球隊上一次比賽是輸贏。

同理,HM2 AM2 就是上上次比賽的輸贏, HM3 AM3 就是上上上次比賽的輸贏。

咱們繼續觀看處理後 05-06 年的後 5 五條數據:

def get_form(playing_stat,num):
    form = get_matchres(playing_stat)
    form_final = form.copy()
    for i in range(num,39):
        form_final[i] = ''
        j = 0
        while j < num:
            form_final[i] += form[i-j]
            j += 1
    return form_final

def add_form(playing_stat,num):
    form = get_form(playing_stat,num)
    # M 表明 unknown, 由於沒有那麼多歷史
    h = ['M' for i in range(num * 10)]
    a = ['M' for i in range(num * 10)]
    j = num
    for i in range((num*10),380):
        ht = playing_stat.iloc[i].HomeTeam
        at = playing_stat.iloc[i].AwayTeam

        past = form.loc[ht][j]
        h.append(past[num-1])

        past = form.loc[at][j]
        a.append(past[num-1])

        if ((i + 1)% 10) == 0:
            j = j + 1

    playing_stat['HM' + str(num)] = h
    playing_stat['AM' + str(num)] = a

    return playing_stat

def add_form_df(playing_statistics):
    playing_statistics = add_form(playing_statistics,1)
    playing_statistics = add_form(playing_statistics,2)
    playing_statistics = add_form(playing_statistics,3)
    return playing_statistics

for i in range(len(playing_statistics)):
    playing_statistics[i] = add_form_df(playing_statistics[i])

#查看構造特徵後的05-06年的後5五條數據
playing_statistics[2].tail()
HomeTeam AwayTeam FTHG FTAG FTR HTGD ATGD HTP ATP HM1 AM1 HM2 AM2 HM3 AM3
375 Fulham Middlesbrough 1 0 H -11 -9 45 45 L D W D W L
376 Man United Charlton 4 0 H 34 -10 80 47 D L L L W W
377 Newcastle Chelsea 1 0 H 4 51 55 91 D L W W W W
378 Portsmouth Liverpool 1 3 A -23 30 38 79 W W W W L W
379 West Ham Tottenham 2 1 H -4 16 52 65 W W L D L L
3.1.4 加入比賽周特徵(第幾個比賽周)

而後咱們把比賽周的信息也放在裏面,也就是這一場比賽發生在第幾個比賽周。
特徵構造後的結果,咱們能夠直接查看 05-06 年的後 5 條數據:

def get_mw(playing_stat):
    j = 1
    MatchWeek = []
    for i in range(380):
        MatchWeek.append(j)
        if ((i + 1)% 10) == 0:
            j = j + 1
    playing_stat['MW'] = MatchWeek
    return playing_stat

for i in range(len(playing_statistics)):
    playing_statistics[i] = get_mw(playing_statistics[i])
    
#查看構造特徵後的05-06年的後五條數據
playing_statistics[2].tail()
HomeTeam AwayTeam FTHG FTAG FTR HTGD ATGD HTP ATP HM1 AM1 HM2 AM2 HM3 AM3 MW
375 Fulham Middlesbrough 1 0 H -11 -9 45 45 L D W D W L 38
376 Man United Charlton 4 0 H 34 -10 80 47 D L L L W W 38
377 Newcastle Chelsea 1 0 H 4 51 55 91 D L W W W W 38
378 Portsmouth Liverpool 1 3 A -23 30 38 79 W W W W L W 38
379 West Ham Tottenham 2 1 H -4 16 52 65 W W L D L L 38
3.1.5 合併比賽的信息

咱們打算把數據集比賽的信息都合併到一個表裏面,而後咱們把咱們剛纔計算獲得的這些得分數據,淨勝球數據除以週數,就獲得了周平均後的值。結果就能夠經過查看構造特徵後數據集的後 5 條數據。

# 將各個DataFrame表合併在一張表中
playing_stat = pd.concat(playing_statistics, ignore_index=True)

# HTGD, ATGD ,HTP, ATP的值 除以 week 數,獲得平均分
cols = ['HTGD','ATGD','HTP','ATP']
playing_stat.MW = playing_stat.MW.astype(float)
for col in cols:
    playing_stat[col] = playing_stat[col] / playing_stat.MW
    
#查看構造特徵後數據集的後5五條數據
playing_stat.tail()
HomeTeam AwayTeam FTHG FTAG FTR HTGD ATGD HTP ATP HM1 AM1 HM2 AM2 HM3 AM3 MW
5695 Newcastle Chelsea 3.0 0.0 H -0.289474 0.710526 1.078947 1.842105 L D L W L W 38.0
5696 Southampton Man City 0.0 1.0 A -0.473684 2.052632 0.947368 2.552632 W W D D W W 38.0
5697 Swansea Stoke 1.0 2.0 A -0.710526 -0.894737 0.868421 0.789474 L L L D L D 38.0
5698 Tottenham Leicester 5.0 4.0 H 0.973684 -0.078947 1.947368 1.236842 W W L L W L 38.0
5699 West Ham Everton 3.0 1.0 H -0.578947 -0.315789 1.026316 1.289474 D D W W L W 38.0

咱們看到數據集最後一行的行數是 5699 ,加上第一行爲 0 行,則一共 5700 條數據;咱們總共統計了 15 年的數據,每年有 380 條數據,計算後發現咱們統計後的數據集大小是準確的。

3.2 刪除某些數據

前面咱們根據初始的特徵構造出了不少的特徵。這其中有一部分是中間的特徵,咱們須要把這些中間特徵拋棄掉。由於前三週的比賽,每一個隊的歷史勝負信息不足,因此咱們打算棄掉前三週的數據。

# 拋棄前三週的比賽
playing_stat = playing_stat[playing_stat.MW > 3]
playing_stat.drop(['HomeTeam', 'AwayTeam', 'FTHG', 'FTAG', 'MW'],1, inplace=True)

#咱們查看下此時的數據的特徵
playing_stat.keys()
Index(['FTR', 'HTGD', 'ATGD', 'HTP', 'ATP', 'HM1', 'AM1', 'HM2', 'AM2', 'HM3','AM3'], dtype='object')

3.3 分析咱們構造的數據

在前面,咱們計算了每一的年主客場的勝率,如今咱們看看有效數據中,是主場勝利的多呢,仍是客場勝利的多呢?

# 比賽總數
n_matches = playing_stat.shape[0]

# 特徵數
n_features = playing_stat.shape[1] - 1

# 主場獲勝的數目
n_homewins = len(playing_stat[playing_stat.FTR == 'H'])

# 主場獲勝的比例
win_rate = (float(n_homewins) / (n_matches)) * 100

# Print the results
print("比賽總數: {}".format(n_matches))
print("總特徵數: {}".format(n_features))
print("主場勝利數: {}".format(n_homewins))
print("主場勝率: {:.2f}%".format(win_rate))
比賽總數: 5250
總特徵數: 10
主場勝利數: 2451
主場勝率: 46.69%

經過統計結果看到:咱們主場勝率 46.69% 與咱們第 2.2.1 小節原始數據分析的結果是一致的,說明咱們前面構造的特徵是有效的,比較貼近實際的。

3.4 解決樣本不均衡問題

經過構造特徵以後,發現主場獲勝的比例接近 50% ,因此對於這個三分類的問題,標籤比例是不均衡的。

咱們把它簡化爲二分類問題,也就是主場球隊會不會勝利,這也是一種解決標籤比例不均衡的問題的方法。

# 定義 target ,也就是否 主場贏
def only_hw(string):
    if string == 'H':
        return 'H'
    else:
        return 'NH'
playing_stat['FTR'] = playing_stat.FTR.apply(only_hw)

3.5 將數據分爲特徵值和標籤值

# 把數據分爲特徵值和標籤值
X_all = playing_stat.drop(['FTR'],1)
y_all = playing_stat['FTR']
# 特徵值的長度
len(X_all)
5250

3.6 數據歸一化、標準化

咱們對全部比賽的特徵 HTP 進行最大最小值歸一化。

def convert_1(data):
    max=data.max()
    min=data.min()
    return (data-min)/(max-min)
r_data=convert_1(X_all['HTGD'])
# 數據標準化
from sklearn.preprocessing import scale
cols = [['HTGD','ATGD','HTP','ATP']]
for col in cols:
    X_all[col] = scale(X_all[col])

3.7 轉換特徵數據類型

# 把這些特徵轉換成字符串類型
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')

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)))
Processed feature columns (22 total features):
['HTGD', 'ATGD', 'HTP', 'ATP', 'HM1_D', 'HM1_L', 'HM1_W', 'AM1_D', 'AM1_L', 'AM1_W', 'HM2_D', 'HM2_L', 'HM2_W', 'AM2_D', 'AM2_L', 'AM2_W', 'HM3_D', 'HM3_L', 'HM3_W', 'AM3_D', 'AM3_L', 'AM3_W']
# 預覽處理好的數據
print("\nFeature values:")
display(X_all.head())
Feature values:
HTGD ATGD HTP ATP HM1_D HM1_L HM1_W AM1_D AM1_L AM1_W ... HM2_W AM2_D AM2_L AM2_W HM3_D HM3_L HM3_W AM3_D AM3_L AM3_W
30 0.724821 0.339985 -0.043566 -0.603098 1 0 0 1 0 0 ... 0 0 0 1 0 0 1 0 1 0
31 -0.702311 -1.088217 -1.097731 -2.192828 0 1 0 1 0 0 ... 0 0 1 0 0 0 1 0 1 0
32 0.011255 0.339985 -0.570649 -0.603098 0 1 0 1 0 0 ... 0 0 0 1 0 0 1 0 1 0
33 -0.345528 -0.374116 -1.097731 -1.662918 0 1 0 1 0 0 ... 0 0 1 0 0 0 1 1 0 0
34 0.011255 1.054086 -0.570649 0.456723 1 0 0 0 0 1 ... 0 0 0 1 0 0 1 0 1 0

5 rows × 22 columns

3.8 皮爾遜相關熱力圖

咱們生成一些特徵的相關圖,以查看特徵與特徵之間的相關性。 爲此,咱們將利用 Seaborn 繪圖軟件包,使咱們可以很是方便地繪製熱力圖,以下所示:

import matplotlib.pyplot as plt
import seaborn as sns
# 防止中文出現錯誤
plt.rcParams['font.sans-serif']=['SimHei'] 
plt.rcParams['axes.unicode_minus']=False
#製成皮爾森熱圖
#把標籤映射爲0和1
y_all=y_all.map({'NH':0,'H':1})
#合併特徵集和標籤
train_data=pd.concat([X_all,y_all],axis=1)
colormap = plt.cm.RdBu
plt.figure(figsize=(21,18))
plt.title('Pearson Correlation of Features', y=1.05, size=15)
sns.heatmap(train_data.astype(float).corr(),linewidths=0.1,vmax=1.0,
            square=True, cmap=colormap, linecolor='white', annot=True)
<matplotlib.axes._subplots.AxesSubplot at 0x211ffda5860>

enter image description here

經過上圖咱們能夠看出特徵 HTP 特徵和 HTGD 特徵相關性很強,一樣 ATP 特徵和 ATGD 特徵相關性很強,能夠代表多重共線性的狀況。這個咱們也很容易理解,主場周平均得分數越高,那麼主場周平均淨勝球數也一樣越高。若是咱們考慮這些變量,咱們能夠得出結論,它們給出了幾乎相同的信息,所以實際上發生了多重共線性,這裏咱們會考慮刪除 HTP 和 'ATP' 這兩個特徵,保留 HTGD 和 ATGD 這兩個特徵。皮爾森熱圖很是適合檢測這種狀況,而且在特徵工程中,它們是必不可少的工具。同時,咱們也能夠看出上上上次球隊的比賽結果對目前比賽的結果影響較小,這裏咱們考慮保留這些特徵。

  • 考慮到樣本集特徵 HTP 和 HTGD,ATP 和 ATGD 的相關性都超過了 90% ,故咱們刪除特徵 HTP , ATP :
X_all=X_all.drop(['HTP','ATP'],axis=1)
  • 看看與FTR最相關的10個特徵
#FTR correlation matrix
plt.figure(figsize=(14,12))
k = 10 # number of variables for heatmap
cols = abs(train_data.astype(float).corr()).nlargest(k, 'FTR')['FTR'].index
cm = np.corrcoef(train_data[cols].values.T)
sns.set(font_scale=1.25)
hm = sns.heatmap(cm, cbar=True, annot=True, square=True, fmt='.2f', annot_kws={'size': 10}, yticklabels=cols.values, xticklabels=cols.values)
plt.show()

enter image description here

咱們能夠看出最相關的特徵是 HTGD ,代表一個球隊主場周平均淨勝球數越高,他們贏的機率也就越大。

4.創建機器學習模型並進行預測

4.1 切分數據

將數據集隨機分紅爲訓練集和測試集,並返回劃分好的訓練集測試集樣本和訓練集測試集標籤。咱們直接採用 train_test_split 接口進行處理。

4.1.1  train_test_split API 接口介紹
  • X_train, X_test, y_train, y_test =cross_validation.train_test_split(train_data,train_target,test_size=0.3, random_state=0)
  • 參數解釋:

    • train_data:被劃分的樣本特徵集
    • train_target:被劃分的樣本標籤
    • test_size:若是是浮點數,在0-1之間,表示樣本佔比;若是是整數的話就是樣本的數量
    • random_state:是隨機數的種子。
  • 返回值解釋:

    • x_train:訓練集特徵值
    • x_test:測試集特徵值
    • y_train:訓練集目標值
    • y_test:測試集目標值

隨機數種子:其實就是該組隨機數的編號,在須要重複試驗的時候,保證獲得一組同樣的隨機數。好比你每次都填1,其餘參數同樣的狀況下你獲得的隨機數組是同樣的。但填0或不填,每次都會不同。隨機數的產生取決於種子,隨機數和種子之間的關係聽從如下兩個規則:種子不一樣,產生不一樣的隨機數;種子相同,即便實例不一樣也產生相同的隨機數。

4.1.2 代碼處理分割數據
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X_all, y_all,test_size = 0.3,random_state = 2,stratify = y_all)

4.2 相關模型及其接口介紹

下面咱們分別使用邏輯迴歸、支持向量機和 XGBoost 這三種不一樣的模型,來看看他們的表現。咱們先定義一些輔助函數,記錄模型的訓練時長和評估時長,計算模型的準確率和 f1 分數。咱們首先介紹一下這三個模型聯繫與區別和相關的接口:

4.2.1 邏輯迴歸介紹

邏輯迴歸模型是:假設數據服從伯努利分佈,經過極大化似然函數的方法,運用梯度降低來求解參數,來達到將數據二分類的目的。該模型的主要優勢是解釋性比較好;若是特徵工程作得好,模型效果也很是不錯;訓練速度也比較快;輸出結果也很容易調整。可是該模型的缺點也很突出,好比:準確率不是很高,比較難處理數據不均衡問題等。

4.2.2 邏輯迴歸模型接口介紹

API:sklearn.linear_model.LogisticRegression(penalty='l2', dual=False, tol=0.0001, C=1.0,fit_intercept=True, intercept_scaling=1, class_weight=None, random_state=None,solver='liblinear', max_iter=100, multi_class='ovr', verbose=0,warm_start=False, n_jobs=1)

  • 主要參數解析:

    • penalty:正則化參數,l1 or  l2, default: l2;
    • C:正則化係數λ的倒數,default: 1.0;
    • fit_intercept : 是否存在截距, default: True
    • solver:損失函數的優化方法,有如下四種可供選擇{newton-cg, lbfgs, liblinear,sag},  default: liblinear
    • multi_class:分類方式選擇,通常有{ovr, multinomial}, default:ovr;
    • class_weight:類型權重參數,默認爲None
    • random_state:隨機數種子,默認爲無
    • tol:迭代終止判據的偏差範圍
    • n_jobs:並行數,爲-1時跟CPU核數一致,默認值爲1。

以上是主要參數的簡單解析,若是你們想深刻了解,能夠參看官方網址

4.2.3 支持向量機介紹

SVM(Support Vector Machine) 是一種二類分類模型。它的基本模型是在特徵空間中尋找間隔最大化的分離超平面的線性分類器。

(1)當訓練樣本線性可分時,經過硬間隔最大化,學習一個線性分類器,即線性可分支持向量機;
(2)當訓練數據近似線性可分時,引入鬆弛變量,經過軟間隔最大化,學習一個線性分類器,即線性支持向量機;
(3)當訓練數據線性不可分時,經過使用核技巧及軟間隔最大化,學習非線性支持向量機。

4.2.4 支持向量機分類模型API

sklearn.svm.SVC(C=1.0,kernel='rbf',degree=3,gamma='auto',coef0=0.0,shrinking=True,probability=False,tol=0.001,cache_size=200,class_weight=None,verbose=False,max_iter=-1,decision_function_shape=None,random_state=None)

  • 主要參數解析:

    • C:C-SVC的懲罰參數C,默認值是1.0。C越大,至關於懲罰鬆弛變量,但願鬆弛變量接近0,即對誤分類的懲罰增大,趨向於對訓練集全分對的狀況,這樣對訓練集測試時準確率很高,但泛化能力弱。C值小,對誤分類的懲罰減少,容許容錯,將他們當成噪聲點,泛化能力較強。
    • kernel :核函數,默認是rbf,能夠是‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’

      • 0 – 線性:u'v
      • 1 – 多項式:(gamma_u'_v + coef0)^degree
      • 2 – RBF函數:exp(-gamma|u-v|^2)
      • 3 –sigmoid:tanh(gamma_u'_v + coef0)
    • degree :多項式poly函數的維度,默認是3,選擇其餘核函數時會被忽略。
    • gamma :rbf,poly 和sigmoid的核函數參數。默認是auto,則會選擇1/n_features
    • coef0 :核函數的常數項。對於poly和 sigmoid有用。
    • max_iter :最大迭代次數。-1爲無限制。
    • decision_function_shape :ovo, ovr or None, default=None。

主要調節的參數有:C、kernel、degree、gamma、coef0;參數詳解請參考官網

4.2.5 XGBoost 原理介紹

XGBoost 是 Boosting算法的其中一種, Boosting 算法的思想是許多弱分類器集成在一塊兒,造成一個強分類器,基本原理是下一棵決策樹輸入樣本會與前面決策樹的訓練和預測相關。覺得 XGBoost 是一種提高樹模型,因此他是將許多樹模型集成在一塊兒,造成一個很強的分類器。而所用到的樹模型則是 CART 迴歸樹模型。

4.2.6 XGBoost 接口介紹

XGBoost.XGBRegressor(max_depth=3, learning_rate=0.1, n_estimators=100, silent=True, objective='reg:linear', booster='gbtree', n_jobs=1, nthread=None, gamma=0, min_child_weight=1, max_delta_step=0, subsample=1, colsample_bytree=1, colsample_bylevel=1, reg_alpha=0, reg_lambda=1, scale_pos_weight=1, base_score=0.5, random_state=0, seed=None, missing=None, **kwargs)

  • 主要參數解析:

    • booster:模型類別,主要有2種,gbtree 和 gbliner,默認是: gbtree ;
    • nthread:使用 CPU 個數,爲 -1 時表示使用所有 CPU 進行並行運算(默認),等於 1 時表示使用1個 CPU 進行運算;
    • scale_pos_weight:正樣本的權重,在二分類任務中,當正負樣本比例失衡時,設置正樣本的權重,模型效果更好。例如,當正負樣本比例爲 1:10 時,scale_pos_weight=10;
    • n_estimatores:總共迭代的次數,即決策樹的個數;
    • early_stopping_rounds:在驗證集上,當連續n次迭代,分數沒有提升後,提早終止訓練
    • max_depth:樹的深度,默認值爲6,典型值3-10;
    • min_child_weight:值越大,越容易欠擬合;值越小,越容易過擬合(值較大時,避免模型學習到局部的特殊樣本),默認爲1;
    • learning_rate:學習率,控制每次迭代更新權重時的步長,默認0.3;
    • gamma:懲罰項係數,指定節點分裂所需的最小損失函數降低值;
    • alpha:L1 正則化係數,默認爲 1 ;
    • lambda:L2 正則化係數,默認爲 1 ;
    • seed:隨機種子。

如想詳細學習該 API ,能夠參考官網網址

4.3 創建機器學習模型並評估

4.3.1 創建模型
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("訓練時間 {:.4f} 秒".format(end - start))
    
def predict_labels(clf, features, target):
    ''' 使用模型進行預測 '''
    # 記錄預測時長
    start = time()
    y_pred = clf.predict(features)
    end = time()
    print("預測時間 in {:.4f} 秒".format(end - start))
    return f1_score(target, y_pred, pos_label=1), sum(target == y_pred) / float(len(y_pred))

def train_predict(clf, X_train, y_train, X_test, y_test):
    ''' 訓練並評估模型 '''
    # Indicate the classifier and the training set size
    print("訓練 {} 模型,樣本數量 {}。".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 分數和準確率爲: {:.4f} , {:.4f}。".format(f1 , acc))

    f1, acc = predict_labels(clf, X_test, y_test)
    print("測試集上的 F1 分數和準確率爲: {:.4f} , {:.4f}。".format(f1 , acc))
4.3.2 分別初始化,訓練和評估模型
import xgboost as xgb
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC

# 分別創建三個模型
clf_A = LogisticRegression(random_state = 42)
clf_B = SVC(random_state = 42, kernel='rbf',gamma='auto')
clf_C = xgb.XGBClassifier(seed = 42)

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('')
訓練 LogisticRegression 模型,樣本數量 3675。
訓練時間 0.0050 秒
預測時間 in 0.0010 秒
訓練集上的 F1 分數和準確率爲: 0.6232 , 0.6648。
預測時間 in 0.0010 秒
測試集上的 F1 分數和準確率爲: 0.6120 , 0.6457。

訓練 SVC 模型,樣本數量 3675。
訓練時間 0.5755 秒
預測時間 in 0.3620 秒
訓練集上的 F1 分數和準確率爲: 0.6152 , 0.6746。
預測時間 in 0.1486 秒
測試集上的 F1 分數和準確率爲: 0.5858 , 0.6400.

訓練 XGBClassifier 模型,樣本數量 3675. . .
訓練時間 0.4079 秒
預測時間 in 0.0110 秒
訓練集上的 F1 分數和準確率爲: 0.6652 , 0.7067.
預測時間 in 0.0060 秒
測試集上的 F1 分數和準確率爲: 0.5844 , 0.6279。

經過運行結果,咱們發現:

  • 在訓練時間上,邏輯迴歸耗時最短,XGBoost 耗時最長,爲 2 秒多。
  • 在預測時間上,邏輯迴歸耗時最短,支持向量機耗時最長。
  • 在訓練集上 F1 分數方面,XGBoost 得分最高,支持向量機得分最低,可是差距不是很大。
  • 在訓練集上準確率方面分析,XGBoost得分最高,邏輯迴歸最低。
  • 在測試集上 F1 分數方面分析,邏輯迴歸的最好,其他兩個模型基本相等,相對較低。
  • 在測試集上準確率方面分析,邏輯迴歸支持向量機** 2 個模型基本相等,稍微比 XBGoost 高一點。

4.4 超參數調整

咱們使用 sklearn 的 GridSearch 來進行超參數調參。

from sklearn.model_selection import GridSearchCV
from sklearn.metrics import make_scorer
import xgboost as xgb

# 設置想要自動調參的參數
parameters = { 'n_estimators':[90,100,110],
               'max_depth': [5,6,7],
             }
# 初始化模型
clf = xgb.XGBClassifier(seed=42)
f1_scorer = make_scorer(f1_score,pos_label=1)
# 使用 grdi search 自動調參
grid_obj = GridSearchCV(clf,
                        scoring=f1_scorer,
                        param_grid=parameters,
                        cv=5)
grid_obj = grid_obj.fit(X_train,y_train)
# 獲得最佳的模型
clf = grid_obj.best_estimator_
# print(clf)
# 查看最終的模型效果
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))
預測時間 in 0.0368 秒
F1 score and accuracy score for training set: 0.7991 , 0.8201。
預測時間 in 0.0149 秒
F1 score and accuracy score for test set: 0.5702 , 0.6133。

4.5 保存模型和加載模型

而後咱們能夠把模型保存下來,以供之後使用。

import joblib
#保存模型
joblib.dump(clf, 'xgboost_model.model')
#讀取模型
xgb = joblib.load('xgboost_model.model')
# 而後咱們嘗試來進行一個預測
sample1 = X_test.sample(n=5, random_state=2)
y_test_1 = y_test.sample(n=5, random_state=2)
print(sample1)
# 進行預測
y_pred = xgb.predict(sample1)
print("實際值:%s \n預測值:%s"%(y_test_1.values,y_pred))
HTGD      ATGD  HM1_D  HM1_L  HM1_W  AM1_D  AM1_L  AM1_W  HM2_D  \
70    0.189646 -1.088217      0      0      1      0      1      0      0   
5529 -0.668332 -0.901190      0      1      0      1      0      0      0   
4297 -0.702311 -0.136082      0      1      0      0      1      0      0   
5230 -0.654740 -1.302447      0      0      1      0      1      0      0   
1307  1.438387 -0.269101      1      0      0      0      0      1      0   

      HM2_L  HM2_W  AM2_D  AM2_L  AM2_W  HM3_D  HM3_L  HM3_W  AM3_D  AM3_L  \
70        0      1      0      1      0      1      0      0      1      0   
5529      0      1      0      1      0      1      0      0      0      1   
4297      1      0      0      1      0      1      0      0      1      0   
5230      1      0      1      0      0      0      1      0      0      1   
1307      0      1      0      0      1      1      0      0      0      0   

      AM3_W  
70        0  
5529      0  
4297      0  
5230      0  
1307      1  
實際值:[0 0 1 1 1]
預測值:[1 0 1 1 1]

經過以上,咱們從 test 數據集中隨機挑選5個,預測值跟實際值相同的有 4 個,考慮到咱們準確率不高,可以獲得這個結果來講仍是比較幸運的。

5. 總結與展望:

經過該文章,您應該初步熟悉數據挖掘與分析和機器學習的流程,瞭解監督學習中邏輯迴歸模型,支持向量機模型和 XGBoost 模型的基本思想,熟悉機器學習庫 Pandas、Scikit-Learn、Searbon、XGBoost、joblib 的基本使用。須要注意的是:若是您未使用 MO 平臺,可能還須要安裝 XGBoost、SKlearn 等第三方庫,目前 Mo 平臺已安裝經常使用的機器學習相關的庫,能夠省去您安裝開發平臺的時間;另外,數據集也已在平臺公開,能夠直接導入。目前對於主流的機器學習庫的相關資料,咱們總結以下:

目前咱們模型的準確率還不是很高,還能夠進一步的改進咱們的模型,這裏咱們提供一下解決思路:

  • 一、獲取更多的數據或者使用更多的特徵;
  • 二、對數據集進行交叉驗證方式處理;
  • 三、能夠對以上模型深刻處理或者採用模型融合技術等;
  • 四、分析參賽球員的踢球技術信息和健康信息等;
  • 五、採用更全面的模型評估機制,目前咱們僅僅考慮了準確率和 F1 分數,能夠進一步考慮 ROCAUC** 曲線等。

咱們已經將以上內容整理成機器學習實戰相關課程,您能夠在網站 訓練營實戰教程 中選擇 監督學習-分析和預測足球比賽結果 進行實操學習。您在學習的過程當中,發現咱們的錯誤或者遇到難題,能夠隨時聯繫咱們。


Mo(網址:momodel.cn)是一個支持 Python 的人工智能在線建模平臺,能幫助你快速開發、訓練並部署模型。


Mo 人工智能俱樂部 是由網站的研發與產品設計團隊發起、致力於下降人工智能開發與使用門檻的俱樂部。團隊具有大數據處理分析、可視化與數據建模經驗,已承擔多領域智能項目,具有從底層到前端的全線設計開發能力。主要研究方向爲大數據管理分析與人工智能技術,並以此來促進數據驅動的科學研究。

目前俱樂部每週六在杭州舉辦以機器學習爲主題的線下技術沙龍活動,不按期進行論文分享與學術交流。但願能匯聚來自各行各業對人工智能感興趣的朋友,不斷交流共同成長,推進人工智能民主化、應用普及化。

enter image description here

相關文章
相關標籤/搜索