Cloud Native AI Suite developer guide

更新时间:
复制 MD 格式

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:

Tutorial environment

Use the cloud-native AI suite to develop, train, accelerate, evaluate, and deploy a Fashion-MNIST workload on your ACK cluster.

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).

    Note

    The 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

  1. 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
  2. Create the fashion-mnist dataset.

    kubectl create -f fashion-mnist.yaml
  3. View the PV and PVC status.

    • Check the status of the PV.

      kubectl get pv fashion-mnist-jackwg

      Expected 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 ns1

      Expected 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 Bound status.

Step 2: create an accelerated dataset

Administrators use the AI Dashboard to accelerate datasets.

  1. Access the AI Dashboard as an administrator.
  2. In the left-side navigation pane of AI Dashboard, choose Dataset > Dataset List.
  3. 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:

  1. (Optional) Create a Jupyter Notebook from a custom image.

  2. Develop and test your model in a Jupyter Notebook.

  3. Push code to a Git repository from the Jupyter Notebook.

  4. 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.

  1. Create a file named Dockerfile based 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
  2. 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
  3. 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
  4. 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>
  5. Create a Jupyter Notebook in the AI Developer Console.

    See Create and use notebooks.

    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

  1. Access the AI Developer Console
  2. In the left-side navigation pane of AI Developer Console, click Notebook.
  3. On the Notebook page, click the name of the target Jupyter Notebook with a Status of Running.

  4. Open a new terminal and run the following commands to verify that the data is mounted successfully:

    pwd
    /root/data
    ls -alh

    Expected 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
  5. 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()
    Important

    In the code, dataset_path and model_path must 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.

  6. In the target Notebook, click the Run icon 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.

  1. Install Git.

    apt-get update
    apt-get install git
  2. Initialize the Git configuration and store your credentials.

    git config --global credential.helper store
    git pull ${YOUR_GIT_REPO}
  3. Push the code to the Git repository.

    git push origin fashion-test

    Expected 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

  1. Install the dependencies for the Arena SDK.

    !pip install coloredlogs
  2. 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 the demo-ns namespace.

    • with_sync_source: The URL of the Git repository.

    • with_envs: The username and password for the Git repository.

  3. In the target Notebook, click the Run icon 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

  1. Configure a data source.

    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

  2. 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

  3. 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.

    See Arena CLI parameters for TFJob.

  4. After submitting the job, view its logs.

    1. In the left-side navigation pane of the AI Developer Console, click Tasks.

    2. On the Tasks page, click the name of the target job.

    3. 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
  5. View training metrics in TensorBoard.

    Forward the TensorBoard service port to your local machine using kubectl port-forward.

    1. Get the TensorBoard service details.

      kubectl get svc -n demo-ns

      Expected output:

      NAME                        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)               AGE
      tf-dist-arena-tensorboard   NodePort    172.16.XX.XX     <none>        6006:32226/TCP        80m
    2. Forward the TensorBoard port.

      kubectl port-forward svc/tf-dist-arena-tensorboard -n demo-ns 6006:6006

      Expected output:

      Forwarding from 127.0.0.1:6006 -> 6006
      Forwarding from [::1]:6006 -> 6006
      Handling connection for 6006
      Handling connection for 6006
    3. 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

  1. 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"
  2. Get the TensorBoard service details.

    kubectl get svc -n demo-ns

    Expected output:

    NAME                        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                 AGE
    tf-dist-arena-tensorboard   NodePort    172.16.204.248   <none>        6006:32226/TCP          80m
  3. Forward the TensorBoard port.

    kubectl port-forward svc/tf-dist-arena-tensorboard -n demo-ns 6006:6006

    Expected output:

    Forwarding from 127.0.0.1:6006 -> 6006
    Forwarding from [::1]:6006 -> 6006
    Handling connection for 6006
    Handling connection for 6006
  4. 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

  1. Configure a data source.

    Reuses data from Step 1.

  2. Configure a source code repository.

    Reuses code from Step 2.

  3. 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.

    See Arena CLI parameters for TFJob.

  4. 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:

  1. An administrator accelerates an existing dataset from the O&M console.

  2. A developer submits a training job that uses the accelerated dataset.

  3. Compare the runtimes of the jobs using the arena list command.

  1. Accelerate an existing dataset.

    Skip this step if you already accelerated fashion-demo-pvc in Step 2: Create a dataset. See Manage datasets.

  2. 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 is fashion-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 --data parameter.

    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"
  3. Compare the execution times of the two training jobs.

    arena list -n demo-ns

    Expected 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.8

    With 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.

  1. 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"
  2. 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 --overwrite

    Create the training job. The --gputopology=true flag 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
  3. Compare the performance of the two jobs.

    1. Compare the runtimes.

      arena list -n demo-ns

      Expected 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
    2. View the throughput of the topology-aware job.

      arena logs tensorflow-topo-4-vgg16 -n demo-ns

      Expected output:

      100 images/sec: 251.7 +/- 0.1 (jitter = 1.2)  7.262
      ----------------------------------------------------------------
      total images/sec: 1006.44
    3. View the throughput of the standard job.

      arena logs tensorflow-4-vgg16-image-warned -n demo-ns

      Expected 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

  1. Access the AI Developer Console
  2. In the left-side navigation pane of AI Developer Console, click Model Manage.
  3. On the Model Management page, click Create Model.

  4. 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.

  5. 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:

  1. Submit a training job with checkpointing enabled using Arena.

  2. Submit a model evaluation job using Arena.

  3. Compare the evaluation results in the AI Developer Console.

  1. Submit a training job with checkpointing enabled.

    Submit a training job with Arena. The job outputs checkpoints to the fashion-demo-pvc volume.

    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"
  2. Submit a model evaluation job.

    1. 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
    2. Get the MySQL service details.

      kubectl get svc -n kube-ai ack-mysql

      Expected output:

      NAME        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
      ack-mysql   ClusterIP   172.16.XX.XX    <none>        3306/TCP   28h
    3. 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"
      Note

      You can obtain the IP address and port of the MySQL service from the previous step.

  3. Compare the evaluation results.

    1. 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.

    2. 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.

  1. 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
  2. Get the name of the deployed inference service.

    arena serve list -n demo-ns

    Expected output:

    NAME           TYPE        VERSION       DESIRED  AVAILABLE  ADDRESS         PORTS                   GPU
    fashion-mnist  Tensorflow  202111031203  1        1          172.16.XX.XX    GRPC:8500,RESTFUL:8501  1

    You can use the ADDRESS and PORTS values in the output to call the service from within the cluster.

  3. 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_ip with the ADDRESS (172.16.XX.XX) returned in the previous step.

    • Set server_http_port to 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 Run icon 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=