1. 總覽
本研究室將說明如何使用 Vertex AI 調整超參數及進行分散式訓練。雖然本研究室使用 TensorFlow 編寫模型程式碼,但這些概念也適用於其他機器學習架構。
課程內容
您將學習下列內容:
- 在自訂容器上使用分散式訓練來訓練模型
- 啟動多次測試訓練程式碼,自動執行超參數調整作業
在 Google Cloud 中執行這個研究室的總費用約為 $6 美元。
2. Vertex AI 簡介
這個研究室使用 Google Cloud 最新的 AI 產品服務。Vertex AI 將 Google Cloud 中的機器學習產品整合到流暢的開發體驗中。先前使用 AutoML 訓練的模型和自訂模型,都能透過不同的服務存取。這項新產品會與其他新產品一起合併為一個 API。您也可以將現有專案遷移至 Vertex AI。如有任何意見,請參閱支援頁面。
Vertex AI 提供許多不同的產品,可支援端對端機器學習工作流程。本研究室將著重在 Training 和 Workbench
3. 用途總覽
在本研究室中,您將使用超參數調整功能,針對以 TensorFlow 資料集訓練的馬或人類資料集訓練圖像分類模型,找出最佳參數。
超參數調整
使用 Vertex AI 訓練進行超參數調整的運作方式,是依照您所選超參數值 (不超出指定限制),執行訓練應用程式多次的試驗。Vertex AI 會追蹤每次測試的結果,並調整後續的測試。
如要搭配使用超參數調整與 Vertex AI 訓練,您必須對訓練程式碼做出下列兩項變更:
- 在主要訓練模組中,為您要調整的每個超參數定義指令列引數。
- 使用這些引數傳送的值,在應用程式程式碼中設定對應的超參數。
分散式訓練
如果您使用單一 GPU,TensorFlow 會使用這個加速器來加快模型訓練速度,您不須執行額外操作。不過,如果希望使用多個 GPU 提升額外效能,就需要使用 tf.distribute
,這是在多部裝置上執行運算的 TensorFlow 模組。
本研究室使用 tf.distribute.MirroredStrategy
。您只要稍微修改程式碼,即可將這項功能新增至訓練應用程式。這項策略會為電腦上的每個 GPU 建立模型副本。後續的漸層更新則會以同步方式進行。也就是說,每個 GPU 都會根據輸入資料的不同部分,計算向前和向後傳遞模型。接著,根據每個配量計算出的梯度,則會匯總所有 GPU 的梯度,並在名為 all-reduce 的處理程序中算出平均值。模型參數使用這些平均值的梯度更新。
您不需要具備詳細資料即可完成本研究室活動,但如果你想進一步瞭解分散式訓練在 TensorFlow 中的運作方式,請觀看以下影片:
4. 設定環境
您需要已啟用計費功能的 Google Cloud Platform 專案,才能執行這個程式碼研究室。如要建立專案,請按照這裡的操作說明進行。
步驟 1:啟用 Compute Engine API
前往「Compute Engine」,並選取「啟用」 (如果尚未啟用)。
步驟 2:啟用 Container Registry API
前往「Container Registry」,然後選取「Enable」(啟用) (如果尚未啟用)。您將使用這個選項為自訂訓練工作建立容器。
步驟 3:啟用 Vertex AI API
前往 Cloud 控制台的 Vertex AI 專區,然後按一下「啟用 Vertex AI API」。
步驟 4:建立 Vertex AI Workbench 執行個體
在 Cloud 控制台的 Vertex AI 專區中,按一下 Workbench:
啟用 Notebooks API (如果尚未啟用)。
啟用後,按一下「代管的筆記本」:
然後選取「新增筆記本」。
為筆記本命名,然後按一下「進階設定」。
在進階設定下方啟用閒置關閉,並將時間長度設為 60。也就是說,您的筆記本在未使用時自動關閉,因此不會產生不必要的費用。
選取「安全性」下方的「啟用終端機」(如果尚未啟用)。
其他進階設定皆可保持原樣。
接著點選「建立」。執行個體的佈建作業會在幾分鐘內完成。
執行個體建立完成後,請選取「Open JupyterLab」。
首次使用新的執行個體時,系統會要求您進行驗證。請按照 UI 中的步驟操作。
5. 編寫訓練程式碼
如要開始使用,請透過「啟動器」選單開啟筆記本執行個體中的「終端機」視窗:
建立名為 vertex-codelab
的新目錄,然後使用 cd 功能。
mkdir vertex-codelab
cd vertex-codelab
執行下列指令,為訓練程式碼建立目錄,以及您要新增程式碼的 Python 檔案:
mkdir trainer
touch trainer/task.py
現在 vertex-codelab
目錄中應會顯示以下內容:
+ trainer/
+ task.py
接著,開啟剛剛建立的 task.py
檔案,並貼入下方的所有程式碼。
import tensorflow as tf
import tensorflow_datasets as tfds
import argparse
import hypertune
import os
NUM_EPOCHS = 10
BATCH_SIZE = 64
def get_args():
'''Parses args. Must include all hyperparameters you want to tune.'''
parser = argparse.ArgumentParser()
parser.add_argument(
'--learning_rate',
required=True,
type=float,
help='learning rate')
parser.add_argument(
'--momentum',
required=True,
type=float,
help='SGD momentum value')
parser.add_argument(
'--num_units',
required=True,
type=int,
help='number of units in last hidden layer')
args = parser.parse_args()
return args
def preprocess_data(image, label):
'''Resizes and scales images.'''
image = tf.image.resize(image, (150,150))
return tf.cast(image, tf.float32) / 255., label
def create_dataset(batch_size):
'''Loads Horses Or Humans dataset and preprocesses data.'''
data, info = tfds.load(name='horses_or_humans', as_supervised=True, with_info=True)
# Create train dataset
train_data = data['train'].map(preprocess_data)
train_data = train_data.shuffle(1000)
train_data = train_data.batch(batch_size)
# Create validation dataset
validation_data = data['test'].map(preprocess_data)
validation_data = validation_data.batch(batch_size)
return train_data, validation_data
def create_model(num_units, learning_rate, momentum):
'''Defines and compiles model.'''
inputs = tf.keras.Input(shape=(150, 150, 3))
x = tf.keras.layers.Conv2D(16, (3, 3), activation='relu')(inputs)
x = tf.keras.layers.MaxPooling2D((2, 2))(x)
x = tf.keras.layers.Conv2D(32, (3, 3), activation='relu')(x)
x = tf.keras.layers.MaxPooling2D((2, 2))(x)
x = tf.keras.layers.Conv2D(64, (3, 3), activation='relu')(x)
x = tf.keras.layers.MaxPooling2D((2, 2))(x)
x = tf.keras.layers.Flatten()(x)
x = tf.keras.layers.Dense(num_units, activation='relu')(x)
outputs = tf.keras.layers.Dense(1, activation='sigmoid')(x)
model = tf.keras.Model(inputs, outputs)
model.compile(
loss='binary_crossentropy',
optimizer=tf.keras.optimizers.SGD(learning_rate=learning_rate, momentum=momentum),
metrics=['accuracy'])
return model
def main():
args = get_args()
# Create distribution strategy
strategy = tf.distribute.MirroredStrategy()
# Get data
GLOBAL_BATCH_SIZE = BATCH_SIZE * strategy.num_replicas_in_sync
train_data, validation_data = create_dataset(GLOBAL_BATCH_SIZE)
# Wrap variable creation within strategy scope
with strategy.scope():
model = create_model(args.num_units, args.learning_rate, args.momentum)
# Train model
history = model.fit(train_data, epochs=NUM_EPOCHS, validation_data=validation_data)
# Define metric
hp_metric = history.history['val_accuracy'][-1]
hpt = hypertune.HyperTune()
hpt.report_hyperparameter_tuning_metric(
hyperparameter_metric_tag='accuracy',
metric_value=hp_metric,
global_step=NUM_EPOCHS)
if __name__ == "__main__":
main()
讓我們深入瞭解程式碼,並檢查分散式訓練和超參數調整的專屬元件。
分散式訓練
- 在
main()
函式中建立MirroredStrategy
物件。接下來,您要將模型變數建立作業納入策略範圍內。這個步驟會告知 TensorFlow 應在 GPU 上鏡像哪些變數。 - 批量會由
num_replicas_in_sync
向上擴充。在 TensorFlow 中使用同步資料平行處理策略時,調整批次大小是最佳做法。如要瞭解詳情,請按這裡。
超參數調整
- 指令碼會匯入
hypertune
程式庫。稍後建構容器映像檔時,我們會需要確定會安裝這個程式庫。 - 函式
get_args()
會為您要調整的每個超參數定義指令列引數。在這個範例中,要調整的超參數包括學習率、最佳化工具中的動量值,以及模型最後一個隱藏層中的單位數量,不過您可以自由測試其他參數。接著,透過這些引數傳遞的值,可在程式碼中設定對應的超參數 (例如設定learning_rate = args.learning_rate
) - 在
main()
函式的結尾,hypertune
程式庫是用來定義您要最佳化的指標。在 TensorFlow 中,Kerasmodel.fit
方法會傳回History
物件。History.history
屬性是連續訓練週期的訓練損失值和指標值記錄。如果將驗證資料傳遞至model.fit
,History.history
屬性也會包含驗證損失和指標值。舉例來說,假設您使用驗證資料來訓練三個訓練週期的模型,並將accuracy
做為指標,History.history
屬性看起來應該會與以下字典類似。
{
"accuracy": [
0.7795261740684509,
0.9471358060836792,
0.9870933294296265
],
"loss": [
0.6340447664260864,
0.16712145507335663,
0.04546636343002319
],
"val_accuracy": [
0.3795261740684509,
0.4471358060836792,
0.4870933294296265
],
"val_loss": [
2.044623374938965,
4.100203514099121,
3.0728273391723633
]
如要讓超參數調整服務找出可將模型驗證準確率最大化的值,請將指標定義為 val_accuracy
清單的最後一個項目 (或 NUM_EPOCS - 1
)。然後將這個指標傳遞至 HyperTune
的執行個體。您可以為 hyperparameter_metric_tag
挑選任何喜歡的字串,但稍後當您啟動超參數調整工作時,就需要再次使用該字串。
6. 容器化程式碼
將程式碼容器化的第一步,就是建立 Dockerfile。而 Dockerfile 包含執行映像檔所需的所有指令。其會安裝所有必要的程式庫,並設定訓練程式碼的進入點。
步驟 1:編寫 Dockerfile
在終端機中,確認您位於 vertex-codelab
目錄,並建立空白的 Dockerfile:
touch Dockerfile
現在 vertex-codelab
目錄中應會顯示以下內容:
+ Dockerfile
+ trainer/
+ task.py
開啟 Dockerfile,並將下列內容複製到該檔案中:
FROM gcr.io/deeplearning-platform-release/tf2-gpu.2-7
WORKDIR /
# Installs hypertune library
RUN pip install cloudml-hypertune
# Copies the trainer code to the docker image.
COPY trainer /trainer
# Sets up the entry point to invoke the trainer.
ENTRYPOINT ["python", "-m", "trainer.task"]
這個 Dockerfile 使用深度學習容器 TensorFlow 企業版 2.7 GPU Docker 映像檔。Google Cloud 中的深度學習容器已預先安裝許多常見的機器學習和數據資料學架構。下載該映像檔後,這個 Dockerfile 會設定訓練程式碼的進入點。
步驟 2:建構容器
在終端機執行下列指令,定義專案的環境變數;請務必將 your-cloud-project
替換為您的專案 ID:
PROJECT_ID='your-cloud-project'
使用 Google Container Registry 中容器映像檔的 URI 定義變數:
IMAGE_URI="gcr.io/$PROJECT_ID/horse-human-codelab:latest"
設定 Docker
gcloud auth configure-docker
接著,從 vertex-codelab
目錄的根目錄執行下列指令,以建構容器:
docker build ./ -t $IMAGE_URI
最後,將其推送至 Google Container Registry:
docker push $IMAGE_URI
步驟 3:建立 Cloud Storage 值區
在訓練工作中,我們會將路徑傳入暫存值區。
在終端機中執行下列指令,以便在專案中建立新值區。
BUCKET_NAME="gs://${PROJECT_ID}-hptune-bucket"
gsutil mb -l us-central1 $BUCKET_NAME
7. 啟動超參數調整工作
步驟 1:使用超參數調整功能建立自訂訓練工作
從啟動器中開啟新的 TensorFlow 2 筆記本。
匯入 Vertex AI Python SDK。
from google.cloud import aiplatform
from google.cloud.aiplatform import hyperparameter_tuning as hpt
如要啟動超參數調整工作,您必須先定義 worker_pool_specs
,這會指定機器類型和 Docker 映像檔。下列規格定義一部搭載兩個 NVIDIA Tesla V100 GPU 的機器。
您需要將 image_uri
中的 {PROJECT_ID}
替換為您的專案。
# The spec of the worker pools including machine type and Docker image
# Be sure to replace PROJECT_ID in the "image_uri" with your project.
worker_pool_specs = [{
"machine_spec": {
"machine_type": "n1-standard-4",
"accelerator_type": "NVIDIA_TESLA_V100",
"accelerator_count": 2
},
"replica_count": 1,
"container_spec": {
"image_uri": "gcr.io/{PROJECT_ID}/horse-human-codelab:latest"
}
}]
接著,定義 parameter_spec
,這是一個字典,用來指定您要最佳化的參數。字典鍵是您指派給每個超參數指令列引數的字串,而字典值則是參數規格。
對於每個超參數,您必須定義類型以及調整服務將嘗試的值邊界。超參數可以是雙數、整數、類別或離散類型。如果選取雙精度浮點數或整數,就必須提供最小值和最大值。如果您選取「類別」或「離散」,就需要提供這些值。對於 Double 和 Integer 類型,您還需要提供資源調度值。如要進一步瞭解如何挑選最佳比例,請觀看這部影片。
# Dictionary representing parameters to optimize.
# The dictionary key is the parameter_id, which is passed into your training
# job as a command line argument,
# And the dictionary value is the parameter specification of the metric.
parameter_spec = {
"learning_rate": hpt.DoubleParameterSpec(min=0.001, max=1, scale="log"),
"momentum": hpt.DoubleParameterSpec(min=0, max=1, scale="linear"),
"num_units": hpt.DiscreteParameterSpec(values=[64, 128, 512], scale=None)
}
最後一個要定義的規格是 metric_spec
,此字典代表要最佳化的指標。字典鍵是您在訓練應用程式程式碼中設定的 hyperparameter_metric_tag
,而值則是最佳化目標。
# Dicionary representing metrics to optimize.
# The dictionary key is the metric_id, which is reported by your training job,
# And the dictionary value is the optimization goal of the metric.
metric_spec={'accuracy':'maximize'}
定義規格後,您就可以建立 CustomJob
,這個通用規格會在每個超參數調整試驗上執行工作。
您需要將 {YOUR_BUCKET}
替換為先前建立的值區。
# Replace YOUR_BUCKET
my_custom_job = aiplatform.CustomJob(display_name='horses-humans',
worker_pool_specs=worker_pool_specs,
staging_bucket='gs://{YOUR_BUCKET}')
接著,請建立並執行 HyperparameterTuningJob
。
hp_job = aiplatform.HyperparameterTuningJob(
display_name='horses-humans',
custom_job=my_custom_job,
metric_spec=metric_spec,
parameter_spec=parameter_spec,
max_trial_count=6,
parallel_trial_count=2,
search_algorithm=None)
hp_job.run()
請注意以下引數:
- max_trial_count:您必須設定服務執行的試驗數量上限。測試量越多,成效通常就越好,但效益會因此下滑,若經過額外試驗,對要最佳化的指標幾乎或完全沒有影響。最佳做法是先從少量試驗著手,然後瞭解所選超參數的影響程度,再向上擴充。
- parallel_trial_count:如果使用平行試驗,該服務會佈建多個訓練處理叢集。如增加平行試驗數量,就能縮短超參數調整工作的執行時間。但會降低工作整體效率。這是因為預設調整策略會根據先前試驗的結果,在後續試驗中指派值。
- search_algorithm:您可以將搜尋演算法設為格狀、隨機或預設 (無)。預設選項會套用貝葉斯最佳化功能,以搜尋可能的超參數值空間 (這是建議使用的演算法)。如要進一步瞭解這個演算法,請參閱這篇文章。
工作開始進行後,您就能在使用者介面的「HYPERPARAMETER TUNING JOBS」分頁下追蹤狀態。
工作完成後,您可以查看試驗結果並加以排序,以找出超參數值的最佳組合。
🎉 恭喜!🎉
您已瞭解如何使用 Vertex AI 執行下列作業:
- 使用分散式訓練執行超參數調整工作
如要進一步瞭解 Vertex AI 的其他部分,請參閱說明文件。
8. 清除
因為我們設定了筆記本在 60 分鐘閒置後逾時,所以我們不必擔心如何關閉執行個體。如要手動關閉執行個體,請前往控制台的「Vertex AI Workbench」專區,然後按一下「Stop」按鈕。如想完全刪除筆記本,請按一下「刪除」按鈕。
如要刪除 Storage 值區,請使用 Cloud 控制台中的導覽選單前往「Storage」(儲存空間)、選取值區,然後點選「Delete」(刪除):