決策樹之泰坦尼克號實戰


決策樹分類的應用場景非常廣泛,在各行各業都有應用,比如在金融行業可以用決策樹做貸款風險評估,醫療行業可以用決策樹生成輔助診斷,電商行業可以用決策樹對銷售額進行預測等。

基於決策樹還誕生了很多數據挖掘算法,比如隨機森林(Random forest)。

sklearn 中的決策樹模型

到目前為止,sklearn 中只實現了 ID3 與 CART決策樹,所以我們暫時只能使用這兩種決策樹,在構造 DecisionTreeClassifier 類時,其中有一個參數是criterion,意為標准。它決定了構造的分類樹是采用 ID3 分類樹,還是 CART 分類樹,對應的取值分別是 entropy 或者 gini:

  • entropy: 基於信息熵,也就是 ID3 算法,實際結果與 C4.5 相差不大;

  • gini:默認參數,基於基尼系數。CART 算法是基於基尼系數做屬性划分的,所以 criterion=gini 時,實際上執行的是 CART 算法。

我們通過設置 criterion='entropy’可以創建一個 ID3 決策樹分類器,然后打印下 clf,看下決策樹在sklearn 中是個什么東西?

DecisionTreeClassifier(class_weight=None, criterion='entropy', max_depth=None,
            max_features=None, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=2,
            min_weight_fraction_leaf=0.0, presort=False, random_state=None,
            splitter='best')

這里我們看到了很多參數,除了設置 criterion 采用不同的決策樹算法外,一般建議使用默認的參數,默認參數不會限制決策樹的最大深度,不限制葉子節點數,認為所有分類的權重都相等等。當然你也可以調整這些參數,來創建不同的決策樹模型。

在構造決策樹分類器后,我們可以使用 fit 方法讓分類器進行擬合,使用 predict 方法對新數據進行預測,得到預測的分類結果,也可以使用 score 方法得到分類器的准確率。

Titanic 乘客生存預測

數據集:https://github.com/cystanford/Titanic_Data

其中數據集格式為 csv,一共有兩個文件:

  • train.csv 是訓練數據集,包含特征信息和存活與否的標簽;
  • test.csv: 測試數據集,只包含特征信息。

現在我們需要用決策樹分類對訓練集進行訓練,針對測試集中的乘客進行生存預測,並告知分類器的准確率。

在訓練集中,包括了以下字段,它們具體為:

生存預測的關鍵流程

我們要對訓練集中乘客的生存進行預測,這個過程可以划分為兩個重要的階段:

  1. 准備階段:我們首先需要對訓練集、測試集的數據進行探索,分析數據質量,並對數據進行清洗,然后通過特征選擇對數據進行降維,方便后續分類運算;

  2. 分類階段:首先通過訓練集的特征矩陣、分類結果得到決策樹分類器,然后將分類器應用於測試集。然后我們對決策樹分類器的准確性進行分析,並對決策樹模型進行可視化。

模塊 1:數據探索

  • 使用 info() 了解數據表的基本情況:行數、列數、每列的數據類型、數據完整度;

  • 使用 describe() 了解數據表的統計情況:總數、平均值、標准差、最小值、最大值等;

  • 使用 describe(include=[‘O’]) 查看字符串類型(非數字)的整體情況;

  • 使用 head 查看前幾行數據(默認是前 5 行);

  • 使用 tail 查看后幾行數據(默認是最后 5 行)。

import pandas as pd
# 數據加載
train_data = pd.read_csv('./Titanic_Data/train.csv')
test_data = pd.read_csv('./Titanic_Data/test.csv')
# 數據探索
print(train_data.info())
print('-'*30)
print(train_data.describe())
print('-'*30)
print(train_data.describe(include=['O']))
print('-'*30)
print(train_data.head())
print('-'*30)
print(train_data.tail())

模塊 2:數據清洗

通過數據探索,我們發現 Age、Fare 和 Cabin 這三個字段的數據有所缺失。其中 Age 為年齡字段,是數值型,我們可以通過平均值進行補齊;Fare 為船票價格,是數值型,我們也可以通過其他人購買船票的平均值進行補齊。

# 使用平均年齡來填充年齡中的 nan 值
train_data['Age'].fillna(train_data['Age'].mean(), inplace=True)
test_data['Age'].fillna(test_data['Age'].mean(),inplace=True)
# 使用票價的均值填充票價中的 nan 值
train_data['Fare'].fillna(train_data['Fare'].mean(), inplace=True)
test_data['Fare'].fillna(test_data['Fare'].mean(),inplace=True)

Cabin 為船艙,有大量的缺失值。在訓練集和測試集中的缺失率分別為 77% 和 78%,無法補齊;Embarked 為登陸港口,有少量的缺失值,我們可以把缺失值補齊。

print(train_data['Embarked'].value_counts())


# 運行結果:
S    644
C    168
Q     77

我們發現一共就 3 個登陸港口,其中 S 港口人數最多,占到了 72%,因此我們將其余缺失的 Embarked 數值均設置為 S:

# 使用登錄最多的港口來填充登錄港口的 nan 值
train_data['Embarked'].fillna('S', inplace=True)
test_data['Embarked'].fillna('S',inplace=True)

模塊 3:特征選擇

特征選擇是分類器的關鍵。特征選擇不同,得到的分類器也不同。通過數據探索我們發現,PassengerId 為乘客編號,對分類沒有作用,可以放棄;Name 為乘客姓名,對分類沒有作用,可以放棄;Cabin 字段缺失值太多,可以放棄;Ticke字段為船票號碼,雜亂無章且無規律,可以放棄。其余的字段包括:Pclass、Sex、Age、SibSp、Parch 和 Fare,這些屬性分別表示了乘客的船票等級、性別、年齡、親戚數量以及船票價格,可能會和乘客的生存預測分類有關系。具體是什什么關系,我們可以交給分類器來處理。

因此我們先將 Pclass、Sex、Age 等這些其余的字段作特征,放到特征向量 features 里。

# 特征選擇
features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
train_features = train_data[features]
train_labels = train_data['Survived']
test_features = test_data[features]

特征值里有一些是字符串,這樣不方便后續的運算,需要轉成數值類型,比如 Sex 字段,有 male 和 female 兩種取值。我們可以把它變成 Sex=male 和 Sex=female 兩個字段,數值用 0 或 1 來表示。

同理 Embarked 有 S、C、Q 三種可能,我們也可以改成 Embarked=S、Embarked=C 和 Embarked=Q 三個字段,數值用 0 或 1 來表示。

那該如何操作呢,我們可以使用 sklearn 特征選擇中的 DictVectorizer 類,用它將可以處理符號化的對象,將符號轉成數字 0/1 進行表示。具體方法如下:

from sklearn.feature_extraction import DictVectorizer
dvec=DictVectorizer(sparse=False)
train_features=dvec.fit_transform(train_features.to_dict(orient='record'))

你會看到代碼中使用了 fit_transform 這個函數,它可以將特征向量轉化為特征值矩陣。然后我們看下 dvec 在轉化后的特征屬性是怎樣的,即查看 dvec 的 feature_names_ 屬性值,方法如下:

print(dvec.feature_names_)


# 運行結果
['Age', 'Embarked=C', 'Embarked=Q', 'Embarked=S', 'Fare', 'Parch', 'Pclass', 'Sex=female', 'Sex=male', 'SibSp']

這樣 train_features 特征矩陣就包括 10 個特征值(列),以及 891 個樣本(行),即 891 行,10 列的特征矩陣。

模塊 4:決策樹模型

from sklearn.tree import DecisionTreeClassifier
# 構造 ID3 決策樹
clf = DecisionTreeClassifier(criterion='entropy')
# 決策樹訓練
clf.fit(train_features, train_labels)

模塊 5:模型預測 & 評估

在預測中,我們首先需要得到測試集的特征值矩陣,然后使用訓練好的決策樹 clf 進行預測,得到預測結果 pred_labes:

test_features=dvec.transform(test_features.to_dict(orient='record'))
# 決策樹預測
pred_labels = clf.predict(test_features)

在模型評估中,決策樹提供了 score 函數可以直接得到准確率,但是我們並不知道真實的預測結果,所以無法用預測值和真實的預測結果做比較。我們只能使用訓練集中的數據進行模型評估,可以使用決策樹自帶的 score 函數計算下得到的結果:

# 得到決策樹准確率
acc_decision_tree = round(clf.score(train_features, train_labels), 6)
print(u'score 准確率為 %.4lf' % acc_decision_tree)


# 運行結果:
score 准確率為 0.9820

你會發現你剛用訓練集做訓練,再用訓練集自身做准確率評估自然會很高。但這樣得出的准確率並不能代表決策樹分類器的准確率。

那么有什么辦法,來統計決策樹分類器的准確率呢?

這里可以使用 K 折交叉驗證的方式,交叉驗證是一種常用的驗證分類准確率的方法,原理是拿出大部分樣本進行訓練,少量的用於分類器的驗證。K 折交叉驗證,就是做 K 次交叉驗證,每次選取K 分之一的數據作為驗證,其余作為訓練。輪流 K 次,取平均值。

K 折交叉驗證的原理是這樣的:

  1. 將數據集平均分割成 K 個等份;

  2. 使用 1 份數據作為測試數據,其余作為訓練數據;

  3. 計算測試准確率;

  4. 使用不同的測試集,重復 2、3 步驟。

在 sklearn 的 model_selection 模型選擇中提供了 cross_val_score 函數。cross_val_score 函數中的參數 cv 代表對原始數據划分成多少份,也就是我們的 K 值,一般建議 K 值取 10,因此我們可以設置 CV=10,我們可以對比下 score和 cross_val_score 兩種函數的正確率的評估結果:

import numpy as np
from sklearn.model_selection import cross_val_score
# 使用 K 折交叉驗證 統計決策樹准確率
print(u'cross_val_score 准確率為 %.4lf' % np.mean(cross_val_score(clf, train_features, train_labels, cv=10)))


# 運行結果:
cross_val_score 准確率為 0.7835

模塊 6:決策樹可視化

Graphviz 可視化工具

完整代碼

import pandas as pd
from sklearn.feature_extraction import DictVectorizer
from sklearn.tree import DecisionTreeClassifier
import numpy as np
from sklearn.model_selection import cross_val_score

# 數據加載
train_data = pd.read_csv('./Titanic_Data-master/train.csv')
test_data = pd.read_csv('./Titanic_Data-master/test.csv')

# 數據探索
print(train_data.info())
print(train_data.describe())
print(train_data.describe(include=['O']))
print(train_data.head())
print(train_data.tail())

# 數據清洗
# 使用平均年齡來填充年齡中的Nan值
train_data['Age'].fillna(train_data['Age'].mean(), inplace=True)
test_data['Age'].fillna(test_data['Age'].mean(),inplace=True)
# 使用票價的均值填充票價中的Nan值
train_data['Fare'].fillna(train_data['Fare'].mean(), inplace=True)
test_data['Fare'].fillna(test_data['Fare'].mean(),inplace=True)

# 使用登錄最多的港口來填充登錄港口的nan值
# print(train_data['Embarked'].value_counts())
train_data['Embarked'].fillna('S', inplace=True)
test_data['Embarked'].fillna('S',inplace=True)

# 特征選擇
features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
train_features = train_data[features]
train_labels = train_data['Survived']
test_features = test_data[features]

dvec = DictVectorizer(sparse=False)
train_features = dvec.fit_transform(train_features.to_dict(orient='record'))

# 構造ID3決策樹
clf = DecisionTreeClassifier(criterion='entropy')

# 決策樹訓練
clf.fit(train_features, train_labels)

# # 模型預測 & 評估
# test_features=dvec.transform(test_features.to_dict(orient='record'))
# # 決策樹預測
# pred_labels = clf.predict(test_features)

# 得到決策樹准確率
acc_decision_tree = round(clf.score(train_features, train_labels), 6)
print(u'score准確率為 %.4lf' % acc_decision_tree)

# 使用K折交叉驗證 統計決策樹准確率
print(u'cross_val_score准確率為 %.4lf' % np.mean(cross_val_score(clf, train_features, train_labels, cv=10)))
完成代碼

決策樹模型使用技巧總結

今天用泰坦尼克乘客生存預測案例把決策樹模型的流程跑了一遍。在實戰中,你需要注意一下幾點:

  1. 特征選擇是分類模型好壞的關鍵。選擇什么樣的特征,以及對應的特征值矩陣,決定了分類模型的好壞。通常情況下,特征值不都是數值類型,可以使用 DictVectorizer 類進行轉化;

  2. 模型准確率需要考慮是否有測試集的實際結果可以做對比,當測試集沒有真實結果可以對比時,需要使用 K 折交叉驗證 cross_val_score;

  3. Graphviz 可視化工具可以很方便地將決策模型呈現出來,幫助你更好理解決策樹的構建。

思考題:

我在構造特征向量時使用了 DictVectorizer 類,使用 fit_transform 函數將特征向量轉化為特征值矩陣。DictVectorizer 類同時也提供 transform 函數,那么這兩個函數有什么區別?

fit:從一個訓練集中學習模型參數,其中就包括了歸一化時用到的均值,標准偏差等,可以理解為一個訓練過程。
transform: 在fit的基礎上,對數據進行標准化,降維,歸一化等數據轉換操作
fit_transform: 將模型訓練和轉化合並到一起,訓練樣本先做fit,得到mean,standard deviation,然后將這些參數用於transform(歸一化訓練數據),使得到的訓練數據是歸一化的,而測試數據只需要在原先fit得到的mean,std上來做歸一化就行了,所以用transform就行了。  

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM