#1.目的
基本的なpythonのプログラミングを習ったら、kaggleに取り組んでみましょう!その時、kernelを参考にしてみましょう!という記事や本が多いと思いますし、実際、めちゃくちゃ力が付くやり方だと思います。
ただ、本当の初心者の自分からしたら、**「kernel読んでも難しすぎて意味がわかんない・・」と思ったり、「いや、そんな高度な技術じゃなくて、とりあえずベーシックな機械学習モデルを作ってみたいだけなのに・・」**と思うことが多く、なかなか自分の身になりづらいなと感じました。
そこで、この記事では様々な機械学習モデルを、「超ベーシックなやり方」「ちょっと工夫したやり方」というように、少しずつレベルを上げることでどれくらい精度は変わるのか?を検証していきたいと思います。
そうすることで、「なるほど、超ベーシックな機械学習モデルの作り方ってこうやるのか」「もう少しレベルを上げるにはどうすればいいんだろう」と自分が学んだことを共有させていただく場にすることがこの記事の目的です。
#2.導入
##(1)使用するkaggle(分類問題)
私のqiita記事では度々登場する、kaggleのKickstarter Projectsのデータセットを用います。
https://www.kaggle.com/kemical/kickstarter-projects
##(2)今回比較する機械学習モデル
オーソドックスなものを集めてみました。
・ロジスティック回帰
・SVM
・決定木
・ランダムフォレスト
・アダブースト
##(3)検証していく段階
A:何の調整も無し(デフォルト)
B:(必要なモデルのみ)正則化
C:(必要なモデルのみ)標準化
D:ハイパーパラメータチューニング
E:特徴量選択
(2)(3)と、この後検証していく全パターンの精度結果をまとめたものがこちらです。
###◆表の見方
見方としては、パターン1はロジスティック回帰のデフォルトバージョンで精度は0.52958、パターン2はロジスティック回帰の正則化のみ行ってみたバージョンで精度は0.59815、パターン3はロジスティック回帰で正則化と標準化を行ってみたバージョンで精度は0.66181・・という具合です。
###◆注意点
基本的には各モデルの中でパターンが進むにつれ精度が上がる仮定ですが、今回Eの特徴量選択は組み込み法を使っています。
→組み込み法は線形モデルが前提のため、必ずしも精度が上がるわけではないことだけ注意してください(実際、複数のパターンでEをやらない方が精度が良いパターンがありました)。
※本来は線形でない場合は別の特徴量選択の手法を試した方がいいのですが、今回は時間との兼ね合いがあるので組み込み法だけで進めていきます。
###◆超初心者の方は
各モデルのAのバージョンを先に見ていただき、各モデルでパターンを少しずつ進めてみていただくというのがいいと思います。
※パターン1→6→10→13→16→2→7・・という具合です。
モデル | パターン | 精度 | |
---|---|---|---|
パターン1 | ロジスティック回帰 | A | 0.52958 |
パターン2 | ロジスティック回帰 | B | 0.59815 |
パターン3 | ロジスティック回帰 | B+C | 0.66181 |
パターン4 | ロジスティック回帰 | B+C+D | 0.66181 |
パターン5 | ロジスティック回帰 | B+C+D+E | 0.66185 |
パターン6 | SVM | A | 0.61935 |
パターン7 | SVM | C | 0.64871 |
パターン8 | SVM | C+D | 0.65393 |
パターン9 | SVM | C+D+E | 0.65066 |
パターン10 | 決定木 | A | 0.63727 |
パターン11 | 決定木 | D | 0.66376 |
パターン12 | 決定木 | D+E | 0.65732 |
パターン13 | ランダムフォレスト | A | 0.64522 |
パターン14 | ランダムフォレスト | D | 0.67762 |
パターン15 | ランダムフォレスト | D+E | 0.66308 |
パターン16 | アダブースト | A | 0.63947 |
パターン17 | アダブースト | D | 0.67426 |
パターン18 | アダブースト | D+E | 0.659367 |
##(4)参考
各機械学習モデルについて、今回は実装するのみですが、背景を数学から理解するシリーズの記事を投稿しておりますのでそちらも参考にしてみていただけますと幸いです。
[【機械学習】ロジスティック回帰をscikit-learnと数学の両方から理解する]
(https://qiita.com/Hawaii/items/ee2a0687ca451fe213be)
[【機械学習】SVMをscikit-learnと数学の両方から理解する]
(https://qiita.com/Hawaii/items/4688a50cffb2140f297d)
【機械学習】決定木をscikit-learnと数学の両方から理解する
[【機械学習】ランダムフォレストを理解する]
(https://qiita.com/Hawaii/items/5831e667723b66b46fba)
#3.いよいよ、機械学習モデル構築
##(1)その前に
全モデル共通の処理をここで行っておこうと思います。
###(ⅰ)インポート
一気にインポートしておきましょう。
この共通の処理は各パターンで毎回冒頭に行ってから、それぞれのパターンのコードを続けて書いていく、と思ってください。
#numpy,pandasのインポート
import numpy as np
import pandas as pd
#日付データに一部処理を行うため、インポート
import datetime
#訓練データとテストデータ分割のためにインポート
from sklearn.model_selection import train_test_split
#標準化のためにインポート
from sklearn.preprocessing import StandardScaler
#精度検証のためにインポート
from sklearn.model_selection import cross_val_score
#ハイパーパラメータチューニングのためにインポート
from sklearn.model_selection import train_test_split, GridSearchCV
#特徴量選択のためにインポート
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LassoCV
#ロジスティック回帰のためにインポート
from sklearn.linear_model import SGDClassifier
from sklearn.metrics import log_loss, accuracy_score, confusion_matrix
#SVMのためにインポート
from sklearn.svm import SVC
#決定木のためにインポート
from sklearn.tree import DecisionTreeClassifier, export_graphviz
#ランダムフォレストのためにインポート
from sklearn.ensemble import RandomForestClassifier
#アダブーストのためにインポート
from sklearn.ensemble import AdaBoostClassifier
###(ⅱ)データの読み込み
df = pd.read_csv(r"C:~~\ks-projects-201801.csv")
###(ⅲ)データ外観
下記より、(378661, 15)のデータセットであることが分かります。
データ数はかなり多いので、処理に時間がかかるモデルはこの中の一部のデータを使って訓練しています。
df.shape
また、.headでデータをざっと確認しておきましょう。
df.head()
###(ⅳ)データ成形
####◆募集日数
詳細は割愛しますが、クラウドファンディングの募集開始時期と終了時期がデータの中にありますので、これを「募集日数」に変換します。
df['deadline'] = pd.to_datetime(df["deadline"])
df["launched"] = pd.to_datetime(df["launched"])
df["days"] = (df["deadline"] - df["launched"]).dt.days
####◆目的変数について
こちらも詳細は割愛しますが、目的変数である「state」が成功("successful")と失敗("failed")以外にもカテゴリがありますが、今回は成功と失敗のみのデータにします。
df = df[(df["state"] == "successful") | (df["state"] == "failed")]
この上で、成功を1、失敗を0に置き換えます。
df["state"] = df["state"].replace("failed",0)
df["state"] = df["state"].replace("successful",1)
####◆不要な行の削除
モデル構築の前に、不要だと思われるidやname(これは本来は残しておくべきかもしれないですが今回は消します)、そしてクラウドファンディングをしてからでないとわからない変数を削除します。
df = df.drop(["ID","name","deadline","launched","backers","pledged","usd pledged","usd_pledged_real","usd_goal_real"], axis=1)
####◆カテゴリ変数処理
pd.get_dummiesでカテゴリ変数処理を行います。
df = pd.get_dummies(df,drop_first = True)
##(2)パターン1~5【ロジスティック回帰】
###(ⅰ)パターン1~デフォルト~
特に何の調整もせず、ロジスティック回帰を実装していこうと思います。
まずは訓練データとテストデータに分割します。
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
次に、ランダムフォレストのモデルを構築します。
何もしないと言っておきながらSGDClassifierの中に引数を入れているのは、lossはlogにしないとロジスティック回帰モデルが作れないからですし、この後に正則化ありの場合の精度を検証するので、penaltyはここではnoneにしておきます。
clf = SGDClassifier(loss = "log", penalty = "none",random_state=1234)
clf.fit(X_train,y_train)
最後に、テストデータで精度を検証しておきましょう。
clf.score(X_test, y_test)
すると、精度は0.52958になりました。
###(ⅱ)パターン2~正則化のみ実装~
正則化が何かはここでは割愛しますが、正則化のみ行ってみた場合、精度は向上するのか、L1正則化とL2正則化で検証していきます。
まずは訓練データとテストデータに分割します。
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
◆L1正則化
penaltyをl1にして、精度を確認します。
clf_L1 = SGDClassifier(loss = "log", penalty = "l1", random_state=1234)
clf_L1.fit(X_train,y_train)
clf_L1.score(X_test, y_test)
すると、精度は0.52958と、先ほどと変わらない結果となりました。
◆L2正則化
同様にpenaltyをl2にして、精度を確認します。
clf_L2 = SGDClassifier(loss = "log", penalty = "l2", random_state=1234)
clf_L2.fit(X_train,y_train)
clf_L2.score(X_test, y_test)
すると、精度は0.59815と、パターン1より精度が上がる結果となりました。
今回のデータにはL2正則化の方が適しているのかもしれません。
###(ⅲ)パターン3~正則化+標準化実装~
先ほどの正則化に、標準化処理を加えるとどのような精度になるか検証していきましょう。標準化処理を行った後に、L1正則化とL2正則化を加えていきます。
まずは訓練データとテストデータに分割します。
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
次に、標準化を行います。今回は特徴量が少ないので、標準化すべきと判断したgoalとdaysのみ標準化していて、get_dumiiesでカテゴリ変数の処理を行った列には標準化は行っていません。
ただ、これはデータ全体標準化処理を行っても問題ないそうです(受講している機械学習の講座の先生に伺いました)。
stdsc = StandardScaler()
df["goal"] = stdsc.fit_transform(df[["goal"]].values)
df["days"] = stdsc.fit_transform(df[["days"]].values)
そのうえで、L1正則化処理・L2正則化処理と精度検証を行います。
#L1正則化と精度
clf_L1 = SGDClassifier(loss = "log", penalty = "l1", random_state=1234)
clf_L1.fit(X_train,y_train)
clf_L1.score(X_test, y_test)
#L2正則化と精度
clf_L2 = SGDClassifier(loss = "log", penalty = "l2", random_state=1234)
clf_L2.fit(X_train,y_train)
clf_L2.score(X_test, y_test)
L1正則化の精度は0.66181、L2正則化は0.65750となり、一気に精度が向上しました。
標準化をした後は、L1正則化の方がマッチしていそうです。
ここでは、数値は精度が良かったL1正則化の0.66181を記録として残しておきます。
###(ⅳ)パターン4~正則化+標準化+ハイパーパラメータチューニング~
パターン3に、さらにハイパーパラメータチューニングを行ってみます。
ハイパーパラメータチューニングとは、機械学習のモデルを作る私たちが自分で決めないといけない数値を何にすればいいのか、探索し決めていくことを指します。
ここではGridsearchを使っていきます。
全パラメータをあらゆる範囲で探索できればいいですがそれには時間がかかりすぎるので、今回はSGDClassifierで重要そうなpenaltyとalphaをチューニングしていきます。
まずは標準化処理+訓練データとテストデータの分割です。
※正則化は今回はパラメータチューニングでL1とL2どちらがいいか探索するのでこのタイミングではまだ設定しません。
#標準化
stdsc = StandardScaler()
df["goal"] = stdsc.fit_transform(df[["goal"]].values)
df["days"] = stdsc.fit_transform(df[["days"]].values)
#データ分割
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
parameters = {'penalty':['l1', 'l2'], 'alpha':[0.0001,0.001, 0.01, 0.1, 1, 10, 100],'loss':['log']} # ここを編集する
model = SGDClassifier(random_state=1234)
clf = GridSearchCV(model, parameters, cv=3)
clf.fit(X_train, y_train)
print(clf.best_params_)
今回は冒頭に表で出したあらゆるパターンごとに精度を比較していきたかったので、デフォルト値を含んでGridSearchを使うことに注意しました。デフォルト値を含まないと、精度比較にならないためです。
※デフォルト値は、sklearnのページに記載があります。例えば今回のSGDClassifierだと下記です。
https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.SGDClassifier.html
penaltyはl1かl2、alphaはデフォルト値の0.0001を含んで、結構広めに設定しています。
lossは先ほど同様、logに指定しないとそもそもロジスティック回帰にならないのでlogに指定しておきます。
※超初心者の方向けの注意点としては、上記のように、「どのパラメータを」「どの範囲で」探索するかは自分で設定しないといけないので、ここにも経験則やより深い理解が必要だと感じます。
すると、{'alpha': 0.0001, 'loss': 'log', 'penalty': 'l1'}と表示され、これがベストなパラメータだと探索してくれました。
ここで注意ですが、実はこのベストなパラメータのalphaは、sklearnのサイトを見ると、デフォルト値と全く同じ値です。かつ、パターン3のL1正則化ではlossをlog、penaltyをl1にしたので、理論上、ハイパーパラメータ探索をしたとしてもパターン3のL1正則化と全く同じパラメータを使っていることになります。
つまり、精度はパターン3のL1正則化と同じ0.66181になるはずだと思いながら、続けていきましょう。
それでは、このベストなパラメータを用いて再度モデルを構築していきましょう。
下記は、「**clf.best_paramas_」を使って、先ほど出したベストなパラメータでSGDClassifierを訓練させるという意味です。
clf_2 = SGDClassifier(**clf.best_params_,random_state=1234)
clf_2.fit(X_train,y_train)
※ここでrandom_state=1234として乱数固定しないと、パターン3のL1正則化との比較ができないのでしっかり固定しておきます。
最後に、テストデータで精度を確認します。
clf_2.score(X_test, y_test)
0.66181となり、仮説通りパターン3と同じ制度になりました。
###(ⅴ)パターン5~正則化+標準化+ハイパーパラメータチューニング+特徴量選択~
パターン4までは私が勝手に選んだ特徴量でモデルを作ってきましたが、ここでは組み込み法というやり方を使って特徴量選択を行ってみます。
※組み込み法については今回と全く同じ素材でもう少し踏み込んだ内容を投稿していますので、組み込み法の詳しい説明・解説はこちらを参考にしてみてください。
[【機械学習】特徴量選択~組み込み法をscikitlearnで実装してみる~]
(https://qiita.com/Hawaii/items/1490587aad33b08d3936)
まずは標準化とデータ分割をしておきます。
#標準化
stdsc = StandardScaler()
df["goal"] = stdsc.fit_transform(df[["goal"]].values)
df["days"] = stdsc.fit_transform(df[["days"]].values)
#データ分割
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
次に、組み込み法で特徴量選択をしていきます。
estimator = LassoCV(normalize = True, cv = 10, random_state = 1234)
sfm = SelectFromModel(estimator, threshold = 1e-5)
sfm.fit(X_train,y_train)
これで選択された特徴量を、訓練データとテストデータに上書き更新します。
X_train_selected = sfm.transform(X_train)
X_test_selected = sfm.transform(X_test)
上書きした訓練データでSGDClassifierの精度を確認します(この時点ではまだハイパーパラメータチューニングをしていません)。
classifier = SGDClassifier(random_state=1234)
classifier.fit(X_train_selected, y_train)
ここから、ハイパーパラメータチューニングを行います。
やり方は基本的に先ほどと同じですが、.fitする中身を上書きした訓練データ(X_train_selected)を用います。
parameters = {'penalty':['l1', 'l2'], 'alpha':[0.0001,0.001, 0.01, 0.1, 1, 10, 100],'loss':['log']}
model = SGDClassifier(random_state=1234)
clf = GridSearchCV(model, parameters, cv=3)
clf.fit(X_train_selected, y_train)
print(clf.best_params_)
これで出したベストなパラメータで、再度SGDClassifierを訓練させます。
※何回も恐縮ですが、ここで訓練させるのは組み込み法で選択した特徴量データですので、X_trainではなくX_train_selectedを用います。
clf_2 = SGDClassifier(**clf.best_params_,random_state=1234)
clf_2.fit(X_train_selected,y_train)
最後に、精度を確認します。
clf_2.score(X_test_selected, y_test)
0.66185と、今までの中では最も良い精度を出すことができました。
以上でロジスティック回帰は終了です。一旦、精度をまとめてみます。
モデル | パターン | 精度 | |
---|---|---|---|
パターン1 | ロジスティック回帰 | A | 0.52958 |
パターン2 | ロジスティック回帰 | B | 0.59815 |
パターン3 | ロジスティック回帰 | B+C | 0.66181 |
パターン4 | ロジスティック回帰 | B+C+D | 0.66181 |
パターン5 | ロジスティック回帰 | B+C+D+E | 0.66185 |
パターン6 | SVM | A | |
パターン7 | SVM | C | |
パターン8 | SVM | C+D | |
パターン9 | SVM | C+D+E | |
パターン10 | 決定木 | A | |
パターン11 | 決定木 | D | |
パターン12 | 決定木 | D+E | |
パターン13 | ランダムフォレスト | A | |
パターン14 | ランダムフォレスト | D | |
パターン15 | ランダムフォレスト | D+E | |
パターン16 | アダブースト | A | |
パターン17 | アダブースト | D | |
パターン18 | アダブースト | D+E |
それでは、次にSVMに移ります。
##(3)パターン6~9【SVM】
SVMは、私もやってみてめちゃくちゃ実感したのですが処理に非常に時間がかかります。
ですので、ロジスティック回帰のように全訓練データを用いてモデル構築やハイパーパラメータチューニングをしていたら時間がいくらあっても足りないので、データを小さくするという点に気を遣いました。
####※補足~データ処理にどれくらい時間がかかるかの目安~※
私は一発目から全データを訓練させてみて、何時間かかっても終わらないということを何回も経験したので、まずはめちゃくちゃ少ないデータ数(やパラメータ数)で試してみて、それにかかった時間を控えておきます。
それで、おおよそこの量だったら何倍だから、時間はどれくらいかかりそう・・という目安を立ててから処理を始めるのが良いと思います。
###(ⅰ)パターン6~デフォルト~
特に何の調整もせず、SVMを実装していこうと思います。
まずは訓練データとテストデータに分割します。
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
他のモデルとSVMの処理で大きく私が変えているのは次ですが、ロジスティック回帰のように全訓練データをSVMに学習させるとめちゃくちゃ時間がかかります。
ですので、一旦全体の7割に分割した訓練データを、さらにその1.5割(=全データの10.5%)を訓練データとして扱ってみます。
※1.5割というのは明確に決まっているわけではなく、これくらいは訓練データとしてはほしいかな・・という感覚値です。
本来はもう少し訓練データとしてほしかったのですが、3割で試したところ3時間放置して処理が終わらなかったので、今回は時間との兼ね合いで1.5割にしました。
1.5割でも、処理が終わるのに3時間以上かかりました。。
#訓練データの中から1.5割をサンプリング
X_train_sample = pd.DataFrame(X_train).sample(frac = 0.15, random_state=1234)
y_train_sample = pd.DataFrame(y_train).sample(frac = 0.15, random_state=1234)
#モデル構築
clf = SVC(random_state=1234)
clf.fit(X_train_sample, y_train_sample)
さて、これでモデルができたので、テストデータで精度を確認します。
注意が必要なのは、テストデータは全体の3割から減らさずに、テストデータすべてを使って精度を確認しています。これは、SVMだけテストデータのデータ数を減らすと、他のモデルとの精度比較ができなくなってしまうと考えたからです。
clf.score(X_test,y_test)
精度は0.61935でした。
###(ⅱ)パターン7~標準化のみ実装~
SVMは正則化自体が不要なため、標準化の実装からのスタートになります。
※ハイパーパラメータ調整で、例えば木の深さの調整等がある意味正則化のような役割を果たします。
まずは標準化とデータ分割を行いましょう。以降は、パターン6と同様です。
#標準化
stdsc = StandardScaler()
df["goal"] = stdsc.fit_transform(df[["goal"]].values)
df["days"] = stdsc.fit_transform(df[["days"]].values)
#データ分割
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
#訓練データの中から1.5割をサンプリング
X_train_sample = pd.DataFrame(X_train).sample(frac = 0.15, random_state=1234)
y_train_sample = pd.DataFrame(y_train).sample(frac = 0.15, random_state=1234)
#モデル構築
clf = SVC(random_state=1234)
clf.fit(X_train_sample, y_train_sample)
#精度評価
clf.score(X_test, y_test)
精度は0.64871になり、パターン6よりも向上しました。
###(ⅲ)パターン8~標準化+ハイパーパラメータチューニング~
続いて、標準化+ハイパーパラメータチューニングを実装してみます。
まずは標準化とデータ分割です。
stdsc = StandardScaler()
df["goal"] = stdsc.fit_transform(df[["goal"]].values)
df["days"] = stdsc.fit_transform(df[["days"]].values)
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
次に、ハイパーパラメータチューニングを行います。これも、訓練データすべてを使って探索すると時間がめちゃくちゃかかるので、ハイパーパラメータチューニングには全体の7割の訓練データのうち、3%(=全体の2.1%)を用います。
本来はもっとデータを使ってパラメータ調整をしたいのですが、本当に処理が終わらないので、この数値にしました。
#訓練データの3%をサンプリング
X_train_grid = pd.DataFrame(X_train).sample(frac = 0.03,random_state=1234)
y_train_grid = pd.DataFrame(y_train).sample(frac = 0.03,random_state=1234)
そして、ハイパーパラメータチューニングを行います。
parameters = {'kernel':['linear', 'rbf'], 'C':[0.001, 0.01,0.1,1,10]} # ここを編集する
model = SVC(random_state=1234)
clf = GridSearchCV(model, parameters, cv=2,return_train_score=False)
clf.fit(X_train_grid, y_train_grid)
print(clf.best_params_, clf.best_score_)
ロジスティック回帰と変えているポイントは、GridSearchの引数のcvを2にする+return_train_scoreをFalseにしている点です。
これらは、元はcvを3にして、return_train_scoreも特に設定していなかったのですが、本当にいつまでたっても処理が終わらないのでサイトで調べ、設定しています。
※cvは交差検証する回数(これが比較的効率化に寄与しているらしい)、return_train_score=Falseは訓練データの精度をわざわざ計測しなくていいですよ、という処理のようです。
ここまでで、「最適なパラメータ」が探索できました!
この最適なパラメータで訓練データを使って訓練させ、テストデータで精度検証をしていきます。
※先ほどの3%はあくまで最適なパラメータ探索のために用いるデータです。モデルの学習器の訓練には、ロジスティック回帰と同様のデータ数を使います。
#訓練データの中から1.5割をサンプリング
X_train_sample = pd.DataFrame(X_train).sample(frac = 0.15, random_state=1234)
y_train_sample = pd.DataFrame(y_train).sample(frac = 0.15, random_state=1234)
#モデル訓練
clf = SVC(**clf.best_params_,random_state=1234)
clf.fit(X_train_sample, y_train_sample)
#テストデータで精度確認
clf.score(X_test, y_test)
精度は0.65393でした。
###(ⅳ)パターン9~標準化+ハイパーパラメータチューニング+特徴量選択~
最後に、特徴量選択を加えます。
これも、パターン8にさらに訓練データを特徴量選択のために分割しているので、どの訓練データを何のために使っているのか混同しないように注意してください。
#標準化
stdsc = StandardScaler()
df["goal"] = stdsc.fit_transform(df[["goal"]].values)
df["days"] = stdsc.fit_transform(df[["days"]].values)
#データ分割
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
#特徴量選択
estimator = LassoCV(normalize = True, cv = 10, random_state = 1234)
sfm = SelectFromModel(estimator, threshold = 1e-5)
sfm.fit(X_train,y_train)
#選択された特徴量で訓練データを上書き
X_train_selected = sfm.transform(X_train)
X_test_selected = sfm.transform(X_test)
ここまでが、特徴量選択です。次に、選択した特徴量をもとにハイパーパラメータチューニングを行います。
#ハイパーパラメータチューニングのために、選択された特徴量で上書きした訓練データの3%をサンプリング
X_train_grid = pd.DataFrame(X_train_selected).sample(frac = 0.03,random_state=1234)
y_train_grid = pd.DataFrame(y_train).sample(frac = 0.03,random_state=1234)
#ハイパーパラメータチューニング
parameters = {'kernel':['linear', 'rbf'], 'C':[0.001, 0.01,0.1,1,10]} # ここを編集する
model = SVC(random_state=1234)
clf = GridSearchCV(model, parameters, cv=2,return_train_score=False)
clf.fit(X_train_grid, y_train_grid)
print(clf.best_params_, clf.best_score_)
ここまでで、組み込み法で選択した特徴量を使ったハイパーパラメータチューニングが終わり、ベストなパラメータが決まりました。
この選択された特徴量で上書きした訓練データ(X_train_selected)とベストなパラメータでSVMのモデルを、全体の7割の訓練データのさらに1.5割のデータで学習させましょう。
#上書きした訓練データの中から3割をサンプリング
X_train_sample = pd.DataFrame(X_train_selected).sample(frac = 0.15, random_state=1234)
y_train_sample = pd.DataFrame(y_train).sample(frac = 0.15, random_state=1234)
#全体の7割の訓練データのさらに3割のサンプルデータでモデル構築
clf = SVC(**clf.best_params_,random_state=1234)
clf.fit(X_train_sample, y_train_sample)
#テストデータで精度評価
clf.score(X_test_selected, y_test)
精度は0.65066となりました。
ここで、再度精度をまとめておきましょう。
モデル | パターン | 精度 | |
---|---|---|---|
パターン1 | ロジスティック回帰 | A | 0.52958 |
パターン2 | ロジスティック回帰 | B | 0.59815 |
パターン3 | ロジスティック回帰 | B+C | 0.66181 |
パターン4 | ロジスティック回帰 | B+C+D | 0.66181 |
パターン5 | ロジスティック回帰 | B+C+D+E | 0.66185 |
パターン6 | SVM | A | 0.61935 |
パターン7 | SVM | C | 0.64871 |
パターン8 | SVM | C+D | 0.65393 |
パターン9 | SVM | C+D+E | 0.65066 |
パターン10 | 決定木 | A | |
パターン11 | 決定木 | D | |
パターン12 | 決定木 | D+E | |
パターン13 | ランダムフォレスト | A | |
パターン14 | ランダムフォレスト | D | |
パターン15 | ランダムフォレスト | D+E | |
パターン16 | アダブースト | A | |
パターン17 | アダブースト | D | |
パターン18 | アダブースト | D+E |
##(4)パターン10~12【決定木】
続いて、決定木です。
###(ⅰ)パターン10~デフォルト~
データの分割をします。
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
決定木のモデル構築を行い、精度検証です。
clf = DecisionTreeClassifier(random_state=1234)
clf = clf.fit(X_train, y_train)
clf.score(X_test, y_test)
精度は0.63727になりました。
###(ⅱ)パターン11~ハイパーパラメータチューニング~
決定木は正則化も標準化も必要ないため、ハイパーパラメータチューニングから行います。
まずはデータ分割です。
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
続いて、ハイパーパラメータチューニングを行うgridsearchを行います。その後、ベストなパラメータでモデル構築と、テストデータの精度検証を行います。
#GridSearch
parameters = {'criterion':['gini', 'entropy'], 'max_depth':[i for i in range(1, 11)],'max_features':['auto','sqrt','log2'], 'min_samples_leaf':[i for i in range(1, 11)],'random_state':[1234]} # ここを編集する
model = DecisionTreeClassifier(random_state=1234)
clf = GridSearchCV(model, parameters, cv=3)
clf.fit(X_train, y_train)
print(clf.best_params_, clf.best_score_)
#ベストなパラメータでモデル構築
clf = DecisionTreeClassifier(**clf.best_params_,random_state=1234)
clf.fit(X_train, y_train)
#精度検証
clf.score(X_test,y_test)
精度は0.66376でした。
###(ⅲ)パターン12~ハイパーパラメータチューニング+特徴量選択~
これまで同様、データ分割から始めます。
#データ分割
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
#特徴量選択
estimator = LassoCV(normalize = True, cv = 10, random_state = 1234)
sfm = SelectFromModel(estimator, threshold = 1e-5)
sfm.fit(X_train,y_train)
#選択された特徴量で訓練データを上書き
X_train_selected = sfm.transform(X_train)
X_test_selected = sfm.transform(X_test)
#ハイパーパラメータチューニング
parameters = {'criterion':['gini', 'entropy'], 'max_depth':[i for i in range(1, 11)],'max_features':['auto','sqrt','log2'], 'min_samples_leaf':[i for i in range(1, 11)],'random_state':[1234]}
model = DecisionTreeClassifier(random_state=1234)
clf = GridSearchCV(model, parameters, cv=3)
clf.fit(X_train_selected, y_train)
print(clf.best_params_, clf.best_score_)
#最適なパラメータで学習器を訓練
clf_2 = DecisionTreeClassifier(**clf.best_params_,random_state=1234)
clf_2.fit(X_train_selected,y_train)
#テストデータで精度確認
clf_2.score(X_test_selected, y_test)
精度は0.65732となりました。
モデル | パターン | 精度 | |
---|---|---|---|
パターン1 | ロジスティック回帰 | A | 0.52958 |
パターン2 | ロジスティック回帰 | B | 0.59815 |
パターン3 | ロジスティック回帰 | B+C | 0.66181 |
パターン4 | ロジスティック回帰 | B+C+D | 0.66181 |
パターン5 | ロジスティック回帰 | B+C+D+E | 0.66185 |
パターン6 | SVM | A | 0.61935 |
パターン7 | SVM | C | 0.64871 |
パターン8 | SVM | C+D | 0.65393 |
パターン9 | SVM | C+D+E | 0.65066 |
パターン10 | 決定木 | A | 0.63727 |
パターン11 | 決定木 | D | 0.66376 |
パターン12 | 決定木 | D+E | 0.65732 |
パターン13 | ランダムフォレスト | A | |
パターン14 | ランダムフォレスト | D | |
パターン15 | ランダムフォレスト | D+E | |
パターン16 | アダブースト | A | |
パターン17 | アダブースト | D | |
パターン18 | アダブースト | D+E |
##(5)パターン13~15【ランダムフォレスト】
###(ⅰ)パターン13~デフォルト~
データの分割をします。
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
ランダムフォレストのモデル構築を行い、精度検証です。
clf = RandomForestClassifier(random_state=1234)
clf.fit(X_train, y_train)
clf.score(X_test, y_test)
精度は0.64522になりました。
###(ⅱ)パターン14~ハイパーパラメータチューニング~
決定木同様、ランダムフォレストも正則化と標準化は不要です。
今までと同様、データ分割の後にハイパーパラメータチューニングを行います。
今までのモデルと異なるのは、探索の範囲を少し狭めています(各指標をrangeで1から5の範囲を探索しています)。これだけで35分ほどかかったので、さらに範囲を広げると自分の時間との兼ね合いで処理を仕切ることが難しいと感じ、範囲を狭めました。
#データ分割
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
#ハイパーパラメータチューニング
parameters = {'max_depth':[2,4,6,None], 'min_samples_leaf':[1,3,5],'min_samples_split':[2,4,6]}
model = RandomForestClassifier(random_state=1234)
clf = GridSearchCV(model, parameters, cv=3,)
clf.fit(X_train, y_train)
print(clf.best_params_, clf.best_score_)
#最適なパラメータで学習器を訓練
clf = RandomForestClassifier(**clf.best_params_,random_state=1234)
clf.fit(X_train, y_train)
#テストデータで精度確認
clf.score(X_test, y_test)
ハイパーパラメータ探索で数値を絞っているのは、今回処理にかなり時間がかかったので、デフォルト値を含む前提で、偶数のみや奇数のみの探索に切り替えています。
精度は0.67762でした。
###(ⅲ)パターン15~ハイパーパラメータチューニング+特徴量選択~
#データ分割
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
#特徴量選択
estimator = LassoCV(normalize = True, cv = 10, random_state = 1234)
sfm = SelectFromModel(estimator, threshold = 1e-5)
sfm.fit(X_train,y_train)
#選択された特徴量で訓練データを上書き
X_train_selected = sfm.transform(X_train)
X_test_selected = sfm.transform(X_test)
#ハイパーパラメータチューニング
parameters = {'max_depth':[2,4,6,None], 'min_samples_leaf':[1,3,5],'min_samples_split':[2,4,6]}
model = RandomForestClassifier(random_state=1234)
clf = GridSearchCV(model, parameters, cv=3,)
clf.fit(X_train_selected, y_train)
print(clf.best_params_, clf.best_score_)
#最適なパラメータで学習器を訓練
clf = RandomForestClassifier(**clf.best_params_,random_state=1234)
clf.fit(X_train_selected, y_train)
#テストデータで精度確認
clf.score(X_test_selected, y_test)
精度は0.66308になりました。
あらためて、精度を確認しておきましょう。
モデル | パターン | 精度 | |
---|---|---|---|
パターン1 | ロジスティック回帰 | A | 0.52958 |
パターン2 | ロジスティック回帰 | B | 0.59815 |
パターン3 | ロジスティック回帰 | B+C | 0.66181 |
パターン4 | ロジスティック回帰 | B+C+D | 0.66181 |
パターン5 | ロジスティック回帰 | B+C+D+E | 0.66185 |
パターン6 | SVM | A | 0.61935 |
パターン7 | SVM | C | 0.64871 |
パターン8 | SVM | C+D | 0.65393 |
パターン9 | SVM | C+D+E | 0.65066 |
パターン10 | 決定木 | A | 0.63727 |
パターン11 | 決定木 | D | 0.66376 |
パターン12 | 決定木 | D+E | 0.65732 |
パターン13 | ランダムフォレスト | A | 0.64522 |
パターン14 | ランダムフォレスト | D | 0.67762 |
パターン15 | ランダムフォレスト | D+E | 0.66308 |
パターン16 | アダブースト | A | |
パターン17 | アダブースト | D | |
パターン18 | アダブースト | D+E |
##(6)パターン16~18【アダブースト】
###(ⅰ)パターン16~デフォルト~
データの分割をします。
#データ分割
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
アダブーストのモデル構築を行い、精度検証です。
clf = AdaBoostClassifier(DecisionTreeClassifier(random_state=1234))
clf.fit(X_train, y_train)
clf.score(X_test, y_test)
精度は0.63947になりました。
###(ⅱ)パターン17~ハイパーパラメータチューニング~
#データ分割
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
#ハイパーパラメータチューニング
parameters = {'learning_rate':[0.1,0.5,1.0]}
model = AdaBoostClassifier(random_state=1234)
clf = GridSearchCV(model, parameters, cv=3,)
clf.fit(X_train, y_train)
print(clf.best_params_, clf.best_score_)
#最適なパラメータで学習器を訓練
clf = AdaBoostClassifier(**clf.best_params_,random_state=1234)
clf.fit(X_train, y_train)
#テストデータで精度確認
clf.score(X_test, y_test)
精度は0.67426でした。
###(ⅲ)パターン18~ハイパーパラメータチューニング+特徴量選択~
#データ分割
y = df["state"].values
X = df.drop("state", axis=1).values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1234)
#特徴量選択
estimator = LassoCV(normalize = True, cv = 10, random_state = 1234)
sfm = SelectFromModel(estimator, threshold = 1e-5)
sfm.fit(X_train,y_train)
#選択された特徴量で訓練データを上書き
X_train_selected = sfm.transform(X_train)
X_test_selected = sfm.transform(X_test)
#ハイパーパラメータチューニング
parameters = {'learning_rate':[0.1,0.5,1.0]}
model = AdaBoostClassifier(random_state=1234)
clf = GridSearchCV(model, parameters, cv=3)
clf.fit(X_train_selected, y_train)
print(clf.best_params_, clf.best_score_)
#最適なパラメータで学習器を訓練
clf = AdaBoostClassifier(**clf.best_params_,random_state=1234)
clf.fit(X_train_selected, y_train)
#テストデータで精度確認
clf.score(X_test_selected, y_test)
精度は0.659367でした。
モデル | パターン | 精度 | |
---|---|---|---|
パターン1 | ロジスティック回帰 | A | 0.52958 |
パターン2 | ロジスティック回帰 | B | 0.59815 |
パターン3 | ロジスティック回帰 | B+C | 0.66181 |
パターン4 | ロジスティック回帰 | B+C+D | 0.66181 |
パターン5 | ロジスティック回帰 | B+C+D+E | 0.66185 |
パターン6 | SVM | A | 0.61935 |
パターン7 | SVM | C | 0.64871 |
パターン8 | SVM | C+D | 0.65393 |
パターン9 | SVM | C+D+E | 0.65066 |
パターン10 | 決定木 | A | 0.63727 |
パターン11 | 決定木 | D | 0.66376 |
パターン12 | 決定木 | D+E | 0.65732 |
パターン13 | ランダムフォレスト | A | 0.64522 |
パターン14 | ランダムフォレスト | D | 0.67762 |
パターン15 | ランダムフォレスト | D+E | 0.66308 |
パターン16 | アダブースト | A | 0.63947 |
パターン17 | アダブースト | D | 0.67426 |
パターン18 | アダブースト | D+E | 0.659367 |
#4.結び
いかがでしたでしょうか。
意外に超ベーシックなモデル構築のやり方が紹介されているサイト等が少ないと思い、「そんな高度なことを知りたいんじゃなくてただモデルを1回作ってみたいだけなんだよ!」と常々思っていました。
そういった自分自身の困りごとにフォーカスして作成した記事ですので、少しでも理解の深化の一助になりましたら幸いです。