SIGNATEに新設されたBeginner限定コンペに参加してみました。結果はどうあれ、提出方法など一連の流れを忘れてしまった時に参考になるものが欲しいと思って今回の記事を書いてます。
そのため、データを整理し、仮説を通して新しい特徴量を考えたり、モデルのパラメータチューニングを行ったりなどは一切やっていません。加えて、必要最低限の説明もありません。
あくまで、参加登録から提出までの一連の流れまで分かればいいので、大多数の方にはあまり役に立たない記事です。そのうえで、お読みください。
SIGNATEとは
一言で言えば、Kaggleの日本版です。
将来データサイエンティストを目指す人や、データ分析に興味がある人、Kaggleをやりたいけど英語だらけでもう少し低い難易度でやりたい人などはちょうどいいかなと思います。
アカウント作成自体は無料で、Googleアカウントからもアカウントの作成は可能です。
コンペティションに参加
アカウントを作成したら興味のあるコンペティションを探し参加します。今回は、Beginner限定コンペと書かれた下のコンペに参加します。
参加するコンペティションをクリックしたら、以下のような画面が出てくるかと思います(一部抜粋)。コンペティションのルールや評価方法などが書いてありますので目を通しておきましょう。
上の画像からデータという項目を押すと以下の画面に行きます。それぞれ必要なデータをダウンロードします。
データをダウンロードする場合、利用規約がでてきます。利用規約に同意する場合はチェックをつけます。その後、データのダウンロードが可能です。
簡単ではありますが、以上でSIGNATEの参加からデータの入手までを一通り説明しました。
次からは取得したデータを用いて実際に提出するまでを見てみましょう。
SIGNATEに提出する
ここからは取得したデータから学習モデルを作成し、実際に提出するまでを見ていきましょう。
実行環境
私の場合jupyter labを使っていますが、使うものは何でもいいと思います。
Windows10 Pro Python: 3.7.7 jupyter lab: 2.1.5 pandas: 1.0.5 xgboost:0.90 Optuna:1.5.0 scikit-learn:0.23.1
ファイル構成
私はclient_targetingというフォルダを作成し、その中に先ほど入手したcsvファイルを置きました。
C:. │ submit_sample.csv │ test.csv │ train.csv │
データを読み込む
以下のようにして取得したデータを読み込みます。
import pandas as pd train = pd.read_csv("train.csv", encoding="utf-8") test = pd.read_csv("test.csv", encoding="utf-8")
データの型を確認する
データの型を確認しておきましょう。というのも、モデルの学習では型が数値のものしか計算してくれないからです。データの型の中にstring型やobject型があればそれらを数値に直す必要があります。
以下のようにして、型を調べることが出来ます。
print(train.dtypes)
出力
id int64 age int64 job object marital object education object default object balance int64 housing object loan object contact object day int64 month object duration int64 campaign int64 pdays int64 previous int64 poutcome object y int64 dtype: object
左がデータのカラム名、右が左のカラム名の型になります。何個かobject型のものがありますね。次ではobject型を数値に変換する方法を見ていきます。
データのobject型を数値の型に変換する
本来であれば、各カラムに何が入っているのかをちゃんと調査したうえで、どんな数値に変換すべきかを考えます。
しかし、今回は時間がないため(コンペティション終了まで残り2時間の時、このあたりをやってました。調べる時間なんてなかったんです......)、なんでもいいので適当な数値に直してくれるLabelEncoder
というものを使います。
使い方は以下のような感じ
from sklearn.preprocessing import LabelEncoder lbl = LabelEncoder() nonNumList = ["job", "marital", "education", "default", "housing", "loan", "contact", "month", "poutcome"] for nonNum in nonNumList: lbl.fit( list(train[nonNum].unique()) ) train[nonNum] = lbl.transform(train[nonNum]) lbl.fit( list(test[nonNum].unique()) ) test[nonNum] = lbl.transform(test[nonNum])
これで、先ほどのようにデータの型を調べると
print(train.dtypes)
id int64 age int64 job int32 marital int32 education int32 default int32 balance int64 housing int32 loan int32 contact int32 day int64 month int32 duration int64 campaign int64 pdays int64 previous int64 poutcome int32 y int64 dtype: object
見事に数値の型だけになりましたね。
欠損値が無いかを調べる
続いて、欠損値が無いかを調べてみましょう。
train.isnull().sum()
.isnull().sum()
で各カラムごとの欠損値の数を出してくれます。
id 0 age 0 job 0 marital 0 education 0 default 0 balance 0 housing 0 loan 0 contact 0 day 0 month 0 duration 0 campaign 0 pdays 0 previous 0 poutcome 0 y 0 dtype: int64
今回は奇跡的にありませんでした。とはいえ、もし仮にあった場合はどうするかというと、そのカラムの平均値や中央値を代入するなどして、対応します。
このあたりは、実際にデータを詳しく調べてみないと分かりません。場合によっては、他の値で代入することも考えられます。
学習する
さて、ある程度データが整理出来たらモデルを学習させます。
そのためにまず、訓練データであるtrain
を説明変数と目的変数に分けます。
X = train.drop(["id", "y"], axis = 1) y = train["y"]
.drop
で指定したカラム以外を説明変数としてX
に入れます。train
のカラムにあるid
はただの番号で、特徴量ではありませんので省きます。また、y
は目的変数のため、これも省きます(私はid
を省くのを忘れていました。そのため、結果が芳しくなかったのではないかといまさら考えています)。
反対に、y
には目的変数だけを入れるため、train["y"]
で指定します。
これで、学習の準備は整ったので、早速学習していきましょう。
モデルを作成する
それでは実際にモデルを作成していきましょう。実際、作るモデルはKeras
だろうが、PyTorch
だろうが、なんでもいいのですが、私は以前にXGBoost
とOptuna
を使ったことがあるので、これらを使って学習を行います。もし入っていなければ、pip install
しましょう。
XGBoost
はモデル、Optuna
はハイパーパラメータチューニング用ですね。
$ pip install xgboost $ pip install optuna
Optuna
の使い方は以前の記事で雑に書いてます。個人的には、さらに下にある記事を読んでいただければ、Optuna
の大枠は理解できるかと思います。
また、scikit-learn
のtrain_test_split
で訓練データとテストデータを生成します。その後、訓練データはモデルの学習用、テストデータはモデルの精度評価用として使用されます。
学習部分は以下のようなコードになります。
##学習 import xgboost as xgb from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score import optuna X = train.drop(["y"], axis = 1) y = train["y"] def objective(trial): x_train, x_test, y_train, y_test = train_test_split(X_resampled, y_resampled, test_size=.3) # 訓練データ・テストデータの生成 estimators = trial.suggest_categorical("n_estimators", [6, 10, 15, 20, 30]) learning_rate = trial.suggest_uniform('eta', 0.1, 0.2) # 0.2~0.5 reg_lambda = trial.suggest_uniform('lambda', 0.1, 0.2) # 0.2~0.5 clf = xgb.XGBClassifier(objective='binary:logistic', eval_metric = 'rmse', n_estimators = estimators, learning_rate = learning_rate, reg_lambda = reg_lambda) clf.fit(x_train, y_train) y_pred = clf.predict(x_test) return clf.score(x_test, y_test) study = optuna.create_study() study.optimize(objective, n_trials=100) print(study.best_params) print(study.best_value) print(study.best_trial)
Optuna
でハイパーパラメータチューニングを行っているので、パラメータのチューニングに時間はかかりますが、すぐに終わるかと思います。
次は、Optuna
で採択されたパラメータで予測を行いましょう。
予測する
先ほど、Optunaを使ってパラメータチューニングを行いました。しかし、先ほどの学習で作られたモデルは保存していませんので、再度モデルを作る必要があります。
加えて、先ほどの学習で得られたパラメータを用いて、予測を行いましょう。
ソースコードは以下になります。予測した結果はcsvファイルに書き込んでいます。
clf = xgb.XGBClassifier(objective='binary:logistic', eval_metric = 'rmse', n_estimators = 6, learning_rate = 0.1144182440680572, reg_lambda = 0.10741801759629138) clf.fit(x_train, y_train) # 学習 # test = pd.read_csv("test.csv", encoding="utf-8") # ブログ冒頭で予測したい本番データを読み込んでいる y_pred = clf.predict(test) sub = pd.read_csv('submit_sample.csv', encoding = 'UTF-8', names=['id', 'ans']) sub['ans'] = list(map(int, y_pred)) sub.to_csv("clientTargeting_XGBoost_0831.csv", header=False, index=False)#ヘッダー無しでcsvに書き込み
提出する
ここまでくれば、後は提出するだけです。
SIGNATEの参加しているコンペティションのWebページに移動し、下図の投稿を押します。
すると、下のような画面が出てくるかと思います。この画面のフォルダを選択を押して、先ほど書き込んだcsvファイルを選択します。選択したら、同じ画面の投稿ボタンを押すことで、提出できます。
後は結果を待つだけです。結果は登録してあるメールアドレスに届きますし、しばらくすれば、Webページにも結果が出ます。
ちなみに、提出は1日につき5回までになります。あまり精度が上がらない場合は、無理に提出するのではなく、他の特徴量を考えたりするなどして、工夫を施していきましょう!
1回目の提出結果↓
ちょこちょこ調整して4回目の提出結果↓
最終結果は70%程度ですね。残り2時間から一応ここまで頑張れました。
コラム:フォーラムを見よう
コンペティションにはフォーラムというところがあります。
ここでは、今回のコンペティションに参加している方が運営の方へ質問したり、参加者同士で情報を共有するような場所です。もし詰まっていることがあれば、このフォーラムに疑問点を投稿したり、相談したりしてみましょう。
まとめ
もっと、早めに取り組めば昇格が狙えたかもしれません。また次の機会に頑張ります。
ソースコード全体
本記事では記載していない部分もありますが、そこはご容赦ください。
import pandas as pd train = pd.read_csv("train.csv", encoding="utf-8") test = pd.read_csv("test.csv", encoding="utf-8") print(train.dtypes)#型を調べる from sklearn.preprocessing import LabelEncoder lbl = LabelEncoder() nonNumList = ["job", "marital", "education", "default", "housing", "loan", "contact", "month", "poutcome"] for nonNum in nonNumList: lbl.fit( list(train[nonNum].unique()) ) train[nonNum] = lbl.transform(train[nonNum]) lbl.fit( list(test[nonNum].unique()) ) test[nonNum] = lbl.transform(test[nonNum]) # 要素を調べる print(train.y.unique()) # 欠損値を調べる train.isnull().sum() ##学習 import xgboost as xgb from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score import optuna from imblearn.under_sampling import RandomUnderSampler X = train.drop(["y"], axis = 1) y = train["y"] def objective(trial): sampler = RandomUnderSampler() X_resampled, y_resampled = sampler.fit_resample(X, y) #n_estimators x_train, x_test, y_train, y_test = train_test_split(X_resampled, y_resampled, test_size=.3) # 訓練データ・テストデータの生成 estimators = trial.suggest_categorical("n_estimators", [6, 10, 15, 20, 30]) learning_rate = trial.suggest_uniform('eta', 0.1, 0.2) # 0.2~0.5 reg_lambda = trial.suggest_uniform('lambda', 0.1, 0.2) # 0.2~0.5 clf = xgb.XGBClassifier(objective='binary:logistic', eval_metric = 'rmse', n_estimators = estimators, learning_rate = learning_rate, reg_lambda = reg_lambda) clf.fit(x_train, y_train) y_pred = clf.predict(x_test) return clf.score(x_test, y_test) study = optuna.create_study() study.optimize(objective, n_trials=100) print(study.best_params) print(study.best_value) print(study.best_trial) sampler = RandomUnderSampler(random_state=42) X_resampled, y_resampled = sampler.fit_resample(X, y) clf = xgb.XGBClassifier(objective='binary:logistic', eval_metric = 'rmse', n_estimators = 6, learning_rate = 0.1144182440680572, reg_lambda = 0.10741801759629138) clf.fit(X_resampled, y_resampled) # submmit y_pred = clf.predict(test) sub = pd.read_csv('submit_sample.csv', encoding = 'UTF-8', names=['id', 'ans']) sub['ans'] = list(map(int, y_pred)) sub.to_csv("clientTargeting_XGBoost_0831.csv", header=False, index=False)#ヘッダー無しでcsvに書き込み