Kaggle入門筆記——Titanic: Machine Learning from Disaster
- 1 前言
- 2 資料處理和分析過程:
-
- 2.1 初識資料集
- 2.2 填補缺失值:
- 2.3 處理Sex、Name、Embarked、Ticket特征
- 2.4 處理SibSp和Parch特征
- 2.5 處理異常值
- 2.6 特征選取
- 3 模型建構與結果分析
- 4 小結
1 前言
本人小萌新一枚,也是第一次寫部落格,想利用寒假時間通過實戰學習資料挖掘、機器學習尤其是深度學習,偶然的機會了解到Kaggle平台,完成了第一個小的Project即“Titanic: Machine Learning from Disaster”,目前成績是0.79425,排名前17%,通過部落格記錄做一個總結歸納。
2 資料處理和分析過程:
2.1 初識資料集
train.csv:用于訓練模型的資料
test.csv:Kaggle平台用于檢驗結果正确性的資料
即送出的結果即為test.csv的類别标簽(1表示survived,0表示unsurvived)
首先需要用到的子產品:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from keras import models
from keras import layers
import keras.optimizers as optimizers
大概認識一下資料:
train = 'F:/kaggle/Titanic/Dataset/titanic/train.csv'
test = 'F:/kaggle/Titanic/Dataset/titanic/test.csv'
df1 = pd.read_csv(train,sep=',')
df2 = pd.read_csv(test,sep=',')
print(df1.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 PassengerId 891 non-null int64
1 Survived 891 non-null int64
2 Pclass 891 non-null int64
3 Name 891 non-null object
4 Sex 891 non-null object
5 Age 714 non-null float64
6 SibSp 891 non-null int64
7 Parch 891 non-null int64
8 Ticket 891 non-null object
9 Fare 891 non-null float64
10 Cabin 204 non-null object
11 Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
None
各個特征的具體含義Kaggle上寫的很詳細,這裡就不再贅述。
在模組化前需要填補缺失值、處理異常值,同時Name、Sex、Ticket、Embarked列均為Object類型,需要轉化為數值類型才便于分析
2.2 填補缺失值:
含有缺失值的特征有:Age、Cabin和Embarked
由于Cabin資訊缺失過多,于是删除Cabin特征,保留Age和Embarked
- 對于Embarked特征:采用前一個非缺失值填充
df1['Embarked'].fillna(method='pad',axis=0,inplace=True)
- 對于Age特征:根據不同的Pclass(社會等級)進行均值填充(最開始并沒有按照社會等級填充,經過測試發現效果不是很好)
age_1 = df1[df1['Pclass'] == 1]
age_1 = age_1['Age'].mean()
df1[df1['Pclass']==1].fillna(age_1,inplace=True)
age_2 = df1[df1['Pclass'] == 2]
age_2 = age_2['Age'].mean()
df1[df1['Pclass']==2].fillna(age_2,inplace=True)
age_3 = df1[df1['Pclass'] == 3]
age_3 = age_3['Age'].mean()
df1[df1['Pclass']==3].fillna(age_3,inplace=True)
2.3 處理Sex、Name、Embarked、Ticket特征
- 将Sex中male指派為1,female指派為0
df1['Sex'].replace('male',1,inplace = True)
df1['Sex'].replace('female',2,inplace = True)
-
對于Name特征:考慮名字長度和稱呼與是否存活的關系進行指派(有借鑒Kaggle讨論平台上的地方)
将名字長度大于32的指派為1,小于等于32的指派為0
def fun(x):
if x>32: #此處32為多次嘗試後的經驗值
return 1
else:
return 0
Name_list = df1['Name'].apply(lambda x : fun(len(x)))
df1['Name'] = Name_list
名字稱呼中絕大多數含有Mr、Mrs、Miss,根據此分别指派為1、2、3,其餘項指派為0
bool1 = df1['Name'].str.contains('Mr\.')
bool2 = df1['Name'].str.contains('Mrs\.')
bool3 = df1['Name'].str.contains('Miss\.')
filter_Name1 = df1['Name']
filter_Name1[:] = 0
filter_Name1[bool1] = 1
filter_Name1[bool2] = 2
filter_Name1[bool3] = 3
df1['class'] = filter_Name1
- 對于Embarked特征:最開始采用直接指派的方法,即’C’ = 1,‘Q’ = 2,‘S’ = 3
df1['Embarked'].replace('C',1,inplace=True)
df1['Embarked'].replace('Q',2,inplace=True)
df1['Embarked'].replace('S',3,inplace=True)
後将其變為Embarked_C、Embarked_Q、Embarked_S三個特征,并用0-1變量表示
df1['Embarked_C'] = df1['Embarked'].apply(lambda x: 1 if x==1 else 0)
df1['Embarked_Q'] = df1['Embarked'].apply(lambda x: 1 if x==2 else 0)
df1['Embarked_S'] = df1['Embarked'].apply(lambda x: 1 if x==3 else 0)
- 對于Ticket特征:EXCEL中采用“空格”分符分隔,然後讀取最後一段數字作為Ticket特征(姑且認為是船票号碼,可能與位置有關)
2.4 處理SibSp和Parch特征
考慮建構number特征表示乘客家人數量,即number = slisp+parch+1
df1['Number'] = df1['SibSp']+df1['Parch']+1
2.5 處理異常值
首先通過箱線圖檢視異常值
f,ax=plt.subplots(figsize=(10,8))
sns.boxplot(data=df1,ax=ax)
plt.show()
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL10keNlXUU5keRpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLwczMzIDN1kTMyAjMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
乍一看貌似異常值不少,采用四分位法識别異常值(Fare和Age離群值較多,Number沒處理)
def sifenfa(x):
a = x.quantile(0.75)
b = x.quantile(0.25)
c = x
d = x
c[(c >= (a - b) * 1.5 + a) | (c <= b - (a - b) * 1.5)] = np.nan
c.fillna(c.median(),inplace=True)
d[(d >= (a - b) * 1.5 + a) | (d <= b - (a - b) * 1.5)] = np.nan
d.fillna(d.median(), inplace=True)
sifenfa(df1['Fare'])
sifenfa(df1['Age'])
2.6 特征選取
internal_chars = df1.columns.astype(list)
corrmat = df1[internal_chars].corr()
plt.subplots(figsize=(10,10))
sns.heatmap(corrmat, square=True, linewidths=.5, annot=True)
plt.show()
由于Embarked_Q特征與Survived标簽相關性較弱,将其舍棄,采用剩餘特征作為分類依據
3 模型建構與結果分析
由于前些時在學習keras架構,于是采用一個簡單的全連接配接神經網絡(DNN)作為分類模型
train_label = train_1['Survived']
train_data = train_1[['Name','Pclass','Sex','Fare','Age','Embarked_C','Embarked_S','Number','class','Ticket']]
test_label = test_1['Survived']
test_data = test_1[['Name','Pclass','Sex','Fare','Age','Embarked_C','Embarked_S','Number','class','Ticket']]
d_test = df2[['Name','Pclass','Sex','Fare','Age','Embarked_C','Embarked_S','Number','class','Ticket']]
#标準化後歸一化
train_data = train_data.apply(lambda x : (x-np.mean(x))/np.std(x))
test_data = test_data.apply(lambda x : (x-np.mean(x))/np.std(x))
train_data = train_data.apply(lambda x : (x-np.min(x))/(np.max(x)-np.min(x)))
test_data = test_data.apply(lambda x : (x-np.min(x))/(np.max(x)-np.min(x)))
d_test = d_test.apply(lambda x : (x-np.min(x))/(np.max(x)-np.min(x)))
d_test = d_test.apply(lambda x : (x-np.min(x))/(np.max(x)-np.min(x)))
#DNN模型建立
shape1 = train_data.shape[1]
model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(shape1,)))
model.add(layers.Dense(32, activation='relu'))
model.add(layers.Dropout(0.6))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dropout(0.8))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dropout(0.6))
model.add(layers.Dense(1, activation='sigmoid'))
#Dropout參數取0.5仍舊過拟合,選取0.6、0.8和0.6較為合适
model.compile(optimizer=optimizers.Adam(lr=0.0008),loss='binary_crossentropy',metrics=['acc'])
#參數batch_size = 16 、lr = 0.0008 經過多次嘗試後較為合适
history = model.fit(train_data,train_label,epochs=200,batch_size=16,validation_data=(test_data, test_label))
plot_results(history) #plot_results():keras書中用于可視化history的繪圖方法
result = model.predict(d_test)
result1 = pd.DataFrame(result)
result1.to_excel('my_result.xlsx')
結果如下:經過調整模型參數、網絡結構等可以讓Training_loss和Validation_loss均降到0.4以下,同時驗證精度可達到0.83以上(最高不到0.85)
4 小結
(1) DNN模型層數過深不好,而且貌似一般都需要加上Dropout防止過拟合。剛剛在Kaggle上看到一位大哥沒做太多預處理,用一個簡單的DNN就達到了0.80+的分數額,具體什麼原因後面在學習過程中再出嘗試。
(2) 特征工程非常重要。特征處理和選取上我最開始隻是做了簡單的篩選,分數就達到0.76(好吧雖然排名倒數),後來加上了Number和class特征,又增加了Ticket、Name的處理,才上升到0.79+,同時嘗試過随機森林、支援向量機模型,但是效果沒有DNN好(不過差距也不大)。總的來說給我感覺特征工程需要花很多工夫去做,如何處理得當,如何開發腦洞構思和聯想挺考驗人的。
(3) pandas_profiling,本次學習過程中了解到了pandas_profiling子產品,一行代碼認清原始資料集,在做項目過程中對問題分析和論文寫作都很有幫助。
第一次寫部落格記錄Project,算是一次小小的總結吧,為大四寫畢業論文打個基礎。不足之處太多太多,有待改進的地方數不勝數,希望可以在CSDN上學習的同時分享和總結自己的心得體會,取得更快更大的進步。