最近、EC2上で音声認識モデルを学習・評価する仕事を行っている。 AWSは初めてで、音声認識モデルの事前学習のような大規模データを扱うのも初めて、という状態からスタートしたので、色々と細かい学びが多かった。 それを、ここに書き留めておく。書いたあとに見直すと、とくにAWSに限った話ではないものもあるね。

作業者用のアカウントを作る

適当にEC2のインスタンスを建てると、デフォルトのubuntuとかのユーザーで全員が作業するような構成にしちゃいがちなんだけど、そんなに面倒でもないので、利用者ごとにユーザーを分けといた方が何だかんだ良い。 やることは、

  1. ユーザー用の鍵ペアを作成
  2. ユーザーのパスワードを登録
  3. ユーザーをsudo,dockerグループに追加
  4. ユーザーの鍵ファイルを.ssh以下に配置
  5. ユーザーのログインシェルを変更

という感じ。このポストの最後に、当時の作業記録(をところどころマスクしたもの)を貼っとくので次回作業時の参考になるかもしれない。そのままコピペして使ってはいけない。

大量データは専用のボリュームに置く

今回の事前学習では、ファイルが200万個弱、総容量で1TB超の音声データを扱った。 こういう巨大なデータは、コピーや移動するだけで、非常に時間がかかる。 S3-インスタンス間でコピーするだけで数日とか、べつのボリュームにコピーするだけで10時間とか、ザラにあった。

一方で、機械学習の実験プロジェクトなので、インスタンスをたくさん立てて並列に異なる条件の実験を進めたい。 こういうときに、必要データを入れたEC2のVolumeのスナップショットを作成し、インスタンスの起動時に、そのSnapshotからvolumeを作成→インスタンスにアタッチとすると、 データにアクセス可能な環境をスムーズに構築できた。起動中のボリューム→スナップショットの作成、や、スナップショットからボリュームの作成は、容量が大きくてもすぐに終わる。

ボリュームを扱うときに使ったコマンドのメモ

lsblk # アタッチされているデバイス一覧が見える
df # マウントされているデバイスが見える
mount /dev/<デバイスの名前> <マウント先のパス> # デバイスをマウント
umount <マウント先のパス> # デバイスをアンマウント
du # 各フォルダが、どれくらいの容量を消費しているかを表示 -d<n>でn階層だけ表示。-hで人間に読みやすい出力

できるだけリソースは使い捨てにする

インスタンスやボリュームなどに、作業途中のデータを保存して置きっぱなしにしておくと、中の設定ファイルやディレクトリ構成などが、どんどん秘伝のタレ化していき、 そのインスタンスやボリューム以外では実験の再現などが難しくなってしまう。 毎回、マシンイメージやスナップショットからマッサラな環境を作成し、すぐに実験を再現できる環境を整えておくべき。

リソースの作成・削除は手動でやらない

何個もインスタンスを立てて実験などを行っていくと、どれが大事でどれが消して良いのか・・など、すぐに分からなくなる。 そういうことがないように、インスタンス名やタグなどに情報を埋め込むことができるが、これを毎回手作業で行うのはめんどうくさい(ので、すぐにやらなくなる)。 そうなると、完全に消しちゃうのは怖いのでとりあえずボリュームだけでも取っておくか・・などやっていると、けっこう維持費がかかる。

ある程度、実験の手順が確立したら、インスタンス作成・停止・削除などはコマンド一発で行えるようにしておき、そのコマンドの中で必要な情報を設定できるようにしておく。 今回は、

  • インスタンスを起動
  • データの入ったボリュームをスナップショットから作成
  • 作ったボリュームをアタッチ & マウント
  • アタッチしたボリュームがインスタンスterminate時に削除されるように設定変更

という一連の動作をコマンド一発で実行できるようなシェルスクリプトを書いた。 このとき、aws cliは、作業の結果をjsonで返してくれるので、その情報をファイルに保存しておき、削除時などには、そのファイルから必要な情報(インスタンス名やインスタンスidなど)を取れるようにした。

また、GPUマシンは、リソースが枯渇してすぐに確保できない場合があるので、自動で失敗を検出しリトライする仕組みは必須。

複数リージョンにデータを配置

GPUマシンのリソースには限界がある。その場合、別のavailability zoneでは獲得できる場合がある。 このとき、同じリージョンのAZであれば、スナップショットからすぐにボリュームを作成できるが、リージョンをまたいでしまうと、スナップショットからボリュームを作成できない。 なので、GPUリソースが獲得できない場合に備えて、複数リージョンに予め必要なデータが入ったスナップショットをコピーしとくと良い。 スナップショットの容量が大きいとコピーにも時間がかかるので、時間のあるときに予めやっとくのが肝要。

ユーザーの作成手順

参考リンク

鍵ファイルを作って公開鍵をサーバーにコピー

ssh-keygen -t rsa -b 4096 -C <SSHの公開鍵ファイルに残すコメントを何か書く>
scp -i ~/.ssh/sciseed-jdsc-ohio.pem -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no my_user_name.pub ubuntu@<IPADDRESS>:/tmp

rootで作業

sudo su -
useradd -m my_user_name # -m: ホームディレクトリを作成
passwd my_user_name # パスワードを作成

# グループに追加
usermod -G sudo,docker my_user_name
cat /etc/group | grep my_user_name # my_user_nameがsudo,dockerに入ってるかをチェック

my_user_nameで作業

su - my_user_name

# 鍵ファイルを配置
mkdir -p /home/my_user_name/.ssh
cp /tmp/my_user_name.pub /home/my_user_name/.ssh

# 権限を変更
chmod 700 /home/my_user_name/.ssh
chmod 600 /home/my_user_name/.ssh/*

# ログインシェルを変更
chsh -s /bin/bssh

あと、sshdの設定を変更して、パスワードでのログインは禁じるようにしたほうがセキュア。コマンドは忘れた