Vertex AI: カスタムモデルのトレーニングとサービング

1. 概要

このラボでは、Vertex AI を活用し、カスタム コンテナ内のコードを使用して TensorFlow モデルのトレーニングとサービングを行います。

ここではモデルのコードに TensorFlow を使用していますが、別のフレームワークに簡単に置き換えることができます。

学習内容

次の方法を学習します。

  • Vertex Workbench でモデルのトレーニング コードを構築してコンテナ化する
  • カスタムモデルのトレーニング ジョブを Vertex AI に送信する
  • トレーニング済みモデルをエンドポイントにデプロイし、そのエンドポイントを使用して予測を行う

このラボを Google Cloud で実行するための総費用は約 $1 です。

2. Vertex AI の概要

このラボでは、Google Cloud で利用できる最新の AI プロダクトを使用します。Vertex AI は Google Cloud 全体の ML サービスを統合してシームレスな開発エクスペリエンスを提供します。以前は、AutoML でトレーニングしたモデルやカスタムモデルには、個別のサービスを介してアクセスする必要がありました。Vertex AI は、これらの個別のサービスを他の新しいプロダクトとともに 1 つの API へと結合します。既存のプロジェクトを Vertex AI に移行することもできます。ご意見やご質問がありましたら、サポートページからお寄せください。

Vertex AI には、エンドツーエンドの ML ワークフローをサポートするさまざまなプロダクトが含まれています。このラボでは、以下でハイライト表示されているプロダクト(Training、Prediction、Workbench)を取り上げます。

Vertex プロダクトの概要

3. 環境をセットアップする

この Codelab を実行するには、課金が有効になっている Google Cloud Platform プロジェクトが必要です。プロジェクトを作成するには、こちらの手順を行ってください。

ステップ 1: Compute Engine API を有効にする

まだ有効になっていない場合は、[Compute Engine] に移動して [有効にする] を選択します。これはノートブック インスタンスを作成するために必要です。

ステップ 2: Vertex AI API を有効にする

Cloud コンソールの [Vertex AI] セクションに移動し、[Vertex AI API を有効にする] をクリックします。

Vertex AI ダッシュボード

ステップ 3: Container Registry API を有効にする

まだ有効になっていない場合は、[Container Registry] に移動して [有効にする] を選択します。これは、カスタム トレーニング ジョブのコンテナを作成するために使用します。

ステップ 4: Vertex AI Workbench インスタンスを作成する

Cloud Console の [Vertex AI] セクションで [ワークベンチ] をクリックします。

Vertex AI メニュー

そこから、[ユーザー管理のノートブック] で [新しいノートブック] をクリックします。

ノートブックを新規作成

次に、最新バージョンの TensorFlow Enterprise(LTS 版)インスタンス タイプ(GPU なし)を選択します。

TFE インスタンス

デフォルトのオプションを使用し、[作成] をクリックします。

このラボでトレーニングして提供するモデルは、TensorFlow のドキュメントのこのチュートリアルに基づいて構築されています。このチュートリアルでは、Kaggle の Auto MPG データセットを使用して、自動車の燃料効率を予測します。

4. トレーニング コードをコンテナ化する

このトレーニング ジョブを Vertex に送信しましょう。トレーニング コードを Docker コンテナに格納して、このコンテナを Google Container Registry に push します。この方法を使用すると、任意のフレームワークで構築されたモデルをトレーニングできます。

まず、Launcher メニューから、ノートブック インスタンスでターミナル ウィンドウを開きます。

ノートブックでターミナルを開く

mpg という新しいディレクトリを作成し、そのディレクトリに移動します。

mkdir mpg
cd mpg

ステップ 1: Dockerfile を作成する

コードをコンテナ化する最初のステップは、Dockerfile の作成です。Dockerfile には、イメージの実行に必要なすべてのコマンドを含めます。使用しているすべてのライブラリがインストールされ、トレーニング コードのエントリ ポイントが設定されます。ターミナルで、空の Dockerfile を作成します。

touch Dockerfile

Dockerfile を開き、次の内容をコピーします。

FROM gcr.io/deeplearning-platform-release/tf2-cpu.2-6
WORKDIR /

# Copies the trainer code to the docker image.
COPY trainer /trainer

# Sets up the entry point to invoke the trainer.
ENTRYPOINT ["python", "-m", "trainer.train"]

この Dockerfile は、Deep Learning Containers TensorFlow Enterprise 2.3 Docker イメージを使用します。Google Cloud の Deep Learning Containers には一般的な ML およびデータ サイエンスのフレームワークが数多くプリインストールされています。使用しているものには、TF Enterprise 2.3、Pandas、Scikit-learn などが含まれます。この Dockerfile は、該当するイメージをダウンロードした後、トレーニング コードのエントリポイントを設定します。これらのファイルはまだ作成していません。次のステップで、モデルのトレーニングとエクスポート用のコードを追加します。

ステップ 2: Cloud Storage バケットを作成する

トレーニング ジョブでは、トレーニング済みの TensorFlow モデルを Cloud Storage バケットにエクスポートします。Vertex はこれを使用して、エクスポートされたモデルアセットを読み取り、モデルをデプロイします。ターミナルで以下のように実行して、プロジェクトの環境変数を定義します。その際、your-cloud-project は実際のプロジェクト ID で置き換えてください。

PROJECT_ID='your-cloud-project'

次に、ターミナルで次のコマンドを実行して、プロジェクトに新しいバケットを作成します。-l(ロケーション)フラグは、チュートリアルの後半でモデル エンドポイントをデプロイするのと同じリージョンに存在する必要があるため、重要です。

BUCKET_NAME="gs://${PROJECT_ID}-bucket"
gsutil mb -l us-central1 $BUCKET_NAME

ステップ 3: モデルのトレーニング コードを追加する

ターミナルで、次のように実行してトレーニング コード用のディレクトリと、コードを追加する Python ファイルを作成します。

mkdir trainer
touch trainer/train.py

mpg/ ディレクトリに次のものが作成されます。

+ Dockerfile
+ trainer/
    + train.py

次に、作成した train.py ファイルを開き、以下のコードをコピーします(これは TensorFlow ドキュメントのチュートリアルから引用しています)。

ファイルの先頭で、前のステップで作成した Storage バケットの名前で BUCKET 変数を更新します。

import numpy as np
import pandas as pd
import pathlib
import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers

print(tf.__version__)

"""## The Auto MPG dataset

The dataset is available from the [UCI Machine Learning Repository](https://archive.ics.uci.edu/ml/).

### Get the data
First download the dataset.
"""

dataset_path = keras.utils.get_file("auto-mpg.data", "http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")
dataset_path

"""Import it using pandas"""

column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',
                'Acceleration', 'Model Year', 'Origin']
dataset = pd.read_csv(dataset_path, names=column_names,
                      na_values = "?", comment='\t',
                      sep=" ", skipinitialspace=True)

dataset.tail()

# TODO: replace `your-gcs-bucket` with the name of the Storage bucket you created earlier
BUCKET = 'gs://your-gcs-bucket'

"""### Clean the data

The dataset contains a few unknown values.
"""

dataset.isna().sum()

"""To keep this initial tutorial simple drop those rows."""

dataset = dataset.dropna()

"""The `"Origin"` column is really categorical, not numeric. So convert that to a one-hot:"""

dataset['Origin'] = dataset['Origin'].map({1: 'USA', 2: 'Europe', 3: 'Japan'})

dataset = pd.get_dummies(dataset, prefix='', prefix_sep='')
dataset.tail()

"""### Split the data into train and test

Now split the dataset into a training set and a test set.

We will use the test set in the final evaluation of our model.
"""

train_dataset = dataset.sample(frac=0.8,random_state=0)
test_dataset = dataset.drop(train_dataset.index)

"""### Inspect the data

Have a quick look at the joint distribution of a few pairs of columns from the training set.

Also look at the overall statistics:
"""

train_stats = train_dataset.describe()
train_stats.pop("MPG")
train_stats = train_stats.transpose()
train_stats

"""### Split features from labels

Separate the target value, or "label", from the features. This label is the value that you will train the model to predict.
"""

train_labels = train_dataset.pop('MPG')
test_labels = test_dataset.pop('MPG')

"""### Normalize the data

Look again at the `train_stats` block above and note how different the ranges of each feature are.

It is good practice to normalize features that use different scales and ranges. Although the model *might* converge without feature normalization, it makes training more difficult, and it makes the resulting model dependent on the choice of units used in the input.

Note: Although we intentionally generate these statistics from only the training dataset, these statistics will also be used to normalize the test dataset. We need to do that to project the test dataset into the same distribution that the model has been trained on.
"""

def norm(x):
  return (x - train_stats['mean']) / train_stats['std']
normed_train_data = norm(train_dataset)
normed_test_data = norm(test_dataset)

"""This normalized data is what we will use to train the model.

Caution: The statistics used to normalize the inputs here (mean and standard deviation) need to be applied to any other data that is fed to the model, along with the one-hot encoding that we did earlier.  That includes the test set as well as live data when the model is used in production.

## The model

### Build the model

Let's build our model. Here, we'll use a `Sequential` model with two densely connected hidden layers, and an output layer that returns a single, continuous value. The model building steps are wrapped in a function, `build_model`, since we'll create a second model, later on.
"""

def build_model():
  model = keras.Sequential([
    layers.Dense(64, activation='relu', input_shape=[len(train_dataset.keys())]),
    layers.Dense(64, activation='relu'),
    layers.Dense(1)
  ])

  optimizer = tf.keras.optimizers.RMSprop(0.001)

  model.compile(loss='mse',
                optimizer=optimizer,
                metrics=['mae', 'mse'])
  return model

model = build_model()

"""### Inspect the model

Use the `.summary` method to print a simple description of the model
"""

model.summary()

"""Now try out the model. Take a batch of `10` examples from the training data and call `model.predict` on it.

It seems to be working, and it produces a result of the expected shape and type.

### Train the model

Train the model for 1000 epochs, and record the training and validation accuracy in the `history` object.

Visualize the model's training progress using the stats stored in the `history` object.

This graph shows little improvement, or even degradation in the validation error after about 100 epochs. Let's update the `model.fit` call to automatically stop training when the validation score doesn't improve. We'll use an *EarlyStopping callback* that tests a training condition for  every epoch. If a set amount of epochs elapses without showing improvement, then automatically stop the training.

You can learn more about this callback [here](https://www.tensorflow.org/api_docs/python/tf/keras/callbacks/EarlyStopping).
"""

model = build_model()

EPOCHS = 1000

# The patience parameter is the amount of epochs to check for improvement
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)

early_history = model.fit(normed_train_data, train_labels, 
                    epochs=EPOCHS, validation_split = 0.2, 
                    callbacks=[early_stop])


# Export model and save to GCS
model.save(BUCKET + '/mpg/model')

ステップ 4: コンテナをローカルでビルドしてテストする

ターミナルから、Google Container Registry 内のコンテナ イメージの URI を示す変数を定義します。

IMAGE_URI="gcr.io/$PROJECT_ID/mpg:v1"

続いて、mpg ディレクトリのルートで次のように実行してコンテナをビルドします。

docker build ./ -t $IMAGE_URI

ノートブック インスタンス内でコンテナを実行して、正しく動作していることを確認します。

docker run $IMAGE_URI

モデルのトレーニングは 1~2 分で終了し、検証精度は 72% 程度になります(正確な精度は異なる場合があります)。コンテナのローカルでの実行が終了したら、Google Container Registry に push します。

docker push $IMAGE_URI

コンテナを Container Registry に push したら、カスタムモデルのトレーニング ジョブを開始する準備ができました。

5. Vertex AI でトレーニング ジョブを実行する

Vertex AI では、次の 2 つの方法でモデルをトレーニングできます。

  • AutoML: 最小限の労力と ML の専門知識で高品質なモデルをトレーニングする
  • カスタム トレーニング: Google Cloud のビルド済みコンテナまたは独自のコンテナのいずれかを使用して、クラウド内でカスタム トレーニング アプリケーションを実行する

このラボでは、Google Container Registry 上の独自のカスタム コンテナを使用してカスタム トレーニングを行います。まず、Cloud コンソールの Vertex で [モデル] セクションに移動します。

Vertex メニュー

ステップ 1: トレーニング ジョブを開始する

[作成] をクリックして、トレーニング ジョブとデプロイされたモデルのパラメータを入力します。

  • [Dataset] で [マネージド データセットなし] を選択します。
  • トレーニング方法として [カスタム トレーニング(上級者向け)] を選択し、[続行] をクリックします。
  • [続行] をクリックする

次のステップで、[モデル名] に「mpg」(または任意のモデル名)と入力します。[カスタム コンテナ] を選択します。

カスタム コンテナ オプション

[コンテナ イメージ] テキスト ボックスで [参照] をクリックし、Container Registry にアップロードした Docker イメージを見つけます。残りのフィールドは空白のままにして、[続行] をクリックします。

このチュートリアルではハイパーパラメータ チューニングを使用しないため、[Enable hyperparameter tuning] ボックスはオフのままにして [続行] をクリックします。

[コンピューティングと料金] で、選択されているリージョンはそのままにしておき、マシンタイプとして n1-standard-4 を選択します。

マシンタイプ

アクセラレータ フィールドは空白のままにして、[続行] を選択します。このデモのモデルは短時間でトレーニングされるため、より小さなマシンタイプを使用しています。

[予測コンテナ] ステップで、[ビルド済みコンテナ] を選択し、TensorFlow 2.6 を選択します。

ビルド済みコンテナのデフォルト設定はそのままにしておきます。[モデル ディレクトリ] には、GCS バケットと mpg サブディレクトリを入力します。これは、トレーニング済みモデルをエクスポートするモデル トレーニング スクリプト内のパスです。

予測設定

Vertex はモデルをデプロイするときにこの場所を検索します。これでトレーニングの準備が整いました。[トレーニングを開始] をクリックして、トレーニング ジョブを開始します。コンソールの [トレーニング] セクションには、次のような内容が表示されます。

トレーニング ジョブ

6. モデルのエンドポイントをデプロイする

トレーニング ジョブを設定するときに、Vertex AI がエクスポートしたモデルアセットを検索する場所を指定しました。トレーニング パイプラインの一部として、Vertex はこのアセットパスに基づいてモデルリソースを作成します。モデルリソース自体はデプロイされたモデルではありませんが、モデルが作成できたら、それをエンドポイントにデプロイする準備が整います。Vertex AI のモデルとエンドポイントについて詳しくは、ドキュメントをご覧ください。

このステップでは、トレーニング済みモデルのエンドポイントを作成します。これを使用して、Vertex AI API 経由でモデルの予測を取得できます。

ステップ 1: エンドポイントをデプロイする

トレーニング ジョブが完了すると、コンソールの [モデル] セクションに mpg という名前(または自身で付けた名前)のモデルが表示されます。

完了したジョブ

トレーニング ジョブが実行されると、Vertex がモデルリソースを作成します。このモデルを使用するには、エンドポイントをデプロイする必要があります。モデルごとに多数のエンドポイントを作成できます。モデルをクリックし、[エンドポイントにデプロイ] をクリックします。

[新しいエンドポイントを作成する] を選択し、v1 などの名前を付けて、[アクセス] で [標準] を選択したままにして、[続行] をクリックします。

[トラフィック分割] は 100 のままにし、[コンピューティング ノードの最小数] に 1 を入力します。[マシンタイプ] で、n1-standard-2(または任意のマシンタイプ)を選択します。残りのデフォルトは選択したままにして、[続行] をクリックします。このモデルのモニタリングは有効にしないので、[デプロイ] をクリックしてエンドポイントのデプロイを開始します。

エンドポイントのデプロイには 10 ~ 15 分かかります。デプロイが完了するとメールが届きます。エンドポイントのデプロイが完了すると、モデルリソースの下にデプロイされた 1 つのエンドポイントが次のように表示されます。

エンドポイントにデプロイする

ステップ 2: デプロイしたモデルの予測を取得する

Vertex Python API を使用して、Python ノートブックからトレーニング済みモデルの予測を取得します。ノートブック インスタンスに戻り、Launcher から Python 3 ノートブックを作成します。

ノートブックを開く

ノートブックで、下のコマンドをセル内で実行し、Vertex AI SDK をインストールします。

!pip3 install google-cloud-aiplatform --upgrade --user

次に、SDK をインポートするためのセルをノートブックに追加し、デプロイしたエンドポイントへの参照を作成します。

from google.cloud import aiplatform

endpoint = aiplatform.Endpoint(
    endpoint_name="projects/YOUR-PROJECT-NUMBER/locations/us-central1/endpoints/YOUR-ENDPOINT-ID"
)

上記の endpoint_name 文字列の 2 つの値を、プロジェクト番号とエンドポイントに置き換える必要があります。プロジェクト番号を確認するには、プロジェクト ダッシュボードに移動し、プロジェクト番号の値を取得します。

エンドポイント ID は、コンソールのエンドポイント セクションで確認できます。

エンドポイント ID を確認する

最後に、以下のコードを新しいセルにコピーして実行し、エンドポイントに対する予測を作成します。

test_mpg = [1.4838871833555929,
 1.8659883497083019,
 2.234620276849616,
 1.0187816540094903,
 -2.530890710602246,
 -1.6046416850441676,
 -0.4651483719733302,
 -0.4952254087173721,
 0.7746763768735953]

response = endpoint.predict([test_mpg])

print('API response: ', response)

print('Predicted MPG: ', response.predictions[0][0])

この例には、モデルが期待する形式である正規化された値がすでに含まれています。

このセルを実行すると、1 ガロンあたり約 16 マイルの予測出力が表示されるはずです。

お疲れさまでした

Vertex AI を使って次のことを行う方法を学びました。

  • カスタム コンテナにトレーニング コードを提供してモデルをトレーニングする。ここでは例として TensorFlow モデルを使用しましたが、カスタム コンテナを使って任意のフレームワークで構築されたモデルをトレーニングすることができます。
  • トレーニングに使用したのと同じワークフローの一環として、ビルド済みコンテナを使用して TensorFlow モデルをデプロイする。
  • モデルのエンドポイントを作成し、予測を生成する。

Vertex のさまざまな部分の説明については、ドキュメントをご覧ください。

7. クリーンアップ

このラボで作成したノートブックを引き続き使用する場合は、未使用時にオフにすることをおすすめします。Cloud コンソールの Workbench UI で、ノートブックを選択して [停止] を選択します。

ノートブックを完全に削除する場合は、右上の削除ボタンをクリックします。

デプロイしたエンドポイントを削除するには、Vertex AI コンソールの [エンドポイント] セクションに移動し、作成したエンドポイントをクリックして、[エンドポイントからモデルのデプロイを解除] を選択します。

エンドポイントの削除

ストレージ バケットを削除するには、Cloud コンソールのナビゲーション メニューで [ストレージ] に移動してバケットを選択し、[削除] をクリックします。

ストレージを削除