AWS EC2上で進めるMLOps Basics

# はじめに
先日，[MLOps Basics](https://github.com/graviraja/MLOps-Basics) を読み進める会やろうぜという旨のツイートをしたところ色んな方に反応してもらえました．

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">先月くらいからMLOps Basics を友達と進めています！今日はこの後20時からDVCやるんですが興味ある人一緒にやりませんか？ゆるぼ <a href="https://t.co/9ezulSNzjv">pic.twitter.com/9ezulSNzjv</a></p>&mdash; johannyjm1 (@johannyjm1) <a href="https://twitter.com/johannyjm1/status/1584839496977170432?ref_src=twsrc%5Etfw">October 25, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
 
 
 
 
やはり ONNX や Docker といった ML/DL 周辺で活躍するフレームワークや，モデルのデプロイ周りについて体系的にまとまった資料は需要が高いんだな？という気持ちからこの [MLOps Basics](https://github.com/graviraja/MLOps-Basics) を取り扱うに至りました．

機械学習周りの環境構築は意外に気をつけるべき項目が多く困っている人もよく見かけるので，この記事が何かしらの助けになれば幸いです．

なお，本稿は AI/ML on AWS Advent Calendar 2022の12月9日分の記事です．他の記事も楽しいので是非見てください🙌
[https://qiita.com/advent-calendar/2022/ai-ml-on-aws](https://qiita.com/advent-calendar/2022/ai-ml-on-aws)

# TL; DR

- MLOps Basics というイケてる MLOps 学習教材がある
- これを進める際は，複数人の勉強会形式がオススメ．進め方をまとめた
- オススメの環境の構築方法とスタートまでの手順をまとめた
- Tips と注意点をまとめた

# 目次

- はじめに
- TL; DR
- MLOps Basicsって何ですか
    - MLOps Basicsの特長
- MLOps Basics を進める勉強会について
    - 勉強会の進め方
- 環境構築
- さいごに
- 脚注

# MLOps Basicsって何ですか

MLOps Basics ([https://github.com/graviraja/MLOps-Basics](https://github.com/graviraja/MLOps-Basics)) は，昨年夏[^1] Enterpret 社の Raviraja Ganta 氏によって突如公開された MLOps を学習するためのハンズオン及び Blog 記事です．

<img width="600" alt="MLOpsBasics.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221900/fd598a07-c97e-6dc6-16f8-7bf84333beda.png">


資料としては
- GitHub Repository
[https://github.com/graviraja/MLOps-Basics](https://github.com/graviraja/MLOps-Basics)
- Blog posts
[https://www.ravirajag.dev/blog](https://www.ravirajag.dev/blog)

が用意されており，
GitHub Repository には環境構築に必要な `requirements.txt` や `Dockerfile` ，training や inference を行う Python ファイルが配置されています．

また Blog posts では MLOps の構成要素ごとに概念の解説を行った上で，実践パートとして上記スクリプトの具体的解説を行っています．

本資料では仮想プロジェクトとして「入力された文の文法が合っているかどうかの 2 値分類 [^2] モデルを作成し， Web アプリケーションとしてデプロイする」という一貫したテーマが有り， week を進めることで実践パート部分でそのアプリが徐々に完成していくと言う形式を取っています．

## MLOps Basics の特長

図から読み取れるようにこの資料では

- ML / DL プロジェクトにおける実験管理，再現性管理
- データのバージョニング（及びデータパイプライン）
- モデルのバージョニング
- モデルのデプロイ
- 予測のモニタリング

といったいわゆる MLOps の構成要素たちを，現時点の ( 2022 / 12 / 9 時点でも) デファクトスタンダードと言えそうなツールを採用し「構成要素ごとに体系的」にまとめています．

この「構成要素ごとに体系的」というところがポイントで， MLOps の構成要素を概念として理解することにより，例えば「今まで〜〜の処理に使ってきたフレームワークが使えなくなった」みたいな場面でも代替となる手段を見つけるのに戸惑わないで済みますし，どういう処理をさせればいいか当たりをつけやすくなるでしょう．

# MLOps Basics を進める勉強会について

そんな素晴らしい MLOps Basics なんですが，**複数人での同一環境・同時進行**での読み進めを個人的にとてもオススメしております．

理由としては，

:::note info
**エラーの解決について**
- 画面共有などでリアルタイムにエラーの共有・解決ができる
- 解決方法が共有できる
:::


:::note info
**情報交換について**
- 普段自分はこのツールをどのように使っているか？みたいな実例を共有できる
    - （たまにオレオレ設定を見せ合う会みたいになってそれも良い）
:::



みたいな感じです．
あとは単に一人で黙々と進めるのはつらいけどみんなだと楽しいというのがあります．

## 勉強会の進め方
[Repository](https://github.com/graviraja/MLOps-Basics) を見てみると notebook ファイルも置いてあったりしますが，整備された内容じゃない？（おそらくコーディングする際のテストに使用？）ため基本的に Repository 内ではpyファイルや設定ファイルのみ使用するのが良いと思います．（というか notebook は week0 以外も中身が全部一緒です）

ですので基本的には[Blog posts](https://www.ravirajag.dev/blog)を読みながら適宜該当する python ファイルの中身を読んだり，実行して出力を見たりすることで進めていきます．

僕らの場合は，担当者一人の画面（左に blog ，右に terminal の分割画面）を共有しながら読み進めていました．
<img width="800" alt="screen.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221900/32e142ba-51a7-65eb-5bef-5d745db46b9b.png">

# 環境構築
というわけで本稿のメインパートである環境構築です．



### インスタンス作成
重視したポイントは以下のとおりです
- [AWS EC2](https://aws.amazon.com/ec2/) を用いる
    - 前述の「みんな同じ実行環境」を実現するため
    - Docker でも良さそうだが後にDockerを取り扱うセクションがあり Docker in Docker を避けるため
- できるだけ安くしたい
    - 安いほうが良いため

これらのポイントを押さえつつ，出来上がった環境が以下です

:::note warn
**注意点**
今回紹介するのは今のところ筆者の実践範囲でうまく動いている構成であって，最適・最安な環境を保証するものではありません．

:::

|項目|選んだタイプ|
|:-:|:-:
|Amazon Machine Image|Ubuntu Server 22.04 LTS(HVM), SSD Volume Type|
|Instance type|t3.large[^3]|
|Configure storage|1x 16GiB gp2 Root volume|

##### 選んだ理由
- **Application and OS Images (Amazon Machine Image)**
    - Ubuntu Server 22.04 LTS(HVM), SSD Volume Type
        - Ubuntu に慣れてる人が多かったのと，解決策が見つかりやすい利点から
- **Instance type**
    - t3.large
        - medium ( メモリが 4 GiB 前後のもの ) 以下だと重めの NLP ライブラリの `pip install` で死にます...
        - DL を用いるプロジェクトではありますが，強めの学習は対象外とされ軽い学習のみなので CPU で十分です
- **Configure storage**
    - 1x 16GiB gp2 Root volume (Not encrypted)
        - 割とヘビーなライブラリを仮想環境 ( conda ) にインストールする必要があるためデフォルトの 8GiB だとまず足りません
        - 16GiB でも使い捨てな環境構築（後述）が必要なため余裕がある方は 32GiB とかでも

AWS EC2のページから Launch instance を選択して↑の情報を選択します
それ以外はデフォルトで良いと思います．
ダウンロードした鍵ファイルは Path をきちんと把握するか， `~/.ssh/` 等に置いておきましょう（後に使います）

Launch instance ボタンを押すとすぐにインスタンスが立ち上がり， Instance state が無事「 Running 」状態になると Public IPv4 がアサインされます．

:::note warn
今回の構成の場合 IP アドレスはインスタンスを起動するたびに変わるため，ログイン時に指定する IP アドレスは毎回変更する必要があります．
:::

### 作成したEC2インスタンスへのログイン

ssh を用いて立ち上げたインスタンスにログインしたいですが，　2 つの方法を紹介します．
- ① ターミナルから直接 ssh
    - コードの閲覧や編集を Vim や Emacs など，ターミナル上で行いたい方向け
    - コマンド直打ちでも良いですし， config ファイルを書いても良いです
- ② VSCode の Remote - SSH を使う
    - コードの閲覧や編集を VSCode で行いたい方向け

#### ① ターミナルから直接 ssh
* Ubuntu やMacユーザーを想定します


##### コマンドを用いる場合
```bash
$ ssh -i <鍵ファイルのpath> ubuntu@<インスタンスのPublic IPv4>
```
内容によって， web サーバーを立てる関係からポートをフォワーディングしたい気持ちになることがありますが，そんなときは以下のように `-L` オプションで指定します．
```bash
$ ssh -i <鍵ファイルのpath> -L 8080:localhost:8080 ubuntu@<インスタンスのPublic IPv4>
```

##### config ファイルを用いる場合
以下のように config ファイルを作成し， `~/.ssh/` に配置します
```bash:~/.ssh/config
Host mlops-basics # 好きな名前で良いです
    HostName <インスタンスのPublic IPv4>
    User ubuntu
    IdentityFile <鍵のpath>
    LocalForward 8080 localhost:8080 # 8080番をフォワーディングする場合
```

config ファイルが正しく `~/.ssh/` に置かれていると以下コマンドでインスタンスにログインできます．（ Tab キーでの補完も効きます）
```bash
$ ssh mlops-basics
```
##### `Load key "./mlops_basics.cer": bad permissions` のようなエラーが出るとき
以下の様に鍵ファイルの権限を変更します．
```bash
$ chmod 600 mlops_basics.cer
```

#### ② VSCode の Remote-SSH を使う

- 前準備として前述の config ファイルを作成し， `~/.ssh/` に配置しておきます．
- 左の Extensions タブ で「 ssh 」と検索し，一番上に出る Remote-SSH をインストールします．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221900/9f0d2876-590a-d38e-5d4f-403f437e50b2.png)

- config ファイルが正しく配置されていれば左の Remote Explorer → Remote → SSH に Host が表示されます（複数の Host を config に記述していると複数表示されます）（ Windows ユーザーさんもカスタムコンフィグを編集すれば行けそうな気がしますが未確認です）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221900/369a29c5-a853-eaa8-83a8-69d8f5fdf360.png)

- 後は Host 名の横にある「 Connect in New Window... 」を押すだけでサーバーに繋がります！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221900/ddbf20fb-a164-8016-a889-59bc6ac40332.png)

:::note warn
ここから先は Terminal 上での作業が増えるため，メニューバーの「 Terminal 」から「 New Terminal 」を選択し， Terminal を起動しておきましょう．
:::
### 実行環境の構築

インスタンスに無事ログインできましたが，このままでは必要なライブラリが揃っておらず，例えば `python train.py` を正常に実行できません．

No module エラーの言われたとおりに `pip install` することで解決することもありますが，今回利用するような重めの DL フレームワークを活用するようなケースではライブラリの依存関係で壊滅的な破綻を招きがちです.

ライブラリの依存関係を管理する方法は沢山ありますが，今回は結局**公式に書かれている通り `conda` での環境構築**が最も楽で安定したので，こちらを紹介します．

#### Miniconda のインストール

[Anaconda公式](https://docs.anaconda.com/anaconda/install/linux/)から shell ファイルを取得し，Miniconda をインストールします（ conda の機能がほしいだけなので Miniconda で十分です）
```bash
$ wget https://repo.anaconda.com/miniconda/Miniconda3-py39_4.12.0-Linux-x86_64.sh
$ bash Miniconda3-py39_4.12.0-Linux-x86_64.sh

...yes 等を入力します
```

そのままだと設定だけ書き込まれて Path が通っていない（次回ログイン時に通る）ので `source` します．
```bash
$ source ~/.bashrc
(base) $ 
```
無事 `conda` に Path が通りました．
```bash
(base) $ conda --version
conda 4.12.0
```

#### MLOps Basics Repository の clone
MLOps Basics の Repository を clone します．（ git は最初から入っています．）

```bash
$ git clone https://github.com/graviraja/MLOps-Basics.git
```
:::note info
Public Repository のため，トークンや鍵の設定は必要ありません．
:::

#### 仮想環境の構築
ここからは[公式 Repository ](https://github.com/graviraja/MLOps-Basics/tree/main/week_0_project_setup)に記載のとおりです．

```bash
(base) $ cd ~/MLOps-Basics/week_0_project_setup/
(base) $ conda create --name project-setup python=3.8
(base) $ conda activate project-setup
(project-setup) $ pip install -r requirements.txt
```

必要なライブラリのインストールが終われば，きっと以下のコマンドが動くはずです🚀
```bash
(project-setup) $ python train.py 
```

### 環境構築周りの注意点
[公式 Repository ](https://github.com/graviraja/MLOps-Basics)で各 week の requirements を見てもらうと分かるのですが， **requirements の内容は week ごとに異なっています．**

かつ，どうやら**一つの仮想環境に複数の requirements を用いたインストールを重ねると途中で壊れる**らしいことが分かっております...

解決方法としては week ごとに異なる仮想環境を作成すれば良いです，例えば week1 でしたら，
```bash
(project-setup) $ cd ~/MLOps-Basics/week_1_wandb_logging/
(project-setup) $ conda create --name wandb_logging python=3.8
(project-setup) $ conda activate wandb_logging
(wandb_logging) $ pip install -r requirements.txt
```
のように新たに仮想環境を作成します．

ヘビーな環境が複数出来てしまうため以下のコマンドで既存の環境を消去することが出来ます．[^4][^5][^6]
```bash
(project-setup) $ conda deactivate
$ conda remove --name project-setup --all
```
:::note warn
環境を消す前に `conda deactivate` で環境を無効化する必要があります．
:::


### おまけ，進め方の Tips
##### 多くの人にとってあまり役に立たない，今ちょうど進めている人向けの Tips
- 新しい week に入ったら，ひとまず `train.py` の `wandb` のアカウント名を自分のものに変える
- 再ログイン時，ひとまず `wandb login` する
- week3_dvc は「 blog の内容を一通りやった後」の状態になっているため，`dvc init` をするために week3 最初の時点で `MLOps-Basics/.dvc/`，`MLOps-Basics/.dvcignore` は消しても良い
- week3_dvc でリモートストレージのアクセス許可をするために 8080 番のポートフォワードが必要
- week5_docker
    - fastapi で web サーバーを立てるくだりがあるがおそらく `uvicorn` のバージョン違いでコマンドを `uvicorn app:app --ip 0.0.0.0 --port 8000 --reload` から `uvicorn app:app --host 0.0.0.0 --port 8000 --reload` に変更する必要がある
    - `Dockerfile` 内で `requirements_prod.txt` をしているが `requirements_inference.txt` の間違いのため `Dockerfile` の表記を直す
    - `transformers` のバージョン依存関係に由来するエラーが発生するので `requirements_inference.txt` に `transformers==4.5.1` を追記する
    - Docker image が割とヘビーなので適宜 `docker rmi` するのが良さそう
- 実はまだ week6 なので引き続き Tips は更新していきます...

##### 全ての人に役に立つTips
- インスタンスを落とすことを忘れないこと！！！！

# さいごに
全然 AWS と関係無いことを書いてしまったと震えていましたが， AI / ML on AWS かと言われればそれはそうだなと思いました．

楽しいアドベントカレンダーにお誘いいただいた友人の @kmotohas さん，有難うございます！

それではみなさん良い MLOps ライフを！

# 脚注
[^1]: Repository 自体は 2021 年 5 月 から存在
[^2]: [CoLA(Corpus of Linguistic Acceptability) dataset](https://nyu-mll.github.io/CoLA/)
[^3]: もともとt2.largeを掲載していましたが， t3.large の方が新しい世代 ＋ 安いとの情報を頂いたため変更（他にも良いインスタンスタイプがあれば教えてください！）
[^4]: もしかしたらヘビーで共通なライブラリはキャッシュでなんとかしているみたいな事があるのかもしれませんが，未確認です．
[^5]: いまのところ毎回消す運用で 16GiB ストレージだとほぼ問題ありません．
[^6]: ストレージがいくつだと仮想環境を全部残せるかは，こちらも未確認です．