This guide uses the open-source Fashion-MNIST dataset to show how developers use the Cloud Native AI Suite to run deep learning workloads on ACK, optimize distributed training, debug models, and deploy the trained model to the cluster.
Background
The cloud-native AI suite provides independently deployable components (K8s Helm charts) to accelerate AI engineering.
The cloud-native AI suite has two user roles: administrator and developer.
-
Administrator: Manages users, permissions, cluster resources, external storage, datasets, and monitors cluster usage via the dashboard.
-
Developer: Submits jobs using cluster resources. Requires an administrator-created account with granted permissions. Uses tools such as the Arena CLI and Jupyter Notebook for development.
Prerequisites
Ensure an administrator has completed the following tasks:
-
A Kubernetes cluster is created.
-
Each node in the cluster has at least 300 GB of disk space.
-
For the best data acceleration results, use four instances, each equipped with eight V100 GPUs.
-
For the best topology awareness results, use two V100 instances.
-
-
You have access to the AI Dashboard.
-
You have access to the AI Developer Console.
NoteStarting January 22, 2025, the Alibaba Cloud AI Console, which includes the AI Developer Console and the AI Dashboard, will become a whitelist-only feature. This change does not affect existing deployments. Users not on the whitelist can install and configure the AI Console from the open-source community. For details, see Open-source AI Console.
-
The Fashion MNIST dataset is downloaded and uploaded to OSS.
-
You have the Git repository URL, username, and password for the test code.
-
A kubectl client is connected to the cluster.
Tutorial environment
Use the cloud-native AI suite to develop, train, accelerate, evaluate, and deploy a Fashion-MNIST workload on your ACK cluster.
-
An administrator must first complete Step 1: Create an account and allocate resources for the developer and Step 2: Create a dataset. You can then complete the remaining steps.
-
Submit Arena commands from a Jupyter Notebook terminal (recommended) or a cluster jump server.
The cluster used in this tutorial consists of the following nodes:
|
Host name |
IP |
Role |
GPUs |
vCPUs |
Memory |
|
cn-beijing.192.168.0.13 |
192.168.0.13 |
jump server |
1 |
8 |
30580004 KiB |
|
cn-beijing.192.168.0.16 |
192.168.0.16 |
worker |
1 |
8 |
30580004 KiB |
|
cn-beijing.192.168.0.17 |
192.168.0.17 |
worker |
1 |
8 |
30580004 KiB |
|
cn-beijing.192.168.0.240 |
192.168.0.240 |
worker |
1 |
8 |
30580004 KiB |
|
cn-beijing.192.168.0.239 |
192.168.0.239 |
worker |
1 |
8 |
30580004 KiB |
Objectives
After completing this guide, you can:
-
Manage a dataset
-
Set up a development environment with a Jupyter Notebook
-
Submit a standalone training job
-
Submit a distributed training job
-
Accelerate a training job with Fluid
-
Accelerate a training job with the ACK AI Task Scheduler
-
Manage a model
-
Perform model evaluation
-
Deploy an inference service
Step 1: Create an account and allocate resources
Contact your administrator for the following resources:
-
A username and password.
-
A resource quota.
-
The AI Developer Console endpoint (if you submit jobs using the console).
NoteThe Alibaba Cloud AI Console, which includes the AI Developer Console and the O&M Console, will become an allowlisted feature starting January 22, 2025. If you deployed the AI Developer Console or O&M Console before this date, your use will not be affected. If you are not on the allowlist, you can install and configure the AI suite console from the open-source community. For detailed configuration instructions, see Open-source AI Console.
-
If you submit a job by using the Arena CLI, obtain the kubeconfig for the cluster. For instructions, see Obtain the kubeconfig and connect to a cluster.
Step 2: Create a dataset
Datasets are managed by an administrator. This example uses the fashion-mnist dataset.
Step 1: create the fashion-mnist dataset
-
Create the fashion-mnist.yaml file based on the following YAML example.
This example creates a persistent volume (PV) and persistent volume claim (PVC) of the OSS type.
apiVersion: v1 kind: PersistentVolume metadata: name: fashion-demo-pv spec: accessModes: - ReadWriteMany capacity: storage: 10Gi csi: driver: ossplugin.csi.alibabacloud.com volumeAttributes: bucket: fashion-mnist otherOpts: "" url: oss-cn-beijing.aliyuncs.com akId: "AKID" akSecret: "AKSECRET" volumeHandle: fashion-demo-pv persistentVolumeReclaimPolicy: Retain storageClassName: oss volumeMode: Filesystem --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: fashion-demo-pvc namespace: demo-ns spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi selector: matchLabels: alicloud-pvname: fashion-demo-pv storageClassName: oss volumeMode: Filesystem volumeName: fashion-demo-pv -
Create the fashion-mnist dataset.
kubectl create -f fashion-mnist.yaml -
View the PV and PVC status.
-
Check the status of the PV.
kubectl get pv fashion-mnist-jackwgExpected output:
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE fashion-mnist-jackwg 10Gi RWX Retain Bound ns1/fashion-mnist-jackwg-pvc oss 8h -
Check the status of the PVC.
kubectl get pvc fashion-mnist-jackwg-pvc -n ns1Expected output:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE fashion-mnist-jackwg-pvc Bound fashion-mnist-jackwg 10Gi RWX oss 8h
Both PV and PVC show
Boundstatus. -
Step 2: create an accelerated dataset
Administrators use the AI Dashboard to accelerate datasets.
- Access the AI Dashboard as an administrator.
- In the left-side navigation pane of AI Dashboard, choose .
-
On the Dataset List page, click Accelerate in the Actions column of the target dataset.
After you accelerate the dataset, its information is updated on the Dataset List page. The list includes columns such as Task name, Namespace, Data source, Accelerated, and Status. For the accelerated dataset, the Accelerated column shows Yes, and its Status changes from NotReady to Ready once the acceleration is complete.
Step 3: Develop the model
Set up a development environment with Jupyter Notebook:
-
(Optional) Create a Jupyter Notebook from a custom image.
-
Develop and test your model in a Jupyter Notebook.
-
Push code to a Git repository from the Jupyter Notebook.
-
Submit a training job using the Arena SDK.
(Optional) Step 1: Create a notebook from a custom image
The AI Developer Console provides Jupyter Notebooks with pre-built images for different versions of TensorFlow and PyTorch. If these images do not meet your requirements, you can create a custom image.
-
Create a file named
Dockerfilebased on the following template.See Create and use notebooks for custom image requirements.
cat<<EOF >dockerfile FROM tensorflow/tensorflow:1.15.5-gpu USER root RUN pip install jupyter && \ pip install ipywidgets && \ jupyter nbextension enable --py widgetsnbextension && \ pip install jupyterlab && jupyter serverextension enable --py jupyterlab EXPOSE 8888 #USER jovyan CMD ["sh", "-c", "jupyter-lab --notebook-dir=/home/jovyan --ip=0.0.0.0 --no-browser --allow-root --port=8888 --NotebookApp.token='' --NotebookApp.password='' --NotebookApp.allow_origin='*' --NotebookApp.base_url=${NB_PREFIX} --ServerApp.authenticate_prometheus=False"] EOF -
Build the image from the Dockerfile.
docker build -f dockerfile .Expected output:
Sending build context to Docker daemon 9.216kB Step 1/5 : FROM tensorflow/tensorflow:1.15.5-gpu ---> 73be11373498 Step 2/5 : USER root ---> Using cache ---> 7ee21dc7e42e Step 3/5 : RUN pip install jupyter && pip install ipywidgets && jupyter nbextension enable --py widgetsnbextension && pip install jupyterlab && jupyter serverextension enable --py jupyterlab ---> Using cache ---> 23bc51c5e16d Step 4/5 : EXPOSE 8888 ---> Using cache ---> 76a55822ddae Step 5/5 : CMD ["sh", "-c", "jupyter-lab --notebook-dir=/home/jovyan --ip=0.0.0.0 --no-browser --allow-root --port=8888 --NotebookApp.token='' --NotebookApp.password='' --NotebookApp.allow_origin='*' --NotebookApp.base_url=${NB_PREFIX} --ServerApp.authenticate_prometheus=False"] ---> Using cache ---> 3692f04626d5 Successfully built 3692f04626d5 -
Push the image to your container registry.
docker tag ${IMAGE_ID} registry-vpc.cn-beijing.aliyuncs.com/${DOCKER_REPO}/jupyter:fashion-mnist-20210802a docker push registry-vpc.cn-beijing.aliyuncs.com/${DOCKER_REPO}/jupyter:fashion-mnist-20210802a -
Create an image pull secret to pull the image from your private container registry.
See Create a Secret to pull an image from a private registry.
kubectl create secret docker-registry regcred \ --docker-server=<your-registry-server> \ --docker-username=<your-username> \ --docker-password=<your-password> \ --docker-email=<your-email-address> -
Create a Jupyter Notebook in the AI Developer Console.
Configure the parameters for the Jupyter Notebook as follows: In the Notebook Basic Information section on the left, configure the Notebook Name and Notebook Image (you can select a custom image), and specify the Namespace and Image Pull Secret. For Data Configuration, select the dataset to display the corresponding persistent volume claim and local storage directory. If you enable Workspace PVC, you must specify the Target PVC and Mount Path. In the Notebook Resource Configuration section on the right, set the CPU (Cores), Memory (GB), and GPU (Units). After you finish the configuration, click Create Notebook.
Step 2: Develop and test in a notebook
- Access the AI Developer Console
- In the left-side navigation pane of AI Developer Console, click Notebook.
-
On the Notebook page, click the name of the target Jupyter Notebook with a Status of Running.
-
Open a new terminal and run the following commands to verify that the data is mounted successfully:
pwd /root/data ls -alhExpected output:
total 30M drwx------ 1 root root 0 Jan 1 1970 . drwx------ 1 root root 4.0K Aug 2 04:15 .. drwxr-xr-x 1 root root 0 Aug 1 14:16 saved_model -rw-r----- 1 root root 4.3M Aug 1 01:53 t10k-images-idx3-ubyte.gz -rw-r----- 1 root root 5.1K Aug 1 01:53 t10k-labels-idx1-ubyte.gz -rw-r----- 1 root root 26M Aug 1 01:54 train-images-idx3-ubyte.gz -rw-r----- 1 root root 29K Aug 1 01:53 train-labels-idx1-ubyte.gz -
In your Jupyter Notebook environment, create a new notebook file to develop the fashion-mnist model and initialize it with the following code:
#!/usr/bin/python # -*- coding: UTF-8 -*- import os import gzip import numpy as np import tensorflow as tf from tensorflow import keras print('TensorFlow version: {}'.format(tf.__version__)) dataset_path = "/root/data/" model_path = "./model/" model_version = "v1" def load_data(): files = [ 'train-labels-idx1-ubyte.gz', 'train-images-idx3-ubyte.gz', 't10k-labels-idx1-ubyte.gz', 't10k-images-idx3-ubyte.gz' ] paths = [] for fname in files: paths.append(os.path.join(dataset_path, fname)) with gzip.open(paths[0], 'rb') as labelpath: y_train = np.frombuffer(labelpath.read(), np.uint8, offset=8) with gzip.open(paths[1], 'rb') as imgpath: x_train = np.frombuffer(imgpath.read(), np.uint8, offset=16).reshape(len(y_train), 28, 28) with gzip.open(paths[2], 'rb') as labelpath: y_test = np.frombuffer(labelpath.read(), np.uint8, offset=8) with gzip.open(paths[3], 'rb') as imgpath: x_test = np.frombuffer(imgpath.read(), np.uint8, offset=16).reshape(len(y_test), 28, 28) return (x_train, y_train),(x_test, y_test) def train(): (train_images, train_labels), (test_images, test_labels) = load_data() # scale the values to 0.0 to 1.0 train_images = train_images / 255.0 test_images = test_images / 255.0 # reshape for feeding into the model train_images = train_images.reshape(train_images.shape[0], 28, 28, 1) test_images = test_images.reshape(test_images.shape[0], 28, 28, 1) class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'] print('\ntrain_images.shape: {}, of {}'.format(train_images.shape, train_images.dtype)) print('test_images.shape: {}, of {}'.format(test_images.shape, test_images.dtype)) model = keras.Sequential([ keras.layers.Conv2D(input_shape=(28,28,1), filters=8, kernel_size=3, strides=2, activation='relu', name='Conv1'), keras.layers.Flatten(), keras.layers.Dense(10, activation=tf.nn.softmax, name='Softmax') ]) model.summary() testing = False epochs = 5 model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) logdir = "/training_logs" tensorboard_callback = keras.callbacks.TensorBoard(log_dir=logdir) model.fit(train_images, train_labels, epochs=epochs, callbacks=[tensorboard_callback], ) test_loss, test_acc = model.evaluate(test_images, test_labels) print('\nTest accuracy: {}'.format(test_acc)) export_path = os.path.join(model_path, model_version) print('export_path = {}\n'.format(export_path)) tf.keras.models.save_model( model, export_path, overwrite=True, include_optimizer=True, save_format=None, signatures=None, options=None ) print('\nSaved model success') if __name__ == '__main__': train()ImportantIn the code,
dataset_pathandmodel_pathmust be set to the mount paths of the data source in the Notebook. This allows the Notebook to access the dataset mounted to the local file system. -
In the target Notebook, click the
icon to run the code.Expected output:
TensorFlow version: 1.15.5 train_images.shape: (60000, 28, 28, 1), of float64 test_images.shape: (10000, 28, 28, 1), of float64 Model: "sequential_2" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= Conv1 (Conv2D) (None, 13, 13, 8) 80 _________________________________________________________________ flatten_2 (Flatten) (None, 1352) 0 _________________________________________________________________ Softmax (Dense) (None, 10) 13530 ================================================================= Total params: 13,610 Trainable params: 13,610 Non-trainable params: 0 _________________________________________________________________ Train on 60000 samples Epoch 1/5 60000/60000 [==============================] - 3s 57us/sample - loss: 0.5452 - acc: 0.8102 Epoch 2/5 60000/60000 [==============================] - 3s 52us/sample - loss: 0.4103 - acc: 0.8555 Epoch 3/5 60000/60000 [==============================] - 3s 55us/sample - loss: 0.3750 - acc: 0.8681 Epoch 4/5 60000/60000 [==============================] - 3s 55us/sample - loss: 0.3524 - acc: 0.8757 Epoch 5/5 60000/60000 [==============================] - 3s 53us/sample - loss: 0.3368 - acc: 0.8798 10000/10000 [==============================] - 0s 37us/sample - loss: 0.3770 - acc: 0.8673 Test accuracy: 0.8672999739646912 export_path = ./model/v1 Saved model success
Step 3: Push code to a Git repository
Push code to your Git repository directly from the Notebook.
-
Install Git.
apt-get update apt-get install git -
Initialize the Git configuration and store your credentials.
git config --global credential.helper store git pull ${YOUR_GIT_REPO} -
Push the code to the Git repository.
git push origin fashion-testExpected output:
Total 0 (delta 0), reused 0 (delta 0) To codeup.aliyun.com:60b4cf5c66bba1c04b442e49/tensorflow-fashion-mnist-sample.git * [new branch] fashion-test -> fashion-test
Step 4: Submit a training job using the Arena SDK
-
Install the dependencies for the Arena SDK.
!pip install coloredlogs -
Create a new Python file and initialize it with the following code:
import os import sys import time from arenasdk.client.client import ArenaClient from arenasdk.enums.types import * from arenasdk.exceptions.arena_exception import * from arenasdk.training.tensorflow_job_builder import * from arenasdk.logger.logger import LoggerBuilder def main(): print("start to test arena-python-sdk") client = ArenaClient("","demo-ns","info","arena-system") # The job is submitted to the demo-ns namespace. print("create ArenaClient succeed.") print("start to create tfjob") job_name = "arena-sdk-distributed-test" job_type = TrainingJobType.TFTrainingJob try: # build the training job job = TensorflowJobBuilder().with_name(job_name)\ .with_workers(1)\ .with_gpus(1)\ .with_worker_image("tensorflow/tensorflow:1.5.0-devel-gpu")\ .with_ps_image("tensorflow/tensorflow:1.5.0-devel")\ .with_ps_count(1)\ .with_datas({"fashion-demo-pvc":"/data"})\ .enable_tensorboard()\ .with_sync_mode("git")\ .with_sync_source("https://codeup.aliyun.com/60b4cf5c66bba1c04b442e49/tensorflow-fashion-mnist-sample.git")\ # URL of the Git repository. .with_envs({\ "GIT_SYNC_USERNAME":"USERNAME", \ # Username for the Git repository. "GIT_SYNC_PASSWORD":"PASSWORD",\ # Password for the Git repository. "TEST_TMPDIR":"/",\ })\ .with_command("python code/tensorflow-fashion-mnist-sample/tf-distributed-mnist.py").build() # If the training job already exists, delete it if client.training().get(job_name, job_type): print("The job {} already exists. Deleting it.".format(job_name)) client.training().delete(job_name, job_type) time.sleep(3) output = client.training().submit(job) print(output) count = 0 # Wait for the training job to start running. while True: if count > 160: raise Exception("Timeout waiting for the job to start running") jobInfo = client.training().get(job_name,job_type) if jobInfo.get_status() == TrainingJobStatus.TrainingJobPending: print("job status is PENDING,waiting...") count = count + 1 time.sleep(5) continue print("current status is {} of job {}".format(jobInfo.get_status().value,job_name)) break # get the training job logs logger = LoggerBuilder().with_accepter(sys.stdout).with_follow().with_since("5m") #jobInfo.get_instances()[0].get_logs(logger) # display the training job information print(str(jobInfo)) # delete the training job #client.training().delete(job_name, job_type) except ArenaException as e: print(e) main()-
namespace: In this example, the training job is submitted to thedemo-nsnamespace. -
with_sync_source: The URL of the Git repository. -
with_envs: The username and password for the Git repository.
-
-
In the target Notebook, click the
icon to run the code.Expected output:
2021-11-02/08:57:28 DEBUG util.py[line:19] - execute command: [arena get --namespace=demo-ns --arena-namespace=arena-system --loglevel=info arena-sdk-distributed-test --type=tfjob -o json] 2021-11-02/08:57:28 DEBUG util.py[line:19] - execute command: [arena submit --namespace=demo-ns --arena-namespace=arena-system --loglevel=info tfjob --name=arena-sdk-distributed-test --workers=1 --gpus=1 --worker-image=tensorflow/tensorflow:1.5.0-devel-gpu --ps-image=tensorflow/tensorflow:1.5.0-devel --ps=1 --data=fashion-demo-pvc:/data --tensorboard --sync-mode=git --sync-source=https://codeup.aliyun.com/60b4cf5c66bba1c04b442e49/tensorflow-fashion-mnist-sample.git --env=GIT_SYNC_USERNAME=kubeai --env=GIT_SYNC_PASSWORD=kubeai@ACK123 --env=TEST_TMPDIR=/ python code/tensorflow-fashion-mnist-sample/tf-distributed-mnist.py] start to test arena-python-sdk create ArenaClient succeed. start to create tfjob 2021-11-02/08:57:29 DEBUG util.py[line:19] - execute command: [arena get --namespace=demo-ns --arena-namespace=arena-system --loglevel=info arena-sdk-distributed-test --type=tfjob -o json] service/arena-sdk-distributed-test-tensorboard created deployment.apps/arena-sdk-distributed-test-tensorboard created tfjob.kubeflow.org/arena-sdk-distributed-test created job status is PENDING,waiting... 2021-11-02/09:00:34 DEBUG util.py[line:19] - execute command: [arena get --namespace=demo-ns --arena-namespace=arena-system --loglevel=info arena-sdk-distributed-test --type=tfjob -o json] current status is RUNNING of job arena-sdk-distributed-test { "allocated_gpus": 1, "chief_name": "arena-sdk-distributed-test-worker-0", "duration": "185s", "instances": [ { "age": "13s", "gpu_metrics": [], "is_chief": false, "name": "arena-sdk-distributed-test-ps-0", "node_ip": "192.168.5.8", "node_name": "cn-beijing.192.168.5.8", "owner": "arena-sdk-distributed-test", "owner_type": "tfjob", "request_gpus": 0, "status": "Running" }, { "age": "13s", "gpu_metrics": [], "is_chief": true, "name": "arena-sdk-distributed-test-worker-0", "node_ip": "192.168.5.8", "node_name": "cn-beijing.192.168.5.8", "owner": "arena-sdk-distributed-test", "owner_type": "tfjob", "request_gpus": 1, "status": "Running" } ], "name": "arena-sdk-distributed-test", "namespace": "demo-ns", "priority": "N/A", "request_gpus": 1, "tensorboard": "http://192.168.5.6:31068", "type": "tfjob" }
Step 4: Train the model
The following examples cover standalone training, distributed training, Fluid-accelerated training, and topology-aware scheduling with the AI job scheduler.
Example 1: Submit a standalone TensorFlow training job
Submit a training job with the Arena CLI or AI Developer Console.
Method 1: Use the Arena CLI
arena \
submit \
tfjob \
-n ns1 \
--name=fashion-mnist-arena \
--data=fashion-mnist-jackwg-pvc:/root/data/ \
--env=DATASET_PATH=/root/data/ \
--env=MODEL_PATH=/root/saved_model \
--env=MODEL_VERSION=1 \
--env=GIT_SYNC_USERNAME=<GIT_USERNAME> \
--env=GIT_SYNC_PASSWORD=<GIT_PASSWORD> \
--sync-mode=git \
--sync-source=https://codeup.aliyun.com/60b4cf5c66bba1c04b442e49/tensorflow-fashion-mnist-sample.git \
--image="tensorflow/tensorflow:2.2.2-gpu" \
"python /root/code/tensorflow-fashion-mnist-sample/train.py --log_dir=/training_logs"
Method 2: Use the AI Developer Console
-
The following table describes some of the key parameters.
Parameter
Example
Required
Name
fashion-demo
Yes
Namespace
demo-ns
Yes
PersistentVolumeClaim
fashion-demo-pvc
Yes
Local storage directory
/root/data
No
-
Configure a source code repository.
Parameter
Example
Required
Name
fashion-git
Yes
Git URL
https://codeup.aliyun.com/60b4cf5c66bba1c04b442e49/tensorflow-fashion-mnist-sample.git
Yes
Default branch
master
No
Local storage directory
/root/
No
Private Git username
The username for your private Git repository.
No
Private Git password or token
The password or personal access token for your private Git repository.
No
-
Submit a standalone training job.
After you configure the training parameters, click Submit. You can then view the training job on the Job List page. The following table describes the job submission parameters.
Parameter
Description
Task Name
In this example, the job name is fashion-tf-ui.
Job type
For this example, select Tensorflow Standalone.
Namespace
In this example, use demo-ns. This must be the same namespace as the dataset.
Data source configuration
For this example, select fashion-demo, which was configured in Step 1.
Code configuration
For this example, select fashion-git, which was configured in Step 2.
Code branch
In this example, use master.
Command
In this example, use
"export DATASET_PATH=/root/data/ &&export MODEL_PATH=/root/saved_model &&export MODEL_VERSION=1 &&python /root/code/tensorflow-fashion-mnist-sample/train.py".Private Git repository
If you use a private repository, you must provide the username and password.
Instance count
The default value is 1.
Image
In this example, use
tensorflow/tensorflow:2.2.2-gpu.Image pull secret
If you are using an image from a private registry, you must create an image pull secret first.
CPU (cores)
The default value is 4.
Memory (GB)
The default value is 8.
-
After submitting the job, view its logs.
-
In the left-side navigation pane of the AI Developer Console, click Tasks.
-
On the Tasks page, click the name of the target job.
-
On the job details page, click the Instance tab. Then, in the Actions column for the target instance, click Logs.
The following snippet shows the log output for this example:
train_images.shape: (60000, 28, 28, 1), of float64 test_images.shape: (10000, 28, 28, 1), of float64 Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= Conv1 (Conv2D) (None, 13, 13, 8) 80 _________________________________________________________________ flatten (Flatten) (None, 1352) 0 _________________________________________________________________ Softmax (Dense) (None, 10) 13530 ================================================================= Total params: 13,610 Trainable params: 13,610 Non-trainable params: 0 _________________________________________________________________ Epoch 1/5 2021-08-01 14:21:17.532237: E tensorflow/core/profiler/internal/gpu/cupti_tracer.cc:1430] function cupti_interface_->EnableCallback( 0 , subscriber_, CUPTI_CB_DOMAIN_DRIVER_API, cbid)failed with error CUPTI_ERROR_INVALID_PARAMETER 2021-08-01 14:21:17.532390: I tensorflow/core/profiler/internal/gpu/device_tracer.cc:216] GpuTracer has collected 0 callback api events and 0 activity events. 2021-08-01 14:21:17.533535: I tensorflow/core/profiler/rpc/client/save_profile.cc:168] Creating directory: /training_logs/train/plugins/profile/2021_08_01_14_21_17 2021-08-01 14:21:17.533928: I tensorflow/core/profiler/rpc/client/save_profile.cc:174] Dumped gzipped tool data for trace.json.gz to /training_logs/train/plugins/profile/2021_08_01_14_21_17/fashion-mnist-arena-chief-0.trace.json.gz 2021-08-01 14:21:17.534251: I tensorflow/core/profiler/utils/event_span.cc:288] Generation of step-events took 0 ms 2021-08-01 14:21:17.534961: I tensorflow/python/profiler/internal/profiler_wrapper.cc:87] Creating directory: /training_logs/train/plugins/profile/2021_08_01_14_21_17Dumped tool data for overview_page.pb to /training_logs/train/plugins/profile/2021_08_01_14_21_17/fashion-mnist-arena-chief-0.overview_page.pb Dumped tool data for input_pipeline.pb to /training_logs/train/plugins/profile/2021_08_01_14_21_17/fashion-mnist-arena-chief-0.input_pipeline.pb Dumped tool data for tensorflow_stats.pb to /training_logs/train/plugins/profile/2021_08_01_14_21_17/fashion-mnist-arena-chief-0.tensorflow_stats.pb Dumped tool data for kernel_stats.pb to /training_logs/train/plugins/profile/2021_08_01_14_21_17/fashion-mnist-arena-chief-0.kernel_stats.pb 1875/1875 [==============================] - 3s 2ms/step - loss: 0.5399 - accuracy: 0.8116 Epoch 2/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.4076 - accuracy: 0.8573 Epoch 3/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.3727 - accuracy: 0.8694 Epoch 4/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.3512 - accuracy: 0.8769 Epoch 5/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.3351 - accuracy: 0.8816 313/313 [==============================] - 0s 1ms/step - loss: 0.3595 - accuracy: 0.8733 2021-08-01 14:21:34.820089: W tensorflow/python/util/util.cc:329] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them. WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1817: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version. Instructions for updating: If using Keras pass *_constraint arguments to layers. Test accuracy: 0.8733000159263611 export_path = /root/saved_model/1 Saved model success
-
-
View training metrics in TensorBoard.
Forward the TensorBoard service port to your local machine using kubectl port-forward.
-
Get the TensorBoard service details.
kubectl get svc -n demo-nsExpected output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE tf-dist-arena-tensorboard NodePort 172.16.XX.XX <none> 6006:32226/TCP 80m -
Forward the TensorBoard port.
kubectl port-forward svc/tf-dist-arena-tensorboard -n demo-ns 6006:6006Expected output:
Forwarding from 127.0.0.1:6006 -> 6006 Forwarding from [::1]:6006 -> 6006 Handling connection for 6006 Handling connection for 6006 -
In a web browser, open
http://localhost:6006/to view the TensorBoard dashboard.The GRAPHS tab displays the model's computational graph, showing the connections and data flow between layers such as Conv1, flatten, and Softmax. The side panel provides detailed properties for the selected node.
-
Example 2: Submit a distributed TensorFlow training job
Method 1: Use the Arena CLI
-
Submit the training job.
arena submit tf \ -n demo-ns \ --name=tf-dist-arena \ --working-dir=/root/ \ --data fashion-mnist-pvc:/data \ --env=TEST_TMPDIR=/ \ --env=GIT_SYNC_USERNAME=kubeai \ --env=GIT_SYNC_PASSWORD=kubeai@ACK123 \ --env=GIT_SYNC_BRANCH=master \ --gpus=1 \ --workers=2 \ --worker-image=tensorflow/tensorflow:1.5.0-devel-gpu \ --sync-mode=git \ --sync-source=https://codeup.aliyun.com/60b4cf5c66bba1c04b442e49/tensorflow-fashion-mnist-sample.git \ --ps=1 \ --ps-image=tensorflow/tensorflow:1.5.0-devel \ --tensorboard \ "python code/tensorflow-fashion-mnist-sample/tf-distributed-mnist.py --log_dir=/training_logs" -
Get the TensorBoard service details.
kubectl get svc -n demo-nsExpected output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE tf-dist-arena-tensorboard NodePort 172.16.204.248 <none> 6006:32226/TCP 80m -
Forward the TensorBoard port.
kubectl port-forward svc/tf-dist-arena-tensorboard -n demo-ns 6006:6006Expected output:
Forwarding from 127.0.0.1:6006 -> 6006 Forwarding from [::1]:6006 -> 6006 Handling connection for 6006 Handling connection for 6006 -
In a web browser, open
http://localhost:6006/to view the TensorBoard dashboard.The SCALARS tab shows plots for metrics like accuracy and cross_entropy during training. In the side panel, you can filter by train and test runs, and adjust the Smoothing and axes display options.
Method 2: Use the AI Developer Console
-
Reuses data from Step 1.
-
Configure a source code repository.
Reuses code from Step 2.
-
Submit the distributed TensorFlow training job.
After you configure the training parameters, click Submit. You can then view the job on the Job List page. The following table describes the key job parameters.
Parameter
Description
Task Name
In this example, use fashion-ps-ui.
Job type
Select TF Distributed.
Namespace
In this example, use demo-ns. This must be the same namespace as the dataset.
Data source configuration
In this example, select fashion-demo, which was configured in Step 1 of the previous example.
Code configuration
In this example, select fashion-git, which was configured in Step 2 of the previous example.
Command
In this example, use
"export TEST_TMPDIR=/root/ && python code/tensorflow-fashion-mnist-sample/tf-distributed-mnist.py --log_dir=/training_logs".Image
-
Under Task Resource Configuration, on the Worker tab, set Image to
tensorflow/tensorflow:1.5.0-devel-gpu. -
Under Task Resource Configuration, on the PS tab, set Image to
tensorflow/tensorflow:1.5.0-devel.
-
-
To view TensorBoard, follow steps 2 through 4 in Method 1: Use the Arena CLI.
Example 3: Submit a Fluid-accelerated training job
Accelerate a dataset from the O&M console, submit a job with the accelerated dataset, and compare runtimes:
-
An administrator accelerates an existing dataset from the O&M console.
-
A developer submits a training job that uses the accelerated dataset.
-
Compare the runtimes of the jobs using the
arena listcommand.
-
Accelerate an existing dataset.
Skip this step if you already accelerated fashion-demo-pvc in Step 2: Create a dataset. See Manage datasets.
-
Submit a job that uses the accelerated dataset.
Submit the training job to the demo-ns namespace. Key differences for accelerated datasets:
-
--data: The name of the accelerated PersistentVolumeClaim (PVC). In this example, it isfashion-demo-pvc-acc. -
--env=DATASET_PATH: This path is formed by appending the PVC name (fashion-demo-pvc-acc) to the mount path (/root/data/) specified in the--dataparameter.
arena \ submit \ tfjob \ -n demo-ns \ --name=fashion-mnist-fluid \ --data=fashion-demo-pvc-acc:/root/data/ \ --env=DATASET_PATH=/root/data/fashion-demo-pvc-acc \ --env=MODEL_PATH=/root/saved_model \ --env=MODEL_VERSION=1 \ --env=GIT_SYNC_USERNAME=${GIT_USERNAME} \ --env=GIT_SYNC_PASSWORD=${GIT_PASSWORD} \ --sync-mode=git \ --sync-source=https://codeup.aliyun.com/60b4cf5c66bba1c04b442e49/tensorflow-fashion-mnist-sample.git \ --image="tensorflow/tensorflow:2.2.2-gpu" \ "python /root/code/tensorflow-fashion-mnist-sample/train.py --log_dir=/training_logs" -
-
Compare the execution times of the two training jobs.
arena list -n demo-nsExpected output:
NAME STATUS TRAINER DURATION GPU(Requested) GPU(Allocated) NODE fashion-mnist-fluid SUCCEEDED TFJOB 33s 0 N/A 192.168.5.7 fashion-mnist-arena SUCCEEDED TFJOB 3m 0 N/A 192.168.5.8With the same code and resources, the Fluid-accelerated job finished in 33 seconds vs. 3 minutes for the standard job.
Example 4: Accelerate a distributed training job by using the AI job scheduler
The AI job scheduler is an ACK plugin for AI and big data workloads, supporting Gang Scheduling, Capacity Scheduling, and topology-aware scheduling. This example demonstrates GPU topology-aware scheduling.
The scheduler uses node-level topology information (GPU links such as NVLink and PCIe Switch, or CPU NUMA architecture) to optimize scheduling for AI jobs. See GPU topology-aware scheduling and CPU topology-aware scheduling.
Enable GPU topology-aware scheduling and compare job performance.
-
Create a training job without topology-aware scheduling.
arena submit mpi \ --name=tensorflow-4-vgg16 \ --gpus=1 \ --workers=4 \ --image=registry.cn-hangzhou.aliyuncs.com/kubernetes-image-hub/tensorflow-benchmark:tf2.3.0-py3.7-cuda10.1 \ "mpirun --allow-run-as-root -np "4" -bind-to none -map-by slot -x NCCL_DEBUG=INFO -x NCCL_SOCKET_IFNAME=eth0 -x LD_LIBRARY_PATH -x PATH --mca pml ob1 --mca btl_tcp_if_include eth0 --mca oob_tcp_if_include eth0 --mca orte_keep_fqdn_hostnames t --mca btl ^openib python /tensorflow/benchmarks/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --model=vgg16 --batch_size=64 --variable_update=horovod" -
Create a training job with topology-aware scheduling enabled.
Label the node. Replace cn-beijing.192.168.XX.XX with your actual node name.
kubectl label node cn-beijing.192.168.XX.XX ack.node.gpu.schedule=topology --overwriteCreate the training job. The
--gputopology=trueflag enables topology awareness in Arena.arena submit mpi \ --name=tensorflow-topo-4-vgg16 \ --gpus=1 \ --workers=4 \ --gputopology=true \ --image=registry.cn-hangzhou.aliyuncs.com/kubernetes-image-hub/tensorflow-benchmark:tf2.3.0-py3.7-cuda10.1 \ "mpirun --allow-run-as-root -np "4" -bind-to none -map-by slot -x NCCL_DEBUG=INFO -x NCCL_SOCKET_IFNAME=eth0 -x LD_LIBRARY_PATH -x PATH --mca pml ob1 --mca btl_tcp_if_include eth0 --mca oob_tcp_if_include eth0 --mca orte_keep_fqdn_hostnames t --mca btl ^openib python /tensorflow/benchmarks/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --model=vgg16 --batch_size=64 --variable_update=horovod -
Compare the performance of the two jobs.
-
Compare the runtimes.
arena list -n demo-nsExpected output:
NAME STATUS TRAINER DURATION GPU(Requested) GPU(Allocated) NODE tensorflow-topo-4-vgg16 SUCCEEDED MPIJOB 44s 4 N/A 192.168.4.XX1 tensorflow-4-vgg16-image-warned SUCCEEDED MPIJOB 2m 4 N/A 192.168.4.XX0 -
View the throughput of the topology-aware job.
arena logs tensorflow-topo-4-vgg16 -n demo-nsExpected output:
100 images/sec: 251.7 +/- 0.1 (jitter = 1.2) 7.262 ---------------------------------------------------------------- total images/sec: 1006.44 -
View the throughput of the standard job.
arena logs tensorflow-4-vgg16-image-warned -n demo-nsExpected output:
100 images/sec: 56.4 +/- 0.2 (jitter = 1.5) 7.261 ---------------------------------------------------------------- total images/sec: 225.50
-
The following table summarizes the performance comparison between the two jobs.
|
Training job |
Throughput per GPU (images/sec) |
Total GPU throughput (images/sec) |
Duration (seconds) |
|
Topology-aware scheduling enabled |
251.7 |
1006.44 |
44 |
|
Topology-aware scheduling disabled |
56.4 |
225.50 |
120 |
A node with topology-aware scheduling enabled no longer supports standard GPU scheduling. To restore it, change the node label:
kubectl label node cn-beijing.192.168.XX.XX0 ack.node.gpu.schedule=default --overwrite
Step 5: Manage the model
- Access the AI Developer Console
- In the left-side navigation pane of AI Developer Console, click Model Manage.
-
On the Model Management page, click Create Model.
-
In the Create dialog box, configure the Model Name, Model Version, and Job Name.
In this example, the model name is fashion-mnist-demo, the model version is v1, and the training job is tf-single.
-
Click OK. The new model appears in the list.
To evaluate the model, click New Model Evaluation in the model's row.
Step 6: Evaluate the model
Submit model evaluation jobs with Arena or the AI Developer Console. This example evaluates a checkpoint from the Fashion-MNIST training:
-
Submit a training job with checkpointing enabled using Arena.
-
Submit a model evaluation job using Arena.
-
Compare the evaluation results in the AI Developer Console.
-
Submit a training job with checkpointing enabled.
Submit a training job with Arena. The job outputs checkpoints to the
fashion-demo-pvcvolume.arena \ submit \ tfjob \ -n demo-ns \ # Set the namespace as needed. --name=fashion-mnist-arena-ckpt \ --data=fashion-demo-pvc:/root/data/ \ --env=DATASET_PATH=/root/data/ \ --env=MODEL_PATH=/root/data/saved_model \ --env=MODEL_VERSION=1 \ --env=GIT_SYNC_USERNAME=${GIT_USERNAME} \ # Enter your Git username. --env=GIT_SYNC_PASSWORD=${GIT_PASSWORD} \ # Enter your Git password. --env=OUTPUT_CHECKPOINT=1 \ --sync-mode=git \ --sync-source=https://codeup.aliyun.com/60b4cf5c66bba1c04b442e49/tensorflow-fashion-mnist-sample.git \ --image="tensorflow/tensorflow:2.2.2-gpu" \ "python /root/code/tensorflow-fashion-mnist-sample/train.py --log_dir=/training_logs" -
Submit a model evaluation job.
-
Build the evaluation image.
In the kubeai-sdk directory, run the following commands to build and push the image.
docker build . -t ${DOCKER_REGISTRY}:fashion-mnist docker push ${DOCKER_REGISTRY}:fashion-mnist -
Get the MySQL service details.
kubectl get svc -n kube-ai ack-mysqlExpected output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ack-mysql ClusterIP 172.16.XX.XX <none> 3306/TCP 28h -
Submit a model evaluation job with Arena.
arena evaluate model \ --namespace=demo-ns \ --loglevel=debug \ --name=evaluate-job \ --image=registry.cn-beijing.aliyuncs.com/kube-ai/kubeai-sdk-demo:fashion-minist \ --env=ENABLE_MYSQL=True \ --env=MYSQL_HOST=172.16.77.227 \ --env=MYSQL_PORT=3306 \ --env=MYSQL_USERNAME=kubeai \ --env=MYSQL_PASSWORD=kubeai@ACK \ --data=fashion-demo-pvc:/data \ --model-name=1 \ --model-path=/data/saved_model/ \ --dataset-path=/data/ \ --metrics-path=/data/output \ "python /kubeai/evaluate.py"NoteYou can obtain the IP address and port of the MySQL service from the previous step.
-
-
Compare the evaluation results.
-
In the left-side navigation pane of the AI Developer Console, click Model Management.
The page displays a list of model evaluation jobs. The list includes columns such as Name, Model name, Model version, Namespace, Status, Creation time, and End time. For completed jobs, the Status column shows Complete. You can compare the results of multiple jobs by clicking the Compare Metrics button at the top of the page.
-
In the Tasks, click a job name to view its metrics.
The evaluation results display the following metrics: accuracy, precision, recall, F1_score, and AUC. An ROC curve also visualizes the model's classification performance.
A bar chart visually compares the performance of the selected jobs based on metrics such as AUC and accuracy.
-
Step 7: Deploy the model
Deploy your trained model as a TensorFlow Serving inference service. Arena also supports other frameworks such as Triton and Seldon (see Arena serve documentation).
This example uses the model from Step 4, stored in the fashion-demo-pvc PVC from Step 2. If your model uses a different storage type, create a corresponding PVC first.
-
Deploy the TensorFlow model to TensorFlow Serving with Arena.
arena serve tensorflow \ --loglevel=debug \ --namespace=demo-ns \ --name=fashion-mnist \ --model-name=1 \ --gpus=1 \ --image=tensorflow/serving:1.15.0-gpu \ --data=fashion-demo-pvc:/data \ --model-path=/data/saved_model/ \ --version-policy=latest -
Get the name of the deployed inference service.
arena serve list -n demo-nsExpected output:
NAME TYPE VERSION DESIRED AVAILABLE ADDRESS PORTS GPU fashion-mnist Tensorflow 202111031203 1 1 172.16.XX.XX GRPC:8500,RESTFUL:8501 1You can use the ADDRESS and PORTS values in the output to call the service from within the cluster.
-
In Jupyter, create a new Notebook file to send requests to the TensorFlow Serving HTTP service.
This example uses the Jupyter Notebook created in Step 3: Develop the model to send requests.
-
In the following initialization code, replace the value of
server_ipwith the ADDRESS (172.16.XX.XX) returned in the previous step. -
Set
server_http_portto the RESTFUL port (8501) returned in the previous step.
The initialization code for the Notebook file is as follows:
import os import gzip import numpy as np # import matplotlib.pyplot as plt import random import requests import json server_ip = "172.16.XX.XX" server_http_port = 8501 dataset_dir = "/root/data/" def load_data(): files = [ 'train-labels-idx1-ubyte.gz', 'train-images-idx3-ubyte.gz', 't10k-labels-idx1-ubyte.gz', 't10k-images-idx3-ubyte.gz' ] paths = [] for fname in files: paths.append(os.path.join(dataset_dir, fname)) with gzip.open(paths[0], 'rb') as labelpath: y_train = np.frombuffer(labelpath.read(), np.uint8, offset=8) with gzip.open(paths[1], 'rb') as imgpath: x_train = np.frombuffer(imgpath.read(), np.uint8, offset=16).reshape(len(y_train), 28, 28) with gzip.open(paths[2], 'rb') as labelpath: y_test = np.frombuffer(labelpath.read(), np.uint8, offset=8) with gzip.open(paths[3], 'rb') as imgpath: x_test = np.frombuffer(imgpath.read(), np.uint8, offset=16).reshape(len(y_test), 28, 28) return (x_train, y_train),(x_test, y_test) def show(idx, title): plt.figure() plt.imshow(test_images[idx].reshape(28,28)) plt.axis('off') plt.title('\n\n{}'.format(title), fontdict={'size': 16}) class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'] (train_images, train_labels), (test_images, test_labels) = load_data() train_images = train_images / 255.0 test_images = test_images / 255.0 # reshape for feeding into the model train_images = train_images.reshape(train_images.shape[0], 28, 28, 1) test_images = test_images.reshape(test_images.shape[0], 28, 28, 1) print('\ntrain_images.shape: {}, of {}'.format(train_images.shape, train_images.dtype)) print('test_images.shape: {}, of {}'.format(test_images.shape, test_images.dtype)) rando = random.randint(0,len(test_images)-1) #show(rando, 'An Example Image: {}'.format(class_names[test_labels[rando]])) # !pip install -q requests # import requests # headers = {"content-type": "application/json"} # json_response = requests.post('http://localhost:8501/v1/models/fashion_model:predict', data=data, headers=headers) # predictions = json.loads(json_response.text)['predictions'] # show(0, 'The model thought this was a {} (class {}), and it was actually a {} (class {})'.format( # class_names[np.argmax(predictions[0])], np.argmax(predictions[0]), class_names[test_labels[0]], test_labels[0])) def request_model(data): headers = {"content-type": "application/json"} json_response = requests.post('http://{}:{}/v1/models/1:predict'.format(server_ip, server_http_port), data=data, headers=headers) print('=======response:', json_response, json_response.text) predictions = json.loads(json_response.text)['predictions'] print('The model thought this was a {} (class {}), and it was actually a {} (class {})'.format(class_names[np.argmax(predictions[0])], np.argmax(predictions[0]), class_names[test_labels[0]], test_labels[0])) #show(0, 'The model thought this was a {} (class {}), and it was actually a {} (class {})'.format( # class_names[np.argmax(predictions[0])], np.argmax(predictions[0]), class_names[test_labels[0]], test_labels[0])) # def request_model_version(data): # headers = {"content-type": "application/json"} # json_response = requests.post('http://{}:{}/v1/models/1/version/1:predict'.format(server_ip, server_http_port), data=data, headers=headers) # print('=======response:', json_response, json_response.text) # predictions = json.loads(json_response.text) # for i in range(0,3): # show(i, 'The model thought this was a {} (class {}), and it was actually a {} (class {})'.format( # class_names[np.argmax(predictions[i])], np.argmax(predictions[i]), class_names[test_labels[i]], test_labels[i])) data = json.dumps({"signature_name": "serving_default", "instances": test_images[0:3].tolist()}) print('Data: {} ... {}'.format(data[:50], data[len(data)-52:])) #request_model_version(data) request_model(data)Click the
icon in the Jupyter Notebook to see the following output:train_images.shape: (60000, 28, 28, 1), of float64 test_images.shape: (10000, 28, 28, 1), of float64 Data: {"signature_name": "serving_default", "instances": ... [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]]]]} =======response: <Response [200]> { "predictions": [[7.42696e-07, 6.91237556e-09, 2.66364452e-07, 2.27735413e-07, 4.0373439e-07, 0.00490919966, 7.27086217e-06, 0.0316713452, 0.0010733594, 0.962337255], [0.00685342, 1.8516447e-08, 0.9266119, 2.42278338e-06, 0.0603800081, 4.01338771e-12, 0.00613868702, 4.26091073e-15, 1.35764185e-05, 3.38685469e-10], [1.09047969e-05, 0.999816835, 7.98738e-09, 0.000122893631, 4.85748023e-05, 1.50353979e-10, 3.57102294e-07, 1.89657579e-09, 4.4604468e-07, 9.23274524e-09] ] } The model thought this was a Ankle boot (class 9), and it was actually a Ankle boot (class 9) -
FAQ
-
How do I install software in the Jupyter Notebook console?
To install software, run the following command in the Jupyter Notebook console.
apt-get install <software-name> -
How do I fix garbled characters in the Jupyter Notebook console?
Edit the /etc/locale file to contain the following, then reopen the terminal.
LC_CTYPE="da_DK.UTF-8" LC_NUMERIC="da_DK.UTF-8" LC_TIME="da_DK.UTF-8" LC_COLLATE="da_DK.UTF-8" LC_MONETARY="da_DK.UTF-8" LC_MESSAGES="da_DK.UTF-8" LC_PAPER="da_DK.UTF-8" LC_NAME="da_DK.UTF-8" LC_ADDRESS="da_DK.UTF-8" LC_TELEPHONE="da_DK.UTF-8" LC_MEASUREMENT="da_DK.UTF-8" LC_IDENTIFICATION="da_DK.UTF-8" LC_ALL=