GBDTライブラリ：CatBoostで燃費予測(回帰)をやってみた

GBDT(Gradient Boosting Decision Trees)なライブラリであるXGBoost, lightGBMと並んで使われることがある[CatBoost](https://catboost.ai/)を最近まで知らなかったので、回帰タスクで動かしてみました。

## CatBoost？

公式サイトの紹介文を貼ります。（Google翻訳したもの）

```
CatBoostは、決定木の勾配ブースティングのアルゴリズムです。
Yandexの研究者およびエンジニアによって開発され、
検索、推奨システム、パーソナルアシスタント、自動運転車、天気予報、およびYandexやCERN、Cloudflare、Careemタクシーなどの他社の多くのタスクに使用されます。
```

## 参考にさせていただいた記事
- [回帰：燃費を予測する](https://www.tensorflow.org/tutorials/keras/regression?hl=ja)
- [House Prices Regression Using CatBoost](https://www.kaggle.com/miloofcroton/house-prices-regression-using-catboost-2/)

## 今回使わせていただいたデータセット

- Auto MPG データセット
  - [こちらのTensorFlow Tutorials](https://www.tensorflow.org/tutorials/keras/regression?hl=ja)で使われているデータセットです。
  - 自動車の燃費を予測します。説明変数には、気筒数、排気量、馬力、重量などが含まれています。

## 内容

以下のコードはGoogle Colab上で実行しました。（CPU）

###  CatBoostをpipでインストール

```
!pip install catboost -U
```

### データセットをダウンロード

```python
import urllib.request

url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data'
file_path = './auto-mpg.data'
urllib.request.urlretrieve(url, file_path)
```

### データの前処理

```python
import pandas as pd

column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',
                   'Acceleration', 'Model Year', 'Origin'] 
dataset = pd.read_csv(file_path, names=column_names,
                      na_values = "?", comment='\t',
                      sep=" ", skipinitialspace=True)

# 今回は動かすことが目的なのでnanはdropする
dataset = dataset.dropna().reset_index(drop=True)

# カテゴリ変数: OriginはCatBoostにて取り扱うのでString型にしておく
dataset['Origin'] = dataset['Origin'].astype(str)

train_dataset = dataset.sample(frac=0.8,random_state=0)
test_dataset = dataset.drop(train_dataset.index)
train_labels = train_dataset.pop('MPG')
test_labels = test_dataset.pop('MPG')
```

### CatBoostが使うためのデータセットを用意する

```python
import numpy as np
from catboost import CatBoostRegressor, FeaturesData, Pool

def split_features(df):
    cfc = []
    nfc = []
    for column in df:
        if column == 'Origin':
            cfc.append(column)
        else:
            nfc.append(column)
    return df[cfc], df[nfc]

cat_train, num_train = split_features(train_dataset)
cat_test, num_test = split_features(test_dataset)

train_pool = Pool(
    data = FeaturesData(num_feature_data = np.array(num_train.values, dtype=np.float32), 
                    cat_feature_data = np.array(cat_train.values, dtype=object), 
                    num_feature_names = list(num_train.columns.values), 
                    cat_feature_names = list(cat_train.columns.values)),
    label =  np.array(train_labels, dtype=np.float32)
)

test_pool = Pool(
    data = FeaturesData(num_feature_data = np.array(num_test.values, dtype=np.float32), 
                    cat_feature_data = np.array(cat_test.values, dtype=object), 
                    num_feature_names = list(num_test.columns.values), 
                    cat_feature_names = list(cat_test.columns.values))
)
```

### 学習

```python
model = CatBoostRegressor(iterations=2000, learning_rate=0.05, depth=5)
model.fit(train_pool)
```
上記のパラメータは参考記事のままの値としています。
ちなみに学習は`total: 4.3 s`で終わりました。


### 推論・結果プロット

```python
import matplotlib.pyplot as plt

preds = model.predict(test_pool)

xs = list(range(len(test_labels)))
plt.plot(xs, test_labels.values, color = 'r')
plt.plot(xs, preds, color = 'k');
plt.legend(['Target', 'Prediction'], loc = 'upper left');
plt.show()
```

プロットすると以下のような結果となりました。
![catboost_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120225/32e2faac-56ba-2e52-3f0a-84edfec3a8c6.png)

## 感想など

- 今回は参考記事ほぼそのままで動かしただけなのですが、回帰でのざっくりした使い方がわかってよかったです。
- 参考にしたKaggleのKernelへもコメントされている内容ですが、ハイパーパラメータチューニングでBayesSearchCVを使うのが良いようなので、次に試してみようかと思います。（[こちらの資料](https://colab.research.google.com/github/lmassaron/kaggledays-2019-gbdt/blob/master/Kaggle%20Days%20Paris%20-%20%20GBDT%20workshop.ipynb#scrollTo=WvJdFN3xbyIz)が参考になりそうでした）