Scikit中的特征選擇,XGboost進行回歸預測,模型優化的實戰
前天偶然在一個網站上看到一個數據分析的比賽(sofasofa),自己雖然學習一些關於機器學習的內容,但是並沒有在比賽中實踐過,於是我帶着一種好奇心參加了這次比賽。
賽題:足球運動員身價估計
比賽概述
本比賽為個人練習賽,主要針對於於數據新人進行自我練習、自我提高,與大家切磋。
練習賽時限:2018-03-05 至 2020-03-05
任務類型:回歸
背景介紹: 每個足球運動員在轉會市場都有各自的價碼。本次數據練習的目的是根據球員的各項信息和能力值來預測該球員的市場價值。

根據以上描述,我們很容易可以判斷出這是一個回歸預測類的問題。當然,要想進行預測,我們首先要做的就是先看看數據的格式以及內容(由於參數太多,我就不一一列舉了,大家可以直接去網上看,下面我簡單貼個圖):

簡單了解了數據的格式以及大小以后,由於沒有實踐經驗,我就憑自己的感覺,單純的認為一下幾個字段可能是最重要的:
| 字段 | 含義 |
|---|---|
| club | 該球員所屬的俱樂部。該信息已經被編碼。 |
| league | 該球員所在的聯賽。已被編碼。 |
| potential | 球員的潛力。數值變量。 |
| international_reputation | 國際知名度。數值變量。 |
巧合的是剛好這些字段都沒有缺失值,我很開心啊,心想着可以直接利用 XGBoost 模型進行預測了。具體 XGBoost 的使用方法,可以參考:XGBoost 以及官方文檔 XGBoost Parameters。說來就來,我開始了 coding 工作,下面就貼出我的第一版代碼:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : soccer_value.py
# @Author: Huangqinjian
# @Date : 2018/3/22
# @Desc :
import pandas as pd
import matplotlib.pyplot as plt
import xgboost as xgb
import numpy as np
from xgboost import plot_importance
from sklearn.preprocessing import Imputer
def loadDataset(filePath):
df = pd.read_csv(filepath_or_buffer=filePath)
return df
def featureSet(data):
data_num = len(data)
XList = []
for row in range(0, data_num):
tmp_list = []
tmp_list.append(data.iloc[row]['club'])
tmp_list.append(data.iloc[row]['league'])
tmp_list.append(data.iloc[row]['potential'])
tmp_list.append(data.iloc[row]['international_reputation'])
XList.append(tmp_list)
yList = data.y.values
return XList, yList
def loadTestData(filePath):
data = pd.read_csv(filepath_or_buffer=filePath)
data_num = len(data)
XList = []
for row in range(0, data_num):
tmp_list = []
tmp_list.append(data.iloc[row]['club'])
tmp_list.append(data.iloc[row]['league'])
tmp_list.append(data.iloc[row]['potential'])
tmp_list.append(data.iloc[row]['international_reputation'])
XList.append(tmp_list)
return XList
def trainandTest(X_train, y_train, X_test):
# XGBoost訓練過程
model = xgb.XGBRegressor(max_depth=5, learning_rate=0.1, n_estimators=160, silent=False, objective='reg:gamma')
model.fit(X_train, y_train)
# 對測試集進行預測
ans = model.predict(X_test)
ans_len = len(ans)
id_list = np.arange(10441, 17441)
data_arr = []
for row in range(0, ans_len):
data_arr.append([int(id_list[row]), ans[row]])
np_data = np.array(data_arr)
# 寫入文件
pd_data = pd.DataFrame(np_data, columns=['id', 'y'])
# print(pd_data)
pd_data.to_csv('submit.csv', index=None)
# 顯示重要特征
# plot_importance(model)
# plt.show()
if __name__ == '__main__':
trainFilePath = 'dataset/soccer/train.csv'
testFilePath = 'dataset/soccer/test.csv'
data = loadDataset(trainFilePath)
X_train, y_train = featureSet(data)
X_test = loadTestData(testFilePath)
trainandTest(X_train, y_train, X_test)
然后我就把得到的結果文件 submit.csv 提交到網站上,看了結果,MAE 為 106.6977,排名 24/28, 很不理想。不過這也在預料之中,因為我基本沒有進行特征處理。
我當然不滿意啦,一直想着怎么能提高准確率呢?后來就想到了可以利用一下 scikit 這個庫啊!在 scikit 中包含了一個特征選擇的模塊 sklearn.feature_selection,而在這個模塊下面有以下幾個方法:
- Removing features with low variance(剔除低方差的特征)
- Univariate feature selection(單變量特征選擇)
- Recursive feature elimination(遞歸功能消除)
- Feature selection using SelectFromModel(使用 SelectFromModel 進行特征選擇)
我首先想到的是利用單變量特征選擇的方法選出幾個跟預測結果最相關的特征。根據官方文檔,有以下幾種得分函數來檢驗變量之間的依賴程度:
- 對於回歸問題: f_regression, mutual_info_regression
- 對於分類問題: chi2, f_classif, mutual_info_classif
由於這個比賽是一個回歸預測問題,所以我選擇了 f_regression 這個得分函數(剛開始我沒有注意,錯誤使用了分類問題中的得分函數 chi2,導致程序一直報錯!心很累~)
f_regression 的參數:
sklearn.feature_selection.f_regression(X, y, center=True)
X:一個多維數組,大小為 (n_samples, n_features),即行數為訓練樣本的大小,列數為特征的個數
y:一個一維數組,長度為訓練樣本的大小
return:返回值為特征的 F 值以及 p 值
不過在進行這個操作之前,我們還有一個重大的任務要完成,那就是對於空值的處理!幸運的是 scikit 中也有專門的模塊可以處理這個問題:Imputation of missing values
sklearn.preprocessing.Imputer 的參數:
sklearn.preprocessing.Imputer(missing_values=’NaN’, strategy=’mean’, axis=0, verbose=0, copy=True)
其中 strategy 代表對於空值的填充策略(默認為 mean,即取所在列的平均數進行填充):
- strategy=’median’,代表取所在列的中位數進行填充
- strategy=’most_frequent’, 代表取所在列的眾數進行填充
axis 默認值為 0:
- axis=0,代表按列進行填充
- axis=1,代表按行進行填充
其他具體參數可以參考:sklearn.preprocessing.Imputer
根據以上,我對數據進行了一些處理:
from sklearn.feature_selection import f_regression
from sklearn.preprocessing import Imputer
imputer = Imputer(missing_values='NaN', strategy='mean', axis=0)
imputer.fit(data.loc[:, 'rw':'lb'])
x_new = imputer.transform(data.loc[:, 'rw':'lb'])
data_num = len(x_new)
XList = []
yList = []
for row in range(0, data_num):
tmp_list = []
tmp_list.append(x_new[row][0])
tmp_list.append(x_new[row][1])
tmp_list.append(x_new[row][2])
tmp_list.append(x_new[row][3])
tmp_list.append(x_new[row][4])
tmp_list.append(x_new[row][5])
tmp_list.append(x_new[row][6])
tmp_list.append(x_new[row][7])
tmp_list.append(x_new[row][8])
tmp_list.append(x_new[row][9])
XList.append(tmp_list)
yList.append(data.iloc[row]['y'])
F = f_regression(XList, yList)
print(len(F))
print(F)
測試結果:
(array([2531.07587725, 1166.63303449, 2891.97789543, 2531.07587725,
2786.75491791, 2891.62686404, 3682.42649607, 1394.46743196,
531.08672792, 1166.63303449]), array([0.00000000e+000, 1.74675421e-242, 0.00000000e+000, 0.00000000e+000,
0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 1.37584507e-286,
1.15614152e-114, 1.74675421e-242]))
根據以上得到的結果,我選取了 rw,st,lw,cf,cam,cm(選取 F 值相對大的) 幾個特征加入模型之中。以下是我改進后的代碼:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : soccer_value.py
# @Author: Huangqinjian
# @Date : 2018/3/22
# @Desc :
import pandas as pd
import matplotlib.pyplot as plt
import xgboost as xgb
import numpy as np
from xgboost import plot_importance
from sklearn.preprocessing import Imputer
def loadDataset(filePath):
df = pd.read_csv(filepath_or_buffer=filePath)
return df
def featureSet(data):
imputer = Imputer(missing_values='NaN', strategy='mean', axis=0)
imputer.fit(data.loc[:, ['rw', 'st', 'lw', 'cf', 'cam', 'cm']])
x_new = imputer.transform(data.loc[:, ['rw', 'st', 'lw', 'cf', 'cam', 'cm']])
data_num = len(data)
XList = []
for row in range(0, data_num):
tmp_list = []
tmp_list.append(data.iloc[row]['club'])
tmp_list.append(data.iloc[row]['league'])
tmp_list.append(data.iloc[row]['potential'])
tmp_list.append(data.iloc[row]['international_reputation'])
tmp_list.append(data.iloc[row]['pac'])
tmp_list.append(data.iloc[row]['sho'])
tmp_list.append(data.iloc[row]['pas'])
tmp_list.append(data.iloc[row]['dri'])
tmp_list.append(data.iloc[row]['def'])
tmp_list.append(data.iloc[row]['phy'])
tmp_list.append(data.iloc[row]['skill_moves'])
tmp_list.append(x_new[row][0])
tmp_list.append(x_new[row][1])
tmp_list.append(x_new[row][2])
tmp_list.append(x_new[row][3])
tmp_list.append(x_new[row][4])
tmp_list.append(x_new[row][5])
XList.append(tmp_list)
yList = data.y.values
return XList, yList
def loadTestData(filePath):
data = pd.read_csv(filepath_or_buffer=filePath)
imputer = Imputer(missing_values='NaN', strategy='mean', axis=0)
imputer.fit(data.loc[:, ['rw', 'st', 'lw', 'cf', 'cam', 'cm']])
x_new = imputer.transform(data.loc[:, ['rw', 'st', 'lw', 'cf', 'cam', 'cm']])
data_num = len(data)
XList = []
for row in range(0, data_num):
tmp_list = []
tmp_list.append(data.iloc[row]['club'])
tmp_list.append(data.iloc[row]['league'])
tmp_list.append(data.iloc[row]['potential'])
tmp_list.append(data.iloc[row]['international_reputation'])
tmp_list.append(data.iloc[row]['pac'])
tmp_list.append(data.iloc[row]['sho'])
tmp_list.append(data.iloc[row]['pas'])
tmp_list.append(data.iloc[row]['dri'])
tmp_list.append(data.iloc[row]['def'])
tmp_list.append(data.iloc[row]['phy'])
tmp_list.append(data.iloc[row]['skill_moves'])
tmp_list.append(x_new[row][0])
tmp_list.append(x_new[row][1])
tmp_list.append(x_new[row][2])
tmp_list.append(x_new[row][3])
tmp_list.append(x_new[row][4])
tmp_list.append(x_new[row][5])
XList.append(tmp_list)
return XList
def trainandTest(X_train, y_train, X_test):
# XGBoost訓練過程
model = xgb.XGBRegressor(max_depth=5, learning_rate=0.1, n_estimators=160, silent=False, objective='reg:gamma')
model.fit(X_train, y_train)
# 對測試集進行預測
ans = model.predict(X_test)
ans_len = len(ans)
id_list = np.arange(10441, 17441)
data_arr = []
for row in range(0, ans_len):
data_arr.append([int(id_list[row]), ans[row]])
np_data = np.array(data_arr)
# 寫入文件
pd_data = pd.DataFrame(np_data, columns=['id', 'y'])
# print(pd_data)
pd_data.to_csv('submit.csv', index=None)
# 顯示重要特征
# plot_importance(model)
# plt.show()
if __name__ == '__main__':
trainFilePath = 'dataset/soccer/train.csv'
testFilePath = 'dataset/soccer/test.csv'
data = loadDataset(trainFilePath)
X_train, y_train = featureSet(data)
X_test = loadTestData(testFilePath)
trainandTest(X_train, y_train, X_test)
再次提交,這次 MAE 為 42.1227,排名 16/28。雖然提升了不少,不過距離第一名還是有差距,仍需努力。
接下來,我們來處理一下下面這個字段:

由於這兩個字段是標簽,需要進行處理以后(標簽標准化)才用到模型中。我們要用到的函數是 sklearn.preprocessing.LabelEncoder:
le = preprocessing.LabelEncoder()
le.fit(['Low', 'Medium', 'High'])
att_label = le.transform(data.work_rate_att.values)
# print(att_label)
def_label = le.transform(data.work_rate_def.values)
# print(def_label)
當然你也可以使用 pandas 直接來處理離散型特征變量,具體內容可以參考:pandas 使用 get_dummies 進行 one-hot 編碼。順帶提一句,scikit 中也有一個方法可以來處理,可參考:sklearn.preprocessing.OneHotEncoder。
調整后的代碼:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : soccer_value.py
# @Author: Huangqinjian
# @Date : 2018/3/22
# @Desc :
import pandas as pd
import matplotlib.pyplot as plt
import xgboost as xgb
from sklearn import preprocessing
import numpy as np
from xgboost import plot_importance
from sklearn.preprocessing import Imputer
from sklearn.cross_validation import train_test_split
def featureSet(data):
imputer = Imputer(missing_values='NaN', strategy='mean', axis=0)
imputer.fit(data.loc[:, ['rw', 'st', 'lw', 'cf', 'cam', 'cm']])
x_new = imputer.transform(data.loc[:, ['rw', 'st', 'lw', 'cf', 'cam', 'cm']])
le = preprocessing.LabelEncoder()
le.fit(['Low', 'Medium', 'High'])
att_label = le.transform(data.work_rate_att.values)
# print(att_label)
def_label = le.transform(data.work_rate_def.values)
# print(def_label)
data_num = len(data)
XList = []
for row in range(0, data_num):
tmp_list = []
tmp_list.append(data.iloc[row]['club'])
tmp_list.append(data.iloc[row]['league'])
tmp_list.append(data.iloc[row]['potential'])
tmp_list.append(data.iloc[row]['international_reputation'])
tmp_list.append(data.iloc[row]['pac'])
tmp_list.append(data.iloc[row]['sho'])
tmp_list.append(data.iloc[row]['pas'])
tmp_list.append(data.iloc[row]['dri'])
tmp_list.append(data.iloc[row]['def'])
tmp_list.append(data.iloc[row]['phy'])
tmp_list.append(data.iloc[row]['skill_moves'])
tmp_list.append(x_new[row][0])
tmp_list.append(x_new[row][1])
tmp_list.append(x_new[row][2])
tmp_list.append(x_new[row][3])
tmp_list.append(x_new[row][4])
tmp_list.append(x_new[row][5])
tmp_list.append(att_label[row])
tmp_list.append(def_label[row])
XList.append(tmp_list)
yList = data.y.values
return XList, yList
def loadTestData(filePath):
data = pd.read_csv(filepath_or_buffer=filePath)
imputer = Imputer(missing_values='NaN', strategy='mean', axis=0)
imputer.fit(data.loc[:, ['rw', 'st', 'lw', 'cf', 'cam', 'cm']])
x_new = imputer.transform(data.loc[:, ['rw', 'st', 'lw', 'cf', 'cam', 'cm']])
le = preprocessing.LabelEncoder()
le.fit(['Low', 'Medium', 'High'])
att_label = le.transform(data.work_rate_att.values)
# print(att_label)
def_label = le.transform(data.work_rate_def.values)
# print(def_label)
data_num = len(data)
XList = []
for row in range(0, data_num):
tmp_list = []
tmp_list.append(data.iloc[row]['club'])
tmp_list.append(data.iloc[row]['league'])
tmp_list.append(data.iloc[row]['potential'])
tmp_list.append(data.iloc[row]['international_reputation'])
tmp_list.append(data.iloc[row]['pac'])
tmp_list.append(data.iloc[row]['sho'])
tmp_list.append(data.iloc[row]['pas'])
tmp_list.append(data.iloc[row]['dri'])
tmp_list.append(data.iloc[row]['def'])
tmp_list.append(data.iloc[row]['phy'])
tmp_list.append(data.iloc[row]['skill_moves'])
tmp_list.append(x_new[row][0])
tmp_list.append(x_new[row][1])
tmp_list.append(x_new[row][2])
tmp_list.append(x_new[row][3])
tmp_list.append(x_new[row][4])
tmp_list.append(x_new[row][5])
tmp_list.append(att_label[row])
tmp_list.append(def_label[row])
XList.append(tmp_list)
return XList
def trainandTest(X_train, y_train, X_test):
# XGBoost訓練過程
model = xgb.XGBRegressor(max_depth=6, learning_rate=0.05, n_estimators=500, silent=False, objective='reg:gamma')
model.fit(X_train, y_train)
# 對測試集進行預測
ans = model.predict(X_test)
ans_len = len(ans)
id_list = np.arange(10441, 17441)
data_arr = []
for row in range(0, ans_len):
data_arr.append([int(id_list[row]), ans[row]])
np_data = np.array(data_arr)
# 寫入文件
pd_data = pd.DataFrame(np_data, columns=['id', 'y'])
# print(pd_data)
pd_data.to_csv('submit.csv', index=None)
# 顯示重要特征
# plot_importance(model)
# plt.show()
if __name__ == '__main__':
trainFilePath = 'dataset/soccer/train.csv'
testFilePath = 'dataset/soccer/test.csv'
data = pd.read_csv(trainFilePath)
X_train, y_train = featureSet(data)
X_test = loadTestData(testFilePath)
trainandTest(X_train, y_train, X_test)
這次只提高到了 40.8686。暫時想不到提高的方法了,還請大神多多賜教!
