【SciKit-Learn學習筆記】4:決策樹擬合泰坦尼克號資料集並提交到Kaggle
阿新 • • 發佈:2018-12-26
學習《scikit-learn機器學習》時的一些實踐。
決策樹擬合泰坦尼克號資料集
這裡用繪製引數-score曲線的方式去直觀看出模型引數對模型得分的影響,作者使用了GridSearchCV
來自動做k-fold交叉驗證,並且能在多組模型引數中找到最優的一組和最優值(用平均score來評估)。
這種方式可以避免一次隨機劃分造成的不確定性太大,得到的曲線很不穩定。
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
import numpy as np
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV
# 使matplotlib正常顯示負號
plt.rcParams['axes.unicode_minus'] = False
BASE_DIR = "E:/WorkSpace/ReadingNotes/scikit-learn機器學習/data/"
'''
泰坦尼克號資料集: https://www.kaggle.com/c/titanic/data
'''
# 讀取泰坦尼克號資料,並做一定的預處理
def read_dataset(file):
# index_col指定作為行索引的列,這裡第一列是PassengerId
df = pd.read_csv(file, index_col=0)
# 丟棄無用的特徵(指定axis=1即列),inplace=True則在df物件上操作,而不是返回操作後的df
df.drop(["Name", "Ticket", "Cabin"], axis=1, inplace=True)
# 將性別轉換為男1女0:先轉換為True/False序列再進行型別轉換
df['Sex'] = (df['Sex'] == 'male' ).astype('int')
# 將登船港口資料轉化為數值型資料,先獲得其中的所有可能取值放在列表中,再直接取其在列表中的下標即可
embarked_unique = df['Embarked'].unique().tolist()
df['Embarked'] = df['Embarked'].apply(lambda x: embarked_unique.index(x))
# 缺失資料(NaN)設定為0
df = df.fillna(0)
return df
# 計算決策樹模型在指定引數下訓練集和驗證集上的得分
def get_score(X, y, **kwargs):
clf = DecisionTreeClassifier(**kwargs)
clf.fit(X, y)
train_score = clf.score(X_train, y_train)
cv_score = clf.score(X_cv, y_cv)
return (train_score, cv_score)
# 尋找模型中引數的較優值,這裡尋找max_depth(決策樹前剪枝:最大深度)
def find_max_depth():
global X_train, y_train
depths = range(2, 15)
scores = [get_score(X_train, y_train, max_depth=d) for d in depths]
train_scores, cv_scores = [s[0] for s in scores], [s[1] for s in scores]
# 找出cv_scores中數值最大的數字的下標,交叉驗證集中評分最高的對應的索引,這和depths中的索引相對應
best_cv_score_index = np.argmax(cv_scores)
# 從而找到最好的引數值和對應的驗證集評分
print("最好的引數值:{},對應的驗證集評分:{}".format(depths[best_cv_score_index], cv_scores[best_cv_score_index]))
# 繪製得分隨引數值變化的曲線
plt.figure(figsize=(6, 4), dpi=144)
plt.grid()
plt.xlabel("決策樹的max_depth引數")
plt.ylabel("score")
plt.plot(depths, train_scores, '.r--', label="訓練集得分")
plt.plot(depths, cv_scores, '.b--', label="驗證集得分")
plt.legend()
plt.show()
# 尋找min_impurity_decrease(決策樹前剪枝:資訊熵或基尼不純度的閾值)
def find_min_impurity_decrease():
global X_train, y_train
values = np.linspace(0, 0.5, 50)
# 這裡criterion='gini'指定用基尼不純度作為衡量資訊不確定性的指標,即是CART
scores = [get_score(X_train, y_train, criterion='gini', min_impurity_decrease=d) for d in values]
train_scores, cv_scores = [s[0] for s in scores], [s[1] for s in scores]
# 找出cv_scores中數值最大的數字的下標,交叉驗證集中評分最高的對應的索引,這和depths中的索引相對應
best_cv_score_index = np.argmax(cv_scores)
# 從而找到最好的引數值和對應的驗證集評分
print("最好的引數值:{},對應的驗證集評分:{}".format(values[best_cv_score_index], cv_scores[best_cv_score_index]))
# 繪製得分隨引數值變化的曲線
plt.figure(figsize=(6, 4), dpi=144)
plt.grid()
plt.xlabel("決策樹的min_impurity_decrease引數")
plt.ylabel("score")
plt.plot(values, train_scores, '.r--', label="訓練集得分")
plt.plot(values, cv_scores, '.b--', label="驗證集得分")
plt.legend()
plt.show()
# clf.cv_results儲存了計算過程的所有中間結果,用它來繪製score隨引數變化圖
def plot_curve(xs, cv_results, xlabel):
train_score_mean = cv_results['mean_train_score']
train_score_std = cv_results['std_train_score']
test_score_mean = cv_results['mean_test_score']
test_score_std = cv_results['std_test_score']
plt.figure(figsize=(6, 4), dpi=144)
plt.title("引數變化影響score")
plt.grid()
plt.xlabel(xlabel)
plt.ylabel("得分")
plt.fill_between(xs, train_score_mean - train_score_std, train_score_mean + train_score_std, alpha=0.1, color='r')
plt.fill_between(xs, test_score_mean - test_score_std, test_score_mean + test_score_std, alpha=0.1, color='b')
plt.plot(xs, train_score_mean, '.--', color='r', label='訓練集score平均值')
plt.plot(xs, test_score_mean, '.--', color='b', label='cv集score平均值')
plt.legend(loc='best')
plt.show()
# 在多組引數中選擇最優的引數
def find_in_mix():
global X, y
entropy_thhs = np.linspace(0, 1, 50)
gini_thhs = np.linspace(0, 0.5, 50)
# 引數表
param_grid = [
{'criterion': ['entropy'], 'min_impurity_decrease': entropy_thhs, 'max_depth': range(2, 10),
'min_samples_split': range(2, 30, 2)},
{'criterion': ['gini'], 'min_impurity_decrease': gini_thhs, 'max_depth': range(2, 10),
'min_samples_split': range(2, 30, 2)}
]
# 將對引數表中每個字典中的多組引數進行組合,找到最優的一組
clf = GridSearchCV(DecisionTreeClassifier(), param_grid, cv=5)
clf.fit(X, y)
print("最佳引數和引數值:{}\n最佳得分:{}".format(clf.best_params_, clf.best_score_))
if __name__ == '__main__':
with open(BASE_DIR + "z7/train.csv") as f:
df = read_dataset(f)
# 標籤即"是否存活"一列
y = df['Survived'].values
# 特徵裡要去掉標籤這一列
X = df.drop(['Survived'], axis=1).values
# 劃分訓練集和驗證集
X_train, X_cv, y_train, y_cv = train_test_split(X, y, test_size=0.2)
'''
# 決策樹訓練和評估
train_score, cv_score = get_score(X_train, y_train)
print("訓練集得分:{},驗證集得分:{}".format(train_score, cv_score))
# 尋找兩個預剪枝引數的較優值,這種方式多次執行不穩定,因為太隨機了,可以用後面的GridSearchCV做交叉驗證取平均
find_max_depth()
find_min_impurity_decrease()
'''
thresholds = np.linspace(0, 0.06, 50) # 在觀察影象後調整範圍
# 引數表
param_grid = {'min_impurity_decrease': thresholds}
# 列舉引數表中的所有值來構建模型,cv折交叉驗證,最終得到指定引數值的平均評分和標準差等
# 注意,這裡指定了return_train_score=True才能用後面的clf.v_results_等
clf = GridSearchCV(DecisionTreeClassifier(), param_grid, cv=5, return_train_score=True)
clf.fit(X, y)
print("最佳引數和引數值:{}\n最佳得分:{}".format(clf.best_params_, clf.best_score_))
plot_curve(thresholds, clf.cv_results_, "min_impurity_decrease")
# 測試下多組混合
print('-' * 20 + '多組混合' + '-' * 20)
find_in_mix()
執行結果:
最佳引數和引數值:{'min_impurity_decrease': 0.006122448979591836}
最佳得分:0.8114478114478114
--------------------多組混合--------------------
最佳引數和引數值:{'criterion': 'entropy', 'max_depth': 8, 'min_impurity_decrease': 0.0, 'min_samples_split': 22}
最佳得分:0.819304152637486
第一次提交Kaggle
在Kaggle上可以看到有三個csv檔案,除了訓練集和測試集(不帶標籤)之外,還有一個就是提交的樣本。提交的時候是提交一個和提交樣本格式一樣的csv,伺服器會根據提交的結果和測試集實際的標籤進行對比,這個問題裡是用ACC來評分的。
前面得到了最佳(其實是在給定的引數內使得cv集的平均score最高)的引數,用它來對整個樣本集擬合,然後生成模型,對測試集進行預測,然後變成指定格式的csv檔案提交即可。
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
import numpy as np
BASE_DIR = "E:/WorkSpace/ReadingNotes/scikit-learn機器學習/data/"
# 讀取泰坦尼克號資料,並做一定的預處理
def read_dataset(file):
# index_col指定作為行索引的列,這裡第一列是PassengerId
df = pd.read_csv(file, index_col=0)
# 丟棄無用的特徵(指定axis=1即列),inplace=True則在df物件上操作,而不是返回操作後的df
df.drop(["Name", "Ticket", "Cabin"], axis=1, inplace=True)
# 將性別轉換為男1女0:先轉換為True/False序列再進行型別轉換
df['Sex'] = (df['Sex'] == 'male').astype('int')
# 將登船港口資料轉化為數值型資料,先獲得其中的所有可能取值放在列表中,再直接取其在列表中的下標即可
embarked_unique = df['Embarked'].unique().tolist()
df['Embarked'] = df['Embarked'].apply(lambda x: embarked_unique.index(x))
# 缺失資料(NaN)設定為0
df = df.fillna(0)
return df
if __name__ == '__main__':
with open(BASE_DIR + "z7/train.csv") as f:
df = read_dataset(f)
# 標籤即"是否存活"一列
y = df['Survived'].values
# 特徵裡要去掉標籤這一列
X = df.drop(['Survived'], axis=1).values
clf = DecisionTreeClassifier(criterion='entropy', max_depth=8, min_impurity_decrease=0.0, min_samples_split=22)
clf.fit(X, y)
print(clf.score(X, y))
# 讀入測試集並做預測,然後儲存結果
with open(BASE_DIR + "z7/test.csv") as f2:
test_df = read_dataset(f2)
predictions = clf.predict(test_df.values)
# 轉換成要求的格式
result = pd.DataFrame({'PassengerId': test_df.index, 'Survived': predictions.astype(np.int32)})
# print(result)
result.to_csv("./titanic.csv", index=False)