Kaggle(30) - タイタニックをRandom Forestで予測

Random Forestというアルゴリズムを使って、タイタニックの生存率を予測します。

Random Forestはアンサンブル学習と呼ばれる学習の1つです。

アンサンブル学習は、いくつかの性能の低い分類器(弱仮説器)を組み合わせて、性能の高い1つの分類器を作る手法です。

データの読み込み

Kaggleに準備されているタイタニックの訓練データを読み込みます。

データの前処理(不要列の削除・欠損処理・カテゴリ変数の変換)と、正解ラベルとそれ以外にデータを分けるところまで一気に実行します。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pandas as pd

df_train = pd.read_csv('/kaggle/input/titanic/train.csv')

# データ前処理
def preprocessing(df):
# 不要な列の削除
df.drop(['Name', 'Ticket', 'Cabin'], axis=1, inplace=True)

# 欠損値処理
df['Age'] = df['Age'].fillna(df['Age'].median())
df['Fare'] = df['Fare'].fillna(df['Fare'].median())
df['Embarked'] = df['Embarked'].fillna('S')

# カテゴリ変数の変換
df = pd.get_dummies(df, columns=['Sex', 'Embarked'])

return df

x_titanic = preprocessing(df_train.drop(['Survived'], axis=1))
y_titanic = df_train['Survived']

Random Forestで予測

Random Forestのインスタンスを作成し、とても便利なcross_val_score関数で分割交差検証を行い、どのくらいの正答率になるか調べてみます。

[ソース]

1
2
3
4
5
from sklearn import ensemble, model_selection
clf = ensemble.RandomForestClassifier()
score = model_selection.cross_val_score(clf, x_titanic, y_titanic, cv=4) # cv=4は4分割の意
print('各正解率', score)
print('正解率', score.mean())

[出力]

81.14%という、まずまずの正解率となりました。

Kaggleに提出

訓練データ全体で学習を行います。

その後、検証データを読み込み、推論・提出用のCSVの出力を行い、Kaggleに提出します。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
# 学習
clf.fit(x_titanic, y_titanic)

# 検証データの読み込み
df_test = pd.read_csv('/kaggle/input/titanic/test.csv')
df_test = preprocessing(df_test)

# df_test.isnull().sum()
pre = clf.predict(df_test)

result = pd.DataFrame(df_test['PassengerId'])
result['Survived'] = pre
result.to_csv('result0308.csv', index=False)

[提出結果]

正解率78.46%となりました。

なかなか80%の壁を超えることができません・・・。

Kaggle(29) - Optunaでハイパーパラメータ調整 - LightGBM編6

学習パラメータの調整をする場合、グリッドサーチ機能を使うとパラメータの候補をリストで指定し、そのリストの組み合わせより最も成績のよい組み合わせを調べることができました。(前回記事参照)

Optunaを使うと、パラメータの候補リストではなく、パラメータの範囲を指定して最適な組み合わせを調べることができるのでより便利です。

データの読み込み

タイタニックのデータセットを読み込み、データの前処理を行って、正解ラベルとそれ以外にデータを分割します。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pandas as pd

df_train = pd.read_csv('/kaggle/input/titanic/train.csv')

# データ前処理
def preprocessing(df):
# 不要な列の削除
df.drop(['Name', 'Ticket', 'Cabin'], axis=1, inplace=True)

# カテゴリ変数の変換
df['Sex'] = df['Sex'].astype('category')
df['Embarked'] = df['Embarked'].astype('category')

return df

x_titanic = preprocessing(df_train.drop(['Survived'], axis=1))
y_titanic = df_train['Survived']

Optunaでハイパーパラメータ調整

Optunaで学習パラメータを最適化する場合には、目的関数の定義の定義を行い、その中で学習パラメータの種類と範囲を指定します。

返値としては、成績の良いものほど数値が低くなるようにします。今回は正解率が高いほど、低い数値が返るように定義しています。(13行目)

ハイパーパラメータの自動最適化を行うためにはoptunaのcreate_studyメソッドで最適化のセッションを作り、そのセッションのoptimizeメソッドに目的関数(objective)と試行回数(n_trials)を指定します。(20行目)

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import optuna
import lightgbm as lgb
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split

# 目的関数の定義(最小値問題として定式化)
def objective(trial):
learning_rate = trial.suggest_loguniform('learning_rate', 1e-5, 1e-1)
num_leaves = trial.suggest_int('num_leaves', 5, 30)

gbm = lgb.LGBMClassifier(objective='binary', learning_rate=learning_rate, num_leaves=num_leaves)
gbm.fit(train_x, train_y)
return 1.0 - accuracy_score(valid_y, gbm.predict(valid_x))

# 訓練データをtrainとvalidに分割
train_x, valid_x, train_y, valid_y = train_test_split(x_titanic, y_titanic, test_size=0.33, random_state=0)

# ハイパーパラメータの自動最適化
study = optuna.create_study()
study.optimize(objective, n_trials = 100)

print('求めたハイパーパラメータ', study.best_params)
print('正答率', 1.0 - study.best_value)

[出力]

今回は、学習率(learning_rate)と木にある分岐の個数(num_leaves)の最適化を行い、各パラメータが0.08623、8という結果となりました。

正答率は84.06%と、まずまずの結果になっていると思います。

Kaggleに提出

最適化したハイパーパラメータを指定して、LightGBMのインスタンスを作成し、学習を行います。

その後、検証データを読み込み、推論・提出用のCSVの出力を行い、Kaggleに提出します。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
gbm = lgb.LGBMClassifier(objective='binary', learning_rate=0.08623, num_leaves=8)

# 学習
gbm.fit(x_titanic, y_titanic)

# 検証データの読み込み
df_test = pd.read_csv('/kaggle/input/titanic/test.csv')
df_test = preprocessing(df_test)

pre = gbm.predict(df_test)

result = pd.DataFrame(df_test['PassengerId'])
result['Survived'] = pre
result.to_csv('result0307c.csv', index=False)

[提出結果]

正解率77.99%となりました。

これまでの結果とほぼ同じ正解率となっていて、パラメータの調整だけでは成績向上は難しいのかもしれません。

もしくは、調整するパラメータの選択がよくないのか、範囲の指定がイマイチなのか・・・壁にぶつかっているような気がします。

Kaggle(28) - パラメータをチューニングする - LightGBM編5

これまではLightGBMの初期パラメータで学習を行ってきました。

LightGBMは各種パラメータを設定することができますので、今回はそのパラメータを調整して学習・推測を行ってみます。

データの読み込み

タイタニックのデータセットを読み込み、データの前処理を行って、正解ラベルとそれ以外にデータを分割します。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np
import pandas as pd

df_train = pd.read_csv('/kaggle/input/titanic/train.csv')

# データ前処理
def preprocessing(df):
# 不要な列の削除
df.drop(['Name', 'Ticket', 'Cabin'], axis=1, inplace=True)

# カテゴリ変数の型変換
df['Sex'] = df['Sex'].astype('category')
df['Embarked'] = df['Embarked'].astype('category')

return df

x_titanic = preprocessing(df_train.drop(['Survived'], axis=1))
y_titanic = df_train['Survived']

パラメータの表示

まず、LightGBMの各種パラメータを表示します。

[ソース]

1
2
3
4
5
6
import lightgbm as lgb

# LightGBMの分類器をインスタンス化
gbm = lgb.LGBMClassifier(objective='binary')

gbm.get_params()

[出力]

グリッドサーチ

いろいろなパラメータが表示されましたが、今回はこのうち次の3パラメータに対してチューニングを行います。

  • num_leaves
    木にある分岐の個数です。大きくすると精度は上がるが過学習しやすくなります。
  • reg_alpha
    L1正則化に相当するものです。デフォルトでは 0.1 ぐらいを使うことが多いです。
    L1正則化にあたるのであまり大きな値を使用するとかなり重要な変数以外を無視するようなモデルになってしまうため、精度を求めている場合あまり大きくしないほうが良いでしょう。
  • reg_lambda
    L2 正則化に相当するものです。デフォルトでは L1 と同様に 0.1 ぐらいを指定します。
    L1と違い大きな値を設定してもその変数が使われないということはありませんので、特徴量の数が多い時や、徐々に木を大きくしたいすぐにオーバーフィットするような問題に対して大きく取ることが多いです。

params変数に候補となるパラメータのリストを指定し、グリッドサーチを使って最適パラメータを探索します。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from sklearn.model_selection import GridSearchCV

# 試行するパラメータを羅列
params = {
'num_leaves': [3, 4, 5, 6, 7, 8, 9, 10],
'reg_alpha': [0, 1, 2, 3, 4, 5,10, 100],
'reg_lambda': [10, 15, 18, 20, 21, 22, 23, 25, 27, 29]
}

grid_search = GridSearchCV(gbm, param_grid=params, cv=3)
grid_search.fit(x_titanic, y_titanic)

print(grid_search.best_score_)
print(grid_search.best_params_)

[出力]

チューニングしたパラメータの性能確認

グリッドサーチでのパラメータ探索の結果、num_leaves=5、reg_alpha=0、reg_lambda=22が交差検証でのスコアがベストであることが分かりました。

デフォルトのパラメータの場合と、このパラメータを使った場合の正解率の違いを確認してみます。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from sklearn import model_selection

print('== デフォルト・パラメータの場合 ==')
gbm = lgb.LGBMClassifier(objective='binary')

score = model_selection.cross_val_score(gbm, x_titanic, y_titanic, cv=3) # cv=3は3分割の意
print('各正解率', score)
print('正解率', score.mean())

print('\n== パラメータをチューニングした場合 ==')
gbm = lgb.LGBMClassifier(objective='binary', num_leaves=5, reg_alpha=0, reg_lambda=22)
score = model_selection.cross_val_score(gbm, x_titanic, y_titanic, cv=3) # cv=3は3分割の意
print('各正解率', score)
print('正解率', score.mean())

[出力]

デフォルト・パラメータの正解率が74.41%、チューニングしたパラメータでの正解率が82.49%となっており、だいぶ正解率が向上しています。

学習・推論

チューニングしたパラメータを使って、学習・推論を行います。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split

# 訓練データをtrainとvalidに分割
train_x, valid_x, train_y, valid_y = train_test_split(x_titanic, y_titanic, test_size=0.33, random_state=0)

# 学習
gbm.fit(train_x, train_y)

# 推論
pre = gbm.predict(valid_x, num_iteration=gbm.best_iteration_)
print('score', round(accuracy_score(valid_y, pre) * 100, 2))

[出力]

Kaggleに提出

検証データを読み込み、推論・提出用のCSVの出力を行い、Kaggleに提出してみます。

[ソース]

1
2
3
4
5
6
7
8
9
# 検証データの読み込み
df_test = pd.read_csv('/kaggle/input/titanic/test.csv')
df_test = preprocessing(df_test)

pre = gbm.predict(df_test)

result = pd.DataFrame(df_test['PassengerId'])
result['Survived'] = pre
result.to_csv('result0306.csv', index=False)

[提出結果]

正解率77.51%と、チューニング前よりも成績が悪くなってしまいました。

reg_lambdaの指定リストがよくなかったような気がします。

グリッドサーチ機能は便利なのですが、最適なパラメータ候補を指定するのがなかなかに難しいです。

ハイパーパラメータを自動最適化するOptunaというツールがあるらしいので今度使ってみたいと思います。

Kaggle(27) - Sickit-learn interfaceを使う - LightGBM編4

Sickit-learn wapper interfaceというラッパーを使うと、LightGBMをSickit-learnを介して使うことができます。

端的にいうとSickit-learnでお馴染みの、fitで学習しpredictで推論することができるようになります。

データの読み込み

タイタニックのデータセットを読み込み、データの前処理を行って、正解ラベルとそれ以外にデータを分割します。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np
import pandas as pd

df_train = pd.read_csv('/kaggle/input/titanic/train.csv')

# データ前処理
def preprocessing(df):
# 不要な列の削除
df.drop(['Name', 'Ticket', 'Cabin'], axis=1, inplace=True)

# カテゴリ変数の型変換
df['Sex'] = df['Sex'].astype('category')
df['Embarked'] = df['Embarked'].astype('category')

return df

x_titanic = preprocessing(df_train.drop(['Survived'], axis=1))
y_titanic = df_train['Survived']

Sickit-learn interfaceを使う

LightGBMをSickit-learn interfaceでラップし(12行目)、学習・推論を行います。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import lightgbm as lgb
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split

# 訓練データをtrainとvalidに分割
train_x, valid_x, train_y, valid_y = train_test_split(x_titanic, y_titanic, test_size=0.33, random_state=0)

# パラメータを定義
lgbm_params = {'objective': 'binary'}

# LightGBMの分類器をインスタンス化
gbm = lgb.LGBMClassifier(objective='binary')

# 学習
gbm.fit(train_x, train_y, eval_set=[(train_x, train_y), (valid_x, valid_y)],
early_stopping_rounds=20, # 20回連続でlossが下がらなかったら終了
verbose=10) # 10roundごとにlossを表示
# gbm.fit(train_x, train_y) # 単純学習

# 推論
pre = gbm.predict(valid_x, num_iteration=gbm.best_iteration_)
print('score', round(accuracy_score(valid_y, pre) * 100, 2))

[出力]

正解率は83.05%となりました。なかなかの結果です。

検証データを読み込み、推論・提出用のCSVの出力を行い、Kaggleに提出してみます。

[ソース]

1
2
3
4
5
6
7
8
9
# 検証データの読み込み
df_test = pd.read_csv('/kaggle/input/titanic/test.csv')
df_test = preprocessing(df_test)

pre = gbm.predict(df_test)

result = pd.DataFrame(df_test['PassengerId'])
result['Survived'] = pre
result.to_csv('result0305.csv', index=False)

[提出結果]

正解率78.82%と、もう少しで8割に到達できそうです。

便利な交差分割検証関数

LightGBMをSickit-learnと同じように扱えるようになると、cross_val_scoreというとても便利な関数を使うことができるようになります。

この関数を使うと、たった1行で分割交差検証ができてしまいます。

[ソース]

1
2
3
4
from sklearn import model_selection
score = model_selection.cross_val_score(gbm, x_titanic, y_titanic, cv=3) # cv=3は3分割の意
print('各正解率', score)
print('正解率', score.mean())

[出力]

便利すぎます。

このような便利関数があるのでSickit-learnは広く普及しているんだと思います。

Kaggle(26) - 重要度の表示 - LightGBM編3

LightGBMでは、学習済みモデルのfeature_importanceメソッドを使うと特徴の重要度を表示することができます。

重要度の表示

前回記事で学習したモデルについての重要度を表示します。

[ソース]

1
gbm.feature_importance()

[出力]

feature_importanceメソッドは、デフォルトの引数としてimportance_type=’split’が指定されており、重要度としては「特徴が使用された回数」が表示されます。

引数にimportance_type=’gain’を指定した場合は、「その特徴によりどれくらいトレーニングデータの損失を小さくできたか」を表示します。

[ソース]

1
gbm.feature_importance(importance_type='gain')

[出力]


2種類の重要度を比較しやすいように、1つのデータフレームにして表示します。

トレーニングデータの損失(gain)の方は、intに丸めてみました。

[ソース]

1
2
3
pd.DataFrame({'特徴':x_titanic.columns,
'重要度(split)':gbm.feature_importance(),
'重要度(gain)':gbm.feature_importance(importance_type='gain').astype(int)})

[出力]

重要度(split)では年齢(Age)の比重が大きく、重要度(gain)では性別(Sex)が最重要であることが把握できます。

Kaggle(25) - タイタニック(Titanic)コンペ - LightGBM編2

これまでデータの前処理を行うときに、カテゴリー変数についてはOne-Hotエンコーディングを行っていました。

しかし【LightGBMのドキュメント】によると、カテゴリー変数はOne-Hotエンコーディングするよりも0から始まる連続した整数に変換するほうが優れたパフォーマンスを発揮するとのことでした。

今回はOne-Hotエンコーディングの代わりに、カテゴリー変数を整数に変換してタイタニック・コンペに提出してみます。

データ読み込みと前処理

まずはいつも通りタイタニックのデータセットを読み込みます。

[ソース]

1
2
3
4
import numpy as np
import pandas as pd

df_train = pd.read_csv('/kaggle/input/titanic/train.csv')

今回のポイントであるデータ前処理ですが、LightGBMで学習・推測するためには下記の5変数を変換する必要があります。
  • Name
  • Sex
  • Ticket
  • Cabin
  • Embarked

このうち生存率に関係のなさそうなNameとTicketとCabinは単純に削除します。

そしてSexとEmbarkedを整数に変換したいのですが、一番カンタンそうな方法はデータ型をcategory型に変えることでした。

こうすることによって、自動でカテゴリーを変換した整数として扱ってくれるので、自分で0や1などの整数に置き換える必要がなくとてもラクチンでした。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# データ前処理
def preprocessing(df):
# 不要な列の削除
df.drop(['Name', 'Ticket', 'Cabin'], axis=1, inplace=True)

# カテゴリ変数の変換
# df = pd.get_dummies(df, columns=['Sex', 'Embarked'])
df['Sex'] = df['Sex'].astype('category')
df['Embarked'] = df['Embarked'].astype('category')

return df

x_titanic = preprocessing(df_train.drop(['Survived'], axis=1))
y_titanic = df_train['Survived']

データの前処理が終わりましたら、以前実行した時と同じようにLightGBMを使って学習を行います。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import lightgbm as lgb
from sklearn.metrics import accuracy_score
from sklearn.model_selection import KFold

# 3分割交差検証を指定しインスタンス化する
kf = KFold(n_splits=3, shuffle=True)

# スコアとモデルを格納するリスト
score_list = []
models = []

for fold_, (train_index, valid_index) in enumerate(kf.split(x_titanic, y_titanic)):
print(f'fold{fold_ + 1}start')
train_x = x_titanic.iloc[train_index]
valid_x = x_titanic.iloc[valid_index]
train_y = y_titanic.iloc[train_index]
valid_y = y_titanic.iloc[valid_index]

# lab.Datasetを使って、trainとvalidを作っておく
lgb_train = lgb.Dataset(train_x, train_y)
lgb_valid = lgb.Dataset(valid_x, valid_y)

# パラメータを定義
lgbm_params = {'objective': 'binary'}

# lgb.trainで学習
evals_result = {}
gbm = lgb.train(params=lgbm_params,
train_set=lgb_train,
valid_sets=[lgb_train, lgb_valid],
early_stopping_rounds=20,
evals_result=evals_result,
verbose_eval=-1) # 学習の状態を表示しない
# valid_xについて推論
oof = (gbm.predict(valid_x) > 0.5).astype(int)
score_list.append(round(accuracy_score(valid_y, oof) * 100, 2))
# 学習が終わったモデルをリストに入れておく
models.append(gbm)
print(f'fold_{fold_ + 1} end\n')

print(score_list, '平均score', round(np.mean(score_list), 2))

[出力]

平均正解率は81.6%とそこそこの結果というところでしょうか。

最後に、学習したモデルを使って、生存の推測を行い提出用のCSVファイルを出力します。

検証データに対してもデータの前処理(3行目)を行うことをお忘れなく。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 検証データの読み込み
df_test = pd.read_csv('/kaggle/input/titanic/test.csv')
df_test = preprocessing(df_test)

# テストデータの予測を格納する行列を作成
test_pred = np.zeros((len(df_test), 3))

for fold, gbm in enumerate(models):
test_pred[:, fold] = gbm.predict(df_test)

result = pd.DataFrame(df_test['PassengerId'])
# 平均が0.5より大きい場合,1(生存)とする
result['Survived'] = (np.mean(test_pred, axis=1) > 0.5).astype(int)
result
result.to_csv('result.csv', index=False)

予測結果を提出します。

[提出結果]

提出結果は77.99%という正解率になりました。

カテゴリ変数をOne-Hotエンコーディングしときの結果は75.59%でしたので2.4%ほど正解率が上がりました。

Kaggle(24) - タイタニック(Titanic)コンペ - 決定木編

今回はアルゴリズムに決定木を使ってみます。

決定木とは、ツリー状に条件分岐を繰り返すことによって分類するクラスを予測するモデルです。

枝分かれ部分の条件は、データの変数を使って作ります。

データ読み込みと前処理

Kaggleに準備されているタイタニックの訓練データを読み込みます。

データの前処理(不要列の削除・欠損処理・カテゴリ変数の変換)と、正解ラベルとそれ以外にデータを分けるところまで一気に実行します。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import numpy as np
import pandas as pd

df_train = pd.read_csv('/kaggle/input/titanic/train.csv')

# データ前処理
def preprocessing(df):
# 不要な列の削除
df.drop(['Name', 'Ticket', 'Cabin'], axis=1, inplace=True)

# 欠損値処理
df['Age'] = df['Age'].fillna(df['Age'].median())
df['Fare'] = df['Fare'].fillna(df['Fare'].median())
df['Embarked'] = df['Embarked'].fillna('S')

# カテゴリ変数の変換
df = pd.get_dummies(df, columns=['Sex', 'Embarked'])

return df

x_titanic = preprocessing(df_train.drop(['Survived'], axis=1))
y_titanic = df_train['Survived']

決定木アルゴリズムで学習・推測

決定木アルゴリズムを準備します。

学習・推測を行う前に、分割交差検証を使ってどのくらいの正答率になるか調べてみます。

[ソース]

1
2
3
4
5
6
7
8
9
from sklearn import svm, metrics, model_selection
from sklearn import tree

clf = tree.DecisionTreeClassifier(max_depth=5)

# 分割交差検証を実行
score = model_selection.cross_val_score(clf, x_titanic, y_titanic, cv=4) # cv=4は4分割の意
print('各正解率', score)
print('正解率', score.mean())

[出力]

平均正解率は78.34%とそこそこの結果というところでしょうか。


決定木アルゴリズムで学習を行い、データを予測し提出用のCSVファイルを作成します。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
# 学習する
clf.fit(x_titanic, y_titanic)

# データを予測
df_test = pd.read_csv('/kaggle/input/titanic/test.csv')
pre = clf.predict(preprocessing(df_test))

# 予測結果をファイルに出力
result = pd.DataFrame(df_test['PassengerId'])
result['Survived'] = pre
result
result.to_csv('tree_result.csv', index=False)

予測結果を提出します。

[提出結果]

提出結果は71.291%という正解率になりました。

前回記事のSVMの時よりはましになりましたが、7割程度の正解率ではイマイチですね。

Kaggle(23) - タイタニック(Titanic)コンペ - SVM編

前回記事にてはじめてKaggleコンペの1つタイタニック(Titanic)に結果を提出(Submit)してみました。

結果としては正解率75%ほどで十分な結果とは言えなかったので、手法を少し変えて成績を向上させたいと思います。

今回はアルゴリズムをLightGBMからSVMに変更してみます。

SVMは以前、アヤメの品種を分類するときに使用しました。

Python scikit-learn - 機械学習でアヤメの品種を分類する - https://ailog.site/2020/04/21/0421/


データ読み込み

まずは、Kaggleに準備されているタイタニックの訓練データを読み込みます。

[ソース]

1
2
3
4
import numpy as np
import pandas as pd

df_train = pd.read_csv('/kaggle/input/titanic/train.csv')

データの前処理(不要列の削除・欠損処理・カテゴリ変数の変換)を行う処理を関数化します。

LightGBMとは違ってSVMでは欠損値があると学習できないので、欠損値が1つもなくなるように気を付けます。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# データ前処理
def preprocessing(df):
# 不要な列の削除
df.drop(['Name', 'Ticket', 'Cabin'], axis=1, inplace=True)

# 欠損値処理
df['Age'] = df['Age'].fillna(df['Age'].median())
df['Fare'] = df['Fare'].fillna(df['Fare'].median())
df['Embarked'] = df['Embarked'].fillna('S')

# カテゴリ変数の変換
df = pd.get_dummies(df, columns=['Sex', 'Embarked'])

return df

データを正解ラベルとそれ以外に分けて、学習を行います。

今回はとりあえず1度だけ学習を行い、学習済みモデルを1つだけ作成しました。

[ソース]

1
2
3
4
5
6
7
8
x_titanic = df_train.drop(['Survived'], axis=1)
y_titanic = df_train['Survived']

from sklearn import svm

# データの学習
clf = svm.SVC()
clf.fit(preprocessing(x_titanic), y_titanic)

検証データを読み込み、予測を行います。

予測したデータをコンペ提出用にcsvファイル出力します。

[ソース]

1
2
3
4
5
6
7
8
# データを予測
df_test = pd.read_csv('/kaggle/input/titanic/test.csv')
pre = clf.predict(preprocessing(df_test))

# 予測結果をファイルに出力
result = pd.DataFrame(df_test['PassengerId'])
result['Survived'] = pre
result.to_csv('svm_result.csv', index=False)

出力したsvm_result.csvを提出した結果は次の通りです。


[提出結果]

提出結果は63.397%という正解率になりました。

LightGBMの時よりだいぶ正解率が下がってしまいました。。。

Kaggle(22) - タイタニック(Titanic)コンペに初めての提出

前回記事の3分割交差検証で、正解率90%となかなかの好成績となったので試しにタイタニックコンペに参加してみます。

(KaggleのNotebook環境で実行しています。)


データ読み込み

まずは、Kaggleに準備されているタイタニックの訓練データを読み込みます。

[ソース]

1
2
3
4
5
import numpy as np
import pandas as pd

df_train = pd.read_csv('/kaggle/input/titanic/train.csv')
df_train

[出力結果]


前処理

前処理として、不要列の削除・カテゴリ変数の変換を行います。

(LightGBMでは欠損値処理が不要とのことなので、今回欠損値処理は行いませんでした。)

[ソース]

1
2
3
4
5
6
7
8
9
10
11
# 不要な列の削除
df_train.drop(['Name', 'Ticket', 'Cabin'], axis=1, inplace=True)

# 欠損値処理
# df_train['Age'] = df_train['Age'].fillna(df_train['Age'].median())
# df_train['Embarked'] = df_train['Embarked'].fillna('S')

# カテゴリ変数の変換
df_train = pd.get_dummies(df_train, columns=['Sex', 'Embarked'])

df_train

[出力結果]

訓練データを、生存(正解ラベル)とそれ以外に分割しておきます。

[ソース]

1
2
x_titanic = df_train.drop(['Survived'], axis=1)
y_titanic = df_train['Survived']

分割交差検証で学習

前回記事と同様にLightGBNアルゴリズムの3分割交差検証を使って、学習を行います。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import lightgbm as lgb
from sklearn.metrics import accuracy_score
from sklearn.model_selection import KFold

# 3分割交差検証を指定しインスタンス化する
kf = KFold(n_splits=3, shuffle=True)

# スコアとモデルを格納するリスト
score_list = []
models = []

for fold_, (train_index, valid_index) in enumerate(kf.split(x_titanic, y_titanic)):
print(f'fold{fold_ + 1}start')
train_x = x_titanic.iloc[train_index]
valid_x = x_titanic.iloc[valid_index]
train_y = y_titanic.iloc[train_index]
valid_y = y_titanic.iloc[valid_index]

# lab.Datasetを使って、trainとvalidを作っておく
lgb_train = lgb.Dataset(train_x, train_y)
lgb_valid = lgb.Dataset(valid_x, valid_y)

# パラメータを定義
lgbm_params = {'objective': 'binary'}

# lgb.trainで学習
evals_result = {}
gbm = lgb.train(params=lgbm_params,
train_set=lgb_train,
valid_sets=[lgb_train, lgb_valid],
early_stopping_rounds=20,
evals_result=evals_result,
verbose_eval=-1) # 学習の状態を表示しない
# valid_xについて推論
oof = (gbm.predict(valid_x) > 0.5).astype(int)
score_list.append(round(accuracy_score(valid_y, oof) * 100, 2))
# 学習が終わったモデルをリストに入れておく
models.append(gbm)
print(f'fold_{fold_ + 1} end\n')

print(score_list, '平均score', round(np.mean(score_list), 2))

[出力結果]

平均スコアは82.04%となりました。


学習済みモデルを使って推論

学習済みモデルを使って、推論を行います。

3回推論を行い予測した生存の平均が0.5より大きい場合に、生存している(1)と予測します。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 検証データの読み込み
df_test = pd.read_csv('/kaggle/input/titanic/test.csv')
df_test.drop(['Name', 'Ticket', 'Cabin'], axis=1, inplace=True)
df_test = pd.get_dummies(df_test, columns=['Sex', 'Embarked'])

# テストデータの予測を格納する行列を作成
test_pred = np.zeros((len(df_test), 3))

for fold, gbm in enumerate(models):
test_pred[:, fold] = gbm.predict(df_test)

result = pd.DataFrame(df_test['PassengerId'])
# 平均が0.5より大きい場合,1(生存)とする
result['Survived'] = (np.mean(test_pred, axis=1) > 0.5).astype(int)
result
result.to_csv('result.csv', index=False)


提出用に作成したcsvファイルは下記のようなフォーマットになります。

[出力結果]


結果提出

予測したcsvファイルを提出(submit)してみます。

[提出結果]

提出結果は75.56%という正解率になりました。

もう少しいい成績なると思っていたのですが、残念です。

Kaggle(21) - 分割交差検証での学習・推論(LightGBM)

前回試したホールドアウト法は、簡単にモデルの性能評価ができるので使いやすいのですがvalidセットを学習に使えていないという欠点があります。

精度が必要な場合には、この問題を解決したk分割交差検証を使うのが一般的です。

データの読み込みと前処理

前回と同様に、タイタニックのデータセットを読み込み、前処理(不要列の削除・欠損値処理・カテゴリ変数の変換)を行っておきます。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import pandas as pd
import seaborn as sns
titanic = sns.load_dataset('titanic')

# 不要な列の削除
titanic.drop(['class', 'who', 'adult_male', 'deck', 'embark_town', 'alive', 'alone'], axis=1, inplace=True)

# 欠損値処理
#titanic.isnull().sum()
titanic['age'] = titanic['age'].fillna(titanic['age'].median())
titanic['embarked'] = titanic['embarked'].fillna('S')

# カテゴリ変数の変換
titanic = pd.get_dummies(titanic, columns=['sex', 'embarked'])

x_titanic = titanic.drop(['survived'], axis=1)
y_titanic = titanic['survived']

x_titanic

[出力結果]

分割交差検証

k分割交差検証のk=3の場合の3分割交差検証を行います。

fold1、fold2、fold3の合計3回の学習・検証を行います。

3分割したデータはtrainセット(訓練用)に2回、validセット(検証用)に1回ずつ使われます。

学習を3回行うので学習済みモデルも3つできます。3つの予測結果ができますので、最後にそれらを1つにまとめています。

[ソース]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import lightgbm as lgb
import numpy as np
from sklearn.metrics import accuracy_score
from sklearn.model_selection import KFold

# 3分割交差検証を指定しインスタンス化する
kf = KFold(n_splits=3, shuffle=True)

# スコアとモデルを格納するリスト
score_list = []
models = []

for fold_, (train_index, valid_index) in enumerate(kf.split(x_titanic, y_titanic)):
print(f'fold{fold_ + 1}start')
train_x = x_titanic.iloc[train_index]
valid_x = x_titanic.iloc[valid_index]
trains_y = y_titanic.iloc[train_index]
valid_y = y_titanic.iloc[valid_index]

# lab.Datasetを使って、trainとvalidを作っておく
lgb_train = lgb.Dataset(train_x, train_y)
lgb_valid = lgb.Dataset(valid_x, valid_y)

# パラメータを定義
lgbm_params = {'objective': 'binary'}

# lgb.trainで学習
evals_result = {}
gbm = lgb.train(params=lgbm_params,
train_set=lgb_train,
valid_sets=[lgb_train, lgb_valid],
early_stopping_rounds=20,
evals_result=evals_result,
verbose_eval=-1) # 学習の状態を表示しない
# valid_xについて推論
oof = (gbm.predict(valid_x) > 0.5).astype(int)
score_list.append(round(accuracy_score(valid_y, oof) * 100, 2))
# 学習が終わったモデルをリストに入れておく
models.append(gbm)
print(f'fold_{fold_ + 1} end\n')

print(score_list, '平均score', round(np.mean(score_list), 2))

[出力結果]

正解率は90.35%となりました。

なかなかの好成績になったのではないでしょうか。

(実行環境としてGoogleさんのColaboratoryを使用ています。)