CUBE SUGAR CONTAINER

技術系のこと書きます。

Python: CatBoost をいくつかの環境でベンチマークしてみる

今回は GBDT (Gradient Boosting Decision Tree) フレームワークのひとつである CatBoost について、いくつかの環境で同一のソースコードを使って学習にかかる時間を比較してみた。 きっかけは、最近入手した Apple M2 Pro を搭載した Mac mini が、どれくらいの性能を出せるのか気になったため。

使った Python とパッケージのバージョンは次のとおり。

$ python -V              
Python 3.9.16
$ pip list | egrep -i "(scikit-learn|catboost)"
catboost            1.1.1
scikit-learn        1.2.2

もくじ

下準備

すべての環境で、あらかじめ CatBoost と scikit-learn をインストールしておく。

$ pip install catboost scikit-learn

その他、GPU を使う環境では CUDA Driver のインストールも必要になるけど、その部分は省略する。

ベンチマークについて

ベンチマークに使ったコードを以下に示す。 内容は、scikit-learn で二値分類のダミーデータを生成して、それを catboost.cv() で学習する。 そして、固定の 10,000 イテレーションを実行するのにかかる時間を測定する。

import contextlib
import logging
import time

import catboost
from catboost import Pool
from sklearn.datasets import make_classification
from sklearn.model_selection import StratifiedKFold

LOGGER = logging.getLogger(__name__)


@contextlib.contextmanager
def stopwatch():
    """実行にかかる時間を測定するコンテキストマネージャ"""
    start = time.time()
    LOGGER.info("start: CatBoost")

    yield

    end = time.time()
    LOGGER.info("end: CatBoost")

    elapsed = end - start
    LOGGER.info("elapsed time: %.2f sec", elapsed)


def main():
    log_fmt = (
        "%(process)d %(threadName)s %(name)s %(levelname)s %(message)s"
    )
    logging.basicConfig(level=logging.INFO, format=log_fmt)

    # ダミーデータを生成する
    args = {
        "n_samples": 100_000,
        "n_features": 100,
        "n_informative": 20,
        "n_redundant": 0,
        "n_repeated": 0,
        "class_sep": 0.65,
        "n_classes": 2,
        "random_state": 42,
        "shuffle": False,
    }
    x, y = make_classification(**args)

    # Stratified 5-Fold の交差検証を想定する
    folds = StratifiedKFold(
        n_splits=5,
        shuffle=True,
        random_state=42,
    )

    cat_train = Pool(x, label=y)
    cat_params = {
        "loss_function": "Logloss",
        "num_boost_round": 10_000,
        "metric_period": 1_000,
        # 学習に GPU を使う場合
        # "task_type": "GPU",
    }

    with stopwatch():
        catboost.cv(
            params=cat_params,
            pool=cat_train,
            folds=folds,
            # 時間を測るのが目的なので Early Stopping しない
        )


if __name__ == "__main__":
    main()

Apple M2 Pro w/ macOS

ここからは、実際にそれぞれの環境で実行していく。

まずは、きっかけとなった Apple M2 Pro のマシンから。 コア数は 12 (8 Performance Cores + 4 Efficient Cores) のモデルを使っている。 発売されたのは 2023 年になる。

環境は次のとおり。

$ sysctl -a | grep brand_string
machdep.cpu.brand_string: Apple M2 Pro
$ uname -srm
Darwin 22.3.0 arm64
$ sw_vers                      
ProductName:        macOS
ProductVersion:     13.2.1
BuildVersion:       22D68

ベンチマークを実行する。

$ python benchmark.py
67539 MainThread __main__ INFO start: CatBoost
...
67539 MainThread __main__ INFO end: CatBoost
67539 MainThread __main__ INFO elapsed time: 536.84 sec

実行には 536 秒かかった。

Apple M1 w/ macOS

続いては Apple M1 を載せたモデルでを試す。 M2 Pro と比べて 1 世代古く、コア数も 8 (4 Performance Cores + 4 Efficient Cores) と少ない。 発売されたのは 2020 年になる。

$ sysctl -a | grep brand_string
machdep.cpu.brand_string: Apple M1
$ uname -srm
Darwin 22.3.0 arm64
$ sw_vers                      
ProductName:        macOS
ProductVersion:     13.2.1
BuildVersion:       22D68

同様に、ベンチマークを実行する。

$ python benchmark.py 
7308 MainThread __main__ INFO start: CatBoost
...
7308 MainThread __main__ INFO end: CatBoost
7308 MainThread __main__ INFO elapsed time: 688.01 sec

こちらの環境では 688 秒かかった。

Apple M2 Pro に比べると約 28% 余計に時間がかかっている。 この差を大きいと見るか小さいと見るか。

Intel Core i7-8700B w/ macOS

続いては Intel の CPU を載せた最後の世代の Mac mini でも試す。 発売されたのは 2018 年になる。

$ sysctl -a | grep brand_string
machdep.cpu.brand_string: Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
$ uname -srm
Darwin 22.3.0 x86_64
$ sw_vers                      
ProductName:        macOS
ProductVersion:     13.2.1
BuildVersion:       22D68

実行する。

14256 MainThread __main__ INFO start: CatBoost
...
14256 MainThread __main__ INFO end: CatBoost
14256 MainThread __main__ INFO elapsed time: 850.76 sec

こちらは 850 秒かかった。 どうやら CatBoost の実行に関しては Intel CPU から Apple Silicon になって順当に性能が改善しているようだ。

Intel Core i7-12700 w/ Linux

続いては、割と最近のデスクトップ向けの x86 CPU でも比較しておく。 OS には Ubuntu 20.04 LTS を使った。

環境としてはオンプレマシンの Intel Core i7-12700 を使った。 Apple Silicon と同じようにヘテロジニアスマルチコアで 12 コア (8 Performance Cores + 4 Efficient Cores) のモデル。 発売されたのは 2022 年になる。

$ head /proc/cpuinfo | grep name
model name  : 12th Gen Intel(R) Core(TM) i7-12700
$ uname -srm
Linux 5.15.0-60-generic x86_64
$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.5 LTS"

なお、最近のデスクトップ向けの CPU は、同じモデルであっても供給する電力によってパフォーマンスが大きく変わってくる。 今回は PBT と MTP の両方に固定で 125W を設定している。

ベンチマークを実行する。

136812 MainThread __main__ INFO start: CatBoost
...
136812 MainThread __main__ INFO end: CatBoost
136812 MainThread __main__ INFO elapsed time: 394.81 sec

こちらは 394 秒かかった。

同じコア構成 (8 Performance Cores + 4 Performance Cores) の Apple M2 Pro と比較して約 26 % 時間が短縮されている。 ただし、Apple M2 Pro の消費電力は最大でも 60W なので約半分という点は留意する必要がある。

NVIDIA RTX 3060 w/ Linux

前述のオンプレマシンには NVIDIA RTX 3060 の GPU が載っている。 CatBoost は GPU を使った学習もできるため、試してみよう。

$ nvidia-smi
Fri Mar 10 22:00:21 2023       
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 530.30.02              Driver Version: 530.30.02    CUDA Version: 12.1     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                  Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf            Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  NVIDIA GeForce RTX 3060         On | 00000000:01:00.0 Off |                  N/A |
|  0%   50C    P8               14W / 170W|     14MiB / 12288MiB |      0%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+
                                                                                         
+---------------------------------------------------------------------------------------+
| Processes:                                                                            |
|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
|        ID   ID                                                             Usage      |
|=======================================================================================|
|    0   N/A  N/A      1138      G   /usr/lib/xorg/Xorg                            9MiB |
|    0   N/A  N/A      1283      G   /usr/bin/gnome-shell                          2MiB |
+---------------------------------------------------------------------------------------+

task_typeGPU に変更してベンチマークを実行する。

8379 MainThread __main__ INFO start: CatBoost
...
8379 MainThread __main__ INFO end: CatBoost
8379 MainThread __main__ INFO elapsed time: 816.01 sec

意外なことに GPU を使って学習すると 816 秒かかった。

何が意外かというと、以前であれば CatBoost は GPU を使わないと学習に長い時間がかかる印象を持っていたため。 もしかすると、最近のバージョンアップで CPU を使った学習速度に改善があったのかもしれない 1。 なお、CatBoost は CPU と GPU で使用できるパラメータやデフォルトのパラメータに差異が多い点には留意が必要になる。

NVIDIA Tesla A100 40GB w/ Linux

先ほど使ったのはコンシューマ向けで、しかもミドルレンジの GPU だった。 エンタープライズ向けのハイエンドを使ったらどうなるか気になったので、実際に試してみた。

以下の環境は Google Cloud の a2-highgpu-1g インスタンスを用意した。 NVIDIA Tesla A100 の 40GB モデルを 1 基積んで、12 vCPU と 85GB のメモリが利用できる。

$ head /proc/cpuinfo | grep name
model name  : Intel(R) Xeon(R) CPU @ 2.20GHz
$ uname -srm
Linux 5.15.0-1030-gcp x86_64
$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.5 LTS"
$ nvidia-smi
Fri Mar 10 10:20:35 2023       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 510.47.03    Driver Version: 510.47.03    CUDA Version: 11.6     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA A100-SXM...  Off  | 00000000:00:04.0 Off |                    0 |
| N/A   34C    P0    54W / 400W |      0MiB / 40960MiB |     27%      Default |
|                               |                      |             Disabled |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

ベンチマークを実行する。

11514 MainThread __main__ INFO start: CatBoost
...
11514 MainThread __main__ INFO end: CatBoost
11514 MainThread __main__ INFO elapsed time: 1251.17 sec

実行には 1251 秒かかった。

この結果は非直感的で、オンプレマシンの NVIDIA RTX 3060 よりも時間がかかっている。 もしかすると GPU 以外の何かで律速していたり、何らかのオーバーヘッドに由来するものかもしれない。 実行中の Utilization に関しては 70 ~ 80% 前後で NVIDIA RTX 3060 と大きな違いは見られなかった 2

なお、上記のマシンは以下のコマンドで作成した。

$ gcloud compute instances create a100-vm \
    --preemptible \
    --zone=us-central1-a \
    --machine-type=a2-highgpu-1g \
    --accelerator=count=1,type=nvidia-tesla-a100 \
    --image-project=deeplearning-platform-release \
    --image-family=common-container-ubuntu-2004

まとめ

以下に、各環境ごとの実行時間をグラフで示す。 左側の 3 項目が macOS で、右側の 3 項目が Ubuntu の結果になっている。

環境ごとの実行時間 (秒)

分かったことを以下に示す。

  • Mac における CatBoost の実行性能は最近の SoC で順当に改善はしているように見える
  • ただし、性能を追い求めるならコアが多くて消費電力も大きい CPU と Linux を組み合わせた方が速い
  • 少なくとも今回のベンチマークした条件においては学習に GPU を使うメリットは薄いように見える

いじょう。


  1. 何らかの理由で GPU を使った場合の学習時間が遅くなった可能性もある
  2. ただし、消費電力が 200W もいってないので全力を出せているわけでもなさそう