はじめに
以前,Titanicを解きましたが,訓練データの精度98%でテストデータの精度が70%ほどで過学習を起こしていました.(
Kaggle入門【Titanic】)
今回は過学習をできるだけ抑えて,テストデータの精度を上げたいと思います.
精度を上げるために,パラメータチューニングを行います.しかし,訓練データを単にパラメータチューニングしても過学習は防げません.そこで,
交差検証でチューニングを評価することにより過学習を抑えて精度を上げることができます.データの見直し
パラメータ1つで表せているのか?
パラメータチューニングを行う前に,特徴量を少し見直します.まずは年齢の分布を見てみましょう.
10代前半の生存率が高いことがわかりますよね.10前半(子ども)とそれ以外(大人)では生存率の分布が異なっているように見えます.つまり,この分布を年齢という1つのカテゴリで表現することは,難しいと思います.そこで,以前の記事では,子どもと大人にカテゴリを分けました.
このように
1つのカテゴリに対して意味が異なるものが含まれる場合はパラメータを増やす必要があります.パラメータの拡張
Titanicの可視化では,SibSp(タイタニックに同乗している兄弟/配偶者の数),Parch(タイタニックに同乗している親/子供の数)からFamilyという新しいカラムを作成しました.
ただ,家族がいるか?ということと,家族が何人いるか?ということは意味が異なるように感じます.
例えば,(独身 o r家族が1人いる)ということと,(家族が2人いる or 家族が3人いる)ことを同列に扱ってしまってはいけないような気がします.両者には大きな差があると思います.
つまり,家族の人数というカテゴリを拡張して,家族がいるかどうかというカテゴリを増やすことにします.
train['Family'] = train["Parch"] + train["SibSp"]
train['IsFamily']=1
train['IsFamily'].loc[train['Family']==0]=0
test['Family'] = test["Parch"] + test["SibSp"]
test['IsFamily']=1
test['IsFamily'].loc[test['Family']==0]=0
train = train.drop(['SibSp','Parch'], axis=1)
test = test.drop(['SibSp','Parch'], axis=1)
この時点で精度は75%程になりました.
また,チケットクラスも1~3で表すのではなく,ダミー変数を用いて3つに分割(Class1,Class2,Class3)します.
pclass_dummies_titanic = pd.get_dummies(train['Pclass'])
pclass_dummies_titanic.columns = ['Class_1','Class_2','Class_3']
pclass_dummies_test = pd.get_dummies(test['Pclass'])
pclass_dummies_test.columns = ['Class_1','Class_2','Class_3']
train.drop(['Pclass'],axis=1,inplace=True)
test.drop(['Pclass'],axis=1,inplace=True)
train = train.join(pclass_dummies_titanic)
test = test.join(pclass_dummies_test)
パラメータチューニング
ここからは,グリッドサーチ でハイパーパラメータ(人が決めるパラメータ)を決めていきます.交差検証で検証する事で,過学習を抑えます.
scikit-learnのGridSearchCVは,グリッドサーチでハイパーパラメータを変更して交差検証で最も良いスコアとなるハイパーパラメータを見つけ,学習します.
ランダムフォレストでGridSearchCVを使って最適なモデルを作成します.
parameters = {
"n_estimators":[i for i in range(10,100,10)],
"criterion":["gini","entropy"],
"max_depth":[i for i in range(1,6,1)],
'min_samples_split': [2, 4, 10,12,16],
"random_state":[3],
}
scorer = make_scorer(fbeta_score, beta=0.5)
clf = sklearn.model_selection.GridSearchCV(RandomForestClassifier(), parameters,cv=5,n_jobs=-1)
clf_fit=clf.fit(X_train, Y_train)
predictor=clf_fit.best_estimator_
訓練データに対する性能を見てみます
prediction=predictor.predict(X_train)
table=sklearn.metrics.confusion_matrix(Y_train,prediction)
tn,fp,fn,tp=table[0][0],table[0][1],table[1][0],table[1][1]
print("TPR\t{0:.3f}".format(tp/(tp+fn)))
print("SPC\t{0:.3f}".format(tn/(tn+fp)))
print("PPV\t{0:.3f}".format(tp/(tp+fp)))
print("ACC\t{0:.3f}".format((tp+tn)/(tp+fp+fn+tn)))
print("MCC\t{0:.3f}".format((tp*tn-fp*fn)/((tp+fp)*(tp+fn)*(tn+fp)*(tn+fn))**(1/2)))
print("F1\t{0:.3f}".format((2*tp)/(2*tp+fp+fn)))
print(sorted(predictor.get_params(True).items()))
print(predictor.feature_importances_)
訓練データに対する精度は83%になりました.
性能評価
実際にテストデータを予測したところ
78%でした.80%には届きませんでした ...
70%→78%になり,過学習の改善はされたので良かったです.
今回使わなかった'Name'や'Ticket'のデータを上手く使えば80%を超えそうなのでもう少し頑張りたいと思います.
Y_pred=predictor.predict(X_test)
submission = pd.DataFrame({
"PassengerId": test["PassengerId"],
"Survived": Y_pred
})
submission.to_csv('titanic.csv', index=False)
↓scikit-learnのGridSearchCVの使い方についてこちらを参考にさせて頂きました
Scikit-learnによるランダムフォレスト
import pandas as pd
from pandas import Series,DataFrame
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('whitegrid')
%matplotlib inline
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC, LinearSVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
import sklearn
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.model_selection import cross_val_score, KFold, StratifiedKFold
from sklearn.cross_validation import train_test_split
from sklearn.grid_search import GridSearchCV
from sklearn.metrics import fbeta_score, make_scorer, accuracy_score
train = pd.read_csv("./input/train.csv")
test = pd.read_csv("./input/test.csv")
train = train.drop(['PassengerId','Name','Ticket'], axis=1)
test = test.drop(['Name','Ticket'], axis=1)
train.drop("Cabin",axis=1,inplace=True)
test.drop("Cabin",axis=1,inplace=True)
train["Embarked"] = train["Embarked"].fillna("S")
test["Fare"].fillna(test["Fare"].median(), inplace=True)
average_age_titanic = train["Age"].mean()
std_age_titanic = train["Age"].std()
count_nan_age_titanic = train["Age"].isnull().sum()
average_age_test = test["Age"].mean()
std_age_test = test["Age"].std()
count_nan_age_test = test["Age"].isnull().sum()
rand_1 = np.random.randint(average_age_titanic - std_age_titanic, average_age_titanic + std_age_titanic, size = count_nan_age_titanic)
rand_2 = np.random.randint(average_age_test - std_age_test, average_age_test + std_age_test, size = count_nan_age_test)
train["Age"][np.isnan(train["Age"])] = rand_1
test["Age"][np.isnan(test["Age"])] = rand_2
def get_person(passenger):
age,sex = passenger
return 'child' if age < 16 else sex
train['Person'] = train[['Age','Sex']].apply(get_person,axis=1)
test['Person'] = test[['Age','Sex']].apply(get_person,axis=1)
train.drop(['Sex'],axis=1,inplace=True)
test.drop(['Sex'],axis=1,inplace=True)
train['Family'] = train["Parch"] + train["SibSp"]
train['IsFamily']=1
train['IsFamily'].loc[train['Family']==0]=0
test['Family'] = test["Parch"] + test["SibSp"]
test['IsFamily']=1
test['IsFamily'].loc[test['Family']==0]=0
train = train.drop(['SibSp','Parch'], axis=1)
test = test.drop(['SibSp','Parch'], axis=1)
pclass_dummies_titanic = pd.get_dummies(train['Pclass'])
pclass_dummies_titanic.columns = ['Class_1','Class_2','Class_3']
pclass_dummies_test = pd.get_dummies(test['Pclass'])
pclass_dummies_test.columns = ['Class_1','Class_2','Class_3']
train.drop(['Pclass'],axis=1,inplace=True)
test.drop(['Pclass'],axis=1,inplace=True)
train = train.join(pclass_dummies_titanic)
test = test.join(pclass_dummies_test)
person_dummies_titanic = pd.get_dummies(train['Person'])
person_dummies_titanic.columns = ['Child','Female','Male']
person_dummies_test = pd.get_dummies(test['Person'])
person_dummies_test.columns = ['Child','Female','Male']
train.drop(['Person'],axis=1,inplace=True)
test.drop(['Person'],axis=1,inplace=True)
train = train.join(person_dummies_titanic)
test = test.join(person_dummies_test)
embark_dummies_titanic = pd.get_dummies(train['Embarked'])
embark_dummies_test = pd.get_dummies(test['Embarked'])
train = train.join(embark_dummies_titanic)
test = test.join(embark_dummies_test)
train.drop(['Embarked'], axis=1,inplace=True)
test.drop(['Embarked'], axis=1,inplace=True)
X_train = train.drop("Survived",axis=1)
Y_train = train["Survived"]
X_test = test.drop("PassengerId",axis=1)
"""
# Random Forests
random_forest = RandomForestClassifier(n_estimators=100)
random_forest.fit(X_train, Y_train)
Y_pred = random_forest.predict(X_test)
random_forest.score(X_train, Y_train)
#この時点で0.75119まで精度が上がった
submission = pd.DataFrame({
"PassengerId": test["PassengerId"],
"Survived": Y_pred
})
submission.to_csv('titanic.csv', index=False)
"""
parameters = {
"n_estimators":[i for i in range(10,100,10)],
"criterion":["gini","entropy"],
"max_depth":[i for i in range(1,6,1)],
'min_samples_split': [2, 4, 10,12,16],
"random_state":[3],
}
scorer = make_scorer(fbeta_score, beta=0.5)
clf = sklearn.model_selection.GridSearchCV(RandomForestClassifier(), parameters,cv=5,n_jobs=-1)
clf_fit=clf.fit(X_train, Y_train)
predictor=clf_fit.best_estimator_
print(predictor)
print(clf_fit.best_score_)
prediction=predictor.predict(X_train)
table=sklearn.metrics.confusion_matrix(Y_train,prediction)
tn,fp,fn,tp=table[0][0],table[0][1],table[1][0],table[1][1]
print("TPR\t{0:.3f}".format(tp/(tp+fn)))
print("SPC\t{0:.3f}".format(tn/(tn+fp)))
print("PPV\t{0:.3f}".format(tp/(tp+fp)))
print("ACC\t{0:.3f}".format((tp+tn)/(tp+fp+fn+tn)))
print("MCC\t{0:.3f}".format((tp*tn-fp*fn)/((tp+fp)*(tp+fn)*(tn+fp)*(tn+fn))**(1/2)))
print("F1\t{0:.3f}".format((2*tp)/(2*tp+fp+fn)))
print(sorted(predictor.get_params(True).items()))
print(predictor.feature_importances_)
Y_pred=predictor.predict(X_test)
submission = pd.DataFrame({
"PassengerId": test["PassengerId"],
"Survived": Y_pred
})
submission.to_csv('titanic.csv', index=False)