DOKはコンテナー型のGPUサービスで、NVIDIA V100やH100を実行時間課金で利用できるサービスです。
コンテナー型GPUクラウドサービス 高火力 DOK(ドック) | さくらインターネット
今回はこのDOKを使って、Text2Video-Zeroにてテキストから動画を生成してみます。
結果
結果は以下のようになっています。4FPSがデフォルトなので、別途フレーム補間は必要です。
とりあえず試す
学習済みのデータを使って実行してみたい方は、DOKにて新しいタスクを作成し、以下の情報を入力してください。
項目 | 設定 |
---|---|
イメージ | dok-handson.sakuracr.jp/text2video |
環境変数 | PROMPT=A horse galloping on a street FPS=4 LENGTH=8 |
PROMPTで指定したテキストに対して動画を生成します。FPSはフレームレート、LENGTHは秒数です。処理は5分弱で終わります(V100の場合。Dockerイメージの準備などもあるので、実際の実行時間はもっと短いはずです)。
コンテナイメージの作成と登録
上記タスクで利用したDockerイメージを作成する手順は以下の通りです。ベースとしては、Picsart-AI-Research/Text2Video-Zeroを使っています。
完成版はgoofmint/dok-text2videoにありますので、実装時の参考にしてください。
必要なファイルの取得
上記リポジトリで、処理に必要なファイルを取得します。
- assets
- annotator
- gradio_utils.py
- model.py
- requirements.txt
- text_to_video_pipeline.py
- utils.py
requirements.txt の修正
DOKで実行する際に必要なライブラリを追加します。 huggingface_hub
も必要でした。
huggingface_hub==0.25.0
boto3
Dockerfile の作成
Dockerfileの内容は以下の通りです。必要なライブラリをインストールし、ファイルをコピーしています。また、 requirements.txt
を使ってライブラリをインストールしています。
FROM nvidia/cuda:12.5.1-runtime-ubuntu22.04
ENV DEBIAN_FRONTEND=noninteractive
# 必要なパッケージのインストール
# /appはアプリのディレクトリ、/opt/artifactはアウトプット先のディレクトリ
RUN apt-get update && \
apt-get install -y \
git \
git-lfs \
python3 \
python3-pip \
libgl1-mesa-glx \
libgl1-mesa-dev \
libglib2.0-0 \
&& \
mkdir /app /opt/artifact && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
# 必要なファイルのコピー
COPY * /app/
COPY annotator/ /app/annotator/
COPY __assets__/ /app/__assets__/
# 必要なライブラリのインストール
RUN pip install -r requirements.txt
RUN pip cache purge
# 実行スクリプト(後で作成)
COPY runner.py /app/
# Dockerコンテナー起動時に実行するスクリプト(後で作成)
COPY docker-entrypoint.sh /
# 実行権限を付与
RUN chmod +x /docker-entrypoint.sh /
WORKDIR /
# Dockerコンテナー起動時に実行するスクリプトを指定して実行
CMD ["/bin/bash", "/docker-entrypoint.sh"]
docker-entrypoint.sh の作成
docker-entrypoint.sh
はDockerコンテナー起動時に実行するスクリプトです。ここでは環境変数をチェックして、 runner.py
を呼び出します。以下の内容で作成します。
#!/bin/bash
set -ue
export TZ=${TZ:-Asia/Tokyo}
# アウトプット先ディレクトリ(自動付与) /opt/artifact固定です
if [ -z "${SAKURA_ARTIFACT_DIR:-}" ]; then
echo "Environment variable SAKURA_ARTIFACT_DIR is not set" >&2
exit 1
fi
# DOKのタスクID(自動付与)
if [ -z "${SAKURA_TASK_ID:-}" ]; then
echo "Environment variable SAKURA_TASK_ID is not set" >&2
exit 1
fi
# プロンプト
if [ -z "${PROMPT:-}" ]; then
echo "Environment variable PROMPT is not set" >&2
exit 1
fi
if [ -z "${FPS:-}" ]; then
FPS=4
fi
if [ -z "${LENGTH:-}" ]; then
LENGTH=8
fi
# S3_はすべてboto3用の環境変数です
pushd /app
python3 runner.py \
--id="${SAKURA_TASK_ID}" \
--output="${SAKURA_ARTIFACT_DIR}" \
--prompt="${PROMPT}" \
--fps="${FPS}" \
--length="${LENGTH}" \
--s3-bucket="${S3_BUCKET:-}" \
--s3-endpoint="${S3_ENDPOINT:-}" \
--s3-secret="${S3_SECRET:-}" \
--s3-token="${S3_TOKEN:-}"
popd
runner.py の作成
runner.py は実際に処理を行うスクリプトです。以下の内容で作成します。
import torch
from model import Model
import boto3
import argparse
def main():
# この中に実装します
if __name__ == "__main__":
main() # メイン関数を実行
パラメータの取得
docker-entrypoint.sh
から渡されたパラメータを取得します。
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument(
'--output',
default='/opt/artifact',
help='出力先ディレクトリを指定します。',
)
arg_parser.add_argument(
'--prompt',
help='プロンプトです',
)
arg_parser.add_argument(
'--id',
default='',
help='タスクIDを指定します。',
)
arg_parser.add_argument(
'--fps',
default='4',
help='FPSです。',
)
arg_parser.add_argument(
'--length',
default='8',
help='動画の長さです。',
)
arg_parser.add_argument('--s3-bucket', help='S3のバケットを指定します。')
arg_parser.add_argument('--s3-endpoint', help='S3互換エンドポイントのURLを指定します。')
arg_parser.add_argument('--s3-secret', help='S3のシークレットアクセスキーを指定します。')
arg_parser.add_argument('--s3-token', help='S3のアクセスキーIDを指定します。')
args = arg_parser.parse_args()
また、モデルを読み込む処理も追加します。
model = Model(device = "cuda", dtype = torch.float16)
main 関数の実装
main関数では、動画を生成するパスを作成し、後述する txt2video
関数を呼び出します。また、S3にアップロードする処理も追加しています。
def main():
# この中に実装します
file_path = f"{args.output}/{args.id}.mp4"
print(f"Save to {file_path}")
txt2video(file_path, args.prompt, int(args.fps), int(args.length))
if args.s3_token and args.s3_secret and args.s3_bucket:
# S3クライアントの作成
s3 = boto3.client(
's3',
endpoint_url=args.s3_endpoint if args.s3_endpoint else None,
aws_access_key_id=args.s3_token,
aws_secret_access_key=args.s3_secret,
)
# ファイルアップロード
s3.upload_file(file_path, args.s3_bucket, os.path.basename(file))
txt2video
関数は以下の通りです。 model.process_text2video
で動画を生成しています。
def txt2video(file_path, prompt, fps, length):
params = {"t0": 44, "t1": 47 , "motion_field_strength_x" : 12, "motion_field_strength_y" : 12, "video_length": length}
model.process_text2video(prompt, fps = fps, path = file_path, **params)
全体の処理
runner.py
の全体の処理は以下の通りです。
import torch
from model import Model
import boto3
import argparse
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument(
'--output',
default='/opt/artifact',
help='出力先ディレクトリを指定します。',
)
arg_parser.add_argument(
'--prompt',
help='プロンプトです',
)
arg_parser.add_argument(
'--id',
default='',
help='タスクIDを指定します。',
)
arg_parser.add_argument(
'--fps',
default='4',
help='FPSです。',
)
arg_parser.add_argument(
'--length',
default='8',
help='動画の長さです。',
)
arg_parser.add_argument('--s3-bucket', help='S3のバケットを指定します。')
arg_parser.add_argument('--s3-endpoint', help='S3互換エンドポイントのURLを指定します。')
arg_parser.add_argument('--s3-secret', help='S3のシークレットアクセスキーを指定します。')
arg_parser.add_argument('--s3-token', help='S3のアクセスキーIDを指定します。')
args = arg_parser.parse_args()
model = Model(device = "cuda", dtype = torch.float16)
def txt2video(file_path, prompt, fps, length):
params = {"t0": 44, "t1": 47 , "motion_field_strength_x" : 12, "motion_field_strength_y" : 12, "video_length": length}
model.process_text2video(prompt, fps = fps, path = file_path, **params)
def main():
# この中に実装します
file_path = f"{args.output}/{args.id}.mp4"
print(f"Save to {file_path}")
txt2video(file_path, args.prompt, int(args.fps), int(args.length))
if args.s3_token and args.s3_secret and args.s3_bucket:
# S3クライアントの作成
s3 = boto3.client(
's3',
endpoint_url=args.s3_endpoint if args.s3_endpoint else None,
aws_access_key_id=args.s3_token,
aws_secret_access_key=args.s3_secret,
)
# ファイルアップロード
s3.upload_file(file_path, args.s3_bucket, os.path.basename(file))
if __name__ == "__main__":
main() # メイン関数を実行
Dockerイメージのビルド
上記の内容で、Dockerイメージをビルドします。Linux環境などで行います。
コンテナレジストリの用意
Dockerイメージを登録するコンテナレジストリを作成します。さくらのクラウドではLAB機能で、コンテナレジストリを提供しています。さくらのクラウドにログインしたら さくらのクラウド
を選択します。
左側のメニューの LAB
の中にある コンテナレジストリ
を選択します。
追加
を押して、コンテナレジストリを作成します。最低限、以下の入力が必要です。
項目 | 設定 |
---|---|
名前 | 分かりやすい、任意の名前を入力してください |
コンテナレジストリ名 | ドメイン名に使われます。以下では、 EXAMPLE.sakuracr.jp として説明します |
公開設定 | Pullのみとします |
ユーザーの作成
コンテナレジストリを作成したら、作成したコンテナレジストリを一覧でダブルクリックします。
詳細表示にて、ユーザータブをクリックします。
追加ボタンを押し、ユーザーを作成します。 YOUR_USER_NAME
と PASSWORD
は任意のものを指定してください。
項目 | 設定 |
---|---|
ユーザー名 | YOUR_USER_NAME |
パスワード | YOUR_PASSWORD |
ユーザ権限設定 | All |
Dockerイメージのビルド
DockerイメージはLinuxで行います。今回はUbuntu 24.04を使っています。Dockerが使える環境であれば、Windows + WSL2でも問題ありません。macOSの場合、アーキテクチャが異なるので動かせないかも知れません(未検証です)。
EXAMPLE.sakuracr.jp
の部分は、作成したコンテナレジストリのドメイン名に置き換えてください。
sudo docker build -t EXAMPLE.sakuracr.jp/text2video:latest .
コンテナレジストリへのログイン
作成したコンテナレジストリにログインします。ログインIDとパスワードが求められるので、作成したものを入力してください。
sudo docker login EXAMPLE.sakuracr.jp
イメージのプッシュ
作成したイメージをコンテナレジストリにプッシュします。イメージサイズが大きいので、数十分かかります。
sudo docker push EXAMPLE.sakuracr.jp/text2video:latest
タスクを作成する
後は最初と同じようにDOKでタスクを作成、実行します。
項目 | 設定 |
---|---|
イメージ | EXAMPLE.sakuracr.jp/text2video |
環境変数 | PROMPT=A horse galloping on a street FPS=4 LENGTH=8 |
まとめ
今回はText2Video-Zeroを使って、DOK上で動画を作成する手順を解説しました。まずは実行できるのみ、次にDockerイメージの作成と段階的に進められるようにしています。ぜひ、試してみてください。
DOKはタスクを多数立ち上げて、後は結果を待つのみと言った使い方ができます。ぜひAI・機械学習に活用してください。