PyTorch Tabular による表形式データの深層学習

ちょっくら機械学習ネタ。基本的に Python 書きたくないのでたまにしかやらない。

PyTorch Tabular とは

分類や回帰などの表形式データに対する機械学習には XGBoost, LightGBM などの勾配ブースティング手法が有効であることが知られている。一方で、近年になって表形式データに対する深層学習ベースの手法が提案されており、勾配ブースティングに匹敵する性能を出しつつある。詳細は以下のブログの記事を参照のこと。

deep-and-shallow.com

PyTorch Tabular は表形式データに対する深層学習のフレームワークである。お馴染みの深層学習フレームワークである PyTorch や PyTorch Lightning の上に実装されており、特に手軽に表形式データ(pandas のデータフレーム)に対する深層学習を実現することを狙いとしている。具体的には、深層学習のための高レベルな API が提供されていたり、 最新手法(Neural Oblivious Decision Ensembles for Deep Learning on Tabular DataTabNet: Attentive Interpretable Tabular Learning)の実装が提供されている。Githubリポジトリは以下であるが、2020年の12月に開発が始まったばかりであり、まだまだ開発途上といった感じである。

github.com

PyTorch Tabular のインストール

Github の手順に記載されているとおり、まず PyTorch の公式 から PyTroch をインストールする。このときマシンの GPU に合ったバージョンの CUDA を選択するようにする。マシンが Linux の場合は nvidia-cuda-toolkit パッケージの nvcc --version コマンドとか叩けばよい。詳細は以下。

How to get the CUDA version? - Stack Overflow

あとは pip でインストールするだけ。

$ pip install pytorch_tabular[all]

PyTorch Tabular を動かしてみる

PyTorch Tabular を使って、深層学習による簡単な分類(classification)のプログラムを実装してみた。そのソースコードの一部を以下に示してゆく。

まずは適当なデータセットを入手するところを以下に示す。

from sklearn.datasets import make_classification
def make_mixed_classification(n_samples, n_features, n_categories):
    X,y = make_classification(n_samples=n_samples, n_features=n_features, ...)
    ...

data, cat_col_names, num_col_names = make_mixed_classification(n_samples=10000, n_features=20, n_categories=4)
train, test = train_test_split(data, random_state=42)
train, val  = train_test_split(train, random_state=42)

sklearn.datasets の make_classification 関数を用いて、適当な分類のためのサンプルデータを生成している。n_samples=10000 とすることで、データセットのサイズを10,000 件としている。このデータセットを training set, validation set, test set に分割している。このあたりの用語がややこしくなったら Wikipedia のページ を見るべし。

つぎにモデルを定義する部分を以下に示す。

data_config = DataConfig(
    target=['target'], #target should always be a list. Multi-targets are only supported for regression. Multi-Task Classification is not implemented
    continuous_cols=num_col_names,
    categorical_cols=cat_col_names,
)
trainer_config = TrainerConfig(
    auto_lr_find=True, # Runs the LRFinder to automatically derive a learning rate
    batch_size=1024,
    max_epochs=100,
    gpus=1, #index of the GPU to use. 0, means CPU
)
optimizer_config = OptimizerConfig()

model_config = CategoryEmbeddingModelConfig(
    task="classification",
    layers="1024-512-512",  # Number of nodes in each layer
    activation="LeakyReLU", # Activation between each layers
    learning_rate = 1e-3
)

tabular_model = TabularModel(
    data_config=data_config,
    model_config=model_config,
    optimizer_config=optimizer_config,
    trainer_config=trainer_config,
)

PyTorch Tabular ではTabularModel というモデルを構築する。これに与えるパラメータは DataConfig, TrainerConfig, OptimizerConfig, CategoryEmbeddingModelConfig の4つであり、ニューラルネットワークアーキテクチャなどを指定している。このあたりを手軽にカスタマイズできるのがこのフレームワークの強みである。冒頭であった最新手法のモデルをどう構築できるのかはよく分かっていない。現状ドキュメンテーションが十分でないので、使い方を知るにはソースコード読む必要がありそう。

それにしても "classification" みたいな文字列を関数の引数に与えるのって バグの温床だと思うんだがあまり気にしないのかな。有限個の選択肢を指定するだけなんだから enum を定義してほしい。

最後に、データセットのデータを用いて学習を実行する。

tabular_model.fit(train=train, validation=val)
result = tabular_model.evaluate(test)

もし Jupyter Notebook を使っていて、このとき Widget Javascript not detected. It may not be installed or enabled properly. というエラーが発生する場合は以下を参照。プログレスバーの描画が上手くいっていない状況である。

python - Jupyter notebook: Widget Javascript not detected - Stack Overflow

学習の結果として、以下のような結果がレポートされる。データセットのサイズを 10,000 としたときの test set の accuracy で 0.78。なにも考えずに適当に実行してこの精度ならなかなかよいんじゃないか。ちなみに学習のための乱数のシードを固定してないので、実行ごとに異なる結果がレポートされる。

--------------------------------------------------------------------------------
DATALOADER:0 TEST RESULTS
{'test_accuracy': tensor(0.7776, device='cuda:0'),
 'train_accuracy': tensor(0.6200, device='cuda:0'),
 'train_loss': tensor(0.7699, device='cuda:0'),
 'valid_accuracy': tensor(0.7627, device='cuda:0'),
 'valid_loss': tensor(0.5853, device='cuda:0')}
--------------------------------------------------------------------------------

おまけとして、データセットのサイズを少なめの 1,000 にしたときの実行結果は以下である。

--------------------------------------------------------------------------------
DATALOADER:0 TEST RESULTS
{'test_accuracy': tensor(0.6840, device='cuda:0'),
 'train_accuracy': tensor(0.5516, device='cuda:0'),
 'train_loss': tensor(1.5615, device='cuda:0'),
 'valid_accuracy': tensor(0.4787, device='cuda:0'),
 'valid_loss': tensor(0.8586, device='cuda:0')}
--------------------------------------------------------------------------------

test set の精度が 0.68。やはり10,000 件のときより精度が下がった。機械学習においてデータ量は大切。

※ 実装したプログラム全体は Gist に置いた。

所感

  • 今後ますます性能の高い手法が発表されるのだろうが、こういった透過的なインターフェースをもつフレームワークに最新手法が搭載されていくのはよいことである。既存手法との比較とかやりやすそう。
  • それにしても Python を中心とした機械学習まわりのエコシステムがツラい。書きやすくもないし保守しやすくもない。強いて言うなら一人でフルスクラッチで書くなら分かる。多様なライブラリやフレームワークを使うことが前提なのに Python が主流になってる世の中がツラい。