データの探索: データを扱ってみる

目的・ゴール: まずは取り扱うデータを準備する

まずはトレーニングを実行するために必要なデータを準備します。

データを置く場所であるPersistentVolumeClaimを作成しデータをダウンロード解凍を実施します。

概要

ここでは以下のフローで進んでいきます。 一覧としては以下の通りです。一つ一つの用語がわからない場合があるかもしれませんがまずは動かし体験することを目標としています。

  1. テスト、トレーニングデータ、トレーニング結果(モデル)を保管するPersistentVolumeClaimをデプロイする
  2. データセットのダウンロード、データセットのアノテーション、事前トレーニング済みモデルチェックポイント、トレーニングパイプラインの構成ファイルをダウンロード
  3. ダウンロードしたデータ・セット、事前トレーニング済みデータ・セット、データ・セットアノテーションの解凍
  4. ペット検知器モデルをトレーニングするので、TensorFlowペットレコードを作成

ハンズオンではksonnetというツールを使用し進めます。

https://ksonnet.io/

この例で使用する一連のコンポーネントを含むksonnetアプリ ks-app が存在します。 コンポーネントは ks-app/components ディレクトリにあります、カスタマイズしたい場合はここを編集します。

前章から続けている場合はKubeflowのディレクトリに移動しているため、一旦ホームに戻り、今回のお題の画像解析AI用のディレクトリに移動します。 なお、このサンプルアプリケーションはベースとしてKubeflowのExampleを使用しております。

まずはサンプルコードが含まれているリポジトリをクローンします。

$ cd
$ git clone https://github.com/NetAppJpTechTeam/examples.git

クローンが完了したらサンプルアプリ(物体認識)のディレクトリに移動し、設定を実施します。

$ cd examples/object_detection/ks-app
$ export ENV=default
$ ks env add ${ENV} --context=`kubectl config current-context`
$ ks upgrade
$ ks env set ${ENV} --namespace kubeflow

トレーニングデータの準備

作業ディレクトリで pwd を実行し以下のディレクトリであることを確認しましょう。

$ pwd

/home/localadmin/exmaples/object_detection/ks-app

データ保管用の領域を作成

データを保管するPersistentVolumeClaim(PVC)を作成します。

ハンズオンではダイナミックストレージプロビジョニングが必要となります。 前章でインストール、設定したTridentを使用します。

ksonnet のコンポーネントを編集します。

$ ks param set pets-pvc accessMode "ReadWriteMany"
$ ks param set pets-pvc storage "20Gi"
$ ks param set pets-pvc storageClassName "ontap-gold"

ここまでで上記でセットしたパラメータを確認しましょう。

$ ks param list pets-pvc

COMPONENT PARAM            VALUE
========= =====            =====
pets-pvc  accessMode       'ReadWriteMany'
pets-pvc  name             'pets-pvc'
pets-pvc  storage          '20Gi'
pets-pvc  storageClassName 'ontap-gold'
pets-pvc  volumeMode       'Filesystem'

展開したファイルだと、StorageClassを定義する項目を追加しています。

$ cat components/pets-pvc.jsonnet

storageClassName: params.storageClassName が追記されている場所を確認し、この内容が追加されることで実現できることを考えてみましょう。

以下のファイルとなっていれば完了です。

local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components["pets-pvc"];

local k = import "k.libsonnet";

local pvc = {
  apiVersion: "v1",
  kind: "PersistentVolumeClaim",
  metadata:{
    name: params.name,
    namespace: env.namespace,
  },
  spec:{
    accessModes: [params.accessMode],
    volumeMode: params.volumeMode,
    resources: {
      requests: {
        storage: params.storage,
      },
    },
    storageClassName: params.storageClassName
  },
};

以下のコマンドを実行するとデータ保管用の領域であるPVCが作成されます。

$ ks apply ${ENV} -c pets-pvc

INFO Applying persistentvolumeclaims kubeflow.pets-pvc
INFO Creating non-existent persistentvolumeclaims kubeflow.pets-pvc

以下のコマンドを実行し、Statusが「Bound」となっていれば完了です。

$ kubectl get pvc pets-pvc -n kubeflow

NAME       STATUS   VOLUME                    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pets-pvc   Bound    kubeflow-pets-pvc-e2be6   20Gi       RWX            ontap-gold     6m55s

ここまででデータを保管するPVCが作成できたため、次はPVCに必要なデータをダウンロードします。

AI作成に必要なデータをダウンロード

ここまでに作成した pets-pvc へデータをダウンロードし保管します。

変数定義を実施します。

$ PVC="pets-pvc"
$ MOUNT_PATH="/pets_data"
$ DATASET_URL="http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz"
$ ANNOTATIONS_URL="http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz"
$ MODEL_URL="http://download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_coco_2018_01_28.tar.gz"
$ PIPELINE_CONFIG_URL="https://raw.githubusercontent.com/kubeflow/examples/master/object_detection/conf/faster_rcnn_resnet101_pets.config"

ksonnetにパラメータを指定します。

$ ks param set get-data-job mountPath ${MOUNT_PATH}
$ ks param set get-data-job pvc ${PVC}
$ ks param set get-data-job urlData ${DATASET_URL}
$ ks param set get-data-job urlAnnotations ${ANNOTATIONS_URL}
$ ks param set get-data-job urlModel ${MODEL_URL}
$ ks param set get-data-job urlPipelineConfig ${PIPELINE_CONFIG_URL}

指定したパラメータを確認します。

$ ks param list get-data-job

COMPONENT    PARAM             VALUE
=========    =====             =====
get-data-job mountPath         '/pets_data'
get-data-job name              'get-data-job'
get-data-job pvc               'pets-pvc'
get-data-job urlAnnotations    'http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz'
get-data-job urlData           'http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz'
get-data-job urlModel          'http://download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_coco_2018_01_28.tar.gz'
get-data-job urlPipelineConfig 'https://raw.githubusercontent.com/kubeflow/examples/master/object_detection/conf/faster_rcnn_resnet101_pets.config'

ここで使用しているサンプルの一部ではkubernetesクラスタ内から外部への名前解決が失敗する状態になっています。 同じ動作をするコンテナイメージを作成しましたので以下のファイルの image の部分を変更してください。

image: "inutano/wget" から image: "makotow/wget:dns-fix-0.1.2"へ変更してください。

$ vim components/get-data-job.jsonnet

最終的にファイル全体が以下のようになっていれば完了です。

local env = std.extVar("__ksonnet/environments");
local params = std.extVar("__ksonnet/params").components["get-data-job"];

local k = import "k.libsonnet";

local getDataJob(namespace, name, pvc, url, mountPath) = {
      apiVersion: "batch/v1",
      kind: "Job",
      metadata: {
        name: name,
        namespace: namespace,
      },
      spec: {
        template: {
          spec: {
            containers: [{
              name: "get-data",
              image: "makotow/wget:dns-fix-0.1.2", <- このように変更します。
              imagePullPolicy: "IfNotPresent",
              command: ["wget",  url, "-P", mountPath, "--no-check-certificate"],
              volumeMounts: [{
                  mountPath: mountPath,
                  name: "pets-data",
              },],
              },],
            volumes: [{
                name: "pets-data",
                persistentVolumeClaim: {
                  claimName: pvc,
                },
            },],
            restartPolicy: "Never",
          },
        },
        backoffLimit: 4,
      },
    };

std.prune(k.core.v1.list.new([
  getDataJob(env.namespace, params.name + "-dataset", params.pvc, params.urlData, params.mountPath),
  getDataJob(env.namespace, params.name + "-annotations", params.pvc, params.urlAnnotations, params.mountPath),
  getDataJob(env.namespace, params.name + "-model", params.pvc, params.urlModel, params.mountPath),
  getDataJob(env.namespace, params.name + "-config", params.pvc, params.urlPipelineConfig, params.mountPath)]))

注釈

なぜ名前解決が失敗しているかについて詳しく知りたい方は以下のGitHub Issues のやりとりが参考になります。

https://github.com/kubernetes/kubernetes/issues/64924

kubernetesクラスタに適応します。

$ ks apply ${ENV} -c get-data-job

INFO Applying jobs kubeflow.get-data-job-dataset
INFO Creating non-existent jobs kubeflow.get-data-job-dataset
INFO Applying jobs kubeflow.get-data-job-annotations
INFO Creating non-existent jobs kubeflow.get-data-job-annotations
INFO Applying jobs kubeflow.get-data-job-model
INFO Creating non-existent jobs kubeflow.get-data-job-model
INFO Applying jobs kubeflow.get-data-job-config
INFO Creating non-existent jobs kubeflow.get-data-job-config

ダウンロード完了しているかを確認します。

「COMPLETIONS」がすべて「1/1」となれば完了です。

$ kubectl get jobs -n kubeflow

NAME                       COMPLETIONS   DURATION   AGE
get-data-job-annotations   1/1           10s        95s
get-data-job-config        1/1           8s         93s
get-data-job-dataset       1/1           74s        96s
get-data-job-model         1/1           20s        95s

ダウンロードしたデータを解凍

ダウンロードしたデータを解凍します。

$ ANNOTATIONS_PATH="${MOUNT_PATH}/annotations.tar.gz"
$ DATASET_PATH="${MOUNT_PATH}/images.tar.gz"
$ PRE_TRAINED_MODEL_PATH="${MOUNT_PATH}/faster_rcnn_resnet101_coco_2018_01_28.tar.gz"

ksonnetにパラメータを指定します。

$ ks param set decompress-data-job mountPath ${MOUNT_PATH}
$ ks param set decompress-data-job pvc ${PVC}
$ ks param set decompress-data-job pathToAnnotations ${ANNOTATIONS_PATH}
$ ks param set decompress-data-job pathToDataset ${DATASET_PATH}
$ ks param set decompress-data-job pathToModel ${PRE_TRAINED_MODEL_PATH}

パラメータの定義を確認します。

$ ks param list decompress-data-job

COMPONENT           PARAM             VALUE
=========           =====             =====
decompress-data-job mountPath         '/pets_data'
decompress-data-job name              'decompress-data-job'
decompress-data-job pathToAnnotations '/pets_data/annotations.tar.gz'
decompress-data-job pathToDataset     '/pets_data/images.tar.gz'
decompress-data-job pathToModel       '/pets_data/faster_rcnn_resnet101_coco_2018_01_28.tar.gz'
decompress-data-job pvc               'pets-pvc'

kubernetesクラスタに適応します。

$ ks apply ${ENV} -c decompress-data-job

INFO Applying jobs kubeflow.decompress-data-job-dataset
INFO Creating non-existent jobs kubeflow.decompress-data-job-dataset
INFO Applying jobs kubeflow.decompress-data-job-annotations
INFO Creating non-existent jobs kubeflow.decompress-data-job-annotations
INFO Applying jobs kubeflow.decompress-data-job-model
INFO Creating non-existent jobs kubeflow.decompress-data-job-model
$ kubectl get job -n kubeflow

NAME                              COMPLETIONS   DURATION   AGE
decompress-data-job-annotations   0/1           25s        25s
decompress-data-job-dataset       0/1           25s        25s
decompress-data-job-model         0/1           24s        24s
get-data-job-annotations          1/1           10s        12m
get-data-job-config               1/1           8s         12m
get-data-job-dataset              1/1           74s        12m
get-data-job-model                1/1           20s        12m

最終的に以下のように decompress-data-job のCOMPLETIONSが「1/1」と表示されれば、解凍完了です。

decompress-data-job-annotations   1/1           3m37s      16m
decompress-data-job-dataset       1/1           108s       16m
decompress-data-job-model         1/1           27s        16m

トレーニングに利用するTensorFlowペットレコードを作成

今回は TensorFlow Detection API を使用します、そこで使えるTFRecordフォーマットに変換する必要があります。

そのための create-pet-record-job を準備しています。このジョブを構成し、適応していきましょう。

変数定義を行います。

$ OBJ_DETECTION_IMAGE="lcastell/pets_object_detection"
$ DATA_DIR_PATH="${MOUNT_PATH}"
$ OUTPUT_DIR_PATH="${MOUNT_PATH}"

ksonnetにパラメータを指定します。

$ ks param set create-pet-record-job image ${OBJ_DETECTION_IMAGE}
$ ks param set create-pet-record-job dataDirPath ${DATA_DIR_PATH}
$ ks param set create-pet-record-job outputDirPath ${OUTPUT_DIR_PATH}
$ ks param set create-pet-record-job mountPath ${MOUNT_PATH}
$ ks param set create-pet-record-job pvc ${PVC}

パラメータの定義を確認します。

$ ks param list create-pet-record-job

COMPONENT             PARAM         VALUE
=========             =====         =====
create-pet-record-job dataDirPath   '/pets_data'
create-pet-record-job image         'lcastell/pets_object_detection'
create-pet-record-job mountPath     '/pets_data'
create-pet-record-job name          'create-pet-record-job'
create-pet-record-job outputDirPath '/pets_data'
create-pet-record-job pvc           'pets-pvc'

kubernetesクラスタに適応します。

$ ks apply ${ENV} -c create-pet-record-job

INFO Applying jobs kubeflow.create-pet-record-job
INFO Creating non-existent jobs kubeflow.create-pet-record-job

稼働状況を確認します。

$ kubectl get jobs -n kubeflow

NAME                              COMPLETIONS   DURATION   AGE
create-pet-record-job             0/1           47s        47s
decompress-data-job-annotations   1/1           3m37s      22m
decompress-data-job-dataset       1/1           108s       22m
decompress-data-job-model         1/1           27s        22m
get-data-job-annotations          1/1           10s        34m
get-data-job-config               1/1           8s         34m
get-data-job-dataset              1/1           74s        34m
get-data-job-model                1/1           20s        34m

COMPLETIONSが「1/1」となれば完了です。

create-pet-record-job   1/1   4m15s   4m15s

ここまででデータの準備ができました。

まとめ

マシンラーニング時に使用するデータをKubernetes上にジョブを投入・実行しダウンロードしました。 ダウンロード先の永続化領域(PVC)はデータを保管刷るタイミングで動的にプロビジョニングされました。 データの保管先を動的につくるために Dynamic Storage Provisioning を行うための Kubernetes のプラグインである Trident を使用しました。

ダウンロード後はトレーニング時に使えるようTFRecordフォーマットに変換しデータ準備を完了としました。 ここで作成した永続化領域、TFRecordは後続のワークフローで引き続き使用していく領域となります。

次からはトレーニングの実施をしていきます。