1. 程式人生 > >Kaggle入門Titanic生存預測 v1.0.0

Kaggle入門Titanic生存預測 v1.0.0

目錄標題

前言

一直在學機器學習的理論知識,但是沒有實踐,還是感覺心裡不踏實,Kaggle的入門比賽Titanic號生存預測是一個getting started competition,很適合入門,掌握機器學習的各種常見套路.這裡剛開始也是參考其他程式碼做的,得分是0.78947,不是很高,由此可見,雖然是入門級的比賽,想要拿到好的成績也是要下一番功夫的.

問題定義

生存預測,這是一個二分類問題,根據訓練資料集上得到的模型(其實就是一組權重),去測試集上對其中的乘客,將其歸類為活著(1)還是死亡(0).

資料檢視分析

利用Pandas進行資料分析,用seaborn和matplotlib進行資料視覺化,這樣我們第一步對資料就有一個直觀的認識,可以為後續的特徵工程做好準備

  1. 使用dataframe的info()方法檢視csv檔案各列的情況 ,相比於直接去檢視csv檔案的內容,pandas的這個方法可以更直觀的對檔案各個特徵有一個巨集觀的認識,打印出訓練資料集檔案的特徵如下:
    train.info()
    
    <class ‘pandas.core.frame.DataFrame’>
    RangeIndex: 891 entries, 0 to 890
    Data columns (total 12 columns):
    PassengerId 891 non-null int64
    Survived 891 non-null int64
    Pclass 891 non-null int64
    Name 891 non-null object
    Sex 891 non-null object
    Age 714 non-null float64
    SibSp 891 non-null int64
    Parch 891 non-null int64
    Ticket 891 non-null object
    Fare 891 non-null float64
    Cabin 204 non-null object
    Embarked 889 non-null object
    dtypes: float64(2), int64(5), object(5)
    memory usage: 83.6+ KB
    由上可見,train.csv中共有891個乘客的資訊,每個乘客有12個特徵(屬性),下面逐個對這些特徵進行分析處理:
    • PassengerId:使用者id,每個乘客都有,對於最後的實際分析並沒有幫助,後續要drop掉這一列.
    • Survived:是否活下來,每個乘客都有,用以標記乘客是否最終存活.
    • Pclass: 類似與頭等艙,二等艙的這樣一個概念
    • Name: 乘客姓名,每個乘客都有,原先我以為Name沒有什麼用,可是有的針對這個進行了一下特徵提取,抽取出來了Mr, Mrs, Miss, Master和Others5類,確實是有道理的,畢竟身份越高貴的活下來的機率越大
    • Sex: 乘客性別,kaggle的示例提交檔案中,認定所有女性乘客都活了下來,最後也有0.76555的準確率
    • Age: 乘客年齡,注意只有714項,是存在缺失的,這裡使用了對應類別均值填充的方法來補足.
    • Sibsp:在船上的兄弟姐妹和配偶的數量
    • Parch:在船上的父母和孩子的數量
    • Ticket:票號
    • Fare:船票的價格
    • Cabin:乘客所在的船艙號,只有204項,缺失了很多
    • Embarked:在那個地方上的船,注意缺了兩項
  2. 在知曉資料情況後,使用dataframe.describe()方法檢視各類的數學統計情況.
    在這裡插入圖片描述

資料處理

  1. 針對"Name"特徵的資料處理
    使用seaborn的計數圖,視覺化Name和Survived之間的關係圖: 在這裡插入圖片描述
    可以看到Name的分佈主要集中在"Mr", “Mrs”, "Miss"和"Master"這5類上,剩下的我們統一用"Others"來代替,可以寫一個方法,再結合pandas的apply方法,對一列的資料進行直接應用.
# 對姓名的資料進行特徵處理
def arrangename(name):
    if name == "Mr" or name == "Mrs" or name == "Miss" or name == "Master":
        return name
    else:
        return "Others"
        
# pandas的apply方法對dataframe的某一列進行操作,不必使用低效的迴圈
train["Name"] = train["Name"].apply(arrangename)
test["Name"] = test["Name"].apply(arrangename)
  1. 針對Age的資料進行處理
    之前資料分析的時候已經看到了,Age列存在很多的缺失資料,往往我們對於缺失資料使用平均值進行填充,但這裡由於已經根據Name將人群劃分成了5類,所以可以進一步細化,在每個具體類別中用其平均值進行填充,先對Name和Age進行視覺化,檢視條狀圖:
    在這裡插入圖片描述
    下面對各個類別的缺失值進行平均值填充,對訓練集和測試集都要進行同樣的操作:
# pandas的loc方法的使用,填充缺失的Age值,資料預處理
#  這裡採取的填充方式是平均值填充!這裡的年齡填充不是所有人的年齡平均值,而是細化到對應類別後的一個平均值,這樣更精細,效果更好
train.loc[(train["Name"]=="Mr")&(train["Age"].isnull()), "Age"] = train.loc[train["Name"]=="Mr","Age"].mean()
train.loc[(train["Name"]=="Mrs")&(train["Age"].isnull()), "Age"] = train.loc[train["Name"]=="Mrs","Age"].mean()
train.loc[(train["Name"]=="Miss")&(train["Age"].isnull()), "Age"] = train.loc[train["Name"]=="Miss","Age"].mean()
train.loc[(train["Name"]=="Master")&(train["Age"].isnull()), "Age"] = train.loc[train["Name"]=="Master","Age"].mean()
train.loc[(train["Name"]=="Others")&(train["Age"].isnull()), "Age"] = train["Age"].mean()

# 對測試資料集做同樣的處理
test.loc[(test["Name"]=="Mr")&(test["Age"].isnull()), "Age"] = train.loc[train["Name"]=="Mr","Age"].mean()
test.loc[(test["Name"]=="Mrs")&(test["Age"].isnull()), "Age"] = train.loc[train["Name"]=="Mrs","Age"].mean()
test.loc[(test["Name"]=="Miss")&(test["Age"].isnull()), "Age"] = train.loc[train["Name"]=="Miss","Age"].mean()
test.loc[(test["Name"]=="Master")&(test["Age"].isnull()), "Age"] = train.loc[train["Name"]=="Master","Age"].mean()
test.loc[(test["Name"]=="Others")&(test["Age"].isnull()), "Age"] = train["Age"].mean()
  1. 針對Fare資料
    注意:train中的Fare資料是沒有缺失的,但是test中確實有缺失的,這裡使用fillna方法直接將train中的Fare平均值填充到test的缺失項中.
# using the fillna method to replace the nan with specific value
test['Fare'].fillna(train['Fare'].mean(), inplace=True)
  1. 針對Cabin資料進行處理
    檢視都有那些船艙:
    在這裡插入圖片描述
    可以看到,我們只需要提取首字母就行,程式碼如下:
# 當一個字串使用了正則表示式後,最好在前面加上r
# str.extract(),可用正則從字元資料中抽取匹配的資料,只返回第一個匹配的資料。
# 先檢視資料是什麼樣子的,然後用正則表示式進行篩選,只需要知道船艙等級(A~Z)的值就夠了, 不需要知道具體位置
train['Cabin'] = train['Cabin'].str.extract(r"([A-Z])")
test['Cabin'] = test['Cabin'].str.extract(r"([A-Z])")

下面視覺化"Cabin"與"Survived"之間的計數圖:
在這裡插入圖片描述
由圖可見,在以"B", “C”, “D”, "E"開頭的船艙中乘客存活率較高,具體用0, 1數字表示,程式碼如下:

def cabin2int(cabin):
    if cabin == 'B' or cabin == 'C' or cabin == 'D' or cabin == 'E':
        return 1
    else:
        return 0
train['Cabin'] = train['Cabin'].apply(cabin2int)
test['Cabin'] = test['Cabin'].apply(cabin2int)
  1. 針對"Embarked"進行處理
    注意:Embarked是有缺失值的,但因為其不是數字型資料,不能使用平均值填充,這裡使用出現最頻繁的值進行填充
    在這裡插入圖片描述
  2. 針對PassengerId進行的操作:
    PassengerId沒有實際意義,因此可以就地刪除掉
#  原始資料中的passengerId沒有什麼用,刪除掉
train.drop('PassengerId', axis=1, inplace=True)

資料替換

  1. 因為Name已經用"Mr", “Mrs”, “Miss”, "Master"和"Others"替換了,所以使用one-hot進行編碼:
# 使用get_dummies可以進行one-hot編碼,獨熱編碼之後相當與將之前的name列換成了Mr, MISS, MRS, Master, Others5列
tmp = pd.get_dummies(train["Name"])
train.drop("Name", axis=1, inplace=True)
train = pd.concat((train, tmp), axis=1)
train.drop(['Others'], axis=1, inplace=True)

tmp = pd.get_dummies(test['Name'])
test.drop("Name", axis=1, inplace=True)
test = pd.concat((test, tmp), axis=1)
test.drop(['Others'], axis=1, inplace=True)
  1. 針對sex的資料替換,用0表示男性,1表示女性:
# 針對Series用replace進行單值替換
train['Sex'].replace({'male':0, 'female':1}, inplace=True)
test['Sex'].replace({'male':0, 'female':1}, inplace=True)
  1. 針對Embarked進行資料替換
    在這裡插入圖片描述
    因為視覺化效果中S對應的Embarked值比較高,所以將"C"和"Q"看做一類,"S"看做另一類進行0,1替換:
train.replace({"S":0, "C":1, "Q":1}, inplace=True)
test.replace({"S":0, "C":1, "Q":1}, inplace=True)

將資料匯入模型中進行訓練

構造出訓練資料集(X_train, y_train),構造測試集test

# 訓練資料將資料和對應標籤分開存放,掌握這個技巧
X_train = train.drop("Survived", axis=1)
columns = X_train.columns
y_train = train["Survived"]
# 測試集
Id = test["PassengerId"]
test = test.drop("PassengerId", axis=1)

資料預處理

from sklearn.preprocessing import StandardScaler

# StandardScaler的作用:去均值和方差歸一化。且是針對每一個特徵維度來做的,而不是針對樣本。 
std = StandardScaler()
X_train = std.fit_transform(X_train)
test = std.transform(test)

模型訓練

  1. 進行資料切分,按照1:4的比例將訓練資料集,切分為訓練集和驗證集
from sklearn.model_selection import  train_test_split

# 進行資料集切分, 驗證集的資料量佔五分之一
X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=0.2, random_state=0)
  1. 定義一個統一的方法來進行模型的訓練
def modeling(params, estimator):
    """
    利用Python的語法特性,傳過來的是分類器和對應的引數,這樣一個函式就可以用來對多個模型進行測試了!!!
    GridSearchCV,它存在的意義就是自動調參,只要把引數輸進去,就能給出最優化的結果和引數。但是這個方法
    適合於小資料集
    """
    grid = GridSearchCV(estimator, params, scoring="accuracy", n_jobs=-1)
    grid.fit(X_train, y_train)
    
    clf = grid.best_estimator_
    clf.fit(X_train, y_train)
    predict = clf.predict(X_valid)
    accuracy = accuracy_score(y_valid, predict)
    print("paramater:", grid.best_params_)
    print("accuracy:", accuracy)
    
    return accuracy, grid.best_estimator_
  1. 選用合適的模型去訓練
    1 . 使用隨機森林去訓練
params = {
    "n_estimators": [5, 10, 20, 25],
    "max_depth": [3, 5, 7, 9, None],
    "max_features": ['auto', 'sqrt', 'log2', None]
}
rfc_accuracy, rfc_clf = modeling(params, RandomForestClassifier())
importance = pd.DataFrame({"feature": columns, "importance": rfc_clf.feature_importances_})
importance.sort_values(by="importance", ascending=False)
2. 使用SVM分類器去訓練
params = {
    "C": [0.5, 1.0, 1.5],
    "gamma": [0.01, 0.05, 0.1],
    "probability": [True]
}

svc_accuracy, svc_clf = modeling(params, SVC())
3. 使用邏輯迴歸
# 使用logistic regression
params = {
    "C": [0.1, 1, 10],
    "max_iter": [50, 100, 200]
}
# 使用邏輯迴歸
lr_accuracy, lr_clf = modeling(params, LogisticRegression())
4. 使用knn分類器
# 使用knn分類器
params = {
    "n_neighbors": [2, 3, 4, 5, 10, 15],
    "leaf_size": [20, 30, 50],
    "weights": ["uniform", "distance"],
    "algorithm": ["auto", "ball_tree", "kd_tree"]
}
# 傳入的param是一個字典
knc_accuracy, knc_clf = modeling(params, KNeighborsClassifier())
5. 使用高斯分佈
# 使用高斯分佈
params = {}

gnb_accuracy, gnb_clf = modeling(params, GaussianNB())
6. 使用lsvc分類器
# 使用lsvc分類器
params = {"C": [0.005, 0.01, 0.5, 1.0]}
lsvc_accuracy, lsvc_clf = modeling(params, LinearSVC())
  1. 針對上述的模型進行實際效果測試,選出準確率最高的分類器
accuracy = pd.DataFrame(
    {
        "model":["RandomForestClassifer", "SVC", "LogisticRegression", "KNeighborsClassifier", "GaussianNB", "LinearSVC"],
        "accuracy": [rfc_accuracy, svc_accuracy, lr_accuracy, knc_accuracy, gnb_accuracy, lsvc_accuracy]
    })
# 對於多個模型的效能進行比較
# 掌握這種dataframe的排序方法
accuracy.sort_values(by="accuracy", ascending=False)

在這裡插入圖片描述

測試集預測

從上面各個模型的效果來看,在驗證集上,knn的效果最好,所以用knn在訓練資料集上進行訓練並在測試資料集上進行預測,得到knc_submission.csv後,提交得到0.78947的分數.不過,我又用其他幾個模型試了一些,發現SVC的效果更好,分數達到了0.79

# 因為KNeighboursClassifier的效果最好,使用這個模型來做
knc_clf.fit(X_train, y_train)
submission_predictions = knc_clf.predict(test)
submission = pd.DataFrame({"PassengerId":Id, "Survived": submission_predictions})
# 使用to_csv方法直接寫回到csv檔案中!!
# index=False是為了不保留行索引,預設index值為True,是保留行索引的,但是提交的資料不需要!
submission.to_csv("knc_submission.csv", index=False)

總結

這一系列分析做下來,雖然最終得分一般,但還是學到了一些套路的,現總結如下:

  1. pandas不愧是資料分析神器,要好好的加以利用,善用info(), describe(), values_count()等方法來對資料有一個巨集觀的認識.
  2. 對於缺失的資料,要進行資料填充,如平均值填充, 最大值填充,最好是用細化到最小單位的類別去填充,這樣效果更好.
  3. seaborn, matplotlib等視覺化工具對於刻畫特徵列與目標列(如這裡的"survived")之間的關係很有幫助,可以直觀的進行分析,從而對資料進行歸類
  4. 掌握其中使用多個模型的預測方法

原始碼地址

原始碼和提交csv檔案已放到github上,點此訪問