本例主要針對(duì)kaggle上的Titanic數(shù)據(jù)集進(jìn)行分析預(yù)測(cè),文章主體分為以下兩個(gè)部分:
- 機(jī)器學(xué)習(xí)流程的回顧
- Titanic數(shù)據(jù)集的分析和處理
image
PS:流程回顧來源于Udacity的機(jī)器學(xué)習(xí)入門課程,Titanic數(shù)據(jù)的處理參考了kaggle上眾位大佬的分享。
——————流程回顧——————
在開始進(jìn)行分析之前,讓我們仔細(xì)回顧下一個(gè)機(jī)器學(xué)習(xí)項(xiàng)目的流程。
數(shù)據(jù)集/問題——>特征處理——>算法建?!?gt;評(píng)估
第一步,數(shù)據(jù)集/問題
機(jī)器學(xué)習(xí)是用來解決實(shí)際問題的,收集與實(shí)際問題有關(guān)的數(shù)據(jù),能夠有助于后續(xù)步驟的繼續(xù)進(jìn)行。在這個(gè)階段需要知道
- 數(shù)據(jù)量足夠大?
- 我提出了什么問題?
- 要回答這個(gè)問題,有足夠的正確的特征么?
第二步,特征處理
探索型分析(EDA)
分析特征與變量的相關(guān)性(pearsonr相關(guān)系數(shù))
刪除一些離群值
清理特征與數(shù)據(jù)生成新的特征
一般來說,基于對(duì)現(xiàn)實(shí)業(yè)務(wù)的了解,生成新的特征。-
特征選擇
- 單變量特征選擇工具:
SelectPercentile :最強(qiáng)大的 X% 特征(X 是參數(shù))
SelectKBest :選擇 K 個(gè)最強(qiáng)大的特征(K 是參數(shù)) - 迭代特征(增/減)
- lasso回歸:
在最小誤差與特征數(shù)量之間尋找一個(gè)平衡,實(shí)際應(yīng)用中,該數(shù)值越接近0,說明該特征帶來的影響也就越小。 - 刪除不必要的特征
- 單變量特征選擇工具:
特征的縮放(適用于維度變化的算法):
減去平均值:x - mean
minmax scaler:(x-min)/(max-min)
standard scaler:(x-mean)/sigma-
轉(zhuǎn)換特征:
-
PCA(主成分分析):數(shù)據(jù)中使方差最大化的方向,在對(duì)這些成分壓縮或投影時(shí),將信息丟失的可能性降至最低。
【只對(duì)符合高斯分布的樣本點(diǎn)有效】
一般用于轉(zhuǎn)換成新的特征,也可以用來劃分等級(jí),方差最大為第一等級(jí),以此類推,但第一主成分絕對(duì)不會(huì)與第二主成分疊加。主成分等級(jí)的數(shù)量是有上限的,受制于輸入特征的數(shù)量。用處:
- 尋找隱藏特征
- 降維:可視化高維數(shù)據(jù),減噪
ICA(獨(dú)立成分分析)
FA(因子分析)
-
第三步,算法
根據(jù)是否需要labels,分為監(jiān)督學(xué)習(xí)和非監(jiān)督學(xué)習(xí)
- 監(jiān)督學(xué)習(xí):
- 回歸:線性回歸、lasso回歸、決策樹回歸、sv回歸
- 分類:決策樹、樸素貝葉斯、SVM、隨機(jī)森林、Adaboost、knn、LDA、logistic回歸
- 非監(jiān)督學(xué)習(xí):
- 聚類:K均值、DBSCN、EM算法、離群值檢測(cè)
- 運(yùn)行算法:調(diào)參、可視化檢驗(yàn)、在測(cè)試集上運(yùn)行、尋找最佳參數(shù)(GridsearchCV)
第四步,評(píng)估
- 驗(yàn)證:
分隔測(cè)試集和訓(xùn)練集、k-flods法、可視化 - 評(píng)估指標(biāo)
SSE/R^2、準(zhǔn)確率、召回率、F1 分?jǐn)?shù)、特征曲線
PS:
以上步驟的順序不是絕對(duì)的,為了獲得擁有強(qiáng)大泛化能力的模型,我們需要不斷地重復(fù)某些步驟。
———————Titanic數(shù)據(jù)集的分析與預(yù)測(cè)——————
n久之前,初生不畏牛犢的我,進(jìn)入了kaggel,第一個(gè)練手的數(shù)據(jù)集——Titanic,死得劇慘。重新整理了思路了之后,預(yù)測(cè)效果也好了很多,具體思路見下文。
主要思路
- 加載數(shù)據(jù)集并進(jìn)行簡(jiǎn)要探索性分析
- 特征工程
- 建模與模型評(píng)估
以前分析思路:
加載數(shù)據(jù)集——數(shù)據(jù)清理——探索性分析——特征處理——建模評(píng)估
1. 加載數(shù)據(jù)集&探索性分析
(1)數(shù)據(jù)概覽:
train:



從簡(jiǎn)要的概覽中,可以看出訓(xùn)練集中存在這些情況:
- Age,Cabin,Embarked存在空值
- Age,SibSp,Parch,Fare分布似乎呈偏右分布,具體還需要驗(yàn)證
測(cè)試集的加載和訓(xùn)練集類似,可以自己去試下。
這里為方便處理,合并訓(xùn)練集和測(cè)試集,生成新的DataFrame.
#合并測(cè)試集和訓(xùn)練集
df = pd.concat([train, test], axis=0).reset_index()
(2)從分布來看:
a. 整體的生存狀況

b. SeX

c. Pclass


d. Embarked

e. Age

f. Fare

g. Parch

h. SibSp

發(fā)現(xiàn):
- 女性的存活率高于男性
- Pclass1,2,3的生存率依次降低,這有可能與不同層的乘船人的社會(huì)地位,富裕程度有關(guān)
- Pclass 1,2女性的生存率遠(yuǎn)大于Pclass 3的女性,Pclass 1 的男性的生存率大于Pclass 2,3層的男性
- S口岸登船的人數(shù)最多,生存率也最低,C、Q口岸登船的人數(shù)和生存率正好相反。那個(gè)時(shí)代,遠(yuǎn)沒有現(xiàn)在網(wǎng)絡(luò)時(shí)代的發(fā)達(dá),所以我們可以假定認(rèn)為一個(gè)口岸登船的同一層的很容易坐在一起的,這也很可能是一個(gè)影響逃生率的因素。
- 年齡上來說,似乎很統(tǒng)一,小孩先走,存活率較高
- Fare分布差異很大,可能存在幼童免票,團(tuán)體票,家庭票等情況
- Parch 代表同船的父母或子女,SipSp代表同船的兄弟姐妹,這都是兩個(gè)表現(xiàn)親人的關(guān)系,后面一起會(huì)做特征處理。
- 單人的存活幾率低于有1個(gè)以上的或3個(gè)以下的親人同船的存活幾率,但過于一些過于龐大的家庭成員的生存幾率。家庭成員過多也不好,過少也不好。
2. 特征工程
(1)Embarked
#缺失值填充
df['Embarked'].fillna("S",inplace=True)
#數(shù)值化S,C,Q
le = LabelEncoder()
le.fit(df['Embarked'])
df['Embarked'] = le.transform(df['Embarked'])
(2)Fare
#分配到個(gè)人票價(jià)
df['Fare'] = df['Fare'] / df.groupby('Ticket')['Fare'].transform('count')
df['Fare'].fillna(df['Fare'].median(),inplace=True)
sns.distplot(df["Fare"])
plt.title("Distribution of Fare");

#定義一個(gè)票價(jià)分級(jí)函數(shù)
def fare_level(s):
if s <= 5 :
m = 0
elif s>5 and s<=20:
m = 1
elif s>20 and s<=40:
m = 2
else:
m = 3
return m
df['Fare_level'] = df['Fare'].apply(fare_level)
(3)Parch and SibSp
#組合Parch,SibSp
df['Family_memebers'] = df['Parch'] + df['SibSp'] + 1
(4)Sex
#數(shù)值化性別
le.fit(df['Sex'])
df['Sex'] = le.transform(df['Sex'])
(5)Age
年齡擁有大量的缺失值,處理方法有很多中,這里采用建立一個(gè)回歸模型預(yù)測(cè)年齡缺失值。
#利用線性回歸和隨機(jī)森林回歸模型預(yù)測(cè)Age的值
age_nan = pd.DataFrame(df[['Age', 'Sex','Family_memebers', 'Fare', 'Pclass', 'Embarked']])
age_train = age_nan[age_nan.Age.notnull()]
age_test = age_nan[age_nan.Age.isnull()]
#線性回歸
lr = LinearRegression()
lr_grid_pattern = {'fit_intercept': [True], 'normalize': [True]}
lr_grid = GridSearchCV(lr, lr_grid_pattern, cv=10, n_jobs=25, verbose=1, scoring='neg_mean_squared_error')
lr_grid.fit(age_train.drop("Age",axis=1), age_train["Age"])
print('Age feature Best LR Params:' + str(lr_grid.best_params_))
print('Age feature Best LR Score:' + str(lr_grid.best_score_))
lr = lr_grid.predict(age_test.drop("Age",axis=1))
#隨機(jī)森林回歸
rfr = RandomForestRegressor()
rfr_grid_pattern = {'max_depth': [3], 'max_features': [3]}
rfr_grid = GridSearchCV(rfr, rfr_grid_pattern, cv=10, n_jobs=25, verbose=1, scoring='neg_mean_squared_error')
rfr_grid.fit(age_train.drop("Age",axis=1), age_train["Age"])
print('Age feature Best LR Params:' + str(rfr_grid.best_params_))
print('Age feature Best LR Score:' + str(rfr_grid.best_score_))
rfr = rfr_grid.predict(age_test.drop("Age",axis=1))
#取二者均值
age_test["Age"] = (lr+rfr)/2
#定義年齡分級(jí)的函數(shù)
def age_level(s):
if s <= 15 : #兒童
m = 0
elif s>15 and s<=30: #青年及少年
m = 1
elif s>30 and s<=60: #壯年
m = 2
else: #老年
m = 3
return m
df["Age_level"] = df["Age"].apply(age_level)
3. 建模與模型評(píng)估
X = df[:len(train)][['Age_level', 'Sex','Family_memebers', 'Fare_level', 'Pclass', 'Embarked']]
y = df[:len(train)]["Survived"]
(1)評(píng)估方法
- 混淆矩陣
有準(zhǔn)確率、召回率、F1值等指標(biāo),本文采用準(zhǔn)確率。 - 交叉檢驗(yàn)
將數(shù)據(jù)集劃分成n份,選擇一份作為測(cè)試集,其余n-1份作為訓(xùn)練集,重復(fù)n次(每次的測(cè)試集都不同)。
(2)建模
利用交叉檢驗(yàn)得到結(jié)果,cv = 10,指標(biāo)為準(zhǔn)確率,可以看到SVC具有出色的表現(xiàn)。

(3)模型優(yōu)化
主要利用GridSearchCV尋找最佳擬合的結(jié)果。
SVMC = SVC(probability=True)
svc_param_grid = {'kernel': ['rbf'],
'gamma': [ 0.001, 0.01, 0.1, 1],
'C': [1, 10, 50, 100,200,300, 1000]}
gsSVMC = GridSearchCV(SVMC,param_grid = svc_param_grid, cv=10, scoring="accuracy", n_jobs= 4, verbose = 1)
gsSVMC.fit(X,y)
SVMC_best = gsSVMC.best_estimator_
(4)預(yù)測(cè)
features = ['Age_level', 'Sex','Family_memebers', 'Fare_level', 'Pclass', 'Embarked']
SVMC_best.fit(X,y)
out_text = SVMC_best.predict(df[len(train):][features])
text = pd.DataFrame(out_text.astype(int),index=df[len(train):]['PassengerId'].values).reset_index()
text.rename(columns={"index":"PassengerId",0:"Survived"}).to_csv('predict_02.csv',index=False)
最后的結(jié)果:

筆者終于從倒數(shù)爬到了正數(shù),不容易。
總體上來看,
- Titanic的特征數(shù)量比較少,很容易入手
- 后來我又用VotingClassifier進(jìn)行集成,但結(jié)果居然沒有單獨(dú)的SVC表現(xiàn)更好,這可能與其強(qiáng)大隨機(jī)性有關(guān),具體的原因還可以再探究一下。
- 就特征方面來說,name和ticket可能包含了同一家人的信息,筆者沒有做具體分析,這塊可以再繼續(xù)深入。
原文代碼:在這里
