深層学習のためのDocker(2)

インストール手順

Dockerコンテナ内とホストOSのNVIDIAドライバーの接続が面倒らしく,それをNVIDIA Container Toolkitが解決してくれる. ホストOSに(1)Docker Engine, (2)NVIDIA Driver, (3)NVIDIA Container Toolkitをインストールする. 順番は123もしくは213で行う. ホストOSにはUbuntuを想定.

1) Docker Engineのインストール

- [1]を参考に.

2) NVIDIA Driverのインストール(更新)

- [2]を参考に.

リポジトリを追加
$ sudo add-apt-repository ppa:graphics-drivers/ppa
$ sudo apt update

インストール可能なバージョンを確認
$ apt-cache search nvidia | grep nvidia-driver

インストール(バージョン470)
$ sudo apt install nvidia-driver-470

再起動と確認
$ sudo reboot
$ nvidia-smi

3) NVIDIA Container Toolkitのインストール

- [3]を参考に.

動作確認.

$ sudo docker run --rm --gpus all nvidia/cuda:9.0-base cat /usr/local/cuda/version.txt

Dockerコンテナ内のCUDAのバージョンが表示される.この例だと9.0. nvidia-smiの表示するCUDAのバージョンは実際に使われるものとは別らしいので,この方法で確認. 一般にはnvccでも確認できるが,ここでは使えない.

イメージ・コンテナの準備

Docker Engineを使って,イメージとして保存された実行環境をコンテナとして起動したい. NVIDIAGPUを利用する場合には,NVIDIA Container Toolkit(NVIDIA Docker)によってホストOSのNVIDIAドライバを利用できる. あとはCUDAより上のレイヤをDockerイメージが含んでいる必要がある.

Dockerイメージの選択

NVIDIAがCUDAインストール済みのイメージを公開している. いずれ必要なライブラリを入れていく必要はあるが,まずはベースのイメージを選ぶ.

  • nvidia/cudaが提供するDockerイメージのうち,runtimeフレーバーのやつはcuDNNが入っている.[6]
    • nvidia/cuda:11.4.1-cudnn8-runtime-ubuntu18.04 これが良さそう.cuDNNは8.2が入る[8]

深層学習ライブラリ・GPUの世代を基準にした互換性チェック:

  • RTX30台を使うには,CUDA11.1以降が必要[5].最新版は11.4
  • Chainerのlatestはv7.8.0.これは部分的にCUDA11.1以降/Cupy8,9をサポートしている.[4]
    • cupyの最新版は10台だけどchainerが未対応かもしれない.cupy9台の最新版は9.4.0
  • cupy 9.4.0に対応する最新のcuDNNは8.2 [7]
  • Chainer7.8.0, CUDA11.4, Cupy9.4.0, cuDNN8.2の組み合わせが,互換性のある最新の構成(2021/9/26現在)

参考文献

[1] Install using the repository. https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository
[2] ubuntuにCUDA、nvidiaドライバをインストールするメモ. https://qiita.com/porizou1/items/74d8264d6381ee2941bd
[3] Setting up NVIDIA Container Toolkit. https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#setting-up-nvidia-container-toolkit
[4] Chainer. https://github.com/chainer/chainer/releases
[5] GPUs supported. https://en.wikipedia.org/wiki/CUDA#GPUs_supported
[6] Overview of Images. https://hub.docker.com/r/nvidia/cuda
[7] Cupy v9.4.0. https://docs.cupy.dev/en/v9.4.0/install.html
[8] nvidia/cuda. https://hub.docker.com/layers/nvidia/cuda/11.4.1-cudnn8-runtime-ubuntu18.04/images/sha256-b8441f7e65e345b8b73f5464387711a2b82f60911ac55ed203ea5a1bf0061bc6?context=explore

深層学習のためのDocker(1)

機械学習のために利用可能な実験サーバーが複数ある状況を考える. この時,コードの実行に必要なライブラリをそれぞれのサーバーにおのおのインストールすることには,いくつかの問題がある:

  • 手間: 全てのサーバーにいちいちライブラリをインストール/更新していくのは面倒
  • 副作用: サーバーを他者と共有している場合,ライブラリのバージョン変更などの影響がお互いに気になる

Docker利用の意義

以上の議論から,いくつかのライブラリをまとめてdocker imageとして持っておくと便利だと言える.

この時,それぞれの実験サーバーが異なるcompute capabilityのGPUを搭載していることが想定されるが,CUDAより上のレイヤをできるだけ新しいバージョンにしておけば,いずれのノードにおいても同じDockerイメージを利用できる.これについて次の章で述べる.

GPU/ドライバ/CUDA/cuDNN/深層学習ライブラリの互換性

NVIDIAGPUで深層学習をやるには,5つのレイヤ: GPU/ドライバ/CUDA/cuDNN/深層学習ライブラリ を理解する必要があり,これを復習する.

  • ドライバはGPUに対して後方互換性がある.つまりGPUを固定してドライバを更新し続けることができる.[1]
  • ドライバはCUDAに対しても後方互換性がある.つまりCUDAを固定してドライバを更新し続けることができる.
  • 特定のCUDAを使うには,対応するドライバの最低バージョンがある.つまり新しいCUDAには一定以上新しいドライバしか使えない.[2]
  • Compute capabilityの概念によれば,新しいGPUに対応しているCUDAは新しいバージョンだけ.つまりGPUをアップグレードするとCUDAも必然的にアップグレードしないといけない.[3]
    • 逆に,新しいCUDAは割と古いGPUにも対応している
  • 大抵,新しいCUDAには新しい深層学習ライブラリしか対応していない.CUDA/cuDNN/深層学習ライブラリはセットでバージョンを揃える.
  • つまり:
    • ドライバは常に最新
    • 新しいGPUには新しいCUDA/cuDNN/深層学習ライブラリしか使えない
    • 新しいリソース: コード/深層学習ライブラリ/cuDNN/CUDA,はドライバさえ更新していれば古いGPUでも基本的には使える(はず)
    • 逆に,古いリソースは新しいGPUでは基本的に動かない
  • コード/深層学習ライブラリ/cuDNN/CUDAはセットにして,最新を追従するよう努める

NVIDIA Container Toolkit

DockerコンテナとNVIDIA driverの接続が面倒らしい. そこを自動でやってくれるのがNVIDIA Container Toolkit (NVIDIA Docker). ドライバはホストOSにインストールしておく必要がある.[4]
前述の通りこれは最新を入れれば良い. ベースとなるimageはNVIDIAが色々公開しているので利用すると楽できる.

参考文献

[1] NVIDIAドライバダウンロード. https://www.nvidia.co.jp/Download/index.aspx?lang=jp
[2] NVIDIA CUDA Toolkit Release Notes. https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html
[3] GPUs supported. https://en.wikipedia.org/wiki/CUDA#GPUs_supported
[4] NVIDIA Container Toolkit. https://github.com/NVIDIA/nvidia-docker

実験管理(1)

概要

機械学習の研究開発においては,プログラムやハイパーパラメータの変更・モデルの学習・モデルの評価,というサイクルを繰り返す.本稿では,より素早く質の高いアウトプットを目指し,実験管理:洗練された方法で実験結果を管理する方法を紹介する.

実験管理の方針

筆者は,実験管理に次のような特徴を要請する:

  • 集約: コード・学習済みモデル・メタ情報(実験の概要や評価値など)の3点をセットにして保存できる
  • 再現: 所望のコード・学習済みモデルにロールバックして再学習・再評価を行える
  • 俯瞰: 複数の実験のメタ情報を俯瞰し,比較できる

これらを実現するシンプルな方法を検討する.

1

実験が完了した時点で,コード・学習済みモデル・メタ情報の3点をセットにして適当なドライブのフォルダにまとめて保存しておく方法が考えられる.最もシンプルに「集約」と「再現」を達成できる方法である. 一方で,実験間で共通するプログラムやデータセットについて,これらの共通性は意識されることなく複製される. したがって,非効率的にドライブの容量を消費してしまう懸念がある.

2

Gitを利用して3点を保存する方法が考えられる.Gitの有するオブジェクト・zlib圧縮・パッキングの仕組みにより,一つ目の方法より効率的に容量を圧縮しながらコードを保存することができる. また,Gitに特有の「コミット(スナップショット)の親子関係」の概念により,コードを変更して別の実験を実施していくとき,それらの実験がどのように派生してきたのかを理解しやすい. 一方で,gitの問題点として,gitにコミットするだけだと結局スプレッドシートでメタ情報の一覧を作る羽目になることが挙げられる.単にgitを利用するだけでは,「俯瞰」は達成できない. コードは差分を意識しながら効率よく保存できるが,学習済みモデルのパラメータは共通する部分が皆無なので,git以外に保管する手も考えられる.

TODO

A.pyでモデルAを学習して評価指標A'を計算する. 同様に,B.pyでモデルBを学習して評価指標B'を計算する. これが完了したのち,C.pyでモデルAとモデルBを用いてモデルCを学習して評価指標C'を計算する,というプロセスを考える.

この時,モデルA,Bの学習それ自体も一つの実験であるし,これを前提にしたモデルCの学習も一つの実験であるが,この関係をうまく取り扱う方法はあるか?

Appendix: 実験管理ツールの調査

実験管理のフロー: コーディング -> 実験サーバーに転送 -> モデルを学習・評価 -> 3点セットを保存 -> メタ情報を一覧に追加

WandB

  • wandbは学習曲線の可視化と比較・レポーティングに強みのあるサービス
  • スタンドアローン版もある[2]
  • [3]によれば,特定のディレクトリの内容を保存できるので,コードや学習済みモデルのバージョン管理もできそう
    • いわゆるtrain.pyをartifactsとして保存しておけばバージョン管理できる?
    • ただ,そのへんの機能の紹介に乏しいのでコード管理はgit前提かも?

MLFlow

  • [4]によれば,サーバーは自分で立てないといけない
    • 複数台の結果を統合する時にサーバーが必要なはず
    • 実験サーバーの1台をtrackingサーバーにすれば良いのでは?
    • そんなことしなくてもwandbなら自分でサーバー建てられる上に最初からクラウドも提供されているのに・・・
      • databricksのcommunity editionなら楽できる?[5][6].無料で建てられるっぽいけどやや面倒

Comet.ml

  • self hosting機能がなさそう?
    • 自分らで鯖を立てられない? クラウドに出られない環境とかだと逆にself hosting機能が必要になるので・・・

Neptune.ai

  • [7]によればself hostingできそう

参考文献

[1] 試行回数の増やし方2021年度版. https://speakerdeck.com/butsugiri/increasing-number-of-attempts-ver-2021
[2] Self-Hosting. https://docs.wandb.ai/guides/self-hosted
[3] Model Versioning. https://docs.wandb.ai/guides/artifacts/model-versioning
[4] MLflowを利用して機械学習チームで共有可能な実験管理をする方法. https://qiita.com/MasafumiTsuyuki/items/9e03e285d4b9e0c41a7c
[5] MLflow Tracking. https://mlflow.org/docs/latest/tracking.html#how-runs-and-artifacts-are-recorded
[6] Managed MLflow Now Available on Databricks Community Edition. https://databricks.com/jp/blog/2019/10/17/managed-mlflow-now-available-on-databricks-community-edition.html
[7] Deploying Neptune on your server (on-premises). https://docs.neptune.ai/administration/on-premises-deployment

TryOnGAN

TryOnGAN: Body-Aware Try-On via Layered Interpolationに関する雑なメモ

  • ACM SIGGRAPH 2021 (2021.1にarXiv公開)
  • Virtual Try-On (VITON, VTON)に関する最近の研究

問題設定:

- 2枚の入力画像が与えられる
    - I^pには人物Aが服Bを着用した様子
    - I^gには人物Cが服Dを着用した様子
- 人物Aが服Dを着用したphotorealisticな画像I^tを出力する

概要:

  • photo-realismとhigh resolutionの性質を持つStyleGAN2をベースとして用いる
  • 10万枚のunpairdな画像から学習
  • これまでの手法には,学習にペアデータが必要なものがある
    • 人物Aが服BをポーズC,D,E...で着用している画像
    • 人物Aが服Bを着ている画像と,服B単体の画像
  • ペアデータ不要な手法(O-VITON, Zanfir et al.)と比較して,photo-realismが増している

手法:

  • (1) pose-conditionedなStyleGAN2を学習

    • ファッション画像についてStyleGAN2を学習
    • 各解像度において,RGB画像に加えて,セグメンテーション画像も出力させる
    • 識別器として,poseとsegmentationの正しさを判別するものも導入

    • 潜在空間のポーズとスタイルのentanglementを防ぐため,StyleGAN2をポーズでconditiningして学習する

    • StyleGAN本来の定数のlatentを,ポーズを表現する4x4のpose representationで置き換える
      • posenetを用いて,17箇所のキーポイントを抽出
      • この出力から64x64x17チャネルのヒートマップを作成する
      • ヒートマップをさらにencoderに入力し,最終的に4x4のサイズまで圧縮する
  • (2) 各層でのinterpolation coefficientを探索する

    • IpとIgについてのwを探索して,linear interpolationすることを目指す
    • 各解像度でのスタイルの結合係数q_nをそれぞれ求めたい
    • それぞれのq_nが収束するまで,L_localizationと2つのconsistencyの加重和を最小化する
      • L_localizationは,興味のある領域についてのみスタイルの内挿をとりたい
      • これはつまり,semantic segmentationした結果,衣服(segmentation i)に影響するようなwの一部分だけ内挿したい
      • M^{CxK}は,どのチャネルがどのセグメントに影響するかの指標
      • これを使って,衣服に関係あるiについて影響のあるチャネルだけを内挿するようにqを求める(詳細未理解)
    • 2つのconsistency lossを採用
      • IgとItにsegmentationマスクをかけ,衣服の部分だけを残す
      • VGGのfeature上で,2枚が近づくような誤差を導入(Germent Loss)
      • また,IpとItについて,顔と髪型の領域の特徴が近づくような誤差を導入(Identity Loss)

実験:

  • Tesla V100 (FP32だと1080Tiと同程度?) x 8 で12日学習

感想:

  • StyleGAN2という強力なモデルを用いて,スタイルの編集を通してVITONを実現するところがキモ
    • 画像空間で編集してしまうとphotorealisticでないものが生成されがち.あくまで完成品の画像はStyleGANによって生成されるので,ここでphoto-realismが保証される

OKR

Measure What Matters (John Doerr著) をざっと読んだのでメモ.

OKR(Objectives and Key Results)は,達成したい目標と,これを充足する具体的な成果を検討するマネジメントの一手法である.

  • 目標には,シンプルに(1文で)visionを書く
  • 主要な結果には,測定可能な最大5個の,目標を充足する具体的な成果を書く
  • トップダウン的なOKRでは,上位の主要な結果が,下位の目標になる.アライメントされたOKRとも呼ばれ,複数のOKR間の整合性がとりやすい.
  • ボトムアップ的なOKRでは,末端の従業員のOKRでの目標も,本人が決める.モチベーションアップや,現場からのより優れた提案が起こりやすい.
  • トップダウンボトムアップの割合は50:50が理想
  • 目標がトップダウン的に決められたとしても,それを達成する具体的な成果については現場が判断・決定できる.

達成したい目標と,それを充足する具体的な成果を検討するというモデルがしっくりきた. 例えば,主著国際会議フルペーパー1本採択という目標に対して,それを充足する具体的な成果は無数に(どの会議に,どの研究で)存在し得る. 研究を始める前であれば,確率の高そうなテーマで研究して,それっぽい会議の名前をあげることはできる(トップダウン的). しかし,1年2年とプロジェクトが進めば,自分のやりたい・これまでに検討してきたテーマで,単に時期の近い会議名を挙げるかもしれない(ボトムアップ的). この例えは

という好例で,OKRはそれを表現できるモデル.

また,階層的な意思決定モデルを考えたときに,トップが理想/未来,ボトムが現状/現在だと考えると,それを結ぶパス/プロセスは理想から下ろしてくるだけでは実現し得ない. 現実的に/現場主義的に実行可能なプロセスとそうでないプロセスが存在するのは明らかで,これを踏まえて目標/主要な結果を検討する必要がある. そう考えるとトップダウン/ボトムアップ的なOKRは,本文でも触れられている通り,市場主義的に,同時に調整されていくのが好ましいような気がする.

SQLの勉強1

Abstract

SQLのSELECTについて勉強したのでメモ.

Introduction

SQLのSELECTの難しさとして,次のようなものが挙げられる: (1) SELECTはDBに格納されている値をフィルタリングしたり変換したりと,複数の手順から構成される処理であるにもかかわらず,手続き型プログラミング言語とは異なり,一文のSQLとして記述する点(注: サブクエリを使うと分割できる); (2) 記述順序と評価順序が異なる点; (3) それぞれの句に使える位置の制約がある点.

この難しさを解決するために,次の三点を理解したい: (1) SELECT句を含むSQL文がどのような構造で記述されるか; (2) このSQL文がどのような順序で評価されるか; (3) それぞれの句が何を受け取り,何を返し,どの位置で使えるか.

SELECTの構造

基本構文は次のようなもの:

SELECT
FROM
WHERE
GROUP BY
HAVING
;

ここでの基本構文とは,その位置でしか使えず,1つのSELECTステートメントに1度しか使用できない句を並べたもの. ちなみに

  • ORDER BY句はウィンドウ関数内でも使えるので除外.記述順序はHAVINGの後
  • LIMIT句は標準SQLではない.記述順序はORDER BYの後

各句の評価順序,入力ドメイン,記法と引数,出力は以下の通り:

  • FROM(): FROM < TABLE > -> [W x H]
  • WHERE(PRE=[W x H1]): WHERE < BOOL > -> [W x H]
    • < BOOL >には,PREの各行に対して真偽を与えるような論理式を記述する.オペランドとしてカラムとスカラが使える.
    • それぞれの句は,評価時点で処理済みのテーブル(PRE)を暗黙に受け取っている
    • 判定用のカラムを作成してJOINすることで相関サブクエリを書き換えられる場合がある
  • GROUP BY(PRE=[W x H]): GROUP BY < PRE.cols > -> [(W + G) x H]
    • 指定したカラム群< PRE.cols > (集約キー)において重複する行に同じグループインデックスを割り当てるイメージ.この段階ではまだ行数は減っていない(集約キー以外の情報も保存されている)
    • Gはグループインデックスのバリエーション(つまりグループ数)とする.次元としての[(W + G) x H]の記法は,各行にグループインデックス上のone-hotベクトルを付与するイメージ
    • 以降,集約関数が使用可能になる
  • HAVING(PRE=[(W + G1) x H1]): HAVING < BOOL > -> [(W + G) x H]
    • 各グループに対する真偽値を返す論理式しか< BOOL >に使えないので,オペランドとして使えるカラムにはSELECTと同じ制約がある
  • SELECT(PRE=[(W1 + G) x H]): SELECT < PRE.cols > -> [W x G]
    • SELECTは1グループ1行で出力するので,GROUP BYで指定したカラム群か,集約関数でグループ毎に集約した結果しかSELECTのカラムに指定できない
    • GROUP BYを指定しない場合は1レコード1行で出力.ただし,集約関数は全レコードを1グループと考えた挙動になる

Future Work

  • サブクエリ・スカラサブクエリ
  • 相関サブクエリ
  • ビュー
  • WITH句

参考文献

zdoom/slade3あれこれ

1

Slade3に.wadファイルを読ませたときのSCRIPTにはENTER関数がある. この関数内でSetActorPropertyを実行することである意味チートのような状態を作ることができる.
SetActorPropertyは(tid(プレイヤーの場合は0), プロパティ, 値)という形で実行する.
2行目はプレイヤーのHPを100にしている. 3行目はプレイヤーのHPが決して減らないようにしている. 4行目はプレイヤーの移動速度を2倍にしている.

script 2 ENTER  
{  
    ClearInventory();  
    SetActorProperty(0, APROP_HEALTH, 100);  
    SetActorProperty(0, APROP_Invulnerable, True);  
    SetActorProperty(0, APROP_SPEED, 2.0);
   
}

2

pythonからvizdoomでdoomを実行できる. まずpython側でgame.add_available_game_variable(vzd.GameVariable.ITEMCOUNT)と宣言しておく ゲーム内でアイテムを取得した時にDECORATEに+COUNTITEMの記述をしておく.
するとゲーム側でgame.get_game_variable(vzd.GameVariable.ITEMCOUNT)からアイテムの取得数を確認できる. 強化学習でアイテムの取得数を報酬にしたいときなどに便利.

3

doomでの様々なオブジェクト(アイテムや敵?)はActorというクラスで実装されている. https://zdoom.org/wiki/Actor_properties に記載があるようなプロパティをDECORATEで設定することで,Actorのプロパティを変更できる.
例えば,Scale 4 を宣言したアイテムは大きく描画される.

4

https://github.com/mwydmuch/ViZDoom/issues/191#issuecomment-405961176 にあるように,SCRIPTS内でglobal変数として宣言した値はvizdoomから参照することができる.これにより,例えばランダムな座標に生成したアイテムの座標をpython側に伝えることができる. この例では,global int 1:x_pos;をはじめに宣言し,SCRIPTS内でx_posに値を代入することで,vizdoom側でvzd.GameVariable.USER1からこの値を参照できる.