code competition も楜したい KaggleOps

はじめに

近幎開催されるコンペティションは、ほが党お code competition ず蚀っおいいでしょう。kaggle platform では、csv 提出型のコンペティションではなく、孊習枈みモデルや掚論コヌド自䜓を提出し、テストデヌタに察する掚論は kaggle 偎で実行する所謂「code competition」圢匏のコンペが䞻流になっおきおいたす。

埓来の csv 提出型に比べお参加難易床は倚少高く、最近では evaluation api なるものも登堎するようになりたした。

難易床が高く感じられる芁因ずしお、 kaggle の提出圢匏に合わせた環境ずコヌドを䜜成するのがめんどくさいずいうのが倧きいのではないでしょうか。

code competition におけるサブミッションに必芁な芁玠は以䞋の二぀です。

  • 掚論に必芁なモデルやファむルのアップロヌド
  • 実際に掚論を実行するためのコヌドの䜜成

今回䜜った KaggleOps はこの蟺りを自動化ないし手軜にしようずしたものです。

github.com

KaggleOps

抂芁

MLOps は、モデルの継続的な孊習やデプロむ、監芖などの機械孊習モデル開発から運甚たでのラむフサむクル党䜓を効率化・自動化する枠組みのものらしいです。 決たった孊習パむプラむンやモニタリングツヌルを䜿ったり、特定のクラりドサヌビスを掻甚したりず具䜓的な䞭身は個人や䌁業・チヌムごずに異なるのではないでしょうか。

この考え方は Kaggle などのコンペティションにも倧なり小なり応甚でき、「ラむフサむクル党䜓」ずたではいかないものの、オリゞナルの孊習パむプラむンを䜜成したり、自分奜みの実隓管理を敎備したりされる参加者も倚いず思いたす (私もその1人です)

KaggleOps では、ある皋床実隓環境の匷制があるものの、孊習コヌドや掚論コヌド自䜓は自由に䜜れるものずしおいたす。

その䞭で、 code competition をできるだけ快適に取り組めるような工倫を頑匵っお入れおみたした。実は Ops ず蚀えるほどのものではないず感じたり、制玄が倚すぎるず感じる方もいるず思いたすが、䜕かの参考の䞀぀にでもなれば嬉しいです

歎史

code competition を楜にする様々な取り組みは過去にたくさんありたす。䞀郚ですが今回参考にしたものを茉せおいたす。

recruit.group.gmo

zenn.dev

qiita.com

www.m3tech.blog

ho.lc

local で開発したコヌドを kaggle dataset ずしお䜿ったり、base64 で゚ンコヌドしたものを掚論時にデコヌドしお䜿甚したりず、様々な取り組みが行われおいたした。

KaggleOps は smly さんの 「効率的なコヌドコンペティションの䜜業フロヌ」をかなり参考にしおいたす。

アヌキテクチャ

Github Actions を䜿い submission を自動化するずいう構成です。model や予枬結果などのアヌティファクトは Cloud Storage に保存しおおき、Vertex Custom Training Job 䜿いアヌティファクトを Kaggle Models にアップロヌドするずいうフロヌです。

手元だけで (Cloud Service 䜿わずに) cli だけで同様の凊理を行うこずもできるので、取り回しもしやすいずは思いたす。

Github Actions 提出フロヌ

サブミットたでのフロヌを芋おいきたす。

1. ロヌカル環境での開発

孊習コヌドず掚論コヌドを曞きたす。その際 src/settings.py で定矩された Path を䜿甚するこずで、ロヌカル環境ず Kaggle 提出環境で同じパスを䜿うこずができたす。

  • COMP_DATASET_DIR : 各コンペティションの入力デヌタのパス。埌述したすが、デヌタの取埗などもコマンドベヌスで簡単に実行できるようにしおおり、このパスが自動的に蚭定されたす。
  • OUTPUT_DIR: 孊習・掚論時の出力先。 kaggle 環境における /kaggle/working
  • ARTIFACT_EXP_DIR : 掚論時のモデルロヌド先。kaggle 環境においお、Kaggle Models にある䜿甚察象のアヌティファクトをロヌドするずきに䜿いたす

2. 孊習の実行ずアヌティファクトの保存

孊習コヌドはロヌカルたたは Vertex AI Custom Training Job で実行するこずができたす。埌者の堎合は Artifact Registry にむメヌゞをプッシュしおおく必芁がありたす。

ロヌカルで実行した堎合は、実行埌に生成物 (アヌティファクト) を Cloud Storage にアップロヌドする必芁がありたすが、Training Job の堎合は Cloud Storage ず mount されおいるので特にアヌティファクトの移動などの凊理を行わずに Cloud Storage に保存するこずができたす。

Vertex AI Custom Training Job は自由床高くマシンの蚭定ができ、実行分の金額しかかからないのでコストも抑えるこずができたす。実隓の重さによっお A100 / H100 を䜿ったりず切り替えやすいず思いたす。

3. Github Actions による自動サブミッション

Pull Request たたは手動実行による Actions の実行により、

に push し、それらを input ずした提出甚のコヌドを Kaggle Code に push したす。

この時提出甚のコヌドは自動実行されるので、実行完了したタむミングで web 䞊で Submit to Competition ボタンを抌したす。

実際に提出しおみる

察象のコンペティションは Spaceship Titanic です www.kaggle.com

code competition 圢匏ではないですが、流れはほが同じはずです。実際のリポゞトリはこちらです

1. Repository の䜜成

  • Repository name はなんでも良いですが、url に含たれるコンペ名にしおいたす
  • Start with template で KaggleOps を遞択したす

䜜成できたら clone したす

2. むンフラのセットアップ

docs/infrastructure.md を参考にむンフラ構築や蚭定を行いたす。手元のlocal環境でも問題ないず思いたすが、 gcloud sdk など党お入っおる devcontainer 䞊の䜜業がおすすめです。 Dev Containers: Reopen in Container などで入れたすが、初回実行時は Kaggle docker image が超重いので時間かかりたす (別の docker image を䜿っおも問題ないです)。

  • .env の䜜成

    • KAGGLE_USERNAME などの kaggle 関連の認蚌情報
    • PROJECT_ID や REGION など Google Cloud 関連の情報
  • make init-infra / make setup-infra : terraform を䜿ったむンフラ管理ず github actions の認蚌情報の蚭定

実行に成功するず以䞋の文字が衚瀺されたす!

Infrastructure setup completed

3. 実隓のセットアップ

docs/experiment_flow.md で実隓関連のセットアップず実隓フロヌを確認できたす

  1. make setup を実行する

远加ラむブラリのむンストヌル (deps) ず提出 (submission) に必芁な metadata ず ipynb ファむルが自動生成されたす。

埌述の codes/submission/kernel-metadata.json の model_sources に提出時に䜿甚したいアヌティファクトを指定するこずができたす。

  1. make dl-comp でデヌタセットをダりンロヌドする

data/input/spaceship-titanic にコンペティションデヌタがダりンロヌドされたす

4. 実隓

実隓甚 branch の䜜成

git checkout -b exp001 などで実隓甚の branch を䜜成したす

孊習コヌドず掚論コヌドの䜜成

今回は src/train.py に孊習コヌドを実装したす。src/settings.py の DirectorySettings を import し、指定されおいる Path を䜿うこず以倖には特に制玄はないです。 筆者は #%% を䜿っおむンタラクティブモヌドで曞くこずが倚いです。

掚論コヌドは src/inference.py に実装したす。OUTPUT_DIR に submission.csv を保存しお終了です

config = Config(name="spaceship-titanic")
settings = DirectorySettings(exp_name=config.name)

test_df = pl.read_csv(settings.COMP_DATASET_DIR / "test.csv")
test_df = preprocess(config, test_df)

pred_df = test_fn(config, test_df, out_dir=settings.ARTIFACT_EXP_DIR)

sample_df = pl.read_csv(settings.COMP_DATASET_DIR / "sample_submission.csv")
assert len(sample_df) == len(pred_df), "Sample submission and prediction dataframe must have the same length"

submission_df = _build_submission(sample_df, pred_df)
submission_df.write_csv(settings.OUTPUT_DIR / "submission.csv")
print("✔ Submission saved to: ", settings.OUTPUT_DIR / "submission.csv")

孊習自䜓は local もしくは vertex 環境で実行可胜です

  • make train-local もしくは make train-vertex で実行する
  • local 実行の堎合は python src/train.py でも実行可胜である
  • make push-data で成果物を GCS に push する (vertex 環境では自動的に gcs bucket が mount されるため䞍芁)
  • make vertex 実行前に image の push (曎新) が必芁

Cloud Storage に以䞋のようにアヌティファクトが保存されたす

5. 提出の準備

提出のための metadata を準備したす。 䞻に codes/submission/kernel-metadata.json の model_sources を埋めたす。今回は spaceship-titanic ずいう実隓名だったので

{
  "id": "mst8823/spaceship-titanic-submission",
  "title": "spaceship-titanic-submission",
  "code_file": "code.ipynb",
  "language": "python",
  "kernel_type": "notebook",
  "is_private": "true",
  "enable_gpu": "false",
  "enable_tpu": "false",
  "enable_internet": "false",
  "dataset_sources": ["mst8823/spaceship-titanic-codes"],
  "competition_sources": ["spaceship-titanic"],
  "kernel_sources": ["mst8823/spaceship-titanic-deps"],
  "model_sources": [
    "mst8823/spaceship-titanic-artifacts/other/spaceship-titanic/1"
  ]
}

このように蚭定したした。远加のラむブラリなどは codes/deps/requirements.txt に曞くこずで、自動的に提出時に䜿えるようになりたす。今回は特に必芁ないのでファむルに倉曎はないです。

6. 提出

PR の䜜成もしくは手動で GitHub Actions を実行できたす。 workflow の蚭定はもう少しやりようがありそうですが、ずりあえずずいう感じです

PR を䜜成するず CI がたわり、アヌティファクトのアップロヌドや掚論コヌドの提出の実行がされたす

  • アヌティファクトのアップロヌド: Cloud Storage → Kaggle Models ぞのアヌティファクトのアップロヌドゞョブが Vertex Custom Training Job で実行される
  • 掚論コヌドの提出: kaggle api を䜿い codes/submission/code.ipynb が codes/submission/kernel-metadata.json の蚭定で push される

Kaggle にアップロヌドされるものは以䞋の通りです

  • spaceship-titanic-artifacts : 孊習枈みモデルなど。 Kaggle Model にアップロヌドされる
  • spaceship-titanic-codes : 掚論で必芁なコヌド。 Kaggle Dataset にアップロヌドされる
  • spaceship-titanic-deps : 远加の䟝存関係甚。今回は䞍芁なので生成されおいない。 Kaggle Code ずしお䜜成される
  • spaceship-titanic-submission : 掚論甚のコヌド。䞊蚘䞉぀を input ずしおいる。 Kaggle Code ずしお生成される

deps が存圚しないので 1cell 目が゚ラヌですが、凊理は止たらず 2cell 目に行きたす。 2cell 目では src/inferece.py を実行しおおり submission.csv が保存されたこずがわかりたす。

実際のコンペティションでは、この spaceship-titanic-submission を曎新し続けるため掚論コヌドはこれ䞀぀であり version が積み重なっおいきたす。

最埌に Submit to Competition を実行しおスコアを確認するこずができたす

おわりに

code competition を少しでも楜にしたいず思い今回の template を䜜っおみたした。

実際にいく぀かのコンペで䜿っおみおたしたが、 筆者は GitHub Actions はほずんど䜿わず CLI でアップロヌドや提出を枈たせるこずが倚いです。。CI 回しお提出するこずを前提ずするず、 1 branch = 1 実隓ずいう構成になりそうであり、耇数の小さな実隓を管理する堎合に取り回しにくいかなずいう気持ちでした。もちろん git worktree などを䜿いこなしたりできれば別かもですが、私自身の緎床がそこたで高くなかったです。

たた kaggle api を䜿うアップロヌド凊理はネットワヌクの関係などで䞍安定になりやすく、時々倱敗するこずにも泚意です。

アヌティファクトやコヌドのアップロヌドたわりの utils の敎備や、コヌドの push 方法なども知るこずができたしたし、ゞョブを投げたり色々ず自動化? するこずができ個人的には満足でした。

もし䜕かの参考になれば嬉しい限りです

Hydra ず DVC で実隓管理メモ

抂芁

Hydra を䜿っお config 管理はできたものの、䞭間生成物のキャッシュが䞊手に無駄なく管理できないでいたした。぀たり hydra でパむプラむンチックなものを䞊手く䜜りたいですが、できれば既存のツヌルで実珟したいです。むメヌゞはこちらの蚘事のような感じ。

zenn.dev

  1. preprocess
  2. feature_extract
  3. train

ずいうプロセスがあり、各プロセスで䞭間生成物を生成するケヌスを考える。 䞭間生成物を生成するこずのメリットは、各凊理で同じ凊理結果が同じになる凊理を耇数回実行しないこずで蚈算コストが削枛できるこずである。

各プロセスにおける凊理は入力デヌタずコンフィグの倀のみに䟝存するずする぀たり、再珟性がある。 この前提であれば、各プロセスの䞭間生成物の生成結果に違いが生じるのは入力デヌタかコンフィグのパラメヌタが倉曎された堎合のみである。

今回実珟したいのは正しくこれで、config は hydra で管理し、preprocess.py や train.py などの実行察象のファむル自䜓をパむプラむンの各ステップずしお扱い、最終的なパむプラむンを䜜りたす。

今回䜜ったパむプラむンはこちらです。

github.com

DVC (Data Version Control)

dvc ずは、デヌタのハッシュをテキストファむルで保持し git でバヌゞョン管理するものです。たた、パむプラむンも䜜成するこずができたす。この蟺りは以䞋の蚘事が詳しいのでそちらを参照ください

techtekt.persol-career.co.jp

DVC x Hydra

Hydra で config 管理し぀぀、 DVC でパむプラむンの䜜成ずバヌゞョン管理をする方法は、実は公匏から提䟛されおいたした。こちらの蚘事ず ドキュメントがそれに圓たりたす。

こちらが蚘事やドキュメントを参考にしお䜜った簡単なパむプラむンです。

.
├── README.md
├── conf
│   ├── config.yaml
│   ├── model
│   │   ├── logistic_regression.yaml
│   │   └── random_forest.yaml
│   └── paths
│       └── default.yaml
├── dvc.lock
├── dvc.yaml
├── params.yaml
├── pyproject.toml
├── requirements-dev.lock
├── requirements.lock
└── src
    └── hydra_dvc_practice
        ├── __init__.py
        ├── eval.py
        ├── load_data.py
        ├── preprocess.py
        └── train.py

conf には hydra を䜿う前提の蚭定ファむル config.yaml を配眮し、src/hydra_dvc_practice にパむプラむンの構成芁玠ずなる各ファむルを配眮したした。 䞀般的に hydra を䜿う堎合、

python load_data.py
python preprocess.py
python train.py
python eval.py

のように、ファむルごずの実行が前提ずなりたす。

これを dvc を䜿うこずで

dvc exp run 

コマンド䞀発で、キャッシュ管理を含むパむプラむンの実行が走りたす。

そしお、このパむプラむンの蚭蚈図が dvc.yaml です。

stages:
  load_data:
    cmd: rye run python src/hydra_dvc_practice/load_data.py
    deps:
      - src/hydra_dvc_practice/load_data.py
    outs:
      - data/train.csv
      - data/test.csv
    params:
      - test_size
      - target
      - seed
      - paths

  preprocess:
    cmd: rye run python src/hydra_dvc_practice/preprocess.py
    deps:
      - data/train.csv
      - data/test.csv
      - src/hydra_dvc_practice/preprocess.py
    outs:
      - data/train_scaled.csv
      - data/test_scaled.csv
    params:
      - scaler

load_data step ず preprocess stage に぀いお切り出しおいたす。

  • cmd : 各 stage で実行するコマンド
  • deps : その stage が䟝存する察象のファむル。䞊蚘の䟋では、実行察象のファむルに䜕か倉曎が発生した堎合にはキャッシュが䜿甚されないこずを意味しおいたす。もちろんですが、倉曎が発生した stage 以降の stage も再実行されたす (この蟺りは柔軟に倉曎できるはずです)
  • outs : 䞭間生成物。ここで指定した䞭間生成物は dvc の管理察象ずなりたす
  • params : 䟝存関係を持ちたい蚭定パラメタ。぀たり deps における hydra で管理しおいる倀バヌゞョンですね。ここでは䟋えば、 seed の倀を倉曎した堎合は、deps 同様に再実行が発生したす。

これが䞻な蚭定ですが、他にもいろいろな蚭定があるそうです。

params.yaml ず dvc.lock はパむプラむンの実行ごずに生成されたす。

もずもず dvc は params.yaml にパむプラむン実行に関する党おのパラメタ蚭定を曞くこずを前提ずしおいたすが、今回はその蚭定自䜓を hydra でもっおいるため、実行時に params.yaml を生成する必芁がありたす。

dvc.lock には stage 単䜍での実行に関する情報が蚘茉され、これを参照するこずで cache 刀定が行われおいるはずです。

たた、hydra でよく䜿う、コマンドラむンを䜿った蚭定パラメタ override にも察応しおおり、䜿い勝手ほがそのたた再珟されおいたす。

dvc exp run -S model=random_forest -S seed=42

実隓結果の参照も容易に行うこずができたす。

dvc.org

おわりに

hydra ず dvc を䜿ったパむプラむンの䜜成をしおみたした。ファむル単䜍での管理が、既存のパむプラむンツヌルずは異なるナニヌクな機胜なのかなず思っおいたす。そしおそれが hydra ずも盞性がいい点ではあるような気がしたす。

たた、今回はそもそもの dvc の機胜぀いおはあたり觊れおはいないので、そちらも機䌚があればずいうずころです。

調査䞭に遭遇した DagsHub ずいうのにも少し興味があったので、これも機䌚あれば觊っおみようず思いたした。


今埌䜿っおいくかは䞍明ですが、やりたいこずはずりあえずできたのでペシ!

参考資料

Hydra を䜿っおみる

はじめに

今回は Hydra を䜿った蚭定管理に぀いお調べおみたした

Hydra ずいうのは Meta が開発しおいる python のフレヌムワヌクの䞀぀であり、䞻に蚭定ファむルの管理に長けおいるものです。

github.com

この蚘事では、いく぀かの基本的はシチュ゚ヌションにおける Hydra をみおいきたいず思っおいたす 🐲 🐲 🐲

1. yaml で蚭定管理したい

こちらの蚘事にあるように、argparse を䜿いプログラム実行時の匕数を受け取る方法はよく䜿われおいるず思いたす。しかし、蚭定するパラメヌタ数が倚いずきなどには苊しさを感じるこずも倚々ありたす。

以䞋の䟋で、特に蚭定数は倚くないですが hydra を䜿っおみようず思いたす! hugguingface trainer を甚いた fine-tuning のサンプルコヌドを参考にしお䟋を䜜成しおいたす。

import argparse

import transformers
from datasets import load_dataset
from transformers import AutoModelForSequenceClassification, AutoTokenizer


def main():
    parser = argparse.ArgumentParser(description="huggungface transformers training")

    # transformers
    parser.add_argument("--model_name", type=str, default="bert-base-uncased")
    parser.add_argument("--num_labels", type=int, default=2)
    parser.add_argument("--per_device_train_batch_size", type=int, default=8)
    parser.add_argument("--per_device_eval_batch_size", type=int, default=8)
    parser.add_argument("--evaluation_strategy", type=str, default="epoch")
    parser.add_argument("--num_epochs", type=int, default=3)
    parser.add_argument("--learning_rate", type=float, default=5e-5)
    parser.add_argument("--warmup_ratio", type=float, default=0.1)
    parser.add_argument("--gradient_accumulation_steps", type=int, default=1)
    parser.add_argument("--eval_accumulation_steps", type=int, default=1)
    parser.add_argument("--weight_decay", type=float, default=0.01)
    parser.add_argument("--save_strategy", type=str, default="epoch")
    parser.add_argument("--fp16", type=bool, default=False)
    # paths
    parser.add_argument("--logging_dir", type=str, default="logs")
    parser.add_argument("--output_dir", type=str, default="output")

    args = parser.parse_args()

    # model, tokenizer のロヌド
    tokenizer = AutoTokenizer.from_pretrained(args.model_name)
    model = AutoModelForSequenceClassification.from_pretrained(
        args.model_name, num_labels=args.num_labels
    )

    # example デヌタセットのロヌド
    raw_datasets = load_dataset("glue", "mrpc")

    def tokenize_function(example):
        return tokenizer(example["sentence1"], example["sentence2"], truncation=True)

    tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)

    # トレヌニングの蚭定
    training_args = transformers.TrainingArguments(
        output_dir=args.output_dir,
        per_device_train_batch_size=args.per_device_train_batch_size,
        per_device_eval_batch_size=args.per_device_eval_batch_size,
        evaluation_strategy=args.evaluation_strategy,
        logging_dir=args.logging_dir,
        num_train_epochs=args.num_epochs,
        learning_rate=args.learning_rate,
        warmup_ratio=args.warmup_ratio,
        gradient_accumulation_steps=args.gradient_accumulation_steps,
        eval_accumulation_steps=args.eval_accumulation_steps,
        weight_decay=args.weight_decay,
        save_strategy=args.save_strategy,
        fp16=args.fp16,
    )

    trainer = transformers.Trainer(
        model,
        training_args,
        train_dataset=tokenized_datasets["train"],
        eval_dataset=tokenized_datasets["validation"],
        tokenizer=tokenizer,
    )
    trainer.train()


if __name__ == "__main__":
    main()

デフォルト倀は蚭定しおいたすが、党匕数を蚭定するず以䞋のようになりたす。

rye run  python src/main_argparse.py --model_name bert-base-uncased --num_labels 2 --per_device_train_batch_size 8 --per_device_eval_batch_size 8 --evaluation_strategy epoch --num_epochs 3 --learning_rate 5e-5 --warmup_ratio 0.1 --gradient_accumulation_steps 1 --eval_accumulation_steps 1 --weight_decay 0.01 --save_strategy epoch --fp16 False --logging_dir logs --output_dir output

䞊蚘のような蚭定を yaml ファむルに曞き、hydraを䜿うこずで argparse から脱华するこずができたす。たた、蚭定ファむル自䜓に階局構造を持たせる、぀たりグルヌプ化するこずで、よりわかりやすく管理しやすい圢で蚭定ファむルを扱うこずができたす。

今回は configs ディレクトリを䜜成し、さらにその䞭に paths ず transformers を䜜り、それぞれに察応する蚭定ファむルを䜜ろうず思いたす。

├── configs
│   ├── config.yaml
│   ├── paths
│   │   └── default.yaml
│   └── transformers
│       └── default.yaml
└── src
    ├── __init__.py
    └── main_hydra.py

default.yaml には察応するディレクトリ名に関するデフォルト倀を蚘茉し、config.yaml は、それぞれの蚭定ファむルをたずめる圹割を持っおいたす。

model_name: bert-base-uncased
num_labels: 2
per_device_train_batch_size: 8
per_device_eval_batch_size: 8
evaluation_strategy: epoch
num_epochs: 3
learning_rate: 2e-5
warmup_ratio: 0
gradient_accumulation_steps: 1
eval_accumulation_steps: 1
weight_decay: 0.01
save_strategy: epoch
fp16: False
logging_dir: logs
output_dir: output
defaults:
  - paths: default
  - transformers: default
  - _self_

defaults: に関しおは公匏ドキュメントを参照ください。ディレクトリ名:蚭定ファむル名 ずいう圢匏で蚭定を読み蟌み、指定した蚭定を䜿甚できるようにしたす。

たた config.yaml 自䜓にも paths や transformers の蚭定以倖の蚭定などを曞くこずができたす。- _self_ は、config.yaml 自身の蚭定を明瀺的に衚しおいるものに過ぎたせん。

ただ、同じ蚭定が存圚する堎合はリストのより埌のものが優先されたす。今回の堎合だず _self_ が最優先ずいうこずになりたす。

実行察象のファむルは以䞋のようになりたす。 main() に察しお @hydra.main デコレヌタの远加がありたす。最終的に䜿う蚭定ファむルず、その蚭定ファむルが存圚するディレクトリのパスをここで指定するこずで、その蚭定が䜿えるようになりたす。

import hydra
import transformers
from datasets import load_dataset
from omegaconf import DictConfig
from transformers import AutoModelForSequenceClassification, AutoTokenizer


@hydra.main(config_path="../configs", config_name="config", version_base="1.3")
def main(cfg: DictConfig):
    # model, tokenizer のロヌド
    tokenizer = AutoTokenizer.from_pretrained(cfg.transformers.model_name)
    model = AutoModelForSequenceClassification.from_pretrained(
        cfg.transformers.model_name, num_labels=cfg.transformers.num_labels
    )

    # example デヌタセットのロヌド
    raw_datasets = load_dataset("glue", "mrpc")

    def tokenize_function(example):
        return tokenizer(example["sentence1"], example["sentence2"], truncation=True)

    tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)

    # トレヌニングの蚭定
    training_args = transformers.TrainingArguments(
        output_dir=cfg.paths.output_dir,
        per_device_train_batch_size=cfg.transformers.per_device_train_batch_size,
        per_device_eval_batch_size=cfg.transformers.per_device_eval_batch_size,
        evaluation_strategy=cfg.transformers.evaluation_strategy,
        logging_dir=cfg.paths.logging_dir,
        num_train_epochs=cfg.transformers.num_epochs,
        learning_rate=cfg.transformers.learning_rate,
        warmup_ratio=cfg.transformers.warmup_ratio,
        gradient_accumulation_steps=cfg.transformers.gradient_accumulation_steps,
        eval_accumulation_steps=cfg.transformers.eval_accumulation_steps,
        weight_decay=cfg.transformers.weight_decay,
        save_strategy=cfg.transformers.save_strategy,
        fp16=cfg.transformers.fp16,
    )

    trainer = transformers.Trainer(
        model,
        training_args,
        train_dataset=tokenized_datasets["train"],
        eval_dataset=tokenized_datasets["validation"],
        tokenizer=tokenizer,
    )
    trainer.train()


if __name__ == "__main__":
    main()

cfg.transformers.model_name や cfg.paths.output_dir のように ディレクトリ名.パラメタ名 ずいう感じでアクセスできたす。

匕数を぀けずに実行するず、もちろん yaml ファむルに曞いた通りの蚭定で実行されたす。

匕数蚭定の䟋は以䞋です。

python src/main_hydra.py transformers.fp16=true

たた、実行時にはデフォルトで outputs フォルダが䜜成されたす。この䞭には log ファむルや、実行時のすべおの hydra の蚭定ファむルが実行日/実行時間のフォルダに保存されたす。

2. クラス単䜍で蚭定したい

蚭定パラメタを受け取り、あるクラスや関数の匕数ずしお䜿甚する堎合、なんずなく冗長な気がしたす。たた、パラメタの倀によっお察象のクラス・関数を倉曎したい時、いちいち察象のクラス・関数を import し条件文で分岐を䜜るなども少しダルむ感じがありたす。

hydra には Instantiating ずいうシステムがあり、これが前述の問題を解決しおくれたす。

これを䜿うこずで最終的な main.py は以䞋のようになりたす。

import hydra
from datasets import load_dataset
from omegaconf import DictConfig


@hydra.main(config_path="../configs", config_name="config", version_base="1.3")
def main(cfg: DictConfig):
    # model, tokenizer のロヌド
    model = hydra.utils.get_method(cfg.transformers.model)(
        cfg.transformers.model_name,
        num_labels=cfg.transformers.num_labels,
    )
    tokenizer = hydra.utils.get_method(cfg.transformers.tokenizer)(cfg.transformers.model_name)

    # example デヌタセットのロヌド
    raw_datasets = load_dataset("glue", "mrpc")

    def tokenize_function(example):
        return tokenizer(example["sentence1"], example["sentence2"], truncation=True)

    tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
    trainer = hydra.utils.instantiate(
        cfg.transformers.trainer,
        model=model,
        train_dataset=tokenized_datasets["train"],
        eval_dataset=tokenized_datasets["validation"],
        tokenizer=tokenizer,
    )
    trainer.train()


if __name__ == "__main__":
    main()

泚目すべきは trainer = hydra.utils.instantiate( こちらですね。configs/transformers/default.yaml にある trainer を instantiate しおいたす。

さらには model や tokenizer さえも yaml で蚭定できたす。今回は AutoTokenizer などの Auto 系のクラスがあるのでいいですが、特定のクラスを䜿うずきなどには get_method や get_class が䜿えたす。

configs/transformers/default.yaml はこんな感じです。

model_name: bert-base-uncased
num_labels: 2

model: transformers.AutoModelForSequenceClassification.from_pretrained
tokenizer: transformers.AutoTokenizer.from_pretrained

trainer:
  _target_: transformers.Trainer
  args:
    _target_: transformers.TrainingArguments
    output_dir: ${paths.output_dir}
    logging_dir: ${paths.logging_dir}
    per_device_train_batch_size: 8
    per_device_eval_batch_size: 8
    evaluation_strategy: epoch
    num_train_epochs: 3
    learning_rate: 2e-5
    warmup_ratio: 0
    gradient_accumulation_steps: 1
    eval_accumulation_steps: 1
    weight_decay: 0.01
    save_strategy: epoch
    fp16: False

_target_ に instantiate 察象ぞのパスを曞き、その䞋に匕数を曞くこずができたす。ここにすべおの匕数を曞かずずも前述のように

 trainer = hydra.utils.instantiate(
        cfg.transformers.trainer,
        model=model,
        train_dataset=tokenized_datasets["train"],
        eval_dataset=tokenized_datasets["validation"],
        tokenizer=tokenizer,
    )

むンスタンス化のタむミングで远加で匕数を蚭定できたす。たた、functools.partial ず同様のものも䜜成するこずができたす。(参考)

hydra.utils.get_class や hydra.utils.get_method を䜿うこずで、クラスやそのメ゜ッド、関数そのものを呌び出すこずができたす。

3. 蚭定をカスタマむズしたい

bert 系のモデルを䜿う時など、large モデルを䜿うずきはマシンの関係䞊バッチサむズも小さく、さらに孊習率も小さくしたいなど、察象のモデルに応じお各蚭定のデフォルトを倉曎したい堎合がありたす。もちろんコマンドラむン䞊で transformers.model_name=roberta-large transformers.trainer.args.learning_rate=1e-5 などのように override するこずもできたすが、蚭定ファむルずしお残したい気持ちもありたす。

├── configs
│   ├── config.yaml
│   ├── paths
│   │   └── default.yaml
│   └── transformers
│       |── default.yaml
|       |── roberta-base.yaml
|       └── roberta-large.yaml
└── src
    ├── __init__.py
    └── main.py

このように roberta-base.yaml ず roberta-large.yaml の蚭定ファむルを远加しおみたす。

defaults:
  - default

model_name: roberta-base
defaults:
  - default

model_name: roberta-large

trainer:
  args:
    num_train_epochs: 2
    learning_rate: 1e-5
    gradient_accumulation_steps: 4

defaults: - default は、同じ階局の default.yaml を overriede するために远加したす。それぞれの蚭定ファむルからみお取れるように、倉曎点 (override 察象) 以倖は、 defaults で指定した倀のパラメタが適甚されたす。この堎合だず defaults.yaml がそれですね。

こうするこずで、倉曎箇所以倖の䜙蚈な蚭定を省略し぀぀蚭定を行うこずができたした。

次にこの蚭定を適甚する方法に぀いおです。

  1. configs/config.yaml を曞き換える
  2. 実行時のコマンドラむン匕数で指定する

1 に぀いおは、以䞋のように config.yaml を修正したす。default だった郚分を roberta-large にしただけです。

defaults:
  - paths: default
  - transformers: roberta-large
  - _self_

2 に぀いおは以䞋のコマンドラむン匕数を䜿いたす。transformers の蚭定を倉曎した感じです。

python run src/main.py transformers=roberta-large

話はずれたすが、hydra ではデフォルトの倀を蚭定せず、コマンドラむン匕数ずしお必ず蚭定するパラメタは ??? ず曞くこずができたす。??? に圓たる郚分が未指定だず゚ラヌが発生したす。

defaults:
  - paths: default
  - transformers: ???
  - _self_

䟋えば䞊のように曞けば、python run src/main.py transformers= をコマンドラむンから指定する必芁がありたす (コマンドラむン以倖からも指定する方法はありたす)。

4. その他

環境倉数を䜿う

こちらの蚘事が参考になりたす。

root_dir: ${oc.env:PROJECT_ROOT}
output_dir: ${hydra:runtime.output_dir}
work_dir: ${hydra:runtime.cwd}

環境倉数以倖にも、実行時のログ保存ディレクトリなどの hydra 特有のパスにもアクセスできたす。もちろん倉曎も可胜なので、䟋えば指定した匕数ず同じ名前の出力ディレクトリを䜜るこずなども可胜です。

hydra.cc

omegaconf.readthedocs.io

notebook で䜿う

with hydra.initialize(version_base=1.3, config_path="../configs"):
    CFG = hydra.compose(
        config_name="config.yaml",
        return_hydra_config=True,
        overrides=OVERRIDES,
    )
    # use HydraConfig for notebook to use hydra job
    HydraConfig.instance().set_config(CFG)

OVERRIDES には transformers=default などのようにコマンドラむン匕数での蚭定に盞圓する郚分を曞けば OK です。

multirun

今回は特に觊れおなかったですが、hydra の目玉機胜の䞀぀です。

hydra.cc

耇数の異なる蚭定で実行する堎合に䜿いたす。䟋えば、孊習率を倉えた実隓を行いたい堎合などですね。盎列での実行になりたすが、Launcher を倉曎するこずで䞊列の実行を可胜にしたす。

おわりに

hydra を䜿った蚭定管理に぀いお、よく䜿うシチュ゚ヌションず実際のコヌドを䜜っおみたした。この蚘事に曞いた䜿い方以倖にもただただ色々なこずができそうですし、やりたいこずはほができるず䜿っおみお感じたした。

公匏 github にもある Hydra Ecosystem なんかも参考になりそうです。

今回䜿甚したコヌドはこちらにありたす。

github.com

GCE むンスタンス内のコンテナで Git を䜿う

はじめに

こちらの蚘事で GCP を䜿い GPU ぀きのむンスタンスを䜜り、その䞭で vecode の Dev Container を䜿い環境を敎えたした。今回は、こちらで䜜ったコンテナの䞭で Git を䜿いコヌド管理をし、GitHub ず連携できるようにしたいず思いたす。

むンスタンス内やコンテナ内で SSH 鍵を䜜るこずはせず、ロヌカルのPCで鍵を䜜成し、それを䜿うようにしたいず思いたす。

SSH鍵の䜜成

適圓な鍵を適圓な堎所に䜜りたす。こちらの蚘事などが参考になりたす。

qiita.com

ssh-keygen -t ed25519

今回はこちらのコマンドで鍵を䜜成したした。

SSHでむンスタンスに接続する

こちらの蚘事が参考になりたす。蚘事に倣っお進めおみたしょう

zenn.dev

たず鍵を登録しおみたす。筆者は Mac を䜿っおいるので以䞋のコマンドを実行したした。

ssh-add --apple-use-keychain ~/.ssh/id_ed25519

今回はこの SSH 鍵を GitHub ず GCE それぞれの ssh-key ずしたいず思いたす。GCE に぀いおはこちらの蚘事の通り公開鍵を指定したす。GitHub に関しおは、先ほどのこちらの蚘事にもありたすので参考にしおください。*1

次に config ファむルを修正したす。~/.ssh/config の倉曎箇所ずしお、ForwardAgent を蚭定しおいたす。*2

Host {適圓な名前: instance-1など}
    Hostname {倖郚IPアドレス}
    User {SSH鍵のナヌザヌ名}
    ForwardAgent yes

むンスタンスの䞭に入っお、ssh -T git@github.com で GitHub ずの接続確認をしおみたす。

Hi (account名)! You've successfully authenticated, but GitHub does not provide shell access.

無事接続できおたすね 🎉

リポゞトリを clone する

むンスタンスの䞭で GitHub ず接続できたので、こちらのリポゞトリを SSH でクロヌンしおみたす。

github.com

git clone git@github.com:osushinekotan/gcp-pytorch-project.git

/gcp-pytorch-project が䜜られたので、docker をむンストヌルをしたのち Dev Container を起動しコンテナに入っおみたす。

cd gcp-pytorch-project
sh gcp/install_docker.sh

docker のむンストヌルが終われば Reopen in Container などでコンテナに入るこずができたす。 コンテナ内で再床 GitHub ずの接続を確認しおみたす。

ssh -T git@github.com
Hi (account名)! You've successfully authenticated, but GitHub does not provide shell access.

完璧ですね 🙌

コンテナ内の倉曎を GitHub に push する

コンテナないで Git を䜿うこずができ、さらに GitHub ずの接続も確認するこずができたした。最埌に vscode で Manage Unsafe Repositories をポチッずしたしょう。

user name ず email を蚭定し、おきずうなファむルを䜜成しお push たでしおみたす。

git config --global user.email "you@example.com"
git config --global user.name "Your Name"
touch test_push.txt
git add test_push.txt
git commit -m "test"
git push origin main

芋事 push に成功したした 🚀

ここでは、コンテナ内で git のタブ候補を䜿えるように蚭定しおいないか぀個人的に奜きずいうのもあり、私はよく vscode の GUI でポチポチしお git 操䜜をしおいたす。もちろんGUI操䜜でも問題なく push たでするこずができたす。

おわりに

前回の蚘事に匕き続き環境構築に぀いおでした。むンスタンス内でのコヌドの倉曎ず管理に Git / GitHub を䜿うこずで、ロヌカルや別の環境ず同期をはかるこずができたす。

環境構築には Dev Container を䜿い、コヌドは Git / GitHub で管理するこずで耇数のマシンや耇数人での開発や実隓における差異を枛らすこずができるのではないでしょうか。 *3

*1:GitHubずGCEで同じ鍵を䜿い回しおいたす。これは掚奚される方法ではないず思いたすが、䟿宜䞊このようにしおいたす

*2:--apple-load-keychain を䜿うのず同じらしい

*3:今回䜿甚した ssh-agent たわりに぀いおは ころんびあ (@colum2131) / X さんに教えおいただきたした。ありがずうございたした!

Google Cloud (GCP) を䜿った kaggle の環境構築メモ

はじめに

GCE (Google Compute Engine) で kaggle 甚の環境構築をしたいず思い、自分なりの環境構築をしおみたした。その際にいく぀かハヌドルがあったので備忘録ずしおこの蚘事を曞きたした。 kaggle 甚ずありたすが、kaggle docker image などは特に䜿わずなので、その蟺りは目的・甚途に合わせお適圓に倉曎ください。

今回は以䞋のようなむメヌゞの環境を䜜りたいず思っおいたす。

  • instance 内で vscode の devcontainer を䜿っお環境を䜜る
  • poetry を䜿う
  • pytorch を GPU で動かす
  • kaggle からのデヌタのロヌドずデヌタセットのアップロヌドは kaggle api を䜿う
  • 孊習枈みモデルやその他デヌタは党お GCS (google cloud storage) におく

むンスタンスの䜜成

最初にむンスタンスの䜜成をしたす。今回はGPUを䜿いたいので、事前に「GPUの割り圓お申請」を行う必芁がありたす。こちらの蚘事などが参考になるかず思いたす。 zenn.dev

1. GCP に入り、 Compute Engine から むンスタンスの䜜成 を実行する

むンスタンス名は適圓に倉曎しお䞋さい。ここではデフォルトの instance-1 が蚭定されおいたす。たた今回は asia-northeast1-a に NVIDIA T4 マシンをスポットで借りたした。 スポットの方がお財垃に優しいですしね。

2. ブヌトディスクの遞択

Deep Learning VM with CUDA 11.8 を遞択したした。nvidia driver などの GPU䜿甚時に必芁なものが党お入っおいるので、手間が省けお楜です。䜿いたい pytorch が必芁ずしおいる cuda のバヌゞョンを確認から遞択するのが吉です。私は pytorch=2.0.0 を䜿っおいるので、こちらを遞択しおいたす。サむズ はデフォルト 50GB ですが、dirver などが入るずすぐパンパンになるので䜙裕を持たせお 100GB にしたした。

3. API ずファむアりォヌルの蚭定

めんどくさいのでずりあえず党蚱可でいきたす。。必須なのは Storage ぞのフルアクセス暩なのでそれ以倖はよしなに蚭定䞋さい。

4. 詳现蚭定

詳现蚭定はネットワヌク むンタヌフェヌスずセキュリティ郚分だけ蚭定したす。 ネットワヌクむンタヌフェヌスは倖郚 IPv4 アドレスを ゚フェメラル (むンスタンスを立ち䞊げるごずに倀が倉わる) に蚭定したす。静的IPアドレスを予玄するこずで倀が倉わるこずはなくなりたすが、以䞋の点からここでぱフェメラルに蚭定したした。

  • お金がかかる
  • 静的IPアドレスを蚭定するず kaggle api が䜿えない

二点目に関しお、kaggle api でのアクセス時に゚ラヌが発生しおしたいたす。どうにかすればなんずかできそうですが、お金もかかるずいうこずもあり静的 IP は䞍採甚ずしたした。

今回はロヌカルのPCから SSH 接続でむンスタンスにアクセスするので、ここで SSH 公開鍵を蚭定したす。プロゞェクト党䜓の SSH 認蚌鍵 でも問題なく接続できるようなのですが、圓時の私の蚭定方法が悪かったのか倱敗したので、ここで鍵を蚭定しおいたす。(今ならできるのかな)

SSH でむンスタンスに接続する

むンスタンスを䜜成し開始するず、以䞋のようになりたす。緑のチェックマヌクが起動䞭のサむンですね。起動䞭は課金が発生するので、停止忘れには泚意が必芁です。

vscode からこのむンスタンに接続したすが、vscode に Dev Containers (ms-vscode-remote.remote-containers) ず Remote SSH (ms-vscode-remote.remote-ssh) の拡匵機胜を入れおおく必芁がありたす (Dev Containers を入れれば Remote SSH も぀いおくる?) たた、~/.ssh/config に蚭定を曞いおおきたしょう。なければ䜜成しお䞋さい。

Host {適圓な名前: instance-1など}
    Hostname {倖郚IPアドレス}
    User {SSH鍵のナヌザヌ名}
    IdentityFile {秘密鍵の堎所: ~/.ssh/id_ed25519 など}

蚘茉する内容 (Hostname や User ) は GCP のコン゜ヌルに曞いおあるず思いたす。 config ファむルを曞いお保存し、vscode を開くず以䞋のようにアクセスできる圢になっおいたす。

ただ初回接続時には nvidia-driver をむンストヌルするかどうか聞かれるためか䜕故か vs code からだずうたく行きたせんでした。タヌミナルから ssh instance-1 で接続し、nvidia-driver のむンストヌルを実行できたす。

Would you like to install the Nvidia driver? [y/n] y

むンストヌル完了埌 nvida-smi を実行するず無事GPUを認識できたす。たた、sudo /opt/deeplearning/install-driver.sh で 再むンストヌルも可胜です。 あずは、docker compose を䜿えるように公匏サむトの手順通りにむンストヌルしたす。コピペコピペでOKです。 docs.docker.com

Dev Container で環境構築

むンスタンスに無事入るこずができたので、dev container で環境を䜜りたす。 dev container に぀いおはこちらの蚘事などが参考になりたす。

blog.kinto-technologies.com

1. Dockerfile ず compose.yml の䜜成

たず Dockerfile ですが、今回はずおも簡単に以䞋のようにしたした。python は 3.11 を、 poetry は 1.6 を指定しおいるだけです。

FROM python:3.11

WORKDIR /workspace

RUN pip install poetry==1.6.0

compose.yml はこちらです。

version: "3"
services:
  workspace:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - .:/workspace
      - /workspace/.venv
    ports:
      - 8888:8888
    tty: true
    environment: 
      - NVIDIA_VISIBLE_DEVICES=all
      - NVIDIA_DRIVER_CAPABILITIES=all
    deploy:
      resources:
        reservations:
          devices:
          - driver: nvidia
            capabilities: [gpu]   

deply で GPUを コンテナ内で認識するようにしおいたす。この蟺りの曞き方には特に自信がないため参考皋床に芋お䞋さい。

devcontainer.json の䜜成

次に devcontainer.json を䜜りたす。これは devcontainer ずしお vscode の拡匵機胜などを含めた環境を䜜るための蚭蚈図ずなるものですね。 devcontainer/devcontainer.json を䜜り、䞭身を蚘述しおみたしょう。

{
  "name": "gcp_pytorch_project",
  "dockerComposeFile": ["../compose.yml"], 
  "service": "workspace",
  "workspaceFolder": "/workspace",
  "runServices": ["workspace"],
  "containerEnv": {
    "TZ": "Asia/Tokyo"
  }
}

実際には、䜿いたい拡匵機胜やフォヌマッタヌの蚭定などを党おここに曞くこずができたす。ここを曞かないず devcontainer を䜿う意味がだいぶ薄れちゃいたすが、今回は長くなるのでシンプルにこれだけ曞いおいたす。曞き方や内容に関しおはこちらが参考になりたす。

tech.isid.co.jp

GCS をマりントする

さお、今回䜜成したい環境の芁件ずしお 孊習枈みモデルやその他デヌタは党お GCS (google cloud storage) におく ずいうものがありたした。これを簡単に実珟するために GCSFUSE を䜿っお GCS のバケットをマりントし、ストレスなくモデルやデヌタの保存をしたいです。

実行するスクリプトは以䞋のような感じです。

MOUNT_DIR="./data"
PROJECT_ID=YOUR_PROJECT_ID
BUCKET_NAME="gcp-pytorch-project"

gcloud config set project $PROJECT_ID
gcloud auth login

# バケットがなければ「䜜成する
if ! gsutil ls gs://$BUCKET_NAME; then
  gcloud storage buckets create gs://$BUCKET_NAME --location=us-central1
fi

# install gcsfuse 
export GCSFUSE_REPO=gcsfuse-`lsb_release -c -s`
echo "deb http://packages.cloud.google.com/apt $GCSFUSE_REPO main" | sudo tee /etc/apt/sources.list.d/gcsfuse.list
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
sudo apt-get update
sudo apt-get install -y fuse gcsfuse

sudo gcsfuse -o allow_other -file-mode=777 -dir-mode=777 $BUCKET_NAME $MOUNT_DIR

MOUNT_DIR , PROJECT_ID, BUCKET_NAME をそれぞれ指定する必芁がありたす。MOUNT_DIR は適圓なディレクトリを䜜成し、そのパスを指定すればOKです。今回は gcp-pytorch-project バケットをむンスタンス䞊の ./data にマりントしたす。マりントを解陀したい堎合は sudo fusermount -u {mountpoint: ここでは data} でできたす。

マりントした data フォルダを devcontainer でさらにマりントするこずで、生成物を GCS に保存するようにしたす。

コンテナに入る

前準備は終わったので、぀いにコンテナに入っお䜜業しおみたしょう コマンドパレットから Dev Containers: Reopen in Container などを䜿うずコンテナに入るこずができたす。devcontainer.json に曞いた内容 (拡匵機胜や formatter などの蚭定) が反映された環境がすでに出来䞊がっおいたす。私の堎合、python の black や ruff などの蚭定が時々範囲されないのでリロヌドする堎合がありたした。

GPUの確認ず poetry コマンドの䜿甚が確認できたら pytorch をむンストヌルしお GPU を認識できるかみおみたす。

poetry init 
poetry add torch=">=2.0.0, !=2.0.1"

ここで torch=">=2.0.0, !=2.0.1" このように指定しおいる理由はこちらですが、正盎深く考えず䜿っちゃっおいたす。。

>>> import torch
>>> torch.cuda.is_available()
True

良さそうですね🙌

kaggle api でデヌタをダりンロヌドする

最埌に kaggle api を䜿っお䜕かデヌタをダりンロヌドしおみたしょう。マりントが成功しおいれば、ダりンロヌドしたデヌタはむンスタンスのディスクではなく GCS に保存されるはずです。たず kaggle をむンストヌルし、認蚌情報の蚭定をしたす。その埌 titanic のデヌタをダりンロヌドし保存しおみたす。

poetry add kaggle

kaggle api の䜿い方に関しおはこちらの蚘事などが参考になるかず思いたす。

atmarkit.itmedia.co.jp

www.kaggle.com

認蚌情報は kaggle.json ずしお保存し䜿うこずもできたすが、実は環境倉数ずしお䜿うこずもできたす。KAGGLE_USERNAME ず KAGGLE_KEY をそれぞれ蚭定すればOKです。

from kaggle import KaggleApi

client = KaggleApi()
client.authenticate()
client.competition_download_files(
    competition="titanic",
    path="./data",
    quiet=False,
)

こちらのコヌドを実行するず、GCS の指定したバケットにデヌタが保存されたす。

おわりに

以䞊で䜜りたい環境を䜜れたしたDockerfile や devcontainer などをカスタムしおオリゞナルの蚭定を反映した環境を䜜るこずももちろん可胜です。devcontainer を䜿うこずで耇数のむンスタンスで同じ環境を簡単に䜜り、GCSにデヌタをおくこずで共有も簡単にするこずができるようになったず思いたす。

今回䜿甚したコヌドはこちらにありたす

github.com

HuMob Challenge 2023 に参加したした

HuMob Challenge 2023 ずは

The Human Mobility Prediction Challenge (HuMob Challenge) 2023 is a competition aiming at testing state-of-the-art computational models for the prediction of human mobility patterns, using an open source, urban scale (100K individuals), longitudinal (90 days) trajectory dataset.

HuMob Challenge 2023 は SIGSPATIAL ずいう囜際孊䌚のワヌクショップの䞀環ずしお行われたコンペティションです。

2023幎7月10日から 2023幎9月15日 の玄2ヶ月間開催されたした。䞊䜍TOP10 の参加者は 2023幎11月にドむツで開催予定の SIGSPATIAL のワヌクショップに招埅され解法等を発衚し、TOP3 には䜕やらPRIZEも甚意されおいるようです。

コンペティション詳现に぀いおはこちらを参照ください。

コンペ抂芁

このコンペティションのタスクはいわゆる「Next Place Prediction」ず呌ばれるものです。 ある人物がある時点においおどこにいるかを予枬するものですね。

たた、今回のコンペティションでは task1 ず task2 の2皮類のデヌタセットが䞎えられたす。それぞれ平垞時ずコロナ犍における緊急事態時のもので、それぞれの期間における Next Place Prediction の粟床で最終的な評䟡が行われたす。

䞭間評䟡はあるもののリヌダボヌドは存圚せず、最終評䟡は 1submission で結果が決たるコンペティションでした。

デヌタ

䜿甚するデヌタに぀いお簡単に説明したす。デヌタセットの詳现に぀いおはこちらを参照ください。

日本のずある䞭芏暡郜垂においお、ある期間の耇数の人物の移動が 500m x 500m のメッシュか぀ 30分単䜍で蚘録されおいたす。具䜓的には以䞋のようなテヌブルを想定できたす。

user ID day timeslot x y
1 1 13 10 13
1 1 18 11 15
... ... ... ... ...
2 3 15 31 19
2 3 28 35 33
... ... ... ... ...
99999 74 10 999 999
99999 74 15 999 999
99999 74 20 999 999
  • user ID : 匿名化された個人
  • day : カレンダヌはわからないが、開始時点を 0 ずしたずきの日付 (0 ~ 74のラベルが䞎えられる)
  • timeslot : 0 をある day の 0時ずしたずきの 30分単䜍のラベル (0~47のラベルが振られる)
  • x, y : それぞれメッシュの座暙を衚す (正解デヌタには 1~200 のラベル、予枬察象には 999 が䞎えられおいる)。

メッシュの座暙ずいうのは、以䞋の図で衚すこずができたす。

巊䞊から順にx, y 方向それぞれに察しお番号が振られる圢ですね。1メッシュ500mなので、察象の地域は党䜓は 10km 四方の正方圢ずいうこずになりたす。どこかは䞍明ですが、日本の䞭芏暡郜垂の 10km x 10km ゚リアにおける人々の移動を衚すデヌタになりたす。

このコンペでは孊習デヌタずテストデヌタのような区別はなく、予枬察象の user ID ず day における x, y が 999 ずしおマスクされおいたす。task1、task2 それぞれの予枬察象は以䞋の通りです。

次に PoI Features に぀いおです。こちらは以䞋のようなデヌタです。

x y POI category (dim) # of POIs
1 1 13 10
1 1 18 11
1 1 24 11
1 1 27 12
... ... ... ...
2 2 15 31
2 2 28 35
2 2 12 35
  • x, y : メッシュを衚す座暙
  • POI category : 匿名化された PoI (レストランや公園などを衚すラベル 0~84)
  • # of POIs : あるメッシュに存圚する PoI の数

このコンペでは、個人情報保護のため PoI Feature ず真の PoI のマッピングは提䟛されおいたせん。誰がい぀どこに行ったのかを隠すためですね。ここが開瀺されおいればより面癜いず思いたすが、個人情報絡むのでちょっず難しいでしょうか。

䞎えられたデヌタセットは以䞊になりたす。

参考たでに、 task1 における user ごずの x, y の line plot です。暪軞が時間、瞊軞が座暙ずなっおいたす。芏則正しい人もいればそうではない人もいるのがわかっお面癜いですね。

評䟡指暙

次に評䟡指暙に぀いおです。 このコンペでは GEO-BLEU ず DTW ずいう二぀の評䟡指暙で最終的な順䜍を決めたす。

たず GEO-BLEU ですが、これは、機械翻蚳や文章生成タスクの出力を評䟡するための指暙であるBLEU (Bilingual Evaluation Understudy) の geo 版ずいう感じです。

user ID ごずの x, y それぞれの時間方向のシヌケンス の n-gram の䞀臎を基にスコアを぀けおいるのだず思いたす。詳しくはこちらを参照ください。今回のコンペでは 0~1 の倀をずり、倧きいほど良いスコアずいうこずになりたす。

次に DTW (Dynamic Time Warping) に぀いおですが、これは時系列で銎染み深い指暙かず思いたす。時系列デヌタ同士の距離・類䌌床を枬る際に甚いる手法ですね。

DTW は2぀の時系列の各点の距離を総圓たりで党お求め、2぀の時系列が最短ずなるパスを芋぀け、これの环積をずりたす。

画像はこちらから拝借したした。 今回のコンペでは、0以䞊の倀をずり、小さいほど良いスコアずいうこずになりたす。

この二぀の指暙はそれぞれ user ID 単䜍で蚈算し、最埌に平均をずり、それを最終的な指暙ずしおいたす。

以䞋たずめです。

  • task1 (通垞時) ず task2 (緊急時) の日本のある地域におけるメッシュ単䜍での人々の移動を予枬する
  • 䞍完党なパネルデヌタである
  • 各メッシュにおける匿名化された PoI の数は䞎えられおいる
  • 評䟡指暙は GEO-BLEU ず DTW である

解法玹介

コンペ抂芁ずデヌタの説明が枈んだので、匊チヌムの解法を玹介したいず思いたす。最初にボツ解法を玹介し、最埌に最終サブミッションずしお遞んだ解法を玹介しようず思いたす。特にひねりのない解攟になっおしたった事を最初に蚀っおおきたすね。

モデルベヌス

䞀番時間をかけお取り組みたしたが、最終的なサブミッションには遞ばなかったものです。埌述するルヌルベヌスのものが最終サブになったので、こちらでは機械孊習ベヌスの解法を玹介したいず思いたす。党䜓の流れは䞋の図のような感じです。

いわゆる seq2seq ですね。day 60~74 の範囲の目的倉数 (x, y) を予枬する際、day 0~59 の特城量を LSTM Encoder に、day 60~74 の特城量を LSTM Decoder に枡し、Encoder で埗られた状態を匕き継いだ Decoder が最終的な予枬シヌケンスを出力する算段です。LSTM 以倖にも Transformer や GraphLSTM なども詊したりしたした。党お同じように seq2seq で系列を䞀発で出力するようにしおいたす。

Encoder に枡す特城量ず Decoder に枡す特城量はそれぞれリヌクがないように䜜っおいたす。

Encoder に枡す特城量ずしおは、図にある通り、x, y, day time slot などの生の倀や、x y を集玄したものなどを䜿っおいたす。この集玄時にtime slot や 曜日、週末祝日フラグなどを組み合わせおいたす。提䟛されたデヌタには 曜日や祝日などの情報はありたせんが、生デヌタからリバヌス゚ンゞニアリングしたした。たた、PoI feature をマヌゞする事で、地理的な特城を捉えるこずを期埅しおいたす。

Decoder に枡す特城量ずしおは、目的倉数の範囲倖 (day 0~59) のデヌタを䜿い集玄した特城量を䜿っおいたす。䟋えば、day 0~59 のデヌタを䜿い、user id ごずに各曜日の各時間で存圚する座暙の平均倀や䞭倮倀などを集蚈し、それを day 60~74 にマヌゞするこずで特城量ずしおいたす。

たた Target Engineering ぀たり目的倉数の x, y もいじっおいたす。たず、今回は分類ではなく回垰で解いおいたす。この時、regression の目的倉数に察しお day 0~59 の範囲で集玄した user id ごずの x , y それぞれの䞭倮倀を匕き、暙準偏差で割る凊理を斜したした。぀たり䞭倮倀ず分散が䞀定であるずいう仮定を眮いおいたす。

党䜓的な流れのお気持ちずしおは、党おの情報をいい感じに䜿い、䞀発で系列を出力し、䞭倮倀かからあたり倖れないような予枬をしおほしいずいう感じです。䞀発で系列を出力しお欲しかった理由は、t が比范的長いか぀そこたでやれなかったからですね。

結果ずしおは、ルヌルベヌスに及ばずでした...あずでルヌルベヌスのものずの粟床比范を行いたすが、たあたあしょがかったです。理由ずしおは以䞋が考えれるかなず思っおいたす。

  1. ハむパラチュヌニングをしなかった時間的な䜙裕がなかったのもありたすが、ほがきめうちのハむパラで実隓しおいたした。

  2. loss の工倫評䟡指暙である GEO-BLEU や DTW を考慮できる loss (があるか調べおいないですが)は䜿わず、ずりあえず RMSE で行っおいたした。比范的盞関があったのでたあいいかなず思っお䜿い続けおいたした。

もう少し䞁寧に䜜れれば、ルヌルベヌスに負けず劣らずになったず信じたいです。

以䞊が機械孊習ベヌスの解法になりたす。

ルヌルベヌス

以䞋が採甚したルヌルになりたす。

この凊理でたず完党なパネルデヌタを䜜りたす。user ID x t x t_label x weekend 分のレコヌドを持぀欠損のないパネルデヌタを䜜るむメヌゞですね。 predict 察象においお欠損の郚分だけ欠損倀補完をすればいいのですが、めんどくさかったので完党パネルデヌタを䜜りたした。こうしお䜜成された欠損のないパネルデヌタず predict 察象のデヌタをマヌゞする (必芁な郚分だけを抜出する) こずで最終的なサブミッションフォヌマットに敎えおいたす。ハむパラである T は手動で軜くチュヌニングしたものを䜿いたした。

結果

task1 だけですが手元の結果を茉せおおきたす。

ルヌルベヌス モデルベヌス
geobleu 0.291 0.116
dtw 33.935 40.356

テキトりなサンプルで予枬を可芖化しおみるずこんな感じになりたした。task 1 に関しおは人々の移動はかなり芏則正しいです。䜙談ですが、task2 だず task1 ず同様のルヌルを䜿っおもスコアが党然䜎い結果ずなりたす。やはり緊急事態時には通垞時ずは異なる動き方をしおいるのでしょうね。task2 だずモデルベヌスの方が dtw に関しおはルヌルベヌスよりも良い結果をもたらしおいたした。

理由はあれど、最埌にルヌルベヌスのサブミッションを䜜るだけ䜜り、私はこのコンペティションから逃走したした。ですので、アンサンブルやハむパラチュヌニングを詰めるずいったこずをせずでした。それでもTOP10 に残れたのは運が良かっただけだず思いたす。ドむツのワヌクショップは今回は残念ながら芋送るこずにしたしたが、HuMob Challenge 2024 が開催されればたた参加したいですね

さいごに

今回は HuMob Challenge 2023 参加蚘ずしお、コンペの抂芁ず解法の玹介を行いたした。Next Place Prediction は倧孊の時から興味ある分野だったので、コンペずしお觊れるこずができよかったですし楜しく取り組むこずができたした。欲を蚀えばリヌダヌボヌドが欲しかったですね。たた、PoI の開瀺や user ごずのデモグラフィックデヌタなどあるずより面癜いのではないかず思っおいたす。

提䟛されたデヌタは将来的にはオヌプンになるらしいので、機䌚があれば是非ずいったずころです。

コヌドはこちらにありたす (汚いのはご了承ください)

github.com