ローカル環境で開発した機械学習モデルの Docker コンテナを構築し、ECRへ登録してみた
こんにちは!よしななです。
今回は、ローカルで開発した機械学習モデルを Docker コンテナ化し、Amazon ECR に登録し、登録したイメージをプルするところまでを検証したので、備忘録としてブログに残します。
目次
- 前提
- 事前準備
- 機械学習モデルの用意
- ディレクトリの準備
- Dockerfile の作成
- ECRに登録してみる
- ECR プライベートリポジトリ作成
- Docker イメージのビルド
- ECR へ Push する
- 最後に
前提
以下の環境で実行しています。
- OS
- Windows
- AWS アカウントは作成済み
- Rancher Desktop のインストール
- こちらを参考に、ローカル環境に Rancher Desktop をインストールします。
- 参考URL:https://dev.classmethod.jp/articles/rancherdesktop-install/
事前準備
機械学習モデルの用意
ECR にプッシュする機械学習モデルを用意します。
今回は、以前に構築した自分の飼い猫かそれ以外の猫を判定する画像分類モデルを使用します。
上記の画像分類モデルを.pyファイルに一つにまとめます。
# ライブラリインポート
import numpy as np
import tensorflow as tf
# ディレクトリパスの指定
data_dir = "/app/data"
# バッチサイズの指定
batch_size = 32
# データサイズの指定
img_height = 180
img_width = 180
# データの分割
## 学習用
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
data_dir,
validation_split = 0.3,
subset = "training",
seed = 1,
image_size=(img_height, img_width),
batch_size=batch_size
)
## 検証用
val_ds = tf.keras.utils.image_dataset_from_directory(
data_dir,
validation_split=0.3,
subset="validation",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size
)
# 正規化
normalization_layer = tf.keras.layers.Rescaling(1./255)
normalized_ds = train_ds.map(lambda x, y: (normalization_layer(x), y))
image_batch, labels_batch = next(iter(normalized_ds))
first_image = image_batch[0]
print(np.min(first_image), np.max(first_image))
# データセットの最適化
AUTOTUNE = tf.data.AUTOTUNE
train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
# CNNの構成
num_classes = 2
model = tf.keras.Sequential([
tf.keras.layers.Rescaling(1./255),
tf.keras.layers.Conv2D(32, 3, activation='relu'),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Conv2D(64, 3, activation='relu'),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Conv2D(128, 3, activation='relu'),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(num_classes),
tf.keras.layers.Softmax()
])
# コンパイル
model.compile(
optimizer=tf.keras.optimizers.Adam(),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
# トレーニング
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=10,
verbose=1
)
ディレクトリの準備
次に、画像分類モデル、Dockerfile と Image_Classification_Model.py、dataset.zip を構築環境を格納するためのディレクトリを作成します。ディレクトリ構成としては以下となります。
Dockerfile 上で 学習用データセット:dataset.zip の解凍を行い、data
フォルダに格納します。
Dockerfile の作成
事前準備が完了したら、Dockerfile を作成します。
Docker は Dockerfile から命令を読み込み、自動的にイメージをビルドできます。
Dockerfile はテキストファイルであり、イメージを作り上げるために実行するコマンドライン命令を、すべてこのファイルに含めることができます。
作成した Dockerfile を元に、docker build
を実行すると、Dockerfile に記述したコマンドラインの処理を行い、ビルド結果となるイメージが作成されます。
参考ドキュメント:
今回作成する Dockerfile に必要な要素としては以下の通りとなります。
- FROM
- ベースとなるイメージを指定します。Dockerfile の先頭に必ず必要です。
- 今回は、TensorFlow のコンテナイメージを使用します。
- タグを省略すると
:latest
が指定されたとみなします。
- LABEL
- イメージに、ベンダ名、作者名、バージョン情報などのラベル情報(メタデータ)を設定します。
- 今回は、バージョン情報と Docker イメージを作成した作者名を記入します。
LABEL
を呼び出すたびにイメージのレイヤが増えるため、ひとつにまとめて設定することが推奨されています。
- WORKDIR
- 作業ディレクトリを指定します。
- RUN
- ビルド時に実行するコマンドを指定します。
- 今回は、スクリプト実行時に必要となる
numpy
ライブラリをインストールし、COPY
コマンドでコピーしたデータセットを解凍し、zip ファイルを削除するコマンドを実行します。 LABEL
と同じくRUN
コマンドを実行するたびイメージのレイヤが増え Docker のビルドが遅くなるため、コマンドを&&
や;
で連結し、複数のコマンドをひとつのRUN
にまとめて記述することが推奨されています。
- COPY
- ホストからコンテナイメージにファイルやディレクトリをコピーします。
- CMD
docker run
時に実行するコマンドを指定します。
上記を組み合わせて、以下の通り Dockerfile を作成します。
こちらの Dockerfile を元に、docker build
コマンドを実行していきます。
# 公式イメージを使用する
FROM tensorflow/tensorflow:latest-gpu
# コンテナの作者名とバージョンを指定
LABEL mainteiner="yoshidanana <メールアドレス入力>" \
version="1.0"
# 作業ディレクトリを指定
WORKDIR /app
# 必要なパッケージをインストール
RUN pip install numpy==1.24.3
# ソースコードとデータセットをコピー
COPY Image_Classification_Model.py /app/
COPY dataset.zip /app/
# データセットを解凍し、zipデータを削除
RUN unzip dataset.zip -d /app/data && rm dataset.zip
# 機械学習モデル.py の実行
CMD ["python", "Image_Classification_Model.py"]
ECRに登録してみる
それでは、作成した Dockerfile を使用して機械学習モデルを ECR に登録してみます。
ECR プライベートリポジトリ作成
プライベートリポジトリは AWS マネージドコンソール上からも作成できますが、今回は AWS CLI 上から作成します。
--repository-name
で作成するプライベートリポジトリ名を指定します。
以下のコマンドを実行します。実施前に ECR の操作権限を持っているロールの Assume role を行っています。
$ aws ecr create-repository --repository-name test
実行に成功すると、以下の値が返ります。
"repository": {
"repositoryArn": "arn:aws:ecr:ap-northeast-1:"<アカウントID>":repository/test",
"registryId": ""<アカウントID>"",
"repositoryName": "test",
"repositoryUri": ""<アカウントID>".dkr.ecr.ap-northeast-1.amazonaws.com/test"
} ...
作成したプライベートリポジトリを確認したい場合は、以下のコマンドを実行します。
$ aws ecr describe-repositories --repository-name test
以下の通り、作成したプライベートリポジトリの設定値が返ります。
{
"repositories": [
{
"repositoryArn": "arn:aws:ecr:ap-northeast-1:"<アカウントID>":repository/test",
"registryId": ""<アカウントID>"",
"repositoryName": "test",
"repositoryUri": ""<アカウントID>".dkr.ecr.ap-northeast-1.amazonaws.com/test",
"createdAt": "2024-09-17T16:17:03.293000+09:00",
"imageTagMutability": "MUTABLE",
"imageScanningConfiguration": {
"scanOnPush": false
},
"encryptionConfiguration": {
"encryptionType": "AES256"
}
}
]
}
Docker イメージのビルド
次に、作成した Dockerfile を元にイメージをビルドします。
docker ターミナルを開き、Dockerfile が配置されているディレクトリに移動します。
cd app
次に、以下のコマンドで Dockerfile をビルドします。
docker build . -t classification-model-image:latest
-t
オプションでリポジトリ名とタグを指定しています。
-t <リポジトリ名>:<タグ>
〇ビルド実行中のターミナル:
ビルドが完了したら、以下のコマンドで作成したイメージを確認します。
Docker イメージがビルドされていることを確認しました。
docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
classification-model-image latest fe06c4e2621a 2 minutes ago 7.61GB
ECR へ Pushする
前項で、プライベートリポジトリの作成と Docker イメージのビルドを実行しました。
次に、ビルドした Docker イメージをプライベートリポジトリに Push していきます。
Push に必要なコマンドは、以下の通りマネージドコンソールからも確認が可能です。
1.Docker クライアントの認証
Docker コンテナを Push するためには、Amazon ECR レポジトリに対し、Docker クライアントを認証する必要があります。
以下のコマンドを実行します。
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin "<アカウントID>".dkr.ecr.ap-northeast-1.amazonaws.com
Login Succeeded
が返れば認証が成功です。
2.Dockerfile を 元に Docker イメージをビルド
2.のコマンドでは Docker イメージの作成となりますが、すでに作成済みですので飛ばします。
3.イメージへタグ付け
次に、Docker イメージのビルドの際に付与したタグを ECR プライベートリポジトリ名に変更します。
$ docker tag classification-model-image:latest "<アカウントID>".dkr.ecr.ap-northeast-1.amazonaws.com/test:latest
コマンドの引数の内容は$ docker tag "<元のリポジトリ名>":"<元のタグ名>" "<変更後のリポジトリ名>":"<変更後のタグ名>"
となります。 実行後に以下のコマンドでタグが変更されたかイメージを確認します。
docker image ls
コマンドが変更されていることを確認しました。
REPOSITORY TAG IMAGE ID CREATED SIZE
"<アカウントID>".dkr.ecr.ap-northeast-1.amazonaws.com/test latest fe06c4e2621a 13 minutes ago 7.61GB
4.ECR へ AWS CLI で Push
タグの変更が完了したら、Docker イメージを ローカル環境から ECR に Push していきます。
以下のコマンドを実行します。
$ docker push "<アカウントID>".dkr.ecr.ap-northeast-1.amazonaws.com/test:latest
〇実行中の様子:
Push が完了したら、以下のコマンドで Docker イメージを確認します。
ECR 上に Docker イメージが Push されていることを確認しました。
aws ecr describe-images --repository-name test
{
"imageDetails": [
{
"registryId": "<アカウントID>",
"repositoryName": "test",
"imageDigest": "sha256:08de9457b3988619401bc955451babcfcbc349beb62fb5cbbf8826f8616f4fe9",
"imageTags": [
"latest"
],
"imageSizeInBytes": 3983229617,
"imagePushedAt": "2024-09-17T18:37:33+09:00",
"imageManifestMediaType": "application/vnd.docker.distribution.manifest.v2+json",
"artifactMediaType": "application/vnd.docker.container.image.v1+json"
}
]
}
無事にプッシュできていることが確認できました。
マネージドコンソール上からも以下の通りプッシュできていることを確認しました。
最後に
今回、Docker イメージに Push してみて、Dockerfile の書き方のベストプラクティスを知ることができてよかったです。ビルド時間に影響があることなど普段意識してこなかったのですが、だいぶ短縮できることを知りました。
ここまで読んでいただき、ありがとうございました!