Kaggleをやってみた – Forest Cover Type #02

Pocket

今日はKaggleのForest Cover Type (Kernels Only) というコンペティションの記録第二回。

何をやったかというととりあえずものは試しということで自分がわかるものをすごく簡単に1つ試してみた。

前回の記事通り、データを見てみるとone-hotエンコードされているデータが多いので一旦ランダムフォレストタイプのもので学習させてみようと思い、Kaggleでは有名なLight-GBMを使うことに。

いずれはこのアルゴリズムの解説記事を作りたいところ。

LightGBMを使えるようにする

macOS High Sierra ver10.13.4を使っている。

以下のサイトを参照に必要なモジュールをインストールして使ってみた。

インストール

$ brew install cmake gcc@7

pipを使う。

$ export CXX=g++-7 CC=gcc-7
$ pip install --no-binary lightgbm lightgbm
$ pip list --format=columns | grep -i lightgbm
lightgbm        2.1.1

早速試す

前処理

とりあえず簡単な前処理を。
欠損値(いわゆるNaNとか)は無いのがわかっていたのでそこを気にせずどんどん進めていく。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

import lightgbm as lgb
from sklearn.model_selection import train_test_split
import gc

次にデータを読み込む。

train = pd.read_csv("../input/train.csv")
train.head()

出力結果は以下の通り。


Id Elevation Aspect Slope Horizontal_Distance_To_Hydrology Vertical_Distance_To_Hydrology Horizontal_Distance_To_Roadways Hillshade_9am Hillshade_Noon Hillshade_3pm Horizontal_Distance_To_Fire_Points Wilderness_Area1 Wilderness_Area2 Wilderness_Area3 Wilderness_Area4 Soil_Type1 Soil_Type2 Soil_Type3 Soil_Type4 Soil_Type5 Soil_Type6 Soil_Type7 Soil_Type8 Soil_Type9 Soil_Type10 Soil_Type11 Soil_Type12 Soil_Type13 Soil_Type14 Soil_Type15 Soil_Type16 Soil_Type17 Soil_Type18 Soil_Type19 Soil_Type20 Soil_Type21 Soil_Type22 Soil_Type23 Soil_Type24 Soil_Type25 Soil_Type26 Soil_Type27 Soil_Type28 Soil_Type29 Soil_Type30 Soil_Type31 Soil_Type32 Soil_Type33 Soil_Type34 Soil_Type35 Soil_Type36 Soil_Type37 Soil_Type38 Soil_Type39 Soil_Type40 Cover_Type
0 1 2596 51 3 258 0 510 221 232 148 6279 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 5
1 2 2590 56 2 212 -6 390 220 235 151 6225 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 5
2 3 2804 139 9 268 65 3180 234 238 135 6121 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
3 4 2785 155 18 242 118 3090 238 238 122 6211 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 2
4 5 2595 45 2 153 -1 391 220 234 150 6172 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 5

前処理する関数を作る。
テストデータも同様に処理できるよう、Cover_Typeだけ個別に操作できるように引数を指定して切り替えができるようにしておく。

一応距離関係(特定の地域までの距離を示す`Horizontal…`と標高を表す`Elevation`)については対数の方が効果的だと思い、対数をとる。
今思うと`log(1+x)`をとる関数`np.log1p`があったのでそれを今度から使おう。
以下で定義した関数を実行しようとするとRunTimeWarningが出てしまった。

def pre_process(train, tr=True):
    train["log_Elevation"] = np.log(train["Elevation"] + 1e-1 )
    train["log_Horizontal_Distance_To_Hydrology"] = np.log(train["Horizontal_Distance_To_Hydrology"])
    train["log_Horizontal_Distance_To_Roadways"] = np.log(train["Horizontal_Distance_To_Roadways"])
    train["log_Horizontal_Distance_To_Fire_Points"] = np.log(train["Horizontal_Distance_To_Fire_Points"])
    del train["Horizontal_Distance_To_Fire_Points"]
    del train["Horizontal_Distance_To_Hydrology"]
    del train["Horizontal_Distance_To_Roadways"]
    del train["Elevation"]
    if tr: # ラベルが1~7に対してlgbmでは0~6になるので1つずつ下げる。  
        y = train["Cover_Type"].copy() - 1
        del train["Cover_Type"]
        return train, y
    return train

ここで1つずつラベル(Cover_Type)を1つずつ下げていることに注意。
(どうやらlgbmだと0~6でラベルづけをしているらしい)

提出するファイルを作成するとき、1つずつ足すことを忘れないようにしないと。

モデルに入れて学習

とりあえずモデルに入れて学習させる。

X, y = pre_process(train)
X = X.values
y = y.values
lgbm_params = {
    'objective' : 'multiclass',
    'num_class' : 7,
}
model_list = []
acc_list = []
for i in range(5):
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.25, random_state=42
       )
    lgb_train = lgb.Dataset(X_train, y_train) # lgbに入れ込むようのデータセットに変形する。  
    lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
    # ここから学習開始  
    model = lgb.train(lgbm_params, lgb_train, valid_sets=lgb_eval)
    print("training finished!!")

    y_pred = model.predict(X_test, num_iteration=model.best_iteration)
    y_pred_max = np.argmax(y_pred, axis=1)

    accuracy = sum(y_test == y_pred_max) / len(y_test)
    print(accuracy)
    model_list.append(model)
    acc_list.append(accuracy)

学習モデルでもっとも性能が良かったものを使うことにする。

arr_acc = np.array(acc_list)
model = model_list(np.argmax(arr_acc))

予測値をだす

提出ファイルを作るために予測をしていく。

test = pd.read_csv("data/test.csv")
test_X = pre_process(test, tr=False)

y_pred_max = np.argmax(y_pred + 1, axis=1) # 予測した値の中で一番大きいものをそのセットでの予測ラベルとする。  
y_pred_max = y_pred_max.reshape(-1, 1) # 連結しやすいよう縦ベクトルに変形
test_id = test_id.reshape(-1,1) # 同じく縦ベクトルに変形。  
sub = np.hstack((test_id, y_pred_max)) # 横方向(horizontal)にくっつける  
submission = pd.DataFrame(sub, columns=['Id', 'Cover_Type']) # DataFrameを使ってラベルづけ  
submission.to_csv("output/submission_test.csv", index=False) # 提出ファイルの出力  

結果

とりあえずこれをkernel(Kaggleで与えられたサーバー上の実行環境)で実行して、生成されたファイルを提出するとスコアが記録される。

きになるスコアは

0.6903!! だった。

順位は147チーム中120位だった。

まとめ

かなり簡単なものだったが一応スコア的なものが出たのはちょっと安心。
とはいえまだまだ改善の余地があるので他の人のコードを読み込んで、どんな学習モデルを使っているのかを見ていきたい。

Pocket