こんにちは、JS2IIUです。
Pythonによる機械学習開発において、多くのエンジニアが直面する課題があります。それは「自分の環境では動いたのに、他の人の環境やサーバー上では動かない」という、いわゆる「環境依存の問題」です。
特にStreamlitを使ったAIアプリの開発では、PyTorchやTensorFlowといった重厚なライブラリ、さらには特定のOSパッケージに依存することが多く、この問題はより顕著になります。そこで活用したいのが「Docker」です。
本記事では、Python中級者の皆様に向けて、PyTorchを利用したAIアプリをStreamlitで構築し、それをDockerでコンテナ化して「どこでも動く」状態にするための実践的なガイドをお届けします。今回もよろしくお願いします。
1. はじめに:なぜAIアプリの開発にDockerが必要なのか
機械学習プロジェクトにおいて、環境の再現性は成功の鍵を握ります。
Pythonの世界にはvenvやcondaといった仮想環境管理ツールがありますが、これらは主にPythonライブラリの管理に限定されます。しかし、実際のアプリはOSのライブラリ(例えば画像処理のためのlibGLなど)や、特定のCUDAバージョンにも依存することがあります。
Dockerを使用することで、OSのベースレイヤーからライブラリ、アプリケーションコードまでを一つの「イメージ」としてパッケージ化できます。これにより、開発者のPC、チームメイトのMac、そしてクラウド上のLinuxサーバーまで、全く同じ環境を瞬時に再現することが可能になります。
今回は、PyTorchを用いた画像認識アプリを例に、Streamlitアプリをコンテナ化するベストプラクティスを学んでいきましょう。
2. 構築するStreamlitアプリの準備
まずは、Docker化の対象となるStreamlitアプリを作成します。今回は、事前学習済みのResNet50モデルを使用して、アップロードされた画像が何であるかを判定するシンプルな画像分類アプリを構築します。
プロジェクト構成
プロジェクトのディレクトリ構造は以下のようにします。
my-ai-app/
├── app.py # Streamlitアプリ本体
├── requirements.txt # Pythonライブラリのリスト
└── Dockerfile # Dockerの設定ファイルStreamlitアプリの実装(app.py)
以下のコードは、PyTorchを使用して画像を読み込み、推論結果を表示するスクリプトです。
import streamlit as st
import torch
from torchvision import models, transforms
from PIL import Image
import json
# アプリのタイトル設定
st.title("AI画像分類アプリ(ResNet50)")
st.write("画像をアップロードすると、AIがその中身を推論します。")
# モデルの読み込み(キャッシュを利用して高速化)
# @st.cache_resource を使用することで、アプリの再実行時にモデルを再ロードするのを防ぎます。
@st.cache_resource
def load_model():
# 学習済みResNet50モデルをロード
model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)
model.eval()
return model
# 前処理の定義
def preprocess_image(image):
preprocess = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
return preprocess(image).unsqueeze(0)
# モデルとラベルの準備
model = load_model()
# 画像のアップロード
uploaded_file = st.file_uploader("画像を選択してください...", type=["jpg", "jpeg", "png"])
if uploaded_file is not None:
# 画像の表示
image = Image.open(uploaded_file).convert('RGB')
st.image(image, caption='アップロードされた画像', use_container_width=True)
st.write("推論中...")
# 前処理と推論
input_tensor = preprocess_image(image)
with torch.no_grad():
output = model(input_tensor)
# 結果の取得(Softmaxで確率に変換)
probabilities = torch.nn.functional.softmax(output[0], dim=0)
# 本来はImageNetのラベルファイルが必要ですが、
# 今回は上位5件のインデックスと確率を表示します。
top5_prob, top5_catid = torch.topk(probabilities, 5)
st.subheader("推論結果(Top 5 インデックス):")
for i in range(top5_prob.size(0)):
st.write(f"Index: {top5_catid[i].item()}, 確率: {top5_prob[i].item():.2%}")このコードの重要なポイントは、@st.cache_resource デコレータです。機械学習モデルはサイズが大きく、ロードに時間がかかるため、このデコレータを使ってメモリ上にキャッシュしておくことがStreamlitアプリのパフォーマンス向上には欠かせません。
依存ライブラリの定義(requirements.txt)
次に、アプリに必要なライブラリを記述します。
streamlit
torch
torchvision
Pillow※PyTorchのバージョンを固定することも可能ですが、Docker内で最新のCPU版をインストールさせるために、ここではシンプルに記述します。
3. MLアプリのためのDockerfile設計
ここからが本題のDocker化です。機械学習アプリをコンテナ化する場合、イメージサイズが巨大になりがちです。効率的でポータブルなDockerfileを作成するためのテクニックを盛り込んだ「ひな型」を紹介します。
Dockerfileの記述
プロジェクトのルートディレクトリに Dockerfile という名前のファイルを作成し、以下の内容を記述します。
# 1. ベースイメージの選択
# 軽量なslim版を使用しつつ、Pythonのバージョンを指定します。
FROM python:3.11-slim
# 2. OSパッケージのインストール
# OpenCVやPillowなどの画像処理ライブラリが必要とするシステム依存パッケージを導入。
# 不要なキャッシュを削除してイメージサイズを削減します。
RUN apt-get update && apt-get install -y \
build-essential \
curl \
software-properties-common \
&& rm -rf /var/lib/apt/lists/*
# 3. 作業ディレクトリの設定
WORKDIR /app
# 4. 依存関係のインストール(レイヤーキャッシュの活用)
# 先にrequirements.txtだけをコピーすることで、ソースコードが変更されても
# ライブラリの再インストール(時間がかかる工程)をスキップできます。
COPY requirements.txt .
RUN pip3 install --no-cache-dir -r requirements.txt
# 5. アプリケーションコードのコピー
COPY . .
# 6. Streamlitが使用するポートの開放
EXPOSE 8501
# 7. ヘルスチェックの設定
# コンテナが正常に動作しているか外部から確認できるようにします。
HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
# 8. アプリの実行コマンド
# --server.address=0.0.0.0 はコンテナ外からのアクセスを許可するために必須です。
ENTRYPOINT ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]Dockerfileの解説
このDockerfileには、実務で役立ついくつかの重要な工夫が施されています。
- ベースイメージに
-slimを選択:
通常のpython:3.11イメージは非常に巨大ですが、-slimを使うことで、必要最小限のツールに絞り込み、デプロイ時間を短縮できます。 - レイヤーキャッシュの最適化:
COPY requirements.txt .をCOPY . .よりも先に行っています。Dockerは各行をレイヤーとして保存します。コードの一部を書き換えるたびに重いPyTorchのインストールが走るのは苦痛ですが、この順序にすることで、requirements.txtに変更がない限り、インストール済みのレイヤーが再利用されます。 --no-cache-dirの使用:
pipがダウンロードしたパッケージのキャッシュをコンテナ内に残さないようにし、イメージサイズを数百MB単位で削減します。- アドレスの固定:
StreamlitをDockerで動かす場合、--server.address=0.0.0.0を指定しないと、コンテナ外(ブラウザなど)からの通信を受け付けることができません。
4. イメージのビルドと実行
Dockerfileの準備ができたら、実際にコンテナをビルドして動かしてみましょう。
1. イメージのビルド
ターミナルでプロジェクトのディレクトリに移動し、以下のコマンドを実行します。
docker build -t my-streamlit-ai-app .-t オプションはイメージに名前(タグ)を付けるためのものです。初回はライブラリのインストールに時間がかかりますが、2回目以降はキャッシュが効いて高速に終わります。
2. コンテナの起動
ビルドが完了したら、コンテナを起動します。
docker run -p 8501:8501 my-streamlit-ai-app-p 8501:8501 は「ホストマシンの8501番ポートへのアクセスを、コンテナの8501番ポートに転送する」という設定です。
起動後、ブラウザで http://localhost:8501 にアクセスしてください。作成したAIアプリが表示されれば成功です。
5. 応用:より使いやすくするために
実際の開発現場では、さらにいくつかの考慮事項が必要になります。
不要なファイルを除外する(.dockerignore)
Dockerイメージをビルドする際、.git ディレクトリやローカルの仮想環境(.venv)、__pycache__ などは不要です。これらをコピー対象から外すために、.dockerignore ファイルを作成します。
.venv
__pycache__
.git
.ipynb_checkpoints
data/*.csvこれにより、ビルド速度が向上し、イメージのセキュリティも高まります。
メモリ制限への注意
機械学習モデルを動かす際、Dockerコンテナに割り当てられたメモリが不足して「Killed」というエラーでアプリが落ちることがあります。特にDocker Desktop(Windows/Mac)を使用している場合は、設定画面からDockerに割り当てるメモリを(例えば4GB以上など)増やしておくと安定します。
数学的な観点から言えば、モデルのパラメータ数 \( N \) と、各パラメータのデータ型(float32など)のサイズを考慮したメモリ設計が必要です。例えば、ResNet50は約2,500万個のパラメータを持ち、単精度浮動小数点(4バイト)で保持されるため、モデル自体で約100MBを消費します。しかし、推論時の活性化関数の出力(中間データ)を考慮すると、その数倍から十数倍の作業メモリが必要になります。
$$
\text{必要なメモリ} \approx (\text{モデルサイズ}) + (\text{入力データサイズ} \times \text{バッチサイズ} \times \text{中間レイヤー係数})
$$
このような計算を意識しておくと、より大規模なモデル(LLMなど)を扱う際のインフラ設計に役立ちます。
6. まとめ:コンテナ化がもたらす開発サイクルの高速化
本記事では、StreamlitアプリをDocker化する一連の流れを解説しました。
Dockerを活用することで、開発環境のセットアップ手順を「Dockerfileというコード」として管理できるようになります。これは単に「どこでも動く」だけでなく、GitHub ActionsなどのCI/CDツールと組み合わせて、テストからデプロイまでを自動化するための第一歩でもあります。
今回作成したDockerfileのひな型は、PyTorch以外のライブラリ(Scikit-learnやTensorFlow)を使う場合でも、requirements.txt を書き換えるだけでそのまま応用可能です。
ぜひ、ご自身のAIプロジェクトにもDockerを取り入れ、環境構築の悩みから解放された、快適な開発ライフを送ってください。コンテナという強力な武器を手にすることで、あなたの作った素晴らしいAIアプリを、より多くの人に届けることができるはずです。
最後まで読んでいただきありがとうございます。


コメント