0
雷鋒網(wǎng)按:本文作者楊熹,此前剛剛參加了一次 Kaggle 比賽,文中內(nèi)容是作者根據(jù)自身參賽經(jīng)歷所做的經(jīng)驗(yàn)總結(jié)。原文載于作者個人博客,雷鋒網(wǎng)已獲授權(quán)。
Kaggle 是一個數(shù)據(jù)科學(xué)競賽的平臺,很多公司會發(fā)布一些接近真實(shí)業(yè)務(wù)的問題,吸引愛好數(shù)據(jù)科學(xué)的人來一起解決。
點(diǎn)擊導(dǎo)航欄的 competitions 可以看到有很多比賽,其中正式比賽,一般會有獎金或者工作機(jī)會,除了正式比賽還有一些為初學(xué)者提供的 playground,在這里可以先了解這個比賽,練習(xí)能力,再去參加正式比賽。
https://www.kaggle.com/competitions
以 playground 中的這個 House Prices 預(yù)測為例,
https://www.kaggle.com/c/house-prices-advanced-regression-techniques
Overview: 首先在 overview 中仔細(xì)閱讀問題的描述,這個比賽是讓我們預(yù)測房價(jià),它會給我們 79 個影響房價(jià)的變量,我們可以通過應(yīng)用 random forest,gradient boosting 等算法,來對房價(jià)進(jìn)行預(yù)測。
Data:在這里給我們提供了 train 數(shù)據(jù)集,用來訓(xùn)練模型;test 數(shù)據(jù)集,用來將訓(xùn)練好的模型應(yīng)用到這上面,進(jìn)行預(yù)測,這個結(jié)果也是要提交到系統(tǒng)進(jìn)行評價(jià)的;sample_submission 就是我們最后提交的 csv 文件中,里面的列的格式需要和這里一樣。
Kernels:可以看到一些參賽者分享的代碼。
Discussion:參賽者們可以在這里提問,分享經(jīng)驗(yàn)。
Leaderboard:就是參賽者的排行榜。
參加 kaggle 最簡單的流程就是:
● 第一步:在 Data 里面下載三個數(shù)據(jù)集,最基本的就是上面提到的三個文件,有些比賽會有附加的數(shù)據(jù)描述文件等。
● 第二步:自己在線下分析,建模,調(diào)參,把用 test 數(shù)據(jù)集預(yù)測好的結(jié)果,按照 sample_submission 的格式輸出到 csv 文件中。
● 第三步:點(diǎn)擊藍(lán)色按鈕 ’Submit Predictions’ ,把 csv 文件拖拽進(jìn)去,然后系統(tǒng)就會加載并檢驗(yàn)結(jié)果,稍等片刻后就會在 Leaderboard 上顯示當(dāng)前結(jié)果所在的排名位置。
上傳過一次結(jié)果之后,就直接加入了這場比賽。正式比賽中每個團(tuán)隊(duì)每天有 5 次的上傳機(jī)會,然后就要等 24 小時(shí)再次傳結(jié)果,playground 的是 9 次。
應(yīng)用算法解決 Kaggle 問題,一般會有以下幾個步驟:
● 識別問題
● 探索數(shù)據(jù)
● 數(shù)據(jù)預(yù)處理
● 將 train.csv 分成 train 和 valid 數(shù)據(jù)
● 構(gòu)造新的重要特征數(shù)據(jù)
● 應(yīng)用算法模型
● 優(yōu)化模型
● 選擇提取重要特征
● 再次選擇模型,進(jìn)行訓(xùn)練
● 調(diào)參
● 重復(fù)上述過程,進(jìn)一步調(diào)優(yōu)
● 預(yù)測
當(dāng)然上面是相對細(xì)的分步,如果簡化的話,是這么幾大步:
● 探索數(shù)據(jù)
● 特征工程
● 建立模型
● 調(diào)參
● 預(yù)測提交
之前寫過一篇文章,《一個框架解決幾乎所有機(jī)器學(xué)習(xí)問題》
里面的重點(diǎn)是介紹了常用算法模型一般需要調(diào)節(jié)什么參數(shù),即第四步。
還有這篇,《通過一個kaggle實(shí)例學(xué)習(xí)解決機(jī)器學(xué)習(xí)問題》
主要介紹了第三步建立模型的部分,包括 ensemble 的例子。
今天這篇文章算是一個補(bǔ)充,在觀察數(shù)據(jù)和特征構(gòu)造上學(xué)習(xí)幾種常用的方式。
以 House prices 為例,探索數(shù)據(jù)常用方法有以下 6 步。
https://www.kaggle.com/c/house-prices-advanced-regression-techniques
1. 首先,在 data_description.txt 這里有對 79 個變量含義非常詳細(xì)的描述
我們可以先通過閱讀變量含義,根據(jù)常識猜測一下,哪些變量會對預(yù)測結(jié)果有比較重要的影響。
例如:
OverallQual: Overall material and finish quality 物料和質(zhì)量應(yīng)該是很重要的組成。
GrLivArea: Above grade (ground) living area square feet 面積也是明顯的因素。
YearBuilt: Original construction date 時(shí)間也有影響。
2. 接著,對要預(yù)測的目標(biāo)數(shù)據(jù) y 有一個宏觀的把握,這里是輸出 summary,也可以用 boxplot,histogram 等形式觀察
df_train['SalePrice'].describe()
count 1460.000000
mean 180921.195890
std 79442.502883
min 34900.000000
25% 129975.000000
50% 163000.000000
75% 214000.000000
max 755000.000000
Name: SalePrice, dtype: float64
count 就是有多少行觀察記錄,另外注意一下 min 并未有小于 0 的這樣的不合理的數(shù)值。
3. 通過 Correlation matrix 觀察哪些變量會和預(yù)測目標(biāo)關(guān)系比較大,哪些變量之間會有較強(qiáng)的關(guān)聯(lián)
#correlation matrix
corrmat = df_train.corr()
f, ax = plt.subplots(figsize=(12, 9))
sns.heatmap(corrmat, vmax=.8, square=True);
我們可以看上圖的最右邊一列(也可以是下面最后一行),顏色由深到淺查看,
可以發(fā)現(xiàn) OverallQual 和 GrLivArea 的確是對目標(biāo)影響較大的因素,
另外觀察中間區(qū)域的幾個深色塊,例如 ‘TotalBsmtSF’ 和 ‘1stFlrSF’ 二者關(guān)系較強(qiáng),回看它們的定義,它們所包含的信息差不多所以才有顯示出強(qiáng)關(guān)聯(lián):
TotalBsmtSF: Total square feet of basement area
1stFlrSF: First Floor square feet
那這種時(shí)候,我們可以只取其中一個特征。
或者我們可以把與目標(biāo) ‘SalePrice’ 最緊密關(guān)聯(lián)的 10 個變量的關(guān)聯(lián)度打印出來:
#saleprice correlation matrix
k = 10 #number of variables for heatmap
cols = corrmat.nlargest(k, 'SalePrice')['SalePrice'].index
cm = np.corrcoef(df_train[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()
通過這些數(shù)值,我們再一一觀察變量含義,判斷一下是否可以把其中某些變量刪除。
4. 接下來看 missing value
#missing data
total = df_train.isnull().sum().sort_values(ascending=False)
percent = (df_train.isnull().sum()/df_train.isnull().count()).sort_values(ascending=False)
missing_data = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])
missing_data.head(20)
先把每個變量的 NaN 記錄個數(shù)求和算出來,再把所占的比例計(jì)算一下。
對于占比例太大的變量,例如超過了 15%,就看看它的含義,如果不是很重要,這種數(shù)據(jù)是可以刪掉的。
對于剩下的,再一個一個查看變量的含義,及比例,判斷是否可以刪掉。
最后一個變量只有一條是 missing 的,那么就可以只刪掉這一個記錄。
此外,我們還可以通過補(bǔ)充 missing 的值,通過實(shí)際變量的含義進(jìn)行補(bǔ)充,例如類別型變量,就可以補(bǔ)充成 No,數(shù)值型變量可以補(bǔ)充成 0,或者用平均值來填充。
#dealing with missing data
df_train = df_train.drop((missing_data[missing_data['Total'] > 1]).index,1)
df_train = df_train.drop(df_train.loc[df_train['Electrical'].isnull()].index)
5. 下面是看 outliers
我們可以先來看主要的幾個變量的 outliers
#bivariate analysis saleprice/grlivarea
var = 'GrLivArea'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));
例如 ‘GrLivArea’ 這個變量,它的右下角這幾個點(diǎn)離主體就比較遠(yuǎn),可以猜測一下產(chǎn)生這樣數(shù)據(jù)的原因,但因?yàn)椴荒艽碇黧w的,所以此時(shí)先刪掉:
#deleting points
df_train.sort_values(by = 'GrLivArea', ascending = False)[:2]
df_train = df_train.drop(df_train[df_train['Id'] == 1299].index)
df_train = df_train.drop(df_train[df_train['Id'] == 524].index)
6. 很重要的一步是把不符合正態(tài)分布的變量給轉(zhuǎn)化成正態(tài)分布的
因?yàn)橐恍┙y(tǒng)計(jì)檢驗(yàn)方法需要數(shù)據(jù)滿足正態(tài)分布的條件。
#histogram and normal probability plot
sns.distplot(df_train['SalePrice'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['SalePrice'], plot=plt)
這個圖里可以看到 ‘SalePrice’ 的分布是正偏度,在正偏度的情況下,用 log 取對數(shù)后可以做到轉(zhuǎn)換:
#applying log transformation
df_train['SalePrice'] = np.log(df_train['SalePrice'])
同樣,我們可以把其他不符合正態(tài)分布的變量進(jìn)行轉(zhuǎn)化,
例如 GrLivArea 和 目標(biāo)值 SalePrice 在轉(zhuǎn)化之前的關(guān)系圖是類似錐形的:
#scatter plot
plt.scatter(df_train['GrLivArea'], df_train['SalePrice']);
在對 GrLivArea 轉(zhuǎn)換后,
#data transformation
df_train['GrLivArea'] = np.log(df_train['GrLivArea'])
通過上面的步驟,我們大概可以篩選出一些重要的特征,除了數(shù)據(jù)集給定的變量之外,我們也可以自己建立一些新的特征。
1. 數(shù)值變類別型
例如,MoSold: Month Sold 這個變量看起來是數(shù)值型的,但其實(shí)更符合類別型的,所以要做一下轉(zhuǎn)換:
"MoSold" : {1 : "Jan", 2 : "Feb", 3 : "Mar", 4 : "Apr", 5 : "May", 6 : "Jun", 7 : "Jul", 8 : "Aug", 9 : "Sep", 10 : "Oct", 11 : "Nov", 12 : "Dec"}
2. 類別型加順序
例如,F(xiàn)unctional: Home functionality rating 這個變量,它是個 rating,那么這種數(shù)值應(yīng)該是有序的,并且這種順序是帶有信息的,那我們就給轉(zhuǎn)化成數(shù)字:
"Functional" : {"Sal" : 1, "Sev" : 2, "Maj2" : 3, "Maj1" : 4, "Mod": 5, "Min2" : 6, "Min1" : 7, "Typ" : 8}
3. 簡化類別
當(dāng)然類別太多了的不好,可以進(jìn)一步簡化成兩三個等級:
train["SimplFunctional"] = train.Functional.replace(
{1 : 1, 2 : 1, # bad
3 : 2, 4 : 2, # major
5 : 3, 6 : 3, 7 : 3, # minor
8 : 4 # typical})
4. 構(gòu)造多項(xiàng)式
另外一種常用的方式是構(gòu)造多項(xiàng)式,一般是 2次項(xiàng),3次項(xiàng),開平方:
train["OverallQual-s2"] = train["OverallQual"] ** 2
train["OverallQual-s3"] = train["OverallQual"] ** 3
train["OverallQual-Sq"] = np.sqrt(train["OverallQual"])
5. 加減乘除
還有通過加減乘除的數(shù)學(xué)關(guān)系構(gòu)造:
OverallQual: Overall material and finish quality
OverallCond: Overall condition rating
train["OverallGrade"] = train["OverallQual"] * train["OverallCond"]
6. 變?yōu)?one-h(huán)ot
然后我們來把 categorical 的變量給變成 one-h(huán)ot 的形式:
#convert categorical variable into dummy
df_train = pd.get_dummies(df_train)
接下來用一個最簡單的線性規(guī)劃,來展示一下運(yùn)行步驟,
1. 引入常用包
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
2. 導(dǎo)入數(shù)據(jù)
train = pd.read_csv("train.csv")
test = pd.read_csv("test.csv")
print ("Train data shape:", train.shape)
print ("Test data shape:", test.shape)
#('Train data shape:', (1460, 81))
#('Test data shape:', (1459, 80))
3. 取 log 轉(zhuǎn)化為正態(tài),看 correlation,處理 outliers,missing value
此處可以對 train 數(shù)據(jù)集應(yīng)用數(shù)據(jù)探索的幾種方法。
#取 log 轉(zhuǎn)化為正態(tài)
target = np.log(train.SalePrice)
#看 correlation
numeric_features = train.select_dtypes(include=[np.number])
numeric_features.dtypes
corr = numeric_features.corr()
print (corr['SalePrice'].sort_values(ascending=False)[:5], '\n')
print (corr['SalePrice'].sort_values(ascending=False)[-5:])
#處理 outliers
train = train[train['GarageArea'] < 1200]
#處理 missing value
data = train.select_dtypes(include=[np.number]).interpolate().dropna()
4. 轉(zhuǎn)化為 one-h(huán)ot 向量
這里可以用構(gòu)造特征的幾種方法。
train['enc_street'] = pd.get_dummies(train.Street, drop_first=True)
test['enc_street'] = pd.get_dummies(train.Street, drop_first=True)
5. 模型訓(xùn)練,預(yù)測
用 train_test_split 將 train 數(shù)據(jù)集分為 train 和 valid 數(shù)據(jù),
只用一個簡單的 linear_model 來擬合,用 mean_squared_error 得到誤差值。
y = np.log(train.SalePrice)
X = data.drop(['SalePrice', 'Id'], axis=1)
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, random_state=42, test_size=.33)
from sklearn import linear_model
lr = linear_model.LinearRegression()
model = lr.fit(X_train, y_train)
predictions = model.predict(X_test)
from sklearn.metrics import mean_squared_error
print ('RMSE is: \n', mean_squared_error(y_test, predictions))
對 test.csv 應(yīng)用剛才的模型進(jìn)行預(yù)測,
因?yàn)榍懊鎸?test 數(shù)據(jù)取了 log,這里要用 exp 變?yōu)樵瓉淼姆秶?/p>
feats = test.select_dtypes(
include=[np.number]).drop(['Id'], axis=1).interpolate()
predictions = model.predict(feats)
final_predictions = np.exp(predictions)
6. 提交結(jié)果
構(gòu)造一個 submission 格式的 csv,
將 final_predictions 作為預(yù)測值列輸入進(jìn)去,
輸出這個 csv 后,就可以在比賽主頁上的 submit 藍(lán)色按鈕上點(diǎn)擊提交。
submission = pd.DataFrame()
submission['Id'] = test.Id
submission['SalePrice'] = final_predictions
submission.to_csv('output.csv', index=False)
#Your submission scored 0.13878
初級的結(jié)果出來了,大概在50%的排位,之后可以嘗試其他算法:
例如 Random Forest Regressors , Gradient Boosting,ensembling models 等,以及過擬合的分析,配合特征工程等。
這篇文章里面的代碼例子,并不會帶你進(jìn)入前幾位,只是介紹一個完整的過程,常用的方法和代碼實(shí)現(xiàn),至于如何讓算法發(fā)揮高效作用,就看玩家怎么挖掘特征,怎么組合算法和特征,怎么調(diào)參了,因?yàn)檫@也是最有趣的環(huán)節(jié),以一個輕松的方式入門,再以一個提升的心態(tài)不斷進(jìn)步。
https://www.kaggle.com/pmarcelino/comprehensive-data-exploration-with-python
https://www.kaggle.com/juliencs/a-study-on-regression-applied-to-the-ames-dataset
雷鋒網(wǎng)相關(guān)閱讀:
一個實(shí)例告訴你:Kaggle 數(shù)據(jù)競賽都有哪些套路
加入 Kaggle 大數(shù)據(jù)競賽,總共分幾步?
雷峰網(wǎng)版權(quán)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見轉(zhuǎn)載須知。