天天看點

Kaggle入門——Titanic: Machine Learning from Disaster1 前言2 資料處理和分析過程:3 模型建構與結果分析4 小結

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

  1. 對于Embarked特征:采用前一個非缺失值填充
df1['Embarked'].fillna(method='pad',axis=0,inplace=True)
           
  1. 對于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特征

  1. 将Sex中male指派為1,female指派為0
df1['Sex'].replace('male',1,inplace = True)
df1['Sex'].replace('female',2,inplace = True)
           
  1. 對于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
           
  1. 對于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)
           
  1. 對于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()
           
Kaggle入門——Titanic: Machine Learning from Disaster1 前言2 資料處理和分析過程:3 模型建構與結果分析4 小結

乍一看貌似異常值不少,采用四分位法識别異常值(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()
           
Kaggle入門——Titanic: Machine Learning from Disaster1 前言2 資料處理和分析過程:3 模型建構與結果分析4 小結

由于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)

Kaggle入門——Titanic: Machine Learning from Disaster1 前言2 資料處理和分析過程:3 模型建構與結果分析4 小結
Kaggle入門——Titanic: Machine Learning from Disaster1 前言2 資料處理和分析過程:3 模型建構與結果分析4 小結

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上學習的同時分享和總結自己的心得體會,取得更快更大的進步。