前言
本次練習(xí)還是sofa的下一個(gè)競(jìng)賽,主要內(nèi)容是特征工程,調(diào)參和模型選擇可參考上兩篇競(jìng)賽。(足球運(yùn)動(dòng)員身價(jià)估計(jì))
MAE越小,說明模型預(yù)測(cè)得越準(zhǔn)確。本文最終處理結(jié)果為MAC:19.0208,排名43。
1 數(shù)據(jù)分析
訓(xùn)練集中共有10441條樣本,預(yù)測(cè)集中有7000條樣本。每條樣本代表一位球員,數(shù)據(jù)中每個(gè)球員有63項(xiàng)屬性。數(shù)據(jù)中含有缺失值。
1.1 單特征處理
1.1.1 補(bǔ)充缺失值
print(train.isnull().any())# 缺失值檢查
發(fā)現(xiàn)位置(rw-gk)的值都有缺失,原因是守門員僅有自己gk的值,沒有其他位置的值,非守門員反之。
train = train.loc[:].fillna(0)
test = test.loc[:].fillna(0)
我們可以補(bǔ)充值為0,方便后面計(jì)算。
1.1.2 數(shù)據(jù)量化
①存在像work_rate_att和work_rate_def有序多分類變量
'''字符串特征轉(zhuǎn)換為數(shù)字特征'''
work_rate_columns = ['work_rate_att', 'work_rate_def']
for i in work_rate_columns:
train[i] = pd.factorize(train[i])[0].astype(np.uint16)
for i in work_rate_columns:
test[i] = pd.factorize(test[i])[0].astype(np.uint16)
認(rèn)為低中高又一定的順序,按0、1、2賦值
②還有birth_date這種日期格式
'''生日轉(zhuǎn)換為年齡'''
today = date.today().year
train['birth_date'] = pd.to_datetime(train['birth_date'])
train['age'] = today - train.birth_date.dt.year
test['birth_date'] = pd.to_datetime(test['birth_date'])
test['age'] = today - test.birth_date.dt.year
train = train.drop('birth_date', axis=1)
test = test.drop('birth_date', axis=1)
轉(zhuǎn)換為年齡方便計(jì)算。
1.2 降維處理
觀察訓(xùn)練集csv文件和使用數(shù)據(jù)透視表,比較守門員和非守門員區(qū)別。



圖1可看出守門員和非守門員的y分布形狀相似,但守門員數(shù)量比非守門員少。
圖2和圖3,average_ngk來自crossing到sliding_tackle的平均數(shù),average_gk來自gk_diving到gk_reflexes的平均數(shù)。這是因?yàn)閺臄?shù)據(jù)直接觀察可以發(fā)現(xiàn)守門員有些數(shù)據(jù)(gk_diving到gk_reflexes的)普遍較高,而有些數(shù)據(jù)(crossing到sliding_tackle)普遍偏低,非守門員則相反,于是嘗試計(jì)算出平均值并比較,發(fā)現(xiàn)確實(shí)如此。
'''守門員專屬能力的平均值'''
gk_columns = ['gk_diving', 'gk_diving', 'gk_handling', 'gk_kicking', 'gk_positioning', 'gk_reflexes']
train['average_gk'] = np.mean(list(train[i] for i in gk_columns), 0)
train['average_gk'] = np.around(train['average_gk'])
test['average_gk'] = np.mean(list(test[i] for i in gk_columns), 0)
test['average_gk'] = np.around(test['average_gk'])
train = train.drop(gk_columns, axis=1)
test = test.drop(gk_columns, axis=1)
'''非守門員專屬能力的平均值'''
ngk_columns = ['crossing', 'finishing', 'heading_accuracy', 'short_passing', 'volleys', 'dribbling',
'curve', 'free_kick_accuracy', 'long_passing', 'ball_control', 'acceleration', 'sprint_speed',
'agility', 'reactions', 'balance', 'shot_power', 'jumping', 'stamina', 'strength',
'long_shots', 'aggression', 'interceptions', 'positioning', 'vision', 'penalties',
'marking', 'standing_tackle', 'sliding_tackle']
train['average_ngk'] = np.mean(list(train[i] for i in ngk_columns), 0)
train['average_ngk'] = np.around(train['average_ngk'])
test['average_ngk'] = np.mean(list(test[i] for i in ngk_columns), 0)
test['average_ngk'] = np.around(test['average_ngk'])
train = train.drop(ngk_columns, axis=1)
test = test.drop(ngk_columns, axis=1)
以上特征合成我們還進(jìn)行一項(xiàng)處理:將處理前的特征刪去。
特征合成視為降維處理,包含處理前的特征信息,而且還可以減少計(jì)算開銷。
1.3 特征選擇
模型選擇xgboost回歸模型,原因參考《競(jìng)賽練習(xí)—公共自行車使用量預(yù)測(cè)》這篇。同時(shí)還有個(gè)原因,xgboost訓(xùn)練過程中自動(dòng)進(jìn)行了特征選擇,我們可以通過重要性排序觀察,在調(diào)參前先篩出一些低重要性特征。(用xgboost模型對(duì)特征進(jìn)行重要性排序)
'''劃分守門員和非守門員訓(xùn)練集'''
gk_train = data[data['is_gk'] ==1]
y_gk_train = gk_train.pop('y')
ngk_train = data[data['is_gk'] ==0]
y_ngk_train = ngk_train.pop('y')
y_train = data.pop('y')
'''使用xgboost查看特征重要性'''
for x, y in [(data, y_train), (gk_train, y_gk_train), (ngk_train, y_ngk_train)]:
model = XGBRegressor().fit(x, y)
plot_importance(model, max_num_features=25, height=0.5)
pyplot.show()



認(rèn)為守門員和非守門員之間有一定差別,選擇將兩者分開特征選擇,分開訓(xùn)練。最終都選取重要性超過10的特征。
gk_columns=['age','potential','best_pos','club','sho','average_gk','dri','pac','phy','def','BMI','pas']
ngk_columns=['best_pos','potential','age','sho','cam','def','club','dri',
'international_reputation','pas','average_ngk','phy','nationality']
在考慮保留特征個(gè)數(shù)為多少時(shí)踩了一個(gè)坑,剛開始是采用以下代碼來得到特征的得分以及篩選特征(代碼來自:https://github.com/ymGdragon/Fragmentary_MachineLearning_Algorithm/blob/master/xgboost-23.py)
#篩選出重要的特征 (此為函數(shù)有關(guān)特征選擇的部分,alg表示使用的算法)
feat_imp = pd.Series(alg.get_booster().get_fscore()).sort_values(ascending=False)
feat_sel = list(feat_imp.index)
feat_val = list(feat_imp.values)
featur = []
for i in range(len(feat_sel)):
featur.append([cols[int(feat_sel[i][1:])], feat_val[i]])
print('--------------------所有特征及其得分--------------------\n', featur)
feat_sel2 = list(feat_imp[feat_imp.values > target].index)
featur2 = []
for i in range(len(feat_sel2)):
featur2.append(cols[int(feat_sel2[i][1:])])
return featur2
通過運(yùn)行得到特征的得分,同時(shí)考慮到前兩次競(jìng)賽嘗試刪去特征后,信息不足,不利于模型較好地進(jìn)行預(yù)測(cè),故最終決定對(duì)守門員和非守門員數(shù)據(jù)的特征保留個(gè)數(shù)均在25個(gè)左右,但模型之后經(jīng)過多次調(diào)參,模型的預(yù)測(cè)能力仍然較差,跟同學(xué)討論后,采用以下代碼來進(jìn)行特征選擇
#特征選擇
def Select_feature(data):
#劃分守門員和非守門員訓(xùn)練集
gk_train = data[data['is_gk'] == 1]
y_gk_train = gk_train.pop('y')
ngk_train = data[data['is_gk'] == 0]
y_ngk_train = ngk_train.pop('y')
y_train = data.pop('y')
#使用xgboost查看特征重要性
for x, y in [(data, y_train), (gk_train, y_gk_train), (ngk_train, y_ngk_train)]:
model = XGBRegressor().fit(x, y)
plot_importance(model, max_num_features=25, height=0.5)
plt.show()
gk_columns=['club','potential','age','league','gk','pac','nationality','dri','sho','BMI','reactions','pas']
ngk_columns=['potential','club', 'age','league','best_pos', 'nationality','BMI','vision','heading_accuracy','aggression','crossing','free_kick_accuracy','jumping','pac','long_shots']
return gk_train,y_gk_train,ngk_train,y_ngk_train,gk_columns,ngk_columns
運(yùn)行結(jié)果如下:


最終決定保留的特征如下,保留的特征個(gè)數(shù)均在15個(gè)左右
#守門員保留的特征
gk_columns=['club','potential','age','league','gk','pac','nationality','dri','sho','BMI','reactions','pas']
#非守門員保留的特征
ngk_columns=['potential','club', 'age','league','best_pos', 'nationality','BMI','vision','heading_accuracy','aggression','crossing','free_kick_accuracy','jumping','pac','long_shots']
根據(jù)以上篩選出的特征進(jìn)行多次調(diào)參,排名為:97, 模型預(yù)測(cè)結(jié)果的MAE為:21.6183
思考:由于分別根據(jù)以上兩種方法進(jìn)行特征篩選和調(diào)參后,得到的模型的預(yù)測(cè)能力相差較大,剛開始以為是這兩種方法賦予特征重要性得分的原理不同,搜索后發(fā)現(xiàn)這兩種方法大致是一樣的,只是對(duì)特征重要性的輸出形式不同。(參考:知乎)
要做屬性重要性排名,可以用這個(gè),輸出的是一個(gè)圖形:xgb.plot_importance(xgboost_model)
如果想輸出數(shù)值,可以這樣寫:pd.Series(xgboost_model.get_fscore()).sort_values(ascending=False)
既然兩段代碼大致是一個(gè)意思的話,那么造成兩次結(jié)果差距較大的原因主要是保留的特征個(gè)數(shù)不同,那在特征處理過程中應(yīng)該如何確定保留的特征個(gè)數(shù)呢?如果只是一次一次去嘗試的話,比較花費(fèi)時(shí)間。在網(wǎng)上搜了一下這個(gè)問題,發(fā)現(xiàn)一個(gè)不錯(cuò)的方法:(參考:特征工程-特征選擇、特征表達(dá)、特征預(yù)處理)
第一步是找到該領(lǐng)域懂業(yè)務(wù)的專家,讓他們給一些建議。
比如:我們需要解決一個(gè)藥品療效的分類問題,那么先找到領(lǐng)域?qū)<遥蛩麄冏稍兡男┮蛩兀ㄌ卣鳎?huì)對(duì)該藥品的療效產(chǎn)生影響,較大影響的和較小影響的都要。這些特征就是我們的特征的第一候選集。這個(gè)特征集合有時(shí)候也可能很大,在嘗試降維之前,我們有必要用特征工程的方法去選擇出較重要的特征結(jié)合,這些方法不會(huì)用到領(lǐng)域知識(shí),而僅僅是統(tǒng)計(jì)學(xué)的方法。
之后可以采用合適的特征選擇方法來選擇特征
[特征選擇方法了解:特征工程-特征選擇、特征表達(dá)、特征預(yù)處理]
所以感覺下次在進(jìn)行特征處理前,要先了解一下要預(yù)測(cè)的領(lǐng)域的一些知識(shí),大體確認(rèn)一下哪些特征比較關(guān)鍵,再進(jìn)行特征的構(gòu)造和特征的刪除會(huì)好一點(diǎn)。(像這次在決定保留特征個(gè)數(shù)的時(shí)候,考慮了前兩次sofasofa競(jìng)賽的經(jīng)驗(yàn)是不太合理的,因?yàn)轭A(yù)測(cè)的領(lǐng)域不同。)
2 訓(xùn)練模型
觀察訓(xùn)練集還可以發(fā)現(xiàn)y的預(yù)測(cè)值都在6以上,所以可以將預(yù)測(cè)結(jié)果設(shè)為6以上。
submit['y'] = np.array(test['pred'])
submit['y'] = submit['y'].map(lambda x:6 if x<6 else x)
submit.to_csv('prediction.csv', index=False)
使用網(wǎng)格搜索GridSearchCV進(jìn)行調(diào)參
調(diào)參前:
26.538——最終成績
21.294521517653838——gk_clf
27.97521351856118——ngk_clf
單獨(dú)gk_clf調(diào)參:
25.9657——最終成績
15.974057084529958——gk_clf
27.97521351856118——ngk_clf
單獨(dú)ngk_clf調(diào)參:
19.5931——最終成績
21.294521517653838——gk_clf
20.232612056551094——ngk_clf
同時(shí)調(diào)參:
19.0208——最終成績
15.974057084529958——gk_clf
20.232612056551094——ngk_clf
兩者的最佳參數(shù):
gk_params={'n_estimators':800, 'learning_rate':0.01, 'max_depth':8, 'min_child_weight':1,
'gamma':0.3, 'colsample_bytree':0.9, 'subsample':0.8, 'reg_alpha':0.1, 'reg_lambda':3,
'criterion':'entropy', 'objective':'reg:squarederror'}
ngk_params={'n_estimators':400, 'learning_rate':0.1, 'max_depth':8, 'min_child_weight':4,
'gamma':0.2, 'colsample_bytree':0.9, 'subsample':0.9, 'reg_alpha':0.05, 'reg_lambda':3,
'criterion':'entropy', 'objective':'reg:squarederror'}
發(fā)現(xiàn)gk_clf調(diào)參后守門員的MAE降低很大,但最終成績提升不多。ngk_clf調(diào)參后非守門員的MAE降低不大,但最終成績提升較為同步。原因可能是測(cè)試集和訓(xùn)練集一樣守門員的數(shù)據(jù)較少,所以守門員預(yù)測(cè)提升帶來成績提升不多。
小結(jié)
①本次是回歸問題,數(shù)據(jù)最大問題就是特征較多。
②處理以上問題,我們進(jìn)行特征工程,包含單特征處理、多特征處理中的降維和特征選擇。(使用sklearn做單機(jī)特征工程)
③對(duì)表現(xiàn)出明顯不同的數(shù)據(jù)集分開訓(xùn)練。
④使用xgboost的重要性特征排序選擇特征,在回歸到xgboost訓(xùn)練,可以大幅度降低耗時(shí)。