ホーム » TensorFlow

TensorFlow」カテゴリーアーカイブ

Kubeflow 1.0 : コンポーネント : TensorFlow 訓練 (TFJob)

Kubeflow 1.0 : コンポーネント : TensorFlow 訓練 (TFJob) (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/10/2020 (1.0)

* 本ページは、Kubeflow の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

コンポーネント : TensorFlow 訓練 (TFJob)

このページは TensorFlow で機械学習モデルを訓練するための TFJob を記述します。

 

TFJob とは何でしょう?

TFJob は Kubernetes 上で TensorFlow 訓練ジョブを実行するために利用できる Kubernetes カスタム・リソース です。TFJob の Kubeflow 実装は tf-operator にあります。

TFJob は下の一つのような YAML 表現を持つリソースです (貴方自身の訓練コードのためのコンテナ・イメージとコマンドを使用するために編集します) :

apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
  generateName: tfjob
  namespace: your-user-namespace
spec:
  tfReplicaSpecs:
    PS:
      replicas: 1
      restartPolicy: OnFailure
      template:
        spec:
          containers:
          - name: tensorflow
            image: gcr.io/your-project/your-image
            command:
              - python
              - -m
              - trainer.task
              - --batch_size=32
              - --training_steps=1000
    Worker:
      replicas: 3
      restartPolicy: OnFailure
      template:
        spec:
          containers:
          - name: tensorflow
            image: gcr.io/your-project/your-image
            command:
              - python
              - -m
              - trainer.task
              - --batch_size=32
              - --training_steps=1000

GKE ベースで Kubeflow インストールを行なったときに自動的に作成される GCP クレデンシャルのような、クレデンシャル secret への TFJob ポッド・アクセスを与えることを望む場合、このように secret をマウントして使用できます :

apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
  generateName: tfjob
  namespace: your-user-namespace
spec:
  tfReplicaSpecs:
    PS:
      replicas: 1
      restartPolicy: OnFailure
      template:
        spec:
          containers:
          - name: tensorflow
            image: gcr.io/your-project/your-image
            command:
              - python
              - -m
              - trainer.task
              - --batch_size=32
              - --training_steps=1000
            env:
            - name: GOOGLE_APPLICATION_CREDENTIALS
              value: "/etc/secrets/user-gcp-sa.json"
            volumeMounts:
            - name: sa
              mountPath: "/etc/secrets"
              readOnly: true
          volumes:
          - name: sa
            secret:
              secretName: user-gcp-sa
    Worker:
      replicas: 1
      restartPolicy: OnFailure
      template:
        spec:
          containers:
          - name: tensorflow
            image: gcr.io/your-project/your-image
            command:
              - python
              - -m
              - trainer.task
              - --batch_size=32
              - --training_steps=1000
            env:
            - name: GOOGLE_APPLICATION_CREDENTIALS
              value: "/etc/secrets/user-gcp-sa.json"
            volumeMounts:
            - name: sa
              mountPath: "/etc/secrets"
              readOnly: true
          volumes:
          - name: sa
            secret:
              secretName: user-gcp-sa

Kubernetes リソースに馴染みがないのであれば、ページ Understanding Kubernetes オブジェクト を参照してください。

TFJob を 組込みコントローラ と違わせるものは TFJob spec は 分散 TensorFlow 訓練ジョブ を管理するために設計されていることです。

分散 TensorFlow ジョブは典型的には以下のプロセスの 0 またそれ以上を含みます :

  • Chief : chief はモデルをチェックポイントするように訓練の編成とタスクの遂行の責任を負います。
  • Ps : ps はパラメータ・サーバです ; これらのサーバはモデルパラメータのための分散データストアを提供します。
  • Worker : ワーカーはモデルを訓練する実際の作業を行ないます。ある場合には、ワーカー 0 はまた chief として動作するかもしれません。
  • Evaluator : 評価器はモデルが訓練されるとき評価メトリクスを計算するために使用できます。

TFJob spec のフィールド tfReplicaSpecs は (上でリストされた) レプリカのタイプからそのレプリカのための TFReplicaSpec へのマップを含みます。TFReplicaSpec は 3 フィールドから成ります :

  • replicas この TFJob のために起動するこのタイプのレプリカ数。
  • template 各レプリカのために作成するポッドを記述する PodTemplateSpec
    • ポッドは tensorflow という名前のコンテナを含まなければなりません
  • restartPolicy ポッドがそれらが exit するとき再開始されるか否かを決定します。許可される値は次のようなものです :
    • Always はポッドが常に再開始されることを意味します。このポリシーはパラメータサーバのために良いです、何故ならばそれらは決して exit せずに failure イベントでは常に再開始されるべきであるためです。
    • OnFailure はポッドが failure により exit する場合に再開始されることを意味します。
      • 非ゼロは failure を示すコードです。
      • 0 の exit コードは成功を示しそしてポッドは再開始されません。
      • このポリシーは chief と worker のために良いです。
    • ExitCode は再開始動作は次のように tensorflow コンテナの exit コードに依拠していることを意味します。
      • Exit コード 0 はプロセスは成功的に完了し再開始されません。
      • 以下の exit コードはパーマネントエラーを示しそしてコンテナは再開始されません :
        • 1: 一般エラー
        • 2: misuse of shell builtins
        • 126: command invoked cannot execute
        • 127: command not found
        • 128: invalid argument to exit
        • 139: container terminated by SIGSEGV (invalid memory reference)
      • 以下の exit コードは再試行可能なエラーを示しそしてコンテナは再開始されます :
        • 130: container terminated by SIGINT (keyboard Control-C)
        • 137: container received a SIGKILL
        • 143: container received a SIGTERM
      • Exit コード 138 は SIGUSR1 に対応してユーザ指定再試行可能なエラーのために予約されています。
      • 他の exit コードは未定義で動作について保証はありません。

      exit コードのバックグラウンド情報については、termination シグナルへの GNU ガイドLinux ドキュメント・プロジェクト を見てください。

    • Never は停止するポッドは決して再開始されないことを意味します。このポリシーは滅多に使用されないはずです、何故ならば Kubernetes はどのような数の理由 (e.g. ノードが不安定になる) でもポッドを停止しそしてこれはジョブがリカバーされることを回避するからです。

 

クイックスタート

TensorFlow 訓練ジョブを投入する

Note: 訓練ジョブを投入する前に、kubeflow を貴方のクラスタに配備する べきでした。それを行なうことで訓練ジョブを投入するとき TFJob カスタム・リソース が利用可能であることを確かなものにします。

 

MNist サンプルを実行する

Kubeflow は単純な MNist モデル を実行するために適した サンプル とともに配布されています。

git clone https://github.com/kubeflow/tf-operator
cd tf-operator/examples/v1/mnist_with_summaries
# Deploy the event volume
kubectl apply -f tfevent-volume
# Submit the TFJob
kubectl apply -f tf_job_mnist.yaml

ジョブを監視する (下の詳細ガイド を見てください) :

kubectl -n kubeflow get tfjob mnist -o yaml

それを削除する :

kubectl -n kubeflow delete tfjob mnist

 

TFJob をカスタマイズする

典型的には TFJob yaml ファイルの次の値を変更できます :

  1. イメージを貴方のコードを含む docker イメージを指すように変更する
  2. レプリカの数とタイプを変更する
  3. 各リソースに割り当てられたリソース (リクエストと制限) を変更する
  4. 任意の環境変数を設定する
    • 例えば、GCS or S3 のようなデータストアと通信するために様々な環境変数を configure する必要があるかもしれません
  5. ストレージのために PV を使用することを望む場合 PV を装着する。

 

GPU を使用する

貴方のクラスタは GPU を利用するために configure されなければなりません。

GPU を装着するには GPU を含むべきレプリカのコンテナ上の GPU リソースを指定します ; 例えば :

apiVersion: "kubeflow.org/v1"
kind: "TFJob"
metadata:
  name: "tf-smoke-gpu"
spec:
  tfReplicaSpecs:
    PS:
      replicas: 1
      template:
        metadata:
          creationTimestamp: null
        spec:
          containers:
          - args:
            - python
            - tf_cnn_benchmarks.py
            - --batch_size=32
            - --model=resnet50
            - --variable_update=parameter_server
            - --flush_stdout=true
            - --num_gpus=1
            - --local_parameter_device=cpu
            - --device=cpu
            - --data_format=NHWC
            image: gcr.io/kubeflow/tf-benchmarks-cpu:v20171202-bdab599-dirty-284af3
            name: tensorflow
            ports:
            - containerPort: 2222
              name: tfjob-port
            resources:
              limits:
                cpu: '1'
            workingDir: /opt/tf-benchmarks/scripts/tf_cnn_benchmarks
          restartPolicy: OnFailure
    Worker:
      replicas: 1
      template:
        metadata:
          creationTimestamp: null
        spec:
          containers:
          - args:
            - python
            - tf_cnn_benchmarks.py
            - --batch_size=32
            - --model=resnet50
            - --variable_update=parameter_server
            - --flush_stdout=true
            - --num_gpus=1
            - --local_parameter_device=cpu
            - --device=gpu
            - --data_format=NHWC
            image: gcr.io/kubeflow/tf-benchmarks-gpu:v20171202-bdab599-dirty-284af3
            name: tensorflow
            ports:
            - containerPort: 2222
              name: tfjob-port
            resources:
              limits:
                nvidia.com/gpu: 1
            workingDir: /opt/tf-benchmarks/scripts/tf_cnn_benchmarks
          restartPolicy: OnFailure

GPU を利用するための TensorFlow の 手順 に従ってください。

 

貴方のジョブを監視する

貴方のジョブのステータスを得るには :

kubectl get -o yaml tfjobs ${JOB}

ここのサンプル・ジョブのためのサンプル出力があります :

apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"kubeflow.org/v1","kind":"TFJob","metadata":{"annotations":{},"name":"mnist","namespace":"kubeflow"},"spec":{"cleanPodPolicy":"None","tfReplicaSpecs":{"Worker":{"replicas":1,"restartPolicy":"Never","template":{"spec":{"containers":[{"command":["python","/var/tf_mnist/mnist_with_summaries.py","--log_dir=/train","--learning_rate=0.01","--batch_size=150"],"image":"gcr.io/kubeflow-ci/tf-mnist-with-summaries:1.0","name":"tensorflow","volumeMounts":[{"mountPath":"/train","name":"training"}]}],"volumes":[{"name":"training","persistentVolumeClaim":{"claimName":"tfevent-volume"}}]}}}}}}
  creationTimestamp: "2019-07-16T02:44:38Z"
  generation: 1
  name: mnist
  namespace: your-user-namespace
  resourceVersion: "10429537"
  selfLink: /apis/kubeflow.org/v1/namespaces/kubeflow/tfjobs/mnist
  uid: a77b9fb4-a773-11e9-91fe-42010a960094
spec:
  cleanPodPolicy: None
  tfReplicaSpecs:
    Worker:
      replicas: 1
      restartPolicy: Never
      template:
        spec:
          containers:
          - command:
            - python
            - /var/tf_mnist/mnist_with_summaries.py
            - --log_dir=/train
            - --learning_rate=0.01
            - --batch_size=150
            image: gcr.io/kubeflow-ci/tf-mnist-with-summaries:1.0
            name: tensorflow
            volumeMounts:
            - mountPath: /train
              name: training
          volumes:
          - name: training
            persistentVolumeClaim:
              claimName: tfevent-volume
status:
  completionTime: "2019-07-16T02:45:23Z"
  conditions:
  - lastTransitionTime: "2019-07-16T02:44:38Z"
    lastUpdateTime: "2019-07-16T02:44:38Z"
    message: TFJob mnist is created.
    reason: TFJobCreated
    status: "True"
    type: Created
  - lastTransitionTime: "2019-07-16T02:45:20Z"
    lastUpdateTime: "2019-07-16T02:45:20Z"
    message: TFJob mnist is running.
    reason: TFJobRunning
    status: "True"
    type: Running
  replicaStatuses:
    Worker:
      running: 1
  startTime: "2019-07-16T02:44:38Z"

 

条件

TFJob は TFJobStatus を持ち、これは TFJobConditions の配列を持ちます、それを通して TFJob は通過または非通過しました。TFJobCondition の各要素は 6 つの可能なフィールドを持ちます :

  • lastUpdateTime フィールドはこの条件が更新された最後の時間を提供します。
  • lastTransitionTime フィールドは条件が一つのステータスから他に遷移した最後の時間を提供します。
  • message フィールドは遷移についての詳細を示す可読なメッセージです。
  • reason フィールドは条件の最後の遷移のための一意、1 単語、CamelCase な理由です。
  • status フィールドは可能な値 “True”, “False” と “Unknown” を持つ文字列です。
  • type フィールドは以下の可能な値を持つ文字列です :
    • TFJobCreated は tfjob がシステムにより受け取られたことを意味しますが、ポッド/サービスの一つまたはそれ以上がまだ開始されていません。
    • TFJobRunning はこの TFJob の総てのサブリソース (e.g. サービス/ポッド) が成功的にスケジュールされて起動されてジョブが実行されていることを意味します。
    • TFJobRestarting はこの TFJob の一つまたはそれ以上のサブリソース (e.g. サービス/ポッド) が問題を持ち再開始されていることを意味します。
    • TFJobSucceeded はジョブが成功的に完了したことを意味します。
    • TFJobFailed はジョブが失敗したことを意味します。

ジョブの成功と失敗は次のように決定されます :

  • ジョブが chief 成功か失敗を持つかは chief のステータスにより決定されます。
  • ジョブが chief でない成功か失敗を持つかはワーカーにより決定されます。
  • 両者の場合、TFJob は監視されているプロセスが exit コード 0 で抜ける場合に成功します。
  • 非ゼロ exit コードの場合動作はレプリカのための restartPolicy により決定されます。
  • restartPolicy が再開始を許容する場合にはプロセスは単に再開始されて TFJob は実行し続けます。
    • restartPolicy ExitCode については動作は exit コード依存です。
    • restartPolicy が再開始を許容しない場合、非ゼロ exit コードはパーマネント失敗と考えられてジョブは失敗とマークされます。

 

tfReplicaStatuses

tfReplicaStatuses は与えられた状態にある各レプリカのためのポッドの数を示すマップを提供します。3 つの可能な状態があります :

  • Active は現在実行中のポッドの数です。
  • Succeeded は成功的に完了したポッドの数です。
  • Failed はエラーで完了したポッドの数です。

 

イベント

実行の間、TFJob はポッドとサービスの作成/削除のような発生したことを示すためにイベントを出力します。Kubernetes はデフォルトでは 1 時間よりも古いイベントは保持しません。ジョブのための最近のイベントを見るには次を実行します :

kubectl describe tfjobs ${JOB}

これは次のような出力を生成します :

Name:         mnist
Namespace:    kubeflow
Labels:       
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"kubeflow.org/v1","kind":"TFJob","metadata":{"annotations":{},"name":"mnist","namespace":"kubeflow"},"spec":{"cleanPodPolicy...
API Version:  kubeflow.org/v1
Kind:         TFJob
Metadata:
  Creation Timestamp:  2019-07-16T02:44:38Z
  Generation:          1
  Resource Version:    10429537
  Self Link:           /apis/kubeflow.org/v1/namespaces/kubeflow/tfjobs/mnist
  UID:                 a77b9fb4-a773-11e9-91fe-42010a960094
Spec:
  Clean Pod Policy:  None
  Tf Replica Specs:
    Worker:
      Replicas:        1
      Restart Policy:  Never
      Template:
        Spec:
          Containers:
            Command:
              python
              /var/tf_mnist/mnist_with_summaries.py
              --log_dir=/train
              --learning_rate=0.01
              --batch_size=150
            Image:  gcr.io/kubeflow-ci/tf-mnist-with-summaries:1.0
            Name:   tensorflow
            Volume Mounts:
              Mount Path:  /train
              Name:        training
          Volumes:
            Name:  training
            Persistent Volume Claim:
              Claim Name:  tfevent-volume
Status:
  Completion Time:  2019-07-16T02:45:23Z
  Conditions:
    Last Transition Time:  2019-07-16T02:44:38Z
    Last Update Time:      2019-07-16T02:44:38Z
    Message:               TFJob mnist is created.
    Reason:                TFJobCreated
    Status:                True
    Type:                  Created
    Last Transition Time:  2019-07-16T02:45:20Z
    Last Update Time:      2019-07-16T02:45:20Z
    Message:               TFJob mnist is running.
    Reason:                TFJobRunning
    Status:                True
    Type:                  Running
  Replica Statuses:
    Worker:
      Running:  1
  Start Time:  2019-07-16T02:44:38Z
Events:
  Type    Reason                   Age    From         Message
  ----    ------                   ----   ----         -------
  Normal  SuccessfulCreatePod      8m6s   tf-operator  Created pod: mnist-worker-0
  Normal  SuccessfulCreateService  8m6s   tf-operator  Created service: mnist-worker-0

ここでイベントはポッドとサービスが成功的に作成されたことを示します。

 

TensorFlow ログ

ロギングは標準的な K8s ロギング実践に従います。

削除 されなかった任意のポッドのための標準的な出力/エラーを得るために kubectl を使用できます。

最初に関心のあるレプリカのためのジョブ・コントローラにより作成されたポッドを見つけます。ポッドは次のように名前付けられます :

${JOBNAME}-${REPLICA-TYPE}-${INDEX}

ひとたびポッドを特定すれば、kubectl を使用してログを得ることができます。

kubectl logs ${PODNAME}

TFJob spec の CleanPodPolicy はジョブが停止したときポッドの削除を制御します。ポリシーは以下の値の一つであり得ます :

  • Running はジョブが完了したとき依然として実行しているポッド (e.g. パラメータサーバ) だけが直ちに削除されます ; 完了したポッドはログが保存されるように削除されません。これはデフォルト値です。
  • All ポリシーはジョブが終了したとき総てのポッド、完了したポッドさえも直ちに削除されることを意味します。
  • None ポリシーはジョブが完了したときポッドは削除されないことを意味します。

貴方のクラスタが Kubernetes クラスタ・ロギング を活用するならばログも更なる解析のために適切なデータストアに送られるかもしれません。

 

GKE 上の Stackdriver

Stackdriver を使用してログを得る上での手順については ロギングと監視 へのガイドを見てください。

ロギングと監視 へのガイドで説明されているように、ポッドラベルに基づいて特定のレプリカのためのログを取得することができます。

Stackdriver UI を使用して次のような問い合わせを使用できます :

resource.type="k8s_container"
resource.labels.cluster_name="${CLUSTER}"
metadata.userLabels.tf_job_name="${JOB_NAME}"
metadata.userLabels.tf-replica-type="${TYPE}"
metadata.userLabels.tf-replica-index="${INDEX}"

代わりに gcloud を使用して :

QUERY="resource.type=\"k8s_container\" "
QUERY="${QUERY} resource.labels.cluster_name=\"${CLUSTER}\" "
QUERY="${QUERY} metadata.userLabels.tf_job_name=\"${JOB_NAME}\" "
QUERY="${QUERY} metadata.userLabels.tf-replica-type=\"${TYPE}\" "
QUERY="${QUERY} metadata.userLabels.tf-replica-index=\"${INDEX}\" "
gcloud --project=${PROJECT} logging read  \
     --freshness=24h \
     --order asc  ${QUERY}

 

トラブルシューティング

ここに貴方のジョブのトラブルシュートをするために従うべき幾つかのステップがあります。

  1. ステータスは貴方のジョブのために存在していますか? 次のコマンドを実行してください :
    kubectl -n ${USER_NAMESPACE} get tfjobs -o yaml ${JOB_NAME}
    
    • USER_NAMESPACE は貴方のユーザプロフィールのために作成された名前空間です。
    • 結果としての出力が貴方のジョブのためのステータスを含まない場合、これは典型的にはジョブ spec が不正であることを示しています。
    • TFJob spec が不正であれば tf operator ログにログメッセージがあるはずです :
        kubectl -n ${KUBEFLOW_NAMESPACE} logs `kubectl get pods --selector=name=tf-job-operator -o jsonpath='{.items[0].metadata.name}'` 
        

      • KUBEFLOW_NAMESPACE は TFJob 演算子を配備した名前空間です。

     

  2. ポッドが作成されたかを見るために貴方のジョブのためのイベントを確認します。
    • イベントを得るために幾つかの方法があります ; ジョブが 1 時間未満であれば次を行なうことができます :
        kubectl -n ${USER_NAMESPACE} describe tfjobs -o yaml ${JOB_NAME}
        
    • 出力のボトムはジョブにより出力されたイベントのリストを含むはずです ; e.g.
      Events:
      Type     Reason                          Age                From         Message
      ----     ------                          ----               ----         -------
      Warning  SettedPodTemplateRestartPolicy  19s (x2 over 19s)  tf-operator  Restart policy in pod template will be overwritten by restart policy in replica spec
      Normal   SuccessfulCreatePod             19s                tf-operator  Created pod: tfjob2-worker-0
      Normal   SuccessfulCreateService         19s                tf-operator  Created service: tfjob2-worker-0
      Normal   SuccessfulCreatePod             19s                tf-operator  Created pod: tfjob2-ps-0
      Normal   SuccessfulCreateService         19s                tf-operator  Created service: tfjob2-ps-0
      
    • Kubernetes は 1 時間 の間イベントを保存するだけです (kubernetes/kubernetes#52521 参照)
      • 貴方のクラスタ・セットアップに依拠してイベントは外部ストレージに存続してより長い期間アクセス可能かもしれません。
      • GKE 上ではイベントは stackdriver で存続されて前のセクションの手順を使用してアクセスできます。
    • ポッドとサービスが作成されなければこれは TFJob が処理されていないことを提示します ; 一般的な原因は :
      • TFJob spec が不正である (上を見てください)
      • TFJob 演算子が動作していない

     

  3. ポッドのためのイベントをそれらがスケジュールされたかを確実にするためにクリックします。
    • イベントを得るために幾つかの方法があります ; ポッドが 1 時間未満であれば次を行なうことができます :
        kubectl -n ${USER_NAMESPACE} describe pods ${POD_NAME}
        
    • 出力のボトムは次のようなイベントを含むはずです :
      Events:
      Type    Reason                 Age   From                                                  Message
      ----    ------                 ----  ----                                                  -------
      Normal  Scheduled              18s   default-scheduler                                     Successfully assigned tfjob2-ps-0 to gke-jl-kf-v0-2-2-default-pool-347936c1-1qkt
      Normal  SuccessfulMountVolume  17s   kubelet, gke-jl-kf-v0-2-2-default-pool-347936c1-1qkt  MountVolume.SetUp succeeded for volume "default-token-h8rnv"
      Normal  Pulled                 17s   kubelet, gke-jl-kf-v0-2-2-default-pool-347936c1-1qkt  Container image "gcr.io/kubeflow/tf-benchmarks-cpu:v20171202-bdab599-dirty-284af3" already present on machine
      Normal  Created                17s   kubelet, gke-jl-kf-v0-2-2-default-pool-347936c1-1qkt  Created container
      Normal  Started                16s   kubelet, gke-jl-kf-v0-2-2-default-pool-347936c1-1qkt  Started container
      
    • コンテナを開始から妨げられる幾つかの一般的な問題は :
      • ポッドをスケジュールするために不十分なリソース
      • ポッドが存在しないか利用不可能なボリューム (or secret) をマウントしようとする
      • docker イメージが存在しないかアクセスできない (e.g. パーミッション問題により)
  4. コンテナが開始される場合 ; 前のセクションの手順に従ってコンテナのログを確認してください。
 

以上






TensorFlow 1.15.0 リリースノート

TensorFlow 1.15.0 リリースノート (翻訳)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 10/09/2019

* 本ページは、github TensorFlow の releases の TensorFlow 1.15.0 を翻訳したものです:

 

Release 1.15.0

これは TensorFlow のための最後の 1.x リリースです。特徴を伴う 1.x ブランチを更新する予定はありません、少なくとも 1 年間は脆弱性を修正するためのパッチリリースは発行するつもりですが。

 

主要な特徴と改良

  • アナウンス されたように、現在 GPU サポートを持つプラットフォーム (Linux と Windows) に対しては tensorflow pip パッケージはデフォルトで (今は tensorflow-gpu と同じ) GPU サポートを含みます。それは Nvidia GPU を持つあるいは持たないマシン上で動作します。tensorflow-gpu は依然として利用可能で、そして CPU-only パッケージはパッケージサイズについて心配しているユーザのために tensorflow-cpu でダウンロード可能です。
  • TensorFlow 1.15 は compat.v2 モジュールで 2.0 API の完全な実装を含みます。それは compat.v1 モジュールで (contrib なしの) 1.15 メインモジュールのコピーを含みます。TensorFlow 1.15 は enable_v2_behavior() 関数を使用して 2.0 の動作をエミュレートできます。これは前方互換コードを書くことを可能にします : tensorflow.compat.v1 か tensorflow.compat.v2 を明示的にインポートすることにより、貴方のコードが 1.15 か 2.0 のインストールに対して変更なしに動作することを保証できます。
  • EagerTensor は今では tensor のために numpy buffer インターフェイスをサポートします。
  • v2 制御フローを有効/無効にするためにトグル tf.enable_control_flow_v2() と tf.disable_control_flow_v2() を追加します。
  • tf.enable_v2_behavior() と TF2_BEHAVIOR=1 の一部として v2 制御フローを有効にします。
  • AutoGraph は Python 制御フローを TensorFlow 式に変換して、ユーザに tf.function-decorate された関数内で通常の Python を書くことを可能にします。AutoGraph はまた tf.data, tf.distribute と tf.keras API で使用される関数内にも適用されます。
  • enable_tensor_equality() を追加します、これは次のように動作を切り替えます :
    • Tensor はもはや hashable ではありません。
    • Tensor は == と != で比較可能です、element-wise な比較結果を持つ Boolean Tensor を生成します。これは 2.0 のデフォルトの動作です。
  • Auto Mixed-Precision グラフ optimizer は Volta と Turing Tensor Core 上の高速化のためにモデルを float16 に変換することを単純化します。この特徴は tf.train.experimental.enable_mixed_precision_graph_rewrite() で optimizer クラスをラップすることにより有効にできます。
  • 環境変数 TF_CUDNN_DETERMINISTIC を追加します。”true” or “1” に設定することは決定論的 cuDNN convolution と max-pooling アルゴリズムの選択を強制します。これが有効にされるとき、アルゴリズム選択手続き自身もまた決定論的です。
  • TensorRT
    • TF 2.0 のための準備で TensorRT conversion ソースを contrib から compiler ディレクトリに移行します。
    • TensorRT conversion のために追加の、ユーザ・フレンドリーな TrtGraphConverter API を追加します。
    • TensorRT conversion の TensorFlow 演算子のためのサポートを拡張します (e.g. Gather, Slice, Pack, Unpack, ArgMin, ArgMax,DepthSpaceShuffle)。
    • TensorRT conversion の TensorFlow 演算子 CombinedNonMaxSuppression をサポートします、これはオブジェクト検出モデルを本質的に高速化します。

 

互換性を損なう変更

  • Tensorflow コードは今では 2 つの異なる pip パッケージを生成します : tensorflow_core は総てのコードを含み (将来的にはそれは private 実装だけを含むでしょう) そして tensorflow は仮想 pip パッケージで tensorflow_core への forwarding を行ないます (そして将来的には tensorflow の public API だけを含むでしょう)。私たちはこれを breaking であるとは想定していません、実装から直接的にインポートしていなかった場合には。
  • TensorFlow 1.15 は Ubuntu 16 上 devtoolset7 (GCC7) を使用してビルドされています。これは TensorFlow のより早期のバージョンに対してビルドされた拡張との ABI 非互換性に繋がるかもしれません。
  • ResourceVariable で constraint= と .constraint の使用は deprecated しました。
  • tf.keras :
    • デフォルト Keras config で OMP_NUM_THREADS はもはや使用されません。スレッド数を configure するには、tf.config.threading API を使用します。
    • tf.keras.model.save_model と model.save は今では TensorFlow SavedModel をセーブすることがデフォルトです。
    • keras.backend.resize_images (そして結果的に、keras.layers.Upsampling2D) の動作は変わりました、resizing 実装のバグは修正されました。
    • Layer は今では float32 がデフォルトで、それらの入力を自動的に Layer の dtype にキャストします。float64 を使用したモデルを持っていた場合、それは TensorFlow 2 では多分静かに float32 を使用します、そして ‘Layer “layer-name” is casting an input tensor from dtype float64 to the layer’s dtype of float32’ で始まる警告が発行されます。修正するには、tf.keras.backend.set_floatx(‘float64′) でデフォルト dtype を float64 に設定するか、Layer コンストラクタの各々に dtype=’float64’ を渡します。より多くの情報は tf.keras.layers.Layer を見てください。
    • 幾つかの tf.assert_* メソッドは今では演算作成時 (i.e. この Python 行が実行されるとき) に assertion を上げます、入力 tensor の値が (session.run() の間ではなく) その時に知られる場合には。これが起きるとき、noop が返されて入力 tensor は non-feedable としてマークされます。換言すれば、それらが session.run() への feed_dict 引数でキーとして使用される場合、エラーが上げられます。また、幾つかの assert ops はそれをグラフ内に行ないませんので、グラフ構造が変わります。異なるグラフはそれらが明示的に与えられない (殆どの場合) とき異なる per-op ランダム seed という結果になる可能性があります。

 

バグ修正とその他の変更

(訳注 : 翻訳を省略します、必要であれば 原文 を参照してください。)

 
以上

TensorFlow 1.14.0 リリースノート

TensorFlow 1.14.0 リリースノート (翻訳)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/19/2019

* 本ページは、github TensorFlow の releases の TensorFlow 1.14.0 を翻訳したものです:

 

主要な特徴と改良

  • これは compat.v2 モジュールを含む最初の 1.x リリースです。このモジュールはライブラリに 1.x と 2.x の両者で動作するコードを公開することを可能にするために必要です。このリリースの後、2.0 Python API においてどのような後方非互換な変更も許容されません。
  • デフォルトで MKL-DNN contraction カーネルを有効にします。MKL-DNN は CPU ベクトルアーキテクチャに基づきベストなカーネル実装をディスパッチします。それらを無効にするには、–define=tensorflow_mkldnn_contraction_kernel=0 でビルドします。
  • 非 Windows システム・ライブラリは今ではバージョン付けされます。これは殆どのユーザに対しては no-op であるはずです、何故ならばそれはシステムパッケージ保守者か TensorFlow への拡張を構築する人にだけ影響を与えるからです。
    • Python wheels (Pip パッケージ) は一つのライブラリファイルを含みます。
      • Linux: libtensorflow_framework.so.1
      • MacOS: libtensorflow_framework.1.dylib
    • libtensorflow tarball アーカイブは libtensorflow と 2 つの symlink を含みます。MacOS .dylib ライブラリも同じですが、MacOS ライブラリ命名要件に適合します (i.e. libtensorflow.1.dylib) :
      • libtensorflow.so.1.14.0, 主ライブラリ
      • libtensorflow.so.1, 主ライブラリへのシンボリックシンク
      • libtensorflow.so, .so.1 へのシンボリックリンク

 

動作変更

  • 分散ストラテジーとカスタム訓練ループによる loss スケーリングの信頼性を改良するためにデフォルト loss reduction を AUTO として設定します。AUTO は reduction オプションが使用コンテキストにより決定されることを示しています。 殆ど総ての場合これは SUM_OVER_BATCH_SIZE がデフォルトになります。分散ストラテジースコープ内で使用されるとき、tf.keras compile と fit のような組み込み訓練ループの外では、reduction 値として ‘None’ か ‘SUM’ を想定します。他の値の使用はエラーを上げます。
  • v2 Loss クラスのインスタンスでない、compile API に渡される損失 (文字列か v1 損失) を LossWrapper にラップします。=> 総ての損失は今ではデフォルトとして SUM_OVER_BATCH_SIZE reduction を使用します。
  • add_metric か add_loss を使用してモデルに追加されたシンボリック tensor がある場合 run_eagerly と分散ストラテジーを無効にします。
  • tf.linspace(start, stop, num) は今では最後の値として常に “stop” を使用します (for num > 1)。
  • axis=None と batch_dims<0 のとき tf.gather の挙動は今では正しいです。
  • オブジェクトが既に存在していないのであれば GCS ディレクトリ・オブジェクトを作成するだけです。
  • map_vectorization 最適化で、ベクトル化された map ノードで並列化の次数を削減します。
  • バグ修正: tf.distribute.Strategy を使用するとき損失と勾配はグローバルバッチサイズに関して今ではより確実に正しくスケールされるはずです。
  • コサイン類似度損失を更新します – コサイン類似度から negate sign を除去しました。
  • DType はもはや int に変換可能ではありません。同じ結果を得るために int(dtype) の代わりに dtype.as_datatype_enum を使用してください。
  • for TPU embeddings のための勾配蓄積のためのデフォルトを true に変更しました。
  • deferred build モデルが使用されるとき今では eager モードで callbacks は値を記録します。
  • :pooling_ops への過渡的な依存性は除去されました。あるユーザはそのライブラリから演算子を参照する場合 :pooling_ops への明示的な依存性を追加する必要があるかもしれません。

 

バグ修正とその他の変更

  • ドキュメント
  • Deprecations とシンボル名変更。
    • 使用されていない StringViewVariantWrapper を除去します。
    • 使用されていない Fingerprint64Map op 登録を削除します。
    • SignatureDef util 関数は deprecated になりました。
    • 余分なところでは重複する “image” を除去するために tf.image 関数を名前変更しました
    • tf.keras.experimental.export は tf.keras.experimental.export_saved_model に名前変更されました
    • LayerNormalization API を args norm_axis と params_axis を axis で置き換えることにより標準化しました。
    • Tensor::UnsafeCopyFromInternal は Tensor::BitcastFrom のために deprecated です。
  • Keras & Python API
    • 以下のための v2 モジュールエイリアスを追加します :
      • tf.initializers => tf.keras.initializers
      • tf.losses => tf.keras.losses & tf.metrics => tf.keras.metrics
      • tf.optimizers => tf.keras.optimizers
    • tf.keras.layers.AbstractRNNCell を TF v2 のための RNN セルの好ましい実装として追加します。ユーザはカスタム動作を持つ RNN セルを実装するためにそれを使用できます。
    • eager におけるカスタム訓練ループの forward パスの最後に損失をクリアできるように clear_losses API を追加しています。
    • Keras compile でリストのリストを metrics param に渡すためのサポートを追加
    • precision への top-k と keras metrics への recall を追加しました。
    • cumsum と cumprod keras backend 関数のための public API を追加しています。
    • 修正: model.add_loss(symbolic_tensor) は eager 回りで動作するべきです。
    • tf.string_split と tf.strings_split に name 引数を追加します。
    • tf.keras.experimental.export を使用する Keras からエクスポートされた SavedModels へのマイナー変更。(評価モードのための SignatureDef キーは今では “test” の代わりに “eval” です)。これは近い将来に “test” に戻されるでしょう。
    • 入力が確率であるときの Keras のバイナリ交差エントロピーロジックを更新します。確率をロジットに変換する代わりに、確率のための交差エントロピー式を使用しています。
    • 生 TensorFlow 関数は今ではモデル作成の間に Keras Functional API と連動して使用できます。これは Functional API を使用するときユーザに殆どの場合 Lambda 層を作成する必要を除去します。Lambda 層のように、Variable 作成の結果になったり ops を割り当てる TensorFlow 関数はサポートされません。
    • Keras 訓練と検証カーブは同じプロット上に表示されます。
    • Layer と Model に dynamic constructor 引数を導入します、これは call メソッドで命令的な制御フローを使用しているときに True に設定されるべきです。
    • initializers のコンストラクタの dtype と call の partition_info を除去しています。
  • 新しい ops と改良された op 機能
    • 幾つかのステートレス map のために OpKernels を追加します。
    • AUCCurve と AUCSummationMethod enums のための v2 API を追加します。#tf-metrics-convergence
    • tf.math.nextafter op を追加します。
    • CompositeTensor 基底クラスを追加します。
    • tf.linalg.tridiagonal_solve op を追加します。
    • 共通テーブル演算のための opkernel テンプレートを追加します。
    • TensorFlow 2.0 の TFLite のためのサポートを追加しました。
    • グラフと profile 情報を集めるために summary trace API を追加します。
    • tf.gather に batch_dims 引数を追加します。
    • グラフ関数モードで add_metric のためのサポートを追加します。
    • BatchMatMulV2 のための C++ Gradient を追加します。
    • tf.random.binomial を追加しました。
    • SparseToDense op のための勾配を追加しました。
    • legacy string flat hash map op カーネルを追加します。
    • ragged size op を追加してそれを op ディスパッチャーに登録します。
    • tf.matmul にブロードキャスティング・サポートを追加します。
    • tf.einsum() のための ellipsis (…) サポートを追加します。
    • LinearOperator.adjoint と LinearOperator.H (エイリアス) を追加しました。
    • tf.linalg.tridiagonal_solve の GPU 実装を追加しました。
    • strings.byte_split を追加しました。
    • RaggedTensor.placeholder() を追加します。
    • 新しい “result_type” パラメータを tf.strings.split に追加します。
    • run_eagerly=True で compile された Model の層の上で trainable=False を設定するとき update を無効にするサポートをするために add_update に zero-arg callable を渡すことができます。
    • absl::string_view のために variant wrapper を追加します。
    • 総ての nest.* メソッドに expand_composites 引数を追加します。
    • Squeeze のために pfor converter を追加します。
    • tf.tile 勾配のためのバグ修正。
    • コアで CriticalSection を tf.CriticalSection として公開します。
    • aliases を使用するために Fingerprint64Map を更新します。
    • gather_nd のために ResourceVariable サポート。
    • ResourceVariable の gather op はバッチ次元をサポートします。
    • CPU で Variadic reduce がサポートされます。
    • (SparseTensor と RaggedTensor のような) CompositeTensors のための基本的なサポートによって tf.function を拡張します。
    • 検索テーブルを作成するためのテンプレートとインターフェイスを追加します。
    • 訓練後量子化ツールは複数の演算により共有される重みの量子化をサポートします。このツールのバージョンで作成されたモデルは重みのために INT8 型を使用してこのバージョン以降からのインタープリタでのみ実行可能です。
    • 不正な形式の gif 画像はフレームのカラーパレットで境界外へのアクセスという結果になります。今ではこれは修正されました。
    • image.resize は今では正しいピクセル中心を考慮して新しいカーネル (incl. anti-aliasing) を用います。
  • パフォーマンス
    • デフォルトで MKL-DNN contraction を有効にします。MKL-DNN は CPU ベクトル・アーキテクチャに基づいてベストなカーネル実装を動的にディスパッチします。それらを無効にするには、–define=tensorflow_mkldnn_contraction_kernel=0 でビルドします。
    • 分散ストラテジーのマルチホスト ncclAllReduce のためのサポート。
    • スレッド数が Python ベンチマーク渡り変化することを可能にするフラグを公開します。
  • TensorFlow 2.0 開発
    • v2 スパース・カテゴリカル交差エントロピーを追加します。
    • v2 損失を通して非 Tensor を許容します。
    • tf2.0 のための新しい GRU 実装として UnifiedGRU を追加します。2.0 では GRU のためのデフォルトの recurrent 活性化関数を ‘hard_sigmoid’ から ‘sigmoid’ へ、そして ‘reset_after’ を True に変更します。歴史的には recurrent 活性は ‘hard_sigmoid’ です、何故ならばそれは ‘sigmoid’ よりも高速だからです。CPU と GPU モード間の新しい unified backend では、CuDNN カーネルが sigmoid を使用していますので、CPU モードのためのデフォルトもまた sigmoid に変更します。それにより、デフォルト GRU は CPU と GPU カーネルの両者と互換になります。これは GPU を持つユーザにデフォルトで CuDNN カーネルを使用して訓練で 10x パフォーマンス・ブーストを可能にします。これはチェックポイントの互換性を損なう変更であることに注意してください。ユーザが 1.x 事前訓練チェックポイントを使用することを望む場合、1.x 動作に落としこむために層を GRU(recurrent_activation=’hard_sigmoid’, reset_after=False) で構築してください。
    • TF 2.0 – ユーザが compile で与えたものを常に反映するようにメトリック名を更新します。次のケースに影響します 1. 名前が ‘accuracy’/’crossentropy’ として与えられるとき 2. エイリアスされた関数名が使用されるとき e.g. ‘mse’ 3. 重み付けられたメトリック名から重み付けられた prefix を除去している
    • C Eager API のために Go ラッパーを追加し始めました
    • 2.0 で image.resize は今では新しい resize カーネルのための勾配をサポートします。
    • v2 API から tf.string_split を除去しました
    • tf.io で tf.contrib.proto.* ops を公開します (それらは TF2 で存在します)
    • 2.0 で TFLiteConverter API を更新します。from_concrete_function から from_concrete_functions に変更します。
    • eager モードで tf.distribute.experimental.MultiWorkerMirroredStrategy が動作することを可能にします。
    • v2 hinge と squared hinge 損失でバイナリと -1/1 ラベル入力の両者をサポートします。
  • TensorFlow Lite
    • 2.0 で tflite_convert のためのサポートを追加します。
    • 2.0 API から lite.OpHint, lite.experimental と lite.constant を除去します。
  • tf.contrib
    • https://arxiv.org/abs/1807.08518 で記述されているような Neural Turing 実装を追加しました。
    • TF distributions 上の tf.contrib.timeseries 依存性を除去します。
  • tf.data
    • Add num_parallel_reads and passing in a Dataset containing filenames into TextLineDataset and FixedLengthRecordDataset
    • Going forward we operate in TF 2.0, this change is part of the effort to slowly converting XYZDataset to DatasetV2 type which is the official version going to be used in TF 2.0 and motivated by some compatibility issue found, _BigtableXYZDataset (of type DatasetV2) does not implement the _as_variant_tensor() of DatasetV1, when moving contrib.bigtable to tensorflow_io. DatasetV2 への変換は TF 2.0 へ移行している一方で V1 を維持するためのオーバーヘッドを除去します。
    • dataset ops を python Dataset オブジェクト作成の間に (それを iterator 作成時に行なう代わりに) グラフに追加します (または Eager execution でカーネルを作成します)。
    • TensorArrays のためのサポートを tf.data Dataset に追加します。
    • tf.data 関数を defun を使用するように切り替え、legacy Defun を使用し続けるための非常口を提供します。
  • Toolchains
    • CUDNN_INSTALL_PATH, TENSORRT_INSTALL_PATH, NCCL_INSTALL_PATH, NCCL_HDR_PATH は deprecated です。
    • 代わりに TF_CUDA_PATHS を使用します、これは CUDA ライブラリとヘッダを見つけるために検索されるベースパスのカンマ区切りリストをサポートします。
    • TF コードは今では tensorflow_core に存在して tensorflow は単なる virtual pip パッケージです。TensorFlow を使用するプロジェクトのためにコード変更は必要ではありません、変更は透過です。
  • XLA
    • XLA HLO グラフは今では interactive_graphviz ツールで調査できます。
  • Estimator
    • tf.estimator.inputs の代わりに tf.compat.v1.estimator.inputs を使用します。
    • early_stopping.py の総ての API のために contrib 参照を tf.estimator.experimental.* で置き換えます。
    • TPUEstimator or DistributionStrategy のための –iterations_per_loop の「正しい」値を決定することはユーザのために課題であり続けます。ユーザ target TPU 実行時間に基づいて –iterations_per_loop 変数を動的に調整することを提案します、特に訓練モードで TPUEstimator を使用するためには。ユーザは –iterations_per_loop=300s のような値を指定するかもしれません、これはホスト側演算間の TPU 上で消費されるのがおおよそ 300 秒という結果になります。

 
以上

TensorFlow Text – Tensorflow でテキスト処理

TensorFlow Text – Tensorflow でテキスト処理 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/12/2019

* 本ページは、github レポジトリ tensorflow/text の TensorFlow Text – Text processing in Tensorflow (README.md) を翻訳した上で適宜、補足説明したものです:


* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

TensorFlow Text – Tensorflow でテキスト処理

イントロダクション

TensorFlow Text は TensorFlow 2.0 ですぐに使えるテキスト関連クラスと ops のコレクションを提供します。

ライブラリはテキストベースのモデルで通常必要とされる前処理を遂行できて、コア TensorFlow では提供されない、シークエンスモデル化のために有用な他の特徴を含みます。

貴方のテキスト処理でこれらの ops を使用するメリットはそれらが TensorFlow グラフ内で行われることです。推論時のトークン化とは異なる訓練におけるトークン化や前処理スクリプトを管理することについて貴方は心配する必要はありません。

 

Eager Execution

TensorFlow Text は TensorFlow eager モードとグラフモードの両者と互換です。

import tensorflow as tf
import tensorflow_text as text
tf.enable_eager_execution()

 

Unicode

殆どの ops は文字列が UTF-8 であることを想定しています。異なるエンコーディングを使用している場合、UTF-8 にコード変換するためにコア tensorflow transcode op を使用できます。入力が妥当ではないかもしれない場合には文字列に構造的に正当な UTF-8 を強制するために同じ op を使用することもできます。

docs = tf.constant([u'Everything not saved will be lost.'.encode('UTF-16-BE'),
                    u'Sad☹'.encode('UTF-16-BE')])
utf8_docs = tf.strings.unicode_transcode(docs, input_encoding='UTF-16-BE',
                                         output_encoding='UTF-8')

 

正規化

テキストの異なるソースを扱うとき、同じ単語が同一であると認識されることは重要です。Unicode における case-insensitive (大文字小文字を区別しない) マッチングのための一般的なテクニックは case folding です (lower-casing (小文字化) に似ています)。(case folding は内部的には NFKC 正規化を適用することに注意してください。)

私達はまた (デフォルトでは) 正規化形式 KC (NFKC, Normalization Form KC) によって文字列を文字の標準表現に変形するための Unicode 正規化 ops も提供します。

print(text.case_fold_utf8(['Everything not saved will be lost.']))
print(text.normalize_utf8(['Äffin']))
print(text.normalize_utf8(['Äffin'], 'nfkd'))
tf.Tensor(['everything not saved will be lost.'], shape=(1,), dtype=string)
tf.Tensor(['\xc3\x84ffin'], shape=(1,), dtype=string)
tf.Tensor(['A\xcc\x88ffin'], shape=(1,), dtype=string)

 

トークン化

トークン化は文字列をトークンに分解する過程です。一般に、これらのトークンは単語、数字、and/or 句読点です。

主要なインターフェイスは Tokenizer と TokenizerWithOffsets でそれぞれ単一のメソッド tokenize と tokenizeWithOffsets を持ちます。利用可能な複数のトークナイザーの実装が今はあります。これらの各々は (Tokenizer を拡張した) TokenizerWithOffsets を実装しこれはバイトオフセットを元の文字列に含めるためのオプションを含みます。これは呼び出し側に (そこからトークンが作成された) 元の文字列におけるバイトを知ることを可能にします。

総てのトークナイザーは元の個々の文字列にマップされるトークンの最内部 (= inner-most) 次元を持つ RaggedTensor を返します。その結果、結果としての shape のランクは一つずつ増やされます。それらについて馴染みがないのであれば ragged tensor ガイドをレビューしてください。https://www.tensorflow.org/guide/ragged_tensors

 

WhitespaceTokenizer

これは UTF-8 文字列を ICU 定義された空白文字 (eg. スペース、タブ、改行 (= new line))で分解する基本的なトークナイザーです。

tokenizer = text.WhitespaceTokenizer()
tokens = tokenizer.tokenize(['everything not saved will be lost.', u'Sad☹'.encode('UTF-8')])
print(tokens.to_list())
[['everything', 'not', 'saved', 'will', 'be', 'lost.'], ['Sad\xe2\x98\xb9']]

 

UnicodeScriptTokenizer

このトークナイザーは UTF-8 文字列を Unicode スクリプト境界を元に分解します。使用されるスクリプトコードは International Components for Unicode (ICU) UScriptCode 値に対応しています。http://icu-project.org/apiref/icu4c/uscript_8h.html 参照。

実際には、これは WhitespaceTokenizer に似ています。 (互いに言語テキストを分割もする一方で) それは言語テキスト (eg. USCRIPT_LATIN, USCRIPT_CYRILLIC, etc) から句読点 (USCRIPT_COMMON) を分割するという最も明白な違いを持ちます。

tokenizer = text.UnicodeScriptTokenizer()
tokens = tokenizer.tokenize(['everything not saved will be lost.',
                             u'Sad☹'.encode('UTF-8')])
print(tokens.to_list())
[['everything', 'not', 'saved', 'will', 'be', 'lost', '.'],
 ['Sad', '\xe2\x98\xb9']]

 

Unicode split

単語をセグメントするための空白なしで言語をトークン化するとき、文字で単に分割することは一般的です、これはコアで見つかる unicode_split op を使用して成されます。

tokens = tf.strings.unicode_split([u"仅今年前".encode('UTF-8')], 'UTF-8')
print(tokens.to_list())
[['\xe4\xbb\x85', '\xe4\xbb\x8a', '\xe5\xb9\xb4', '\xe5\x89\x8d']]

 

オフセット

文字列をトークン化するとき、トークンが由来する元の文字列のどこかを知ることはしばしば望ましいです。この理由のため、TokenizerWithOffsets を実装する各トークナイザーは tokenize_with_offsets を持ちます、これはトークンと一緒にバイトオフセットを返します。offset_starts は各トークンが始まる元の文字列のバイトをリストアップし、そして offset_limits は各トークンが終わるところのバイトをリストアップします。

tokenizer = text.UnicodeScriptTokenizer()
(tokens, offset_starts, offset_limits) = tokenizer.tokenize_with_offsets(
    ['everything not saved will be lost.', u'Sad☹'.encode('UTF-8')])
print(tokens.to_list())
print(offset_starts.to_list())
print(offset_limits.to_list())
[['everything', 'not', 'saved', 'will', 'be', 'lost', '.'],
 ['Sad', '\xe2\x98\xb9']]
[[0, 11, 15, 21, 26, 29, 33], [0, 3]]
[[10, 14, 20, 25, 28, 33, 34], [3, 6]]

 

TF.Data サンプル

トークナイザーは tf.data API で期待どおりに動作します。単純な例は下で提供されます。

docs = tf.data.Dataset.from_tensor_slices([['Never tell me the odds.'],
                                           ["It's a trap!"]])
tokenizer = text.WhitespaceTokenizer()
tokenized_docs = docs.map(lambda x: tokenizer.tokenize(x))
iterator = tokenized_docs.make_one_shot_iterator()
print(iterator.get_next().to_list())
print(iterator.get_next().to_list())
[['Never', 'tell', 'me', 'the', 'odds.']]
[["It's", 'a', 'trap!']]

 

他の Text Ops

TF.Text は他の有用な前処理 ops をパッケージ化しています。下で 2, 3 をレビューします。

 

Wordshape

幾つかの自然言語理解モデルで使用される一般的な特徴はテキスト文字列が特定のプロパティを持つかを見ます。例えば、センテンス分解モデルは単語キャピタリゼーションや句読点文字が文字列の最後であるかを調べる特徴を含むかもしれません。

Wordshape は貴方の入力テキストで様々な関連パターンに適合する各種の有用な正規表現ベースのヘルパー関数を定義します。ここに 2,3 のサンプルがあります。

tokenizer = text.WhitespaceTokenizer()
tokens = tokenizer.tokenize(['Everything not saved will be lost.',
                             u'Sad☹'.encode('UTF-8')])

# Is capitalized?
f1 = text.wordshape(tokens, text.WordShape.HAS_TITLE_CASE)
# Are all letters uppercased?
f2 = text.wordshape(tokens, text.WordShape.IS_UPPERCASE)
# Does the token contain punctuation?
f3 = text.wordshape(tokens, text.WordShape.HAS_SOME_PUNCT_OR_SYMBOL)
# Is the token a number?
f4 = text.wordshape(tokens, text.WordShape.IS_NUMERIC_VALUE)

print(f1.to_list())
print(f2.to_list())
print(f3.to_list())
print(f4.to_list())
[[True, False, False, False, False, False], [True]]
[[False, False, False, False, False, False], [False]]
[[False, False, False, False, False, True], [True]]
[[False, False, False, False, False, False], [False]]

 

N-gram & スライディング・ウィンドウ

N-gram は n のスライディング・ウィンドウのサイズが与えられたときのシークエンシャルな単語 (群) です。トークンを結合するとき、サポートされる 3 つの削減メカニズムがあります。テキストについては、Reduction.STRING_JOIN を使用することを望むでしょう、これは文字列を互いに付加します。デフォルトの区切り文字はスペースですが、これは string_separater 引数で変更可能です。

他の 2 つの削減メソッドは殆どの場合数値とともに使用され、これらは Reduction.SUM と Reduction.MEAN です。

tokenizer = text.WhitespaceTokenizer()
tokens = tokenizer.tokenize(['Everything not saved will be lost.',
                             u'Sad☹'.encode('UTF-8')])

# Ngrams, in this case bi-gram (n = 2)
bigrams = text.ngrams(tokens, 2, reduction_type=text.Reduction.STRING_JOIN)

print(bigrams.to_list())
[['Everything not', 'not saved', 'saved will', 'will be', 'be lost.'], []]

 

インストール

PIP を使用してインストール

pip install -U tensorflow-text
 

以上






TensorFlow : Graph Nets : グラフの最短経路を見つける

TensorFlow : Graph Nets : グラフの最短経路を見つける (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/07/2019

* 本ページは、deepmind/graph_nets の README.md 及び “Find the shortest path in a graph” を翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

Graph Nets : README.md

Graph Nets は TensorFlow と Sonnet でグラフネットワークを構築するための DeepMind のライブラリです。

グラフネットワークとは何でしょう?

グラフネットワークは入力としてグラフを取り出力としてグラフを返します。入力グラフはエッジ- (E)、ノード- (V) とグローバルレベル (u) 属性を持ちます。出力グラフは同じ構造、しかし更新された属性を持ちます。グラフネットワークは「グラフニューラルネットワーク」のより広いファミリーの一部です (Scarselli et al., 2009)。

グラフネットワークについてより学習するためには、私達の arXiv ペーパー: Relational inductive biases, deep learning, and graph networks を見てください。

 

使用方法サンプル

次のコードは単純なグラフネット・モジュールを構築してそれをデータに接続します。

import graph_nets as gn
import sonnet as snt

# Provide your own functions to generate graph-structured data.
input_graphs = get_graphs()

# Create the graph network.
graph_net_module = gn.modules.GraphNetwork(
    edge_model_fn=lambda: snt.nets.MLP([32, 32]),
    node_model_fn=lambda: snt.nets.MLP([32, 32]),
    global_model_fn=lambda: snt.nets.MLP([32, 32]))

# Pass the input graphs to the graph network, and return the output graphs.
output_graphs = graph_net_module(input_graphs)

 
 

Graph Nets : グラフの最短経路を見つける

「最短経路デモ」はランダムグラフを作成して、任意の 2 つのノードの間の最短経路上のノードとエッジをラベル付けするためにグラフネットワークを訓練します。メッセージパッシングのステップのシークエンスに渡り (各ステップのプロットで描かれるように)、モデルは最短経路のその予測を改良していきます。

このノートブックと伴うコードはグラフの 2 つのノード間に最短経路を予測することを学習するために Graph Nets ライブラリをどのように使用するかを示します。

開始と終了ノードが与えられたとき、ネットワークは最短経路のノードとエッジをラベル付けするために訓練されます。

訓練後、ネットワークの予測能力はその出力を真の最短経路と比較することにより示されます。それから汎化するためのネットワークの能力がテストされます、類似のしかしより巨大なグラフの最短経路を予測するためにそれを使用することによって。

 

インポート

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import collections
import itertools
import time

from graph_nets import graphs
from graph_nets import utils_np
from graph_nets import utils_tf
from graph_nets.demos import models
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
from scipy import spatial
import tensorflow as tf

SEED = 1
np.random.seed(SEED)
tf.set_random_seed(SEED)

 

ヘルパー関数

DISTANCE_WEIGHT_NAME = "distance"  # The name for the distance edge attribute.


def pairwise(iterable):
  """s -> (s0,s1), (s1,s2), (s2, s3), ..."""
  a, b = itertools.tee(iterable)
  next(b, None)
  return zip(a, b)


def set_diff(seq0, seq1):
  """Return the set difference between 2 sequences as a list."""
  return list(set(seq0) - set(seq1))


def to_one_hot(indices, max_value, axis=-1):
  one_hot = np.eye(max_value)[indices]
  if axis not in (-1, one_hot.ndim):
    one_hot = np.moveaxis(one_hot, -1, axis)
  return one_hot


def get_node_dict(graph, attr):
  """Return a `dict` of node:attribute pairs from a graph."""
  return {k: v[attr] for k, v in graph.node.items()}


def generate_graph(rand,
                   num_nodes_min_max,
                   dimensions=2,
                   theta=1000.0,
                   rate=1.0):
  """Creates a connected graph.

  The graphs are geographic threshold graphs, but with added edges via a
  minimum spanning tree algorithm, to ensure all nodes are connected.

  Args:
    rand: A random seed for the graph generator. Default= None.
    num_nodes_min_max: A sequence [lower, upper) number of nodes per graph.
    dimensions: (optional) An `int` number of dimensions for the positions.
      Default= 2.
    theta: (optional) A `float` threshold parameters for the geographic
      threshold graph's threshold. Large values (1000+) make mostly trees. Try
      20-60 for good non-trees. Default=1000.0.
    rate: (optional) A rate parameter for the node weight exponential sampling
      distribution. Default= 1.0.

  Returns:
    The graph.
  """
  # Sample num_nodes.
  num_nodes = rand.randint(*num_nodes_min_max)

  # Create geographic threshold graph.
  pos_array = rand.uniform(size=(num_nodes, dimensions))
  pos = dict(enumerate(pos_array))
  weight = dict(enumerate(rand.exponential(rate, size=num_nodes)))
  geo_graph = nx.geographical_threshold_graph(
      num_nodes, theta, pos=pos, weight=weight)

  # Create minimum spanning tree across geo_graph's nodes.
  distances = spatial.distance.squareform(spatial.distance.pdist(pos_array))
  i_, j_ = np.meshgrid(range(num_nodes), range(num_nodes), indexing="ij")
  weighted_edges = list(zip(i_.ravel(), j_.ravel(), distances.ravel()))
  mst_graph = nx.Graph()
  mst_graph.add_weighted_edges_from(weighted_edges, weight=DISTANCE_WEIGHT_NAME)
  mst_graph = nx.minimum_spanning_tree(mst_graph, weight=DISTANCE_WEIGHT_NAME)
  # Put geo_graph's node attributes into the mst_graph.
  for i in mst_graph.nodes():
    mst_graph.node[i].update(geo_graph.node[i])

  # Compose the graphs.
  combined_graph = nx.compose_all((mst_graph, geo_graph.copy()))
  # Put all distance weights into edge attributes.
  for i, j in combined_graph.edges():
    combined_graph.get_edge_data(i, j).setdefault(DISTANCE_WEIGHT_NAME,
                                                  distances[i, j])
  return combined_graph, mst_graph, geo_graph


def add_shortest_path(rand, graph, min_length=1):
  """Samples a shortest path from A to B and adds attributes to indicate it.

  Args:
    rand: A random seed for the graph generator. Default= None.
    graph: A `nx.Graph`.
    min_length: (optional) An `int` minimum number of edges in the shortest
      path. Default= 1.

  Returns:
    The `nx.DiGraph` with the shortest path added.

  Raises:
    ValueError: All shortest paths are below the minimum length
  """
  # Map from node pairs to the length of their shortest path.
  pair_to_length_dict = {}
  try:
    # This is for compatibility with older networkx.
    lengths = nx.all_pairs_shortest_path_length(graph).items()
  except AttributeError:
    # This is for compatibility with newer networkx.
    lengths = list(nx.all_pairs_shortest_path_length(graph))
  for x, yy in lengths:
    for y, l in yy.items():
      if l >= min_length:
        pair_to_length_dict[x, y] = l
  if max(pair_to_length_dict.values()) < min_length:
    raise ValueError("All shortest paths are below the minimum length")
  # The node pairs which exceed the minimum length.
  node_pairs = list(pair_to_length_dict)

  # Computes probabilities per pair, to enforce uniform sampling of each
  # shortest path lengths.
  # The counts of pairs per length.
  counts = collections.Counter(pair_to_length_dict.values())
  prob_per_length = 1.0 / len(counts)
  probabilities = [
      prob_per_length / counts[pair_to_length_dict[x]] for x in node_pairs
  ]

  # Choose the start and end points.
  i = rand.choice(len(node_pairs), p=probabilities)
  start, end = node_pairs[i]
  path = nx.shortest_path(
      graph, source=start, target=end, weight=DISTANCE_WEIGHT_NAME)

  # Creates a directed graph, to store the directed path from start to end.
  digraph = graph.to_directed()

  # Add the "start", "end", and "solution" attributes to the nodes and edges.
  digraph.add_node(start, start=True)
  digraph.add_node(end, end=True)
  digraph.add_nodes_from(set_diff(digraph.nodes(), [start]), start=False)
  digraph.add_nodes_from(set_diff(digraph.nodes(), [end]), end=False)
  digraph.add_nodes_from(set_diff(digraph.nodes(), path), solution=False)
  digraph.add_nodes_from(path, solution=True)
  path_edges = list(pairwise(path))
  digraph.add_edges_from(set_diff(digraph.edges(), path_edges), solution=False)
  digraph.add_edges_from(path_edges, solution=True)

  return digraph


def graph_to_input_target(graph):
  """Returns 2 graphs with input and target feature vectors for training.

  Args:
    graph: An `nx.DiGraph` instance.

  Returns:
    The input `nx.DiGraph` instance.
    The target `nx.DiGraph` instance.

  Raises:
    ValueError: unknown node type
  """

  def create_feature(attr, fields):
    return np.hstack([np.array(attr[field], dtype=float) for field in fields])

  input_node_fields = ("pos", "weight", "start", "end")
  input_edge_fields = ("distance",)
  target_node_fields = ("solution",)
  target_edge_fields = ("solution",)

  input_graph = graph.copy()
  target_graph = graph.copy()

  solution_length = 0
  for node_index, node_feature in graph.nodes(data=True):
    input_graph.add_node(
        node_index, features=create_feature(node_feature, input_node_fields))
    target_node = to_one_hot(
        create_feature(node_feature, target_node_fields).astype(int), 2)[0]
    target_graph.add_node(node_index, features=target_node)
    solution_length += int(node_feature["solution"])
  solution_length /= graph.number_of_nodes()

  for receiver, sender, features in graph.edges(data=True):
    input_graph.add_edge(
        sender, receiver, features=create_feature(features, input_edge_fields))
    target_edge = to_one_hot(
        create_feature(features, target_edge_fields).astype(int), 2)[0]
    target_graph.add_edge(sender, receiver, features=target_edge)

  input_graph.graph["features"] = np.array([0.0])
  target_graph.graph["features"] = np.array([solution_length], dtype=float)

  return input_graph, target_graph


def generate_networkx_graphs(rand, num_examples, num_nodes_min_max, theta):
  """Generate graphs for training.

  Args:
    rand: A random seed (np.RandomState instance).
    num_examples: Total number of graphs to generate.
    num_nodes_min_max: A 2-tuple with the [lower, upper) number of nodes per
      graph. The number of nodes for a graph is uniformly sampled within this
      range.
    theta: (optional) A `float` threshold parameters for the geographic
      threshold graph's threshold. Default= the number of nodes.

  Returns:
    input_graphs: The list of input graphs.
    target_graphs: The list of output graphs.
    graphs: The list of generated graphs.
  """
  input_graphs = []
  target_graphs = []
  graphs = []
  for _ in range(num_examples):
    graph = generate_graph(rand, num_nodes_min_max, theta=theta)[0]
    graph = add_shortest_path(rand, graph)
    input_graph, target_graph = graph_to_input_target(graph)
    input_graphs.append(input_graph)
    target_graphs.append(target_graph)
    graphs.append(graph)
  return input_graphs, target_graphs, graphs


def create_placeholders(rand, batch_size, num_nodes_min_max, theta):
  """Creates placeholders for the model training and evaluation.

  Args:
    rand: A random seed (np.RandomState instance).
    batch_size: Total number of graphs per batch.
    num_nodes_min_max: A 2-tuple with the [lower, upper) number of nodes per
      graph. The number of nodes for a graph is uniformly sampled within this
      range.
    theta: A `float` threshold parameters for the geographic threshold graph's
      threshold. Default= the number of nodes.

  Returns:
    input_ph: The input graph's placeholders, as a graph namedtuple.
    target_ph: The target graph's placeholders, as a graph namedtuple.
  """
  # Create some example data for inspecting the vector sizes.
  input_graphs, target_graphs, _ = generate_networkx_graphs(
      rand, batch_size, num_nodes_min_max, theta)
  input_ph = utils_tf.placeholders_from_networkxs(input_graphs)
  target_ph = utils_tf.placeholders_from_networkxs(target_graphs)
  return input_ph, target_ph


def create_feed_dict(rand, batch_size, num_nodes_min_max, theta, input_ph,
                     target_ph):
  """Creates placeholders for the model training and evaluation.

  Args:
    rand: A random seed (np.RandomState instance).
    batch_size: Total number of graphs per batch.
    num_nodes_min_max: A 2-tuple with the [lower, upper) number of nodes per
      graph. The number of nodes for a graph is uniformly sampled within this
      range.
    theta: A `float` threshold parameters for the geographic threshold graph's
      threshold. Default= the number of nodes.
    input_ph: The input graph's placeholders, as a graph namedtuple.
    target_ph: The target graph's placeholders, as a graph namedtuple.

  Returns:
    feed_dict: The feed `dict` of input and target placeholders and data.
    raw_graphs: The `dict` of raw networkx graphs.
  """
  inputs, targets, raw_graphs = generate_networkx_graphs(
      rand, batch_size, num_nodes_min_max, theta)
  input_graphs = utils_np.networkxs_to_graphs_tuple(inputs)
  target_graphs = utils_np.networkxs_to_graphs_tuple(targets)
  feed_dict = {input_ph: input_graphs, target_ph: target_graphs}
  return feed_dict, raw_graphs


def compute_accuracy(target, output, use_nodes=True, use_edges=False):
  """Calculate model accuracy.

  Returns the number of correctly predicted shortest path nodes and the number
  of completely solved graphs (100% correct predictions).

  Args:
    target: A `graphs.GraphsTuple` that contains the target graph.
    output: A `graphs.GraphsTuple` that contains the output graph.
    use_nodes: A `bool` indicator of whether to compute node accuracy or not.
    use_edges: A `bool` indicator of whether to compute edge accuracy or not.

  Returns:
    correct: A `float` fraction of correctly labeled nodes/edges.
    solved: A `float` fraction of graphs that are completely correctly labeled.

  Raises:
    ValueError: Nodes or edges (or both) must be used
  """
  if not use_nodes and not use_edges:
    raise ValueError("Nodes or edges (or both) must be used")
  tdds = utils_np.graphs_tuple_to_data_dicts(target)
  odds = utils_np.graphs_tuple_to_data_dicts(output)
  cs = []
  ss = []
  for td, od in zip(tdds, odds):
    xn = np.argmax(td["nodes"], axis=-1)
    yn = np.argmax(od["nodes"], axis=-1)
    xe = np.argmax(td["edges"], axis=-1)
    ye = np.argmax(od["edges"], axis=-1)
    c = []
    if use_nodes:
      c.append(xn == yn)
    if use_edges:
      c.append(xe == ye)
    c = np.concatenate(c, axis=0)
    s = np.all(c)
    cs.append(c)
    ss.append(s)
  correct = np.mean(np.concatenate(cs, axis=0))
  solved = np.mean(np.stack(ss))
  return correct, solved


def create_loss_ops(target_op, output_ops):
  loss_ops = [
      tf.losses.softmax_cross_entropy(target_op.nodes, output_op.nodes) +
      tf.losses.softmax_cross_entropy(target_op.edges, output_op.edges)
      for output_op in output_ops
  ]
  return loss_ops


def make_all_runnable_in_session(*args):
  """Lets an iterable of TF graphs be output from a session as NP graphs."""
  return [utils_tf.make_runnable_in_session(a) for a in args]


class GraphPlotter(object):

  def __init__(self, ax, graph, pos):
    self._ax = ax
    self._graph = graph
    self._pos = pos
    self._base_draw_kwargs = dict(G=self._graph, pos=self._pos, ax=self._ax)
    self._solution_length = None
    self._nodes = None
    self._edges = None
    self._start_nodes = None
    self._end_nodes = None
    self._solution_nodes = None
    self._intermediate_solution_nodes = None
    self._solution_edges = None
    self._non_solution_nodes = None
    self._non_solution_edges = None
    self._ax.set_axis_off()

  @property
  def solution_length(self):
    if self._solution_length is None:
      self._solution_length = len(self._solution_edges)
    return self._solution_length

  @property
  def nodes(self):
    if self._nodes is None:
      self._nodes = self._graph.nodes()
    return self._nodes

  @property
  def edges(self):
    if self._edges is None:
      self._edges = self._graph.edges()
    return self._edges

  @property
  def start_nodes(self):
    if self._start_nodes is None:
      self._start_nodes = [
          n for n in self.nodes if self._graph.node[n].get("start", False)
      ]
    return self._start_nodes

  @property
  def end_nodes(self):
    if self._end_nodes is None:
      self._end_nodes = [
          n for n in self.nodes if self._graph.node[n].get("end", False)
      ]
    return self._end_nodes

  @property
  def solution_nodes(self):
    if self._solution_nodes is None:
      self._solution_nodes = [
          n for n in self.nodes if self._graph.node[n].get("solution", False)
      ]
    return self._solution_nodes

  @property
  def intermediate_solution_nodes(self):
    if self._intermediate_solution_nodes is None:
      self._intermediate_solution_nodes = [
          n for n in self.nodes
          if self._graph.node[n].get("solution", False) and
          not self._graph.node[n].get("start", False) and
          not self._graph.node[n].get("end", False)
      ]
    return self._intermediate_solution_nodes

  @property
  def solution_edges(self):
    if self._solution_edges is None:
      self._solution_edges = [
          e for e in self.edges
          if self._graph.get_edge_data(e[0], e[1]).get("solution", False)
      ]
    return self._solution_edges

  @property
  def non_solution_nodes(self):
    if self._non_solution_nodes is None:
      self._non_solution_nodes = [
          n for n in self.nodes
          if not self._graph.node[n].get("solution", False)
      ]
    return self._non_solution_nodes

  @property
  def non_solution_edges(self):
    if self._non_solution_edges is None:
      self._non_solution_edges = [
          e for e in self.edges
          if not self._graph.get_edge_data(e[0], e[1]).get("solution", False)
      ]
    return self._non_solution_edges

  def _make_draw_kwargs(self, **kwargs):
    kwargs.update(self._base_draw_kwargs)
    return kwargs

  def _draw(self, draw_function, zorder=None, **kwargs):
    draw_kwargs = self._make_draw_kwargs(**kwargs)
    collection = draw_function(**draw_kwargs)
    if collection is not None and zorder is not None:
      try:
        # This is for compatibility with older matplotlib.
        collection.set_zorder(zorder)
      except AttributeError:
        # This is for compatibility with newer matplotlib.
        collection[0].set_zorder(zorder)
    return collection

  def draw_nodes(self, **kwargs):
    """Useful kwargs: nodelist, node_size, node_color, linewidths."""
    if ("node_color" in kwargs and
        isinstance(kwargs["node_color"], collections.Sequence) and
        len(kwargs["node_color"]) in {3, 4} and
        not isinstance(kwargs["node_color"][0],
                       (collections.Sequence, np.ndarray))):
      num_nodes = len(kwargs.get("nodelist", self.nodes))
      kwargs["node_color"] = np.tile(
          np.array(kwargs["node_color"])[None], [num_nodes, 1])
    return self._draw(nx.draw_networkx_nodes, **kwargs)

  def draw_edges(self, **kwargs):
    """Useful kwargs: edgelist, width."""
    return self._draw(nx.draw_networkx_edges, **kwargs)

  def draw_graph(self,
                 node_size=200,
                 node_color=(0.4, 0.8, 0.4),
                 node_linewidth=1.0,
                 edge_width=1.0):
    # Plot nodes.
    self.draw_nodes(
        nodelist=self.nodes,
        node_size=node_size,
        node_color=node_color,
        linewidths=node_linewidth,
        zorder=20)
    # Plot edges.
    self.draw_edges(edgelist=self.edges, width=edge_width, zorder=10)

  def draw_graph_with_solution(self,
                               node_size=200,
                               node_color=(0.4, 0.8, 0.4),
                               node_linewidth=1.0,
                               edge_width=1.0,
                               start_color="w",
                               end_color="k",
                               solution_node_linewidth=3.0,
                               solution_edge_width=3.0):
    node_border_color = (0.0, 0.0, 0.0, 1.0)
    node_collections = {}
    # Plot start nodes.
    node_collections["start nodes"] = self.draw_nodes(
        nodelist=self.start_nodes,
        node_size=node_size,
        node_color=start_color,
        linewidths=solution_node_linewidth,
        edgecolors=node_border_color,
        zorder=100)
    # Plot end nodes.
    node_collections["end nodes"] = self.draw_nodes(
        nodelist=self.end_nodes,
        node_size=node_size,
        node_color=end_color,
        linewidths=solution_node_linewidth,
        edgecolors=node_border_color,
        zorder=90)
    # Plot intermediate solution nodes.
    if isinstance(node_color, dict):
      c = [node_color[n] for n in self.intermediate_solution_nodes]
    else:
      c = node_color
    node_collections["intermediate solution nodes"] = self.draw_nodes(
        nodelist=self.intermediate_solution_nodes,
        node_size=node_size,
        node_color=c,
        linewidths=solution_node_linewidth,
        edgecolors=node_border_color,
        zorder=80)
    # Plot solution edges.
    node_collections["solution edges"] = self.draw_edges(
        edgelist=self.solution_edges, width=solution_edge_width, zorder=70)
    # Plot non-solution nodes.
    if isinstance(node_color, dict):
      c = [node_color[n] for n in self.non_solution_nodes]
    else:
      c = node_color
    node_collections["non-solution nodes"] = self.draw_nodes(
        nodelist=self.non_solution_nodes,
        node_size=node_size,
        node_color=c,
        linewidths=node_linewidth,
        edgecolors=node_border_color,
        zorder=20)
    # Plot non-solution edges.
    node_collections["non-solution edges"] = self.draw_edges(
        edgelist=self.non_solution_edges, width=edge_width, zorder=10)
    # Set title as solution length.
    self._ax.set_title("Solution length: {}".format(self.solution_length))
    return node_collections

 

サンプルグラフを可視化する

seed = 1  #@param{type: 'integer'}
rand = np.random.RandomState(seed=seed)

num_examples = 15  #@param{type: 'integer'}
# Large values (1000+) make trees. Try 20-60 for good non-trees.
theta = 20  #@param{type: 'integer'}
num_nodes_min_max = (16, 17)

input_graphs, target_graphs, graphs = generate_networkx_graphs(
    rand, num_examples, num_nodes_min_max, theta)

num = min(num_examples, 16)
w = 3
h = int(np.ceil(num / w))
fig = plt.figure(40, figsize=(w * 4, h * 4))
fig.clf()
for j, graph in enumerate(graphs):
  ax = fig.add_subplot(h, w, j + 1)
  pos = get_node_dict(graph, "pos")
  plotter = GraphPlotter(ax, graph, pos)
  plotter.draw_graph_with_solution()

 

モデル訓練と評価をセットアップする

# The model we explore includes three components:
# - An "Encoder" graph net, which independently encodes the edge, node, and
#   global attributes (does not compute relations etc.).
# - A "Core" graph net, which performs N rounds of processing (message-passing)
#   steps. The input to the Core is the concatenation of the Encoder's output
#   and the previous output of the Core (labeled "Hidden(t)" below, where "t" is
#   the processing step).
# - A "Decoder" graph net, which independently decodes the edge, node, and
#   global attributes (does not compute relations etc.), on each
#   message-passing step.
#
#                     Hidden(t)   Hidden(t+1)
#                        |            ^
#           *---------*  |  *------*  |  *---------*
#           |         |  |  |      |  |  |         |
# Input --->| Encoder |  *->| Core |--*->| Decoder |---> Output(t)
#           |         |---->|      |     |         |
#           *---------*     *------*     *---------*
#
# The model is trained by supervised learning. Input graphs are procedurally
# generated, and output graphs have the same structure with the nodes and edges
# of the shortest path labeled (using 2-element 1-hot vectors). We could have
# predicted the shortest path only by labeling either the nodes or edges, and
# that does work, but we decided to predict both to demonstrate the flexibility
# of graph nets' outputs.
#
# The training loss is computed on the output of each processing step. The
# reason for this is to encourage the model to try to solve the problem in as
# few steps as possible. It also helps make the output of intermediate steps
# more interpretable.
#
# There's no need for a separate evaluate dataset because the inputs are
# never repeated, so the training loss is the measure of performance on graphs
# from the input distribution.
#
# We also evaluate how well the models generalize to graphs which are up to
# twice as large as those on which it was trained. The loss is computed only
# on the final processing step.
#
# Variables with the suffix _tr are training parameters, and variables with the
# suffix _ge are test/generalization parameters.
#
# After around 2000-5000 training iterations the model reaches near-perfect
# performance on graphs with between 8-16 nodes.

tf.reset_default_graph()

seed = 2
rand = np.random.RandomState(seed=seed)

# Model parameters.
# Number of processing (message-passing) steps.
num_processing_steps_tr = 10
num_processing_steps_ge = 10

# Data / training parameters.
num_training_iterations = 10000
theta = 20  # Large values (1000+) make trees. Try 20-60 for good non-trees.
batch_size_tr = 32
batch_size_ge = 100
# Number of nodes per graph sampled uniformly from this range.
num_nodes_min_max_tr = (8, 17)
num_nodes_min_max_ge = (16, 33)

# Data.
# Input and target placeholders.
input_ph, target_ph = create_placeholders(rand, batch_size_tr,
                                          num_nodes_min_max_tr, theta)

# Connect the data to the model.
# Instantiate the model.
model = models.EncodeProcessDecode(edge_output_size=2, node_output_size=2)
# A list of outputs, one per processing step.
output_ops_tr = model(input_ph, num_processing_steps_tr)
output_ops_ge = model(input_ph, num_processing_steps_ge)

# Training loss.
loss_ops_tr = create_loss_ops(target_ph, output_ops_tr)
# Loss across processing steps.
loss_op_tr = sum(loss_ops_tr) / num_processing_steps_tr
# Test/generalization loss.
loss_ops_ge = create_loss_ops(target_ph, output_ops_ge)
loss_op_ge = loss_ops_ge[-1]  # Loss from final processing step.

# Optimizer.
learning_rate = 1e-3
optimizer = tf.train.AdamOptimizer(learning_rate)
step_op = optimizer.minimize(loss_op_tr)

# Lets an iterable of TF graphs be output from a session as NP graphs.
input_ph, target_ph = make_all_runnable_in_session(input_ph, target_ph)

 

セッションをリセットする

# This cell resets the Tensorflow session, but keeps the same computational
# graph.

try:
  sess.close()
except NameError:
  pass
sess = tf.Session()
sess.run(tf.global_variables_initializer())

last_iteration = 0
logged_iterations = []
losses_tr = []
corrects_tr = []
solveds_tr = []
losses_ge = []
corrects_ge = []
solveds_ge = []

 

訓練を実行する

# You can interrupt this cell's training loop at any time, and visualize the
# intermediate results by running the next cell (below). You can then resume
# training by simply executing this cell again.

# How much time between logging and printing the current results.
log_every_seconds = 20

print("# (iteration number), T (elapsed seconds), "
      "Ltr (training loss), Lge (test/generalization loss), "
      "Ctr (training fraction nodes/edges labeled correctly), "
      "Str (training fraction examples solved correctly), "
      "Cge (test/generalization fraction nodes/edges labeled correctly), "
      "Sge (test/generalization fraction examples solved correctly)")

start_time = time.time()
last_log_time = start_time
for iteration in range(last_iteration, num_training_iterations):
  last_iteration = iteration
  feed_dict, _ = create_feed_dict(rand, batch_size_tr, num_nodes_min_max_tr,
                                  theta, input_ph, target_ph)
  train_values = sess.run({
      "step": step_op,
      "target": target_ph,
      "loss": loss_op_tr,
      "outputs": output_ops_tr
  },
                          feed_dict=feed_dict)
  the_time = time.time()
  elapsed_since_last_log = the_time - last_log_time
  if elapsed_since_last_log > log_every_seconds:
    last_log_time = the_time
    feed_dict, raw_graphs = create_feed_dict(
        rand, batch_size_ge, num_nodes_min_max_ge, theta, input_ph, target_ph)
    test_values = sess.run({
        "target": target_ph,
        "loss": loss_op_ge,
        "outputs": output_ops_ge
    },
                           feed_dict=feed_dict)
    correct_tr, solved_tr = compute_accuracy(
        train_values["target"], train_values["outputs"][-1], use_edges=True)
    correct_ge, solved_ge = compute_accuracy(
        test_values["target"], test_values["outputs"][-1], use_edges=True)
    elapsed = time.time() - start_time
    losses_tr.append(train_values["loss"])
    corrects_tr.append(correct_tr)
    solveds_tr.append(solved_tr)
    losses_ge.append(test_values["loss"])
    corrects_ge.append(correct_ge)
    solveds_ge.append(solved_ge)
    logged_iterations.append(iteration)
    print("# {:05d}, T {:.1f}, Ltr {:.4f}, Lge {:.4f}, Ctr {:.4f}, Str"
          " {:.4f}, Cge {:.4f}, Sge {:.4f}".format(
              iteration, elapsed, train_values["loss"], test_values["loss"],
              correct_tr, solved_tr, correct_ge, solved_ge))
# (iteration number), T (elapsed seconds), Ltr (training loss), Lge (test/generalization loss), Ctr (training fraction nodes/edges labeled correctly), Str (training fraction examples solved correctly), Cge (test/generalization fraction nodes/edges labeled correctly), Sge (test/generalization fraction examples solved correctly)
# 00029, T 23.8, Ltr 0.8731, Lge 0.6658, Ctr 0.8596, Str 0.0000, Cge 0.9481, Sge 0.0000
# 00078, T 42.1, Ltr 0.6341, Lge 0.4758, Ctr 0.9056, Str 0.0000, Cge 0.9549, Sge 0.0000
# 00133, T 62.5, Ltr 0.5034, Lge 0.3845, Ctr 0.9172, Str 0.0000, Cge 0.9625, Sge 0.0200
# 00189, T 82.7, Ltr 0.5162, Lge 0.3417, Ctr 0.9166, Str 0.1250, Cge 0.9664, Sge 0.0200
# 00244, T 103.0, Ltr 0.4486, Lge 0.3383, Ctr 0.9343, Str 0.1250, Cge 0.9685, Sge 0.1600
# 00299, T 123.1, Ltr 0.4963, Lge 0.3507, Ctr 0.9184, Str 0.2500, Cge 0.9637, Sge 0.1100
# 00354, T 143.4, Ltr 0.3223, Lge 0.2883, Ctr 0.9614, Str 0.4062, Cge 0.9721, Sge 0.3300
# 00407, T 163.5, Ltr 0.4604, Lge 0.3853, Ctr 0.9270, Str 0.2500, Cge 0.9585, Sge 0.0600
# 00461, T 183.7, Ltr 0.2822, Lge 0.2933, Ctr 0.9670, Str 0.5625, Cge 0.9702, Sge 0.3200
# 00517, T 203.8, Ltr 0.3703, Lge 0.2784, Ctr 0.9480, Str 0.4375, Cge 0.9698, Sge 0.2600
# 00571, T 224.1, Ltr 0.4301, Lge 0.2783, Ctr 0.9308, Str 0.3125, Cge 0.9723, Sge 0.2000
# 00626, T 244.1, Ltr 0.3287, Lge 0.2833, Ctr 0.9533, Str 0.4062, Cge 0.9687, Sge 0.2700
# 00682, T 264.4, Ltr 0.2802, Lge 0.2913, Ctr 0.9617, Str 0.5000, Cge 0.9703, Sge 0.3000
# 00736, T 284.7, Ltr 0.3474, Lge 0.2775, Ctr 0.9531, Str 0.5625, Cge 0.9704, Sge 0.1900
# 00790, T 305.1, Ltr 0.3098, Lge 0.3607, Ctr 0.9488, Str 0.4062, Cge 0.9690, Sge 0.1700
# 00844, T 324.9, Ltr 0.3092, Lge 0.2941, Ctr 0.9566, Str 0.4375, Cge 0.9702, Sge 0.2500
# 00899, T 345.1, Ltr 0.3805, Lge 0.2202, Ctr 0.9440, Str 0.2812, Cge 0.9770, Sge 0.3200
# 00953, T 365.0, Ltr 0.2927, Lge 0.2637, Ctr 0.9609, Str 0.5938, Cge 0.9707, Sge 0.1900
# 01008, T 385.0, Ltr 0.3164, Lge 0.2093, Ctr 0.9568, Str 0.4688, Cge 0.9749, Sge 0.3100
# 01063, T 405.1, Ltr 0.2704, Lge 0.2455, Ctr 0.9749, Str 0.5000, Cge 0.9719, Sge 0.2000
# 01117, T 425.2, Ltr 0.2696, Lge 0.2713, Ctr 0.9600, Str 0.6250, Cge 0.9719, Sge 0.2300
# 01172, T 445.3, Ltr 0.4089, Lge 0.2442, Ctr 0.9489, Str 0.5312, Cge 0.9727, Sge 0.2100
# 01227, T 465.3, Ltr 0.3053, Lge 0.2616, Ctr 0.9620, Str 0.5312, Cge 0.9733, Sge 0.2400
# 01280, T 485.2, Ltr 0.2292, Lge 0.2433, Ctr 0.9742, Str 0.6250, Cge 0.9703, Sge 0.3100
# 01335, T 505.6, Ltr 0.3238, Lge 0.2267, Ctr 0.9554, Str 0.5312, Cge 0.9748, Sge 0.3000
# 01390, T 525.7, Ltr 0.3662, Lge 0.2706, Ctr 0.9602, Str 0.5938, Cge 0.9720, Sge 0.2500
# 01445, T 545.8, Ltr 0.2444, Lge 0.2530, Ctr 0.9755, Str 0.6562, Cge 0.9732, Sge 0.2800
# 01498, T 565.8, Ltr 0.3119, Lge 0.3036, Ctr 0.9565, Str 0.5938, Cge 0.9708, Sge 0.2300
# 01552, T 585.9, Ltr 0.3058, Lge 0.2633, Ctr 0.9553, Str 0.4688, Cge 0.9717, Sge 0.2400
# 01606, T 606.1, Ltr 0.2392, Lge 0.2462, Ctr 0.9782, Str 0.6562, Cge 0.9726, Sge 0.3000
# 01661, T 626.4, Ltr 0.2917, Lge 0.2522, Ctr 0.9611, Str 0.5625, Cge 0.9725, Sge 0.3000
# 01716, T 646.7, Ltr 0.3049, Lge 0.2254, Ctr 0.9566, Str 0.5938, Cge 0.9749, Sge 0.3300
# 01771, T 666.8, Ltr 0.2509, Lge 0.2393, Ctr 0.9667, Str 0.6250, Cge 0.9747, Sge 0.3000
# 01824, T 687.1, Ltr 0.1827, Lge 0.1870, Ctr 0.9879, Str 0.7812, Cge 0.9781, Sge 0.3300
# 01879, T 707.2, Ltr 0.3511, Lge 0.2048, Ctr 0.9574, Str 0.6562, Cge 0.9775, Sge 0.4200
# 01935, T 727.6, Ltr 0.2784, Lge 0.2044, Ctr 0.9699, Str 0.5625, Cge 0.9752, Sge 0.2000
# 01990, T 747.7, Ltr 0.3216, Lge 0.1943, Ctr 0.9639, Str 0.6562, Cge 0.9768, Sge 0.2800
# 02044, T 768.0, Ltr 0.1950, Lge 0.1579, Ctr 0.9892, Str 0.8750, Cge 0.9820, Sge 0.4100
# 02098, T 788.0, Ltr 0.2075, Lge 0.1729, Ctr 0.9882, Str 0.7500, Cge 0.9799, Sge 0.3700
# 02153, T 808.0, Ltr 0.2118, Lge 0.1775, Ctr 0.9783, Str 0.7500, Cge 0.9804, Sge 0.3700
# 02207, T 828.4, Ltr 0.2426, Lge 0.1862, Ctr 0.9669, Str 0.6562, Cge 0.9797, Sge 0.3800
# 02262, T 848.7, Ltr 0.2076, Lge 0.1836, Ctr 0.9862, Str 0.8750, Cge 0.9792, Sge 0.4300
# 02317, T 869.0, Ltr 0.1890, Lge 0.1984, Ctr 0.9873, Str 0.8750, Cge 0.9767, Sge 0.3800
# 02371, T 889.2, Ltr 0.1652, Lge 0.1936, Ctr 0.9887, Str 0.8125, Cge 0.9784, Sge 0.3900
# 02426, T 909.4, Ltr 0.2751, Lge 0.1523, Ctr 0.9707, Str 0.6875, Cge 0.9823, Sge 0.4100
# 02481, T 929.4, Ltr 0.1775, Lge 0.1617, Ctr 0.9867, Str 0.8750, Cge 0.9788, Sge 0.3800
# 02536, T 949.6, Ltr 0.2007, Lge 0.1207, Ctr 0.9880, Str 0.8438, Cge 0.9857, Sge 0.4900
# 02591, T 969.8, Ltr 0.1514, Lge 0.1489, Ctr 0.9934, Str 0.9062, Cge 0.9813, Sge 0.3600
# 02646, T 989.9, Ltr 0.2410, Lge 0.1100, Ctr 0.9862, Str 0.8125, Cge 0.9854, Sge 0.4000
# 02702, T 1010.0, Ltr 0.1991, Lge 0.1578, Ctr 0.9827, Str 0.8125, Cge 0.9813, Sge 0.3800
# 02756, T 1030.1, Ltr 0.1464, Lge 0.1388, Ctr 0.9893, Str 0.8750, Cge 0.9814, Sge 0.4000
# 02811, T 1050.1, Ltr 0.1931, Lge 0.1588, Ctr 0.9833, Str 0.8750, Cge 0.9799, Sge 0.3900
# 02866, T 1070.1, Ltr 0.1570, Lge 0.1189, Ctr 0.9858, Str 0.8750, Cge 0.9858, Sge 0.5500
# 02920, T 1090.5, Ltr 0.1420, Lge 0.1113, Ctr 0.9922, Str 0.8750, Cge 0.9855, Sge 0.4500
# 02974, T 1110.8, Ltr 0.1550, Lge 0.1640, Ctr 0.9911, Str 0.8438, Cge 0.9809, Sge 0.3200
# 03029, T 1130.8, Ltr 0.1681, Lge 0.1297, Ctr 0.9936, Str 0.8750, Cge 0.9873, Sge 0.4900
# 03084, T 1151.5, Ltr 0.1810, Lge 0.1909, Ctr 0.9921, Str 0.8750, Cge 0.9785, Sge 0.2300
# 03139, T 1171.1, Ltr 0.2063, Lge 0.1209, Ctr 0.9818, Str 0.8125, Cge 0.9861, Sge 0.4400
# 03195, T 1191.2, Ltr 0.1340, Lge 0.1583, Ctr 0.9945, Str 0.8750, Cge 0.9789, Sge 0.2100
# 03251, T 1211.7, Ltr 0.1461, Lge 0.1520, Ctr 0.9943, Str 0.9062, Cge 0.9856, Sge 0.4800
# 03303, T 1231.8, Ltr 0.1694, Lge 0.1235, Ctr 0.9854, Str 0.7812, Cge 0.9852, Sge 0.4900
# 03359, T 1252.0, Ltr 0.1738, Lge 0.1222, Ctr 0.9852, Str 0.7812, Cge 0.9846, Sge 0.4400
# 03414, T 1272.2, Ltr 0.1498, Lge 0.1101, Ctr 0.9897, Str 0.8438, Cge 0.9867, Sge 0.4900
# 03468, T 1292.5, Ltr 0.1638, Lge 0.1573, Ctr 0.9894, Str 0.8438, Cge 0.9836, Sge 0.4500
# 03523, T 1312.6, Ltr 0.2194, Lge 0.1516, Ctr 0.9761, Str 0.7188, Cge 0.9846, Sge 0.4600
# 03578, T 1332.6, Ltr 0.1490, Lge 0.1425, Ctr 0.9874, Str 0.9375, Cge 0.9846, Sge 0.5300
# 03633, T 1353.0, Ltr 0.1951, Lge 0.0889, Ctr 0.9860, Str 0.8750, Cge 0.9883, Sge 0.5600
# 03686, T 1373.0, Ltr 0.1586, Lge 0.1016, Ctr 0.9900, Str 0.9062, Cge 0.9875, Sge 0.4900
# 03741, T 1393.4, Ltr 0.1404, Lge 0.1356, Ctr 0.9911, Str 0.8750, Cge 0.9855, Sge 0.5800
# 03794, T 1413.3, Ltr 0.1938, Lge 0.1298, Ctr 0.9852, Str 0.7812, Cge 0.9828, Sge 0.4300
# 03847, T 1433.7, Ltr 0.1412, Lge 0.1183, Ctr 0.9899, Str 0.8750, Cge 0.9870, Sge 0.5900
# 03901, T 1453.8, Ltr 0.1894, Lge 0.0941, Ctr 0.9842, Str 0.8438, Cge 0.9890, Sge 0.6000
# 03955, T 1473.8, Ltr 0.1605, Lge 0.0935, Ctr 0.9867, Str 0.8125, Cge 0.9876, Sge 0.5800
# 04008, T 1493.9, Ltr 0.1560, Lge 0.0700, Ctr 0.9886, Str 0.8438, Cge 0.9927, Sge 0.6900
# 04062, T 1514.0, Ltr 0.2174, Lge 0.1912, Ctr 0.9865, Str 0.7812, Cge 0.9780, Sge 0.3800
# 04115, T 1534.0, Ltr 0.1537, Lge 0.0797, Ctr 0.9852, Str 0.8438, Cge 0.9891, Sge 0.5900
# 04169, T 1554.1, Ltr 0.1586, Lge 0.1071, Ctr 0.9871, Str 0.8438, Cge 0.9864, Sge 0.6100
# 04223, T 1574.5, Ltr 0.1071, Lge 0.1316, Ctr 1.0000, Str 1.0000, Cge 0.9869, Sge 0.6200
# 04278, T 1594.7, Ltr 0.1270, Lge 0.1329, Ctr 0.9985, Str 0.9375, Cge 0.9850, Sge 0.5400
# 04333, T 1614.8, Ltr 0.1352, Lge 0.1023, Ctr 0.9929, Str 0.9375, Cge 0.9864, Sge 0.5300
# 04386, T 1635.2, Ltr 0.1423, Lge 0.0890, Ctr 0.9894, Str 0.8125, Cge 0.9875, Sge 0.4600
# 04440, T 1655.1, Ltr 0.1320, Lge 0.0963, Ctr 0.9994, Str 0.9688, Cge 0.9882, Sge 0.5900
# 04495, T 1675.5, Ltr 0.1603, Lge 0.1094, Ctr 0.9889, Str 0.8750, Cge 0.9876, Sge 0.4800
# 04548, T 1695.7, Ltr 0.1474, Lge 0.1107, Ctr 0.9949, Str 0.9375, Cge 0.9868, Sge 0.5600
# 04602, T 1715.5, Ltr 0.1608, Lge 0.1791, Ctr 0.9960, Str 0.8438, Cge 0.9811, Sge 0.3700
# 04656, T 1735.6, Ltr 0.1416, Lge 0.1130, Ctr 0.9899, Str 0.8438, Cge 0.9865, Sge 0.5600
# 04710, T 1755.5, Ltr 0.1868, Lge 0.1135, Ctr 0.9944, Str 0.9375, Cge 0.9862, Sge 0.5500
# 04764, T 1775.7, Ltr 0.1466, Lge 0.0730, Ctr 0.9916, Str 0.9375, Cge 0.9901, Sge 0.6600
# 04819, T 1795.9, Ltr 0.1147, Lge 0.0881, Ctr 0.9966, Str 0.9688, Cge 0.9906, Sge 0.6900
# 04874, T 1816.0, Ltr 0.1130, Lge 0.1065, Ctr 0.9987, Str 0.9688, Cge 0.9868, Sge 0.5900
# 04928, T 1836.3, Ltr 0.1979, Lge 0.0953, Ctr 0.9909, Str 0.8750, Cge 0.9885, Sge 0.5200
# 04982, T 1856.2, Ltr 0.1319, Lge 0.1024, Ctr 0.9929, Str 0.9062, Cge 0.9875, Sge 0.6000
# 05036, T 1876.4, Ltr 0.1575, Lge 0.0744, Ctr 0.9910, Str 0.9062, Cge 0.9914, Sge 0.6300
# 05090, T 1896.6, Ltr 0.1387, Lge 0.1054, Ctr 0.9938, Str 0.9062, Cge 0.9877, Sge 0.5800
# 05144, T 1916.8, Ltr 0.1196, Lge 0.1088, Ctr 0.9929, Str 0.8750, Cge 0.9857, Sge 0.4800
# 05198, T 1936.6, Ltr 0.1441, Lge 0.0758, Ctr 0.9912, Str 0.9375, Cge 0.9902, Sge 0.6900
# 05252, T 1957.3, Ltr 0.1296, Lge 0.1036, Ctr 0.9957, Str 0.8750, Cge 0.9909, Sge 0.7000
# 05304, T 1976.9, Ltr 0.1311, Lge 0.1073, Ctr 0.9956, Str 0.9062, Cge 0.9883, Sge 0.6700
# 05359, T 1997.1, Ltr 0.0968, Lge 0.1633, Ctr 1.0000, Str 1.0000, Cge 0.9860, Sge 0.5500
# 05413, T 2017.0, Ltr 0.1550, Lge 0.0875, Ctr 0.9903, Str 0.9062, Cge 0.9896, Sge 0.6600
# 05466, T 2037.4, Ltr 0.1204, Lge 0.2264, Ctr 0.9966, Str 0.9062, Cge 0.9834, Sge 0.6400
# 05519, T 2057.5, Ltr 0.1255, Lge 0.1421, Ctr 0.9953, Str 0.9375, Cge 0.9868, Sge 0.6100
# 05573, T 2077.5, Ltr 0.1255, Lge 0.0956, Ctr 0.9941, Str 0.9062, Cge 0.9900, Sge 0.6800
# 05628, T 2098.2, Ltr 0.1427, Lge 0.0803, Ctr 0.9953, Str 0.9062, Cge 0.9893, Sge 0.6500
# 05683, T 2118.4, Ltr 0.1344, Lge 0.0857, Ctr 0.9934, Str 0.9062, Cge 0.9909, Sge 0.6000
# 05739, T 2138.5, Ltr 0.1634, Lge 0.1224, Ctr 0.9931, Str 0.9375, Cge 0.9875, Sge 0.5600
# 05793, T 2158.9, Ltr 0.1784, Lge 0.0720, Ctr 0.9853, Str 0.8750, Cge 0.9905, Sge 0.5800
# 05847, T 2179.2, Ltr 0.1259, Lge 0.0641, Ctr 0.9975, Str 0.9688, Cge 0.9921, Sge 0.6800
# 05902, T 2199.3, Ltr 0.0840, Lge 0.1022, Ctr 0.9974, Str 0.9688, Cge 0.9880, Sge 0.5600
# 05957, T 2219.4, Ltr 0.1161, Lge 0.0861, Ctr 0.9978, Str 0.9688, Cge 0.9906, Sge 0.6900
# 06010, T 2239.6, Ltr 0.1470, Lge 0.0660, Ctr 0.9894, Str 0.8125, Cge 0.9906, Sge 0.6700
# 06065, T 2259.7, Ltr 0.1664, Lge 0.1401, Ctr 0.9937, Str 0.9375, Cge 0.9831, Sge 0.5000
# 06120, T 2279.9, Ltr 0.1733, Lge 0.0901, Ctr 0.9829, Str 0.8438, Cge 0.9880, Sge 0.5600
# 06173, T 2300.1, Ltr 0.1269, Lge 0.0721, Ctr 0.9936, Str 0.9375, Cge 0.9926, Sge 0.7600
# 06228, T 2320.4, Ltr 0.1716, Lge 0.0702, Ctr 0.9926, Str 0.9375, Cge 0.9908, Sge 0.6500
# 06282, T 2340.4, Ltr 0.1386, Lge 0.0545, Ctr 0.9975, Str 0.9688, Cge 0.9926, Sge 0.7100
# 06337, T 2360.9, Ltr 0.1400, Lge 0.0512, Ctr 0.9960, Str 0.9062, Cge 0.9926, Sge 0.6100
# 06391, T 2380.5, Ltr 0.1468, Lge 0.0791, Ctr 0.9965, Str 0.8750, Cge 0.9894, Sge 0.6300
# 06446, T 2400.6, Ltr 0.1655, Lge 0.0847, Ctr 0.9900, Str 0.8750, Cge 0.9897, Sge 0.5800
# 06500, T 2420.6, Ltr 0.1530, Lge 0.0538, Ctr 0.9878, Str 0.7812, Cge 0.9925, Sge 0.6800
# 06553, T 2440.5, Ltr 0.1442, Lge 0.0634, Ctr 0.9969, Str 0.9375, Cge 0.9919, Sge 0.7500
# 06609, T 2460.8, Ltr 0.0933, Lge 0.0678, Ctr 0.9987, Str 0.9688, Cge 0.9912, Sge 0.6900
# 06663, T 2481.0, Ltr 0.1460, Lge 0.0936, Ctr 0.9953, Str 0.9375, Cge 0.9879, Sge 0.5400
# 06716, T 2501.4, Ltr 0.1505, Lge 0.0685, Ctr 0.9941, Str 0.9375, Cge 0.9914, Sge 0.7200
# 06769, T 2521.4, Ltr 0.1400, Lge 0.0530, Ctr 0.9955, Str 0.9062, Cge 0.9931, Sge 0.7400
# 06823, T 2541.4, Ltr 0.1310, Lge 0.0799, Ctr 0.9936, Str 0.8750, Cge 0.9896, Sge 0.6900
# 06878, T 2561.7, Ltr 0.1640, Lge 0.0848, Ctr 0.9885, Str 0.7812, Cge 0.9884, Sge 0.5900
# 06931, T 2581.7, Ltr 0.1395, Lge 0.0783, Ctr 0.9954, Str 0.9375, Cge 0.9904, Sge 0.6400
# 06986, T 2601.9, Ltr 0.1150, Lge 0.0546, Ctr 0.9969, Str 0.9375, Cge 0.9923, Sge 0.7400
# 07041, T 2622.1, Ltr 0.0829, Lge 0.0574, Ctr 1.0000, Str 1.0000, Cge 0.9923, Sge 0.6700
# 07095, T 2642.4, Ltr 0.1719, Lge 0.1901, Ctr 0.9907, Str 0.9375, Cge 0.9819, Sge 0.2800
# 07149, T 2662.5, Ltr 0.2284, Lge 0.0478, Ctr 0.9789, Str 0.8438, Cge 0.9934, Sge 0.7100
# 07204, T 2682.8, Ltr 0.1277, Lge 0.0614, Ctr 0.9923, Str 0.8750, Cge 0.9914, Sge 0.6000
# 07256, T 2703.0, Ltr 0.2056, Lge 0.0849, Ctr 0.9938, Str 0.9062, Cge 0.9910, Sge 0.6400
# 07311, T 2723.1, Ltr 0.1456, Lge 0.0573, Ctr 0.9967, Str 0.9688, Cge 0.9924, Sge 0.6700
# 07366, T 2743.3, Ltr 0.1366, Lge 0.0878, Ctr 0.9993, Str 0.9688, Cge 0.9898, Sge 0.7200
# 07420, T 2764.0, Ltr 0.1349, Lge 0.0462, Ctr 0.9953, Str 0.9375, Cge 0.9948, Sge 0.7700
# 07472, T 2783.6, Ltr 0.1244, Lge 0.0604, Ctr 0.9955, Str 0.9375, Cge 0.9929, Sge 0.7300
# 07528, T 2803.8, Ltr 0.1206, Lge 0.0890, Ctr 1.0000, Str 1.0000, Cge 0.9875, Sge 0.5300
# 07583, T 2824.1, Ltr 0.1248, Lge 0.0860, Ctr 0.9993, Str 0.9688, Cge 0.9910, Sge 0.7300
# 07636, T 2844.1, Ltr 0.1737, Lge 0.1036, Ctr 0.9909, Str 0.9062, Cge 0.9891, Sge 0.6600
# 07689, T 2864.1, Ltr 0.1297, Lge 0.0718, Ctr 0.9974, Str 0.9375, Cge 0.9933, Sge 0.7400
# 07744, T 2884.4, Ltr 0.1139, Lge 0.0905, Ctr 0.9962, Str 0.9375, Cge 0.9888, Sge 0.5700
# 07797, T 2904.6, Ltr 0.1405, Lge 0.0703, Ctr 0.9975, Str 0.9062, Cge 0.9905, Sge 0.6900
# 07852, T 2924.8, Ltr 0.1078, Lge 0.0566, Ctr 0.9986, Str 0.9375, Cge 0.9926, Sge 0.7600
# 07906, T 2944.9, Ltr 0.1498, Lge 0.0772, Ctr 0.9923, Str 0.8750, Cge 0.9902, Sge 0.6400
# 07959, T 2965.1, Ltr 0.1378, Lge 0.0657, Ctr 0.9919, Str 0.9375, Cge 0.9919, Sge 0.6900
# 08012, T 2985.2, Ltr 0.1468, Lge 0.0639, Ctr 0.9872, Str 0.8438, Cge 0.9919, Sge 0.6600
# 08067, T 3005.5, Ltr 0.1473, Lge 0.0555, Ctr 0.9967, Str 0.9688, Cge 0.9930, Sge 0.7400
# 08121, T 3025.6, Ltr 0.0928, Lge 0.0502, Ctr 0.9963, Str 0.9375, Cge 0.9930, Sge 0.6500
# 08174, T 3045.9, Ltr 0.1561, Lge 0.0637, Ctr 0.9906, Str 0.8750, Cge 0.9929, Sge 0.7300
# 08229, T 3066.2, Ltr 0.1539, Lge 0.1363, Ctr 0.9885, Str 0.8438, Cge 0.9887, Sge 0.6600
# 08283, T 3086.3, Ltr 0.1270, Lge 0.0807, Ctr 0.9930, Str 0.9375, Cge 0.9889, Sge 0.5900
# 08337, T 3107.0, Ltr 0.1001, Lge 0.0721, Ctr 0.9948, Str 0.9375, Cge 0.9909, Sge 0.6900
# 08391, T 3126.5, Ltr 0.1344, Lge 0.0732, Ctr 0.9944, Str 0.9375, Cge 0.9917, Sge 0.7000
# 08446, T 3146.8, Ltr 0.1127, Lge 0.0597, Ctr 0.9994, Str 0.9688, Cge 0.9907, Sge 0.6600
# 08502, T 3167.0, Ltr 0.1328, Lge 0.0496, Ctr 0.9959, Str 0.9375, Cge 0.9928, Sge 0.7600
# 08556, T 3187.1, Ltr 0.1424, Lge 0.0844, Ctr 0.9953, Str 0.9062, Cge 0.9901, Sge 0.6900
# 08612, T 3207.4, Ltr 0.1434, Lge 0.0986, Ctr 0.9949, Str 0.9062, Cge 0.9863, Sge 0.5700
# 08666, T 3227.6, Ltr 0.1772, Lge 0.0893, Ctr 0.9865, Str 0.8438, Cge 0.9894, Sge 0.6100
# 08720, T 3247.8, Ltr 0.1491, Lge 0.0896, Ctr 0.9906, Str 0.9375, Cge 0.9893, Sge 0.6800
# 08775, T 3268.2, Ltr 0.1873, Lge 0.0815, Ctr 0.9895, Str 0.8438, Cge 0.9905, Sge 0.6900
# 08830, T 3288.4, Ltr 0.1128, Lge 0.0790, Ctr 0.9988, Str 0.9688, Cge 0.9881, Sge 0.6400
# 08882, T 3308.5, Ltr 0.1164, Lge 0.1097, Ctr 0.9957, Str 0.9688, Cge 0.9896, Sge 0.7200
# 08935, T 3328.4, Ltr 0.1509, Lge 0.0733, Ctr 0.9891, Str 0.8438, Cge 0.9901, Sge 0.6100
# 08990, T 3348.7, Ltr 0.1394, Lge 0.0357, Ctr 0.9969, Str 0.9375, Cge 0.9954, Sge 0.7800
# 09045, T 3368.9, Ltr 0.1263, Lge 0.0905, Ctr 0.9933, Str 0.8750, Cge 0.9882, Sge 0.6400
# 09098, T 3388.9, Ltr 0.1421, Lge 0.0697, Ctr 0.9929, Str 0.9062, Cge 0.9935, Sge 0.7900
# 09152, T 3409.1, Ltr 0.1357, Lge 0.0765, Ctr 0.9904, Str 0.7812, Cge 0.9904, Sge 0.6100
# 09207, T 3429.5, Ltr 0.1691, Lge 0.0696, Ctr 0.9950, Str 0.9688, Cge 0.9917, Sge 0.6700
# 09260, T 3449.4, Ltr 0.1421, Lge 0.0924, Ctr 0.9928, Str 0.9062, Cge 0.9896, Sge 0.6700
# 09315, T 3469.8, Ltr 0.1280, Lge 0.0687, Ctr 0.9941, Str 0.9375, Cge 0.9906, Sge 0.6900
# 09370, T 3490.1, Ltr 0.1428, Lge 0.0758, Ctr 0.9968, Str 0.9062, Cge 0.9920, Sge 0.7100
# 09423, T 3510.1, Ltr 0.1391, Lge 0.0665, Ctr 0.9956, Str 0.9062, Cge 0.9915, Sge 0.7100
# 09479, T 3530.1, Ltr 0.1644, Lge 0.1169, Ctr 0.9915, Str 0.9062, Cge 0.9883, Sge 0.6300
# 09535, T 3550.4, Ltr 0.1296, Lge 0.0786, Ctr 0.9898, Str 0.9062, Cge 0.9901, Sge 0.6600
# 09590, T 3570.6, Ltr 0.1611, Lge 0.0532, Ctr 0.9871, Str 0.8750, Cge 0.9927, Sge 0.7000
# 09644, T 3590.7, Ltr 0.1599, Lge 0.0585, Ctr 0.9943, Str 0.9375, Cge 0.9918, Sge 0.6900
# 09698, T 3610.8, Ltr 0.1409, Lge 0.0598, Ctr 0.9944, Str 0.8750, Cge 0.9926, Sge 0.7200
# 09754, T 3631.0, Ltr 0.1637, Lge 0.0523, Ctr 0.9900, Str 0.9375, Cge 0.9926, Sge 0.7300
# 09808, T 3651.2, Ltr 0.1112, Lge 0.0894, Ctr 0.9931, Str 0.9375, Cge 0.9924, Sge 0.7200
# 09863, T 3671.4, Ltr 0.1136, Lge 0.0938, Ctr 0.9979, Str 0.9688, Cge 0.9877, Sge 0.5300
# 09918, T 3691.5, Ltr 0.1333, Lge 0.0761, Ctr 0.9890, Str 0.8750, Cge 0.9905, Sge 0.6200
# 09972, T 3711.8, Ltr 0.1063, Lge 0.0603, Ctr 0.9975, Str 0.9375, Cge 0.9933, Sge 0.7100

 

結果を可視化する

# This cell visualizes the results of training. You can visualize the
# intermediate results by interrupting execution of the cell above, and running
# this cell. You can then resume training by simply executing the above cell
# again.

def softmax_prob_last_dim(x):  # pylint: disable=redefined-outer-name
  e = np.exp(x)
  return e[:, -1] / np.sum(e, axis=-1)


# Plot results curves.
fig = plt.figure(1, figsize=(18, 3))
fig.clf()
x = np.array(logged_iterations)
# Loss.
y_tr = losses_tr
y_ge = losses_ge
ax = fig.add_subplot(1, 3, 1)
ax.plot(x, y_tr, "k", label="Training")
ax.plot(x, y_ge, "k--", label="Test/generalization")
ax.set_title("Loss across training")
ax.set_xlabel("Training iteration")
ax.set_ylabel("Loss (binary cross-entropy)")
ax.legend()
# Correct.
y_tr = corrects_tr
y_ge = corrects_ge
ax = fig.add_subplot(1, 3, 2)
ax.plot(x, y_tr, "k", label="Training")
ax.plot(x, y_ge, "k--", label="Test/generalization")
ax.set_title("Fraction correct across training")
ax.set_xlabel("Training iteration")
ax.set_ylabel("Fraction nodes/edges correct")
# Solved.
y_tr = solveds_tr
y_ge = solveds_ge
ax = fig.add_subplot(1, 3, 3)
ax.plot(x, y_tr, "k", label="Training")
ax.plot(x, y_ge, "k--", label="Test/generalization")
ax.set_title("Fraction solved across training")
ax.set_xlabel("Training iteration")
ax.set_ylabel("Fraction examples solved")

# Plot graphs and results after each processing step.
# The white node is the start, and the black is the end. Other nodes are colored
# from red to purple to blue, where red means the model is confident the node is
# off the shortest path, blue means the model is confident the node is on the
# shortest path, and purplish colors mean the model isn't sure.
max_graphs_to_plot = 6
num_steps_to_plot = 4
node_size = 120
min_c = 0.3
num_graphs = len(raw_graphs)
targets = utils_np.graphs_tuple_to_data_dicts(test_values["target"])
step_indices = np.floor(
    np.linspace(0, num_processing_steps_ge - 1,
                num_steps_to_plot)).astype(int).tolist()
outputs = list(
    zip(*(utils_np.graphs_tuple_to_data_dicts(test_values["outputs"][i])
          for i in step_indices)))
h = min(num_graphs, max_graphs_to_plot)
w = num_steps_to_plot + 1
fig = plt.figure(101, figsize=(18, h * 3))
fig.clf()
ncs = []
for j, (graph, target, output) in enumerate(zip(raw_graphs, targets, outputs)):
  if j >= h:
    break
  pos = get_node_dict(graph, "pos")
  ground_truth = target["nodes"][:, -1]
  # Ground truth.
  iax = j * (1 + num_steps_to_plot) + 1
  ax = fig.add_subplot(h, w, iax)
  plotter = GraphPlotter(ax, graph, pos)
  color = {}
  for i, n in enumerate(plotter.nodes):
    color[n] = np.array([1.0 - ground_truth[i], 0.0, ground_truth[i], 1.0
                        ]) * (1.0 - min_c) + min_c
  plotter.draw_graph_with_solution(node_size=node_size, node_color=color)
  ax.set_axis_on()
  ax.set_xticks([])
  ax.set_yticks([])
  try:
    ax.set_facecolor([0.9] * 3 + [1.0])
  except AttributeError:
    ax.set_axis_bgcolor([0.9] * 3 + [1.0])
  ax.grid(None)
  ax.set_title("Ground truth\nSolution length: {}".format(
      plotter.solution_length))
  # Prediction.
  for k, outp in enumerate(output):
    iax = j * (1 + num_steps_to_plot) + 2 + k
    ax = fig.add_subplot(h, w, iax)
    plotter = GraphPlotter(ax, graph, pos)
    color = {}
    prob = softmax_prob_last_dim(outp["nodes"])
    for i, n in enumerate(plotter.nodes):
      color[n] = np.array([1.0 - prob[n], 0.0, prob[n], 1.0
                          ]) * (1.0 - min_c) + min_c
    plotter.draw_graph_with_solution(node_size=node_size, node_color=color)
    ax.set_title("Model-predicted\nStep {:02d} / {:02d}".format(
        step_indices[k] + 1, step_indices[-1] + 1))

 

以上






TensorFlow Graphics : Tutorials : 球面調和関数レンダリング

TensorFlow Graphics : Advanced Tutorials : 球面調和関数レンダリング (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/31/2019

* 本ページは、TensorFlow Graphics の github レポジトリの次のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

 

TensorFlow Graphics : Advanced Tutorials : 球面調和関数レンダリング

このチュートリアルは進んだトピックをカバーしておりそれ故に step by step に詳細を提供するよりも、球面調和関数の高位な理解とライティングのためのそれらの利用を形成するために制御可能な toy サンプルを提供することに注力しています。球面調和関数とライティングのためのそれらの利用の良い理解を形成するための良い資料は Spherical Harmonics Lighting: the Gritty Details です。

このチュートリアルは球面調和関数を使用して球面に渡り定義された関数をどのように近似するかを示します。これらはライティングと反射率を近似するために使用できて、非常に効率的なレンダリングに繋がります。

より詳細において、後述のセルは以下を示します :

  • 調和関数 (SH) によるライティング環境の近似
  • 帯域調和関数 (ZH) によりランバート BRDF の近似
  • 帯域調和関数の回転
  • SH ライティングと ZH BRDF の球面調和関数畳み込みによるレンダリング

 

セットアップ & Imports

このノートブックに含まれるデモを実行するために必要な総てを import しましょう。

###########
# Imports #
###########
import math

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf

from tensorflow_graphics.geometry.representation import grid
from tensorflow_graphics.geometry.representation import ray
from tensorflow_graphics.geometry.representation import vector
from tensorflow_graphics.rendering.camera import orthographic
from tensorflow_graphics.math import spherical_harmonics
from tensorflow_graphics.math import math_helpers as tf_math

tf.enable_eager_execution()

 

調和関数によるライティングの近似

#@title Controls { vertical-output: false, run: "auto" }
max_band = 2  #@param { type: "slider", min: 0, max: 10 , step: 1 }

#########################################################################
# This cell creates a lighting function which we approximate with an SH #
#########################################################################

def image_to_spherical_coordinates(image_width, image_height):
  pixel_grid_start = np.array((0, 0), dtype=type)
  pixel_grid_end = np.array((image_width - 1, image_height - 1), dtype=type)
  pixel_nb = np.array((image_width, image_height))
  pixels = grid.generate(pixel_grid_start, pixel_grid_end, pixel_nb)
  normalized_pixels = pixels / (image_width - 1, image_height - 1)
  spherical_coordinates = tf_math.square_to_spherical_coordinates(
      normalized_pixels)
  return spherical_coordinates


def light_function(theta, phi):
  theta = tf.convert_to_tensor(theta)
  phi = tf.convert_to_tensor(phi)
  zero = tf.zeros_like(theta)
  return tf.maximum(zero,
                    -4.0 * tf.sin(theta - np.pi) * tf.cos(phi - 2.5) - 3.0)


light_image_width = 30
light_image_height = 30
type = np.float64

# Builds the pixels grid and compute corresponding spherical coordinates.
spherical_coordinates = image_to_spherical_coordinates(light_image_width,
                                                       light_image_height)
theta = spherical_coordinates[:, :, 1]
phi = spherical_coordinates[:, :, 2]

# Samples the light function.
sampled_light_function = light_function(theta, phi)
ones_normal = tf.ones_like(theta)
spherical_coordinates_3d = tf.stack((ones_normal, theta, phi), axis=-1)
samples_direction_to_light = tf_math.spherical_to_cartesian_coordinates(
    spherical_coordinates_3d)

# Samples the SH.
l, m = spherical_harmonics.generate_l_m_permutations(max_band)
l = tf.convert_to_tensor(l)
m = tf.convert_to_tensor(m)
l_broadcasted = tf.broadcast_to(l, [light_image_width, light_image_height] +
                                l.shape.as_list())
m_broadcasted = tf.broadcast_to(m, [light_image_width, light_image_height] +
                                l.shape.as_list())
theta = tf.expand_dims(theta, axis=-1)
theta_broadcasted = tf.broadcast_to(
    theta, [light_image_width, light_image_height, 1])
phi = tf.expand_dims(phi, axis=-1)
phi_broadcasted = tf.broadcast_to(phi, [light_image_width, light_image_height, 1])
sh_coefficients = spherical_harmonics.evaluate_spherical_harmonics(
    l_broadcasted, m_broadcasted, theta_broadcasted, phi_broadcasted)
sampled_light_function_broadcasted = tf.expand_dims(
    sampled_light_function, axis=-1)
sampled_light_function_broadcasted = tf.broadcast_to(
    sampled_light_function_broadcasted,
    [light_image_width, light_image_height] + l.shape.as_list())

# Integrates the light function times SH over the sphere.
projection = sh_coefficients * sampled_light_function_broadcasted * 4.0 * math.pi / (
    light_image_width * light_image_height)
light_coeffs = tf.reduce_sum(projection, (0, 1))

# Reconstructs the image.
reconstructed_light_function = tf.squeeze(
    vector.dot(sh_coefficients, light_coeffs))

print(
    "average l2 reconstruction error ",
    np.linalg.norm(sampled_light_function - reconstructed_light_function) /
    (light_image_width * light_image_height))

vmin = np.minimum(
    np.amin(np.minimum(sampled_light_function, reconstructed_light_function)),
    0.0)
vmax = np.maximum(
    np.amax(np.maximum(sampled_light_function, reconstructed_light_function)),
    1.0)
# Plots results.
plt.figure(figsize=(10, 10))
ax = plt.subplot("131")
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.grid(False)
ax.set_title("Original lighting function")
_ = ax.imshow(sampled_light_function, vmin=vmin, vmax=vmax)
ax = plt.subplot("132")
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.grid(False)
ax.set_title("Spherical Harmonics approximation")
_ = ax.imshow(reconstructed_light_function, vmin=vmin, vmax=vmax)
ax = plt.subplot("133")
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.grid(False)
ax.set_title("Difference")
_ = ax.imshow(
    np.abs(reconstructed_light_function - sampled_light_function),
    vmin=vmin,
    vmax=vmax)

average l2 reconstruction error  0.004166945812788392

 

帯域調和関数によりランバート BRDF を近似する

#################################################################
# This cell creates an SH that approximates the Lambertian BRDF #
#################################################################

# The image dimensions control how many uniform samples we draw from the BRDF.
brdf_image_width = 30
brdf_image_height = 30
type = np.float64

# Builds the pixels grid and compute corresponding spherical coordinates.
spherical_coordinates = image_to_spherical_coordinates(brdf_image_width,
                                                       brdf_image_height)

# Samples the BRDF function.
cos_theta = tf.cos(spherical_coordinates[:, :, 1])
sampled_brdf = tf.maximum(tf.zeros_like(cos_theta), cos_theta / np.pi)

# Samples the zonal SH.
l, m = spherical_harmonics.generate_l_m_zonal(max_band)
l_broadcasted = tf.broadcast_to(l, [brdf_image_width, brdf_image_height] +
                                l.shape.as_list())
m_broadcasted = tf.broadcast_to(m, [brdf_image_width, brdf_image_height] +
                                l.shape.as_list())
theta = tf.expand_dims(spherical_coordinates[:, :, 1], axis=-1)
theta_broadcasted = tf.broadcast_to(
    theta, [brdf_image_width, brdf_image_height, 1])
phi = tf.expand_dims(spherical_coordinates[:, :, 2], axis=-1)
phi_broadcasted = tf.broadcast_to(phi, [brdf_image_width, brdf_image_height, 1])
sh_coefficients = spherical_harmonics.evaluate_spherical_harmonics(
    l_broadcasted, m_broadcasted, theta_broadcasted, phi_broadcasted)
sampled_brdf_broadcasted = tf.expand_dims(sampled_brdf, axis=-1)
sampled_brdf_broadcasted = tf.broadcast_to(
    sampled_brdf_broadcasted,
    [brdf_image_width, brdf_image_height] + l.shape.as_list())

# Integrates the BRDF function times SH over the sphere.
projection = sh_coefficients * sampled_brdf_broadcasted * 4.0 * math.pi / (
    brdf_image_width * brdf_image_height)
brdf_coeffs = tf.reduce_sum(projection, (0, 1))

# Reconstructs the image.
reconstructed_brdf = tf.squeeze(vector.dot(sh_coefficients, brdf_coeffs))

print(
    "average l2 reconstruction error ",
    np.linalg.norm(sampled_brdf - reconstructed_brdf) /
    (brdf_image_width * brdf_image_height))

vmin = np.minimum(np.amin(np.minimum(sampled_brdf, reconstructed_brdf)), 0.0)
vmax = np.maximum(
    np.amax(np.maximum(sampled_brdf, reconstructed_brdf)), 1.0 / np.pi)
# Plots results.
plt.figure(figsize=(10, 10))
ax = plt.subplot("131")
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.grid(False)
ax.set_title("Original reflectance function")
_ = ax.imshow(sampled_brdf, vmin=vmin, vmax=vmax)
ax = plt.subplot("132")
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.grid(False)
ax.set_title("Zonal Harmonics approximation")
_ = ax.imshow(reconstructed_brdf, vmin=vmin, vmax=vmax)
ax = plt.subplot("133")
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.grid(False)
ax.set_title("Difference")
_ = ax.imshow(np.abs(sampled_brdf - reconstructed_brdf), vmin=vmin, vmax=vmax)

plt.figure(figsize=(10, 5))
plt.plot(
    spherical_coordinates[:, 0, 1],
    sampled_brdf[:, 0],
    label="max(0,cos(x) / pi)")
plt.plot(
    spherical_coordinates[:, 0, 1],
    reconstructed_brdf[:, 0],
    label="SH approximation")
plt.title("Approximation quality")
plt.legend()
plt.show()
average l2 reconstruction error  0.0006432566780716586

 

帯域調和関数の回転

###############################
# Rotation of zonal harmonics #
###############################

r_theta = tf.constant(np.pi / 2, shape=(1,), dtype=brdf_coeffs.dtype)
r_phi = tf.constant(0.0, shape=(1,), dtype=brdf_coeffs.dtype)
rotated_zonal_coefficients = spherical_harmonics.rotate_zonal_harmonics(
    brdf_coeffs, r_theta, r_phi)

# Builds the pixels grid and compute corresponding spherical coordinates.
pixel_grid_start = np.array((0, 0), dtype=type)
pixel_grid_end = np.array((brdf_image_width - 1, brdf_image_height - 1),
                          dtype=type)
pixel_nb = np.array((brdf_image_width, brdf_image_height))
pixels = grid.generate(pixel_grid_start, pixel_grid_end, pixel_nb)
normalized_pixels = pixels / (brdf_image_width - 1, brdf_image_height - 1)
spherical_coordinates = tf_math.square_to_spherical_coordinates(
    normalized_pixels)

# reconstruction.
l, m = spherical_harmonics.generate_l_m_permutations(max_band)
l_broadcasted = tf.broadcast_to(
    l, [light_image_width, light_image_height] + l.shape.as_list())
m_broadcasted = tf.broadcast_to(
    m, [light_image_width, light_image_height] + l.shape.as_list())
theta = tf.expand_dims(spherical_coordinates[:, :, 1], axis=-1)
theta_broadcasted = tf.broadcast_to(
    theta, [light_image_width, light_image_height, 1])
phi = tf.expand_dims(spherical_coordinates[:, :, 2], axis=-1)
phi_broadcasted = tf.broadcast_to(
    phi, [light_image_width, light_image_height, 1])
sh_coefficients = spherical_harmonics.evaluate_spherical_harmonics(
    l_broadcasted, m_broadcasted, theta_broadcasted, phi_broadcasted)

reconstructed_rotated_brdf_function = tf.squeeze(
    vector.dot(sh_coefficients, rotated_zonal_coefficients))

plt.figure(figsize=(10, 10))
ax = plt.subplot("121")
ax.set_title("Zonal SH")
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.grid(False)
_ = ax.imshow(reconstructed_brdf)
ax = plt.subplot("122")
ax.set_title("Rotated version")
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.grid(False)
_ = ax.imshow(reconstructed_rotated_brdf_function)

 

SH ライティングと ZH BRDF の球面調和関数畳み込みによる再構築

############################################################################################
# Helper function allowing to estimate sphere normal and depth for each pixel in the image #
############################################################################################
def compute_intersection_normal_sphere(image_width, image_height, sphere_radius,
                                       sphere_center, type):
  pixel_grid_start = np.array((0.5, 0.5), dtype=type)
  pixel_grid_end = np.array((image_width - 0.5, image_height - 0.5), dtype=type)
  pixel_nb = np.array((image_width, image_height))
  pixels = grid.generate(pixel_grid_start, pixel_grid_end, pixel_nb)

  pixel_ray = tf.math.l2_normalize(orthographic.ray(pixels), axis=-1)
  zero_depth = np.zeros([image_width, image_height, 1])
  pixels_3d = orthographic.unproject(pixels, zero_depth)

  intersections_points, normals = ray.intersection_ray_sphere(
      sphere_center, sphere_radius, pixel_ray, pixels_3d)
  return intersections_points[0, :, :, :], normals[0, :, :, :]


###############################
# Setup the image, and sphere #
###############################
# Image dimensions
image_width = 100
image_height = 80

# Sphere center and radius
sphere_radius = np.array((30.0,), dtype=type)
sphere_center = np.array((image_width / 2.0, image_height / 2.0, 100.0),
                         dtype=type)

# Builds the pixels grid and compute corresponding spherical coordinates.
pixel_grid_start = np.array((0, 0), dtype=type)
pixel_grid_end = np.array((image_width - 1, image_height - 1), dtype=type)
pixel_nb = np.array((image_width, image_height))
pixels = grid.generate(pixel_grid_start, pixel_grid_end, pixel_nb)
normalized_pixels = pixels / (image_width - 1, image_height - 1)
spherical_coordinates = tf_math.square_to_spherical_coordinates(
    normalized_pixels)

################################################################################################
# For each pixel in the image, estimate the corresponding surface point and associated normal. #
################################################################################################
intersection_3d, surface_normal = compute_intersection_normal_sphere(
    image_width, image_height, sphere_radius, sphere_center, type)

surface_normals_spherical_coordinates = tf_math.cartesian_to_spherical_coordinates(
    surface_normal)

# SH
l, m = spherical_harmonics.generate_l_m_permutations(
    max_band)  # recomputed => optimize
l = tf.convert_to_tensor(l)
m = tf.convert_to_tensor(m)
l_broadcasted = tf.broadcast_to(l,
                                [image_width, image_height] + l.shape.as_list())
m_broadcasted = tf.broadcast_to(m,
                                [image_width, image_height] + l.shape.as_list())

#################################################
# Estimates result using SH convolution - cheap #
#################################################

sh_integration = spherical_harmonics.integration_product(
    light_coeffs,
    spherical_harmonics.rotate_zonal_harmonics(
        brdf_coeffs,
        tf.expand_dims(surface_normals_spherical_coordinates[:, :, 1], axis=-1),
        tf.expand_dims(surface_normals_spherical_coordinates[:, :, 2],
                       axis=-1)),
    keepdims=False)

# Sets pixels not belonging to the sphere to 0.
sh_integration = tf.where(
    tf.greater(intersection_3d[:, :, 2], 0.0), sh_integration,
    tf.zeros_like(sh_integration))
# Sets pixels with negative light to 0.
sh_integration = tf.where(
    tf.greater(sh_integration, 0.0), sh_integration,
    tf.zeros_like(sh_integration))

###########################################
# 'Brute force' solution - very expensive #
###########################################

factor = 4.0 * np.pi / (light_image_width * light_image_height)
gt = tf.einsum(
    "hwn,uvn->hwuv", surface_normal,
    samples_direction_to_light *
    tf.expand_dims(sampled_light_function, axis=-1))
gt = tf.maximum(gt, 0.0)  # removes negative dot products
gt = tf.reduce_sum(gt, axis=(2, 3))
# Sets pixels not belonging to the sphere to 0.
gt = tf.where(tf.greater(intersection_3d[:, :, 2], 0.0), gt, tf.zeros_like(gt))
gt *= factor

# TODO: gt and sh_integration differ by a factor of pi.
sh_integration = np.transpose(sh_integration, (1, 0))
gt = np.transpose(gt, (1, 0))

plt.figure(figsize=(10, 20))
ax = plt.subplot("121")
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.grid(False)
ax.set_title("SH light and SH BRDF")
_ = ax.imshow(sh_integration, vmin=0.0)
ax = plt.subplot("122")
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.grid(False)
ax.set_title("GT light and GT BRDF")
_ = ax.imshow(gt, vmin=0.0)

 

以上






TensorFlow Graphics : Tutorials : 非剛体サーフェス

TensorFlow Graphics : Intermediate Tutorials : 非剛体サーフェス (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/30/2019

* 本ページは、TensorFlow Graphics の github レポジトリの次のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

 

TensorFlow Graphics : Intermediate Tutorials : 非剛体サーフェス

非剛体サーフェス変形はとりわけ、メッシュを対話的に操作したりポイントクラウドに fit させるためにテンプレートメッシュを変形するために使用できるテクニックです。メッシュを操作するとき、これは例えばユーザにキャラクターの手を動かして腕の残りを現実的な方法で変形させることを可能にします。変形はまたパーツのスケールやメッシュ全体に渡り遂行できることに注意することは興味深いでしょう。

このノートブックは上の画像に含まれる一つに類似した変形を遂行するためにどのように TensorFlow Graphics を使用するかを示します。

 

セットアップ & Imports

このノートブックに含まれるデモを実行するために必要な総てを import しましょう。

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np
import tensorflow as tf

from tensorflow_graphics.geometry.deformation_energy import as_conformal_as_possible
from tensorflow_graphics.geometry.representation.mesh import utils as mesh_utils
from tensorflow_graphics.geometry.transformation import quaternion
from tensorflow_graphics.math.optimizer import levenberg_marquardt
from tensorflow_graphics.notebooks import threejs_visualization
from tensorflow_graphics.notebooks.resources import triangulated_stripe

tf.enable_eager_execution()

この例では、flat と矩形サーフェスに対応するメッシュを構築します。スライダーを使用して、そのサーフェスに適用される変形コンストレイントの位置を制御できます。これらはそれぞれメッシュの左境界、中心、そして右境界に沿った総てのポイントに対応します。

mesh_rest_pose = triangulated_stripe.mesh
connectivity = mesh_utils.extract_unique_edges_from_triangular_mesh(triangulated_stripe.mesh['faces'])
camera = threejs_visualization.build_perspective_camera(
    field_of_view=40.0, position=(0.0, -5.0, 5.0))
width = 500
height = 500
_ = threejs_visualization.triangular_mesh_renderer([mesh_rest_pose],
                                                   width=width,
                                                   height=height,
                                                   camera=camera)

 

以上






TensorFlow Graphics : Tutorials : マテリアルとのライト相互作用 (リフレクション)

TensorFlow Graphics : Intermediate Tutorials : マテリアルとのライト相互作用 (リフレクション) (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/30/2019

* 本ページは、TensorFlow Graphics の github レポジトリの次のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

 

TensorFlow Graphics : Intermediate Tutorials : マテリアルとのライト相互作用 (リフレクション)

私達の回りの世界は非常に複雑でガラスから木に渡る多様なマテリアル (素材) から構成されます。各マテリアルはそれ自身の内部プロパティを所持していてライトと異なった相互作用をします。例えば、幾つかは拡散して (e.g. 紙や大理石) ライティング条件が与えられたとき、どのようなアングルからでも同じように見えます。他の素材 (e.g. 金属) は大きく異なる外見を持ち鏡面 (= specularities) のような視点依存効果を示します。

ライトがどのように素材と相互作用するかを正確にモデル化することは、サブサーフェイス・スキャタリング (e.g. スキン) と反射率 (e.g. 水) のような効果を伴う複雑な過程です。このチュートリアルでは最も一般的な効果に注目します、それは反射です。反射率をモデル化する際、双方向反射率分布関数 (BRDF, Bidirectional Reflectance Distribution Functions) が選択するメソッドです。入射光の方向が与えられた時、BRDF はサーフェスが観測されている方向に跳ね返るライトの総量を制御します (下の画像で任意の gray ベクトル)。

Lambertian = ランバート反射 ; specular = 鏡面反射

 
このチュートリアルでは、3 つの球上にライトをあてます、それぞれ上の画像で記述されたマテリアルを持ち、そこでは鏡面マテリアルは Phong の鏡面モデルでモデル化されます。

Note: このチュートリアルは進んだトピックをカバーしておりそれ故に step by step に詳細を提供するよりも、BRDF の高位な理解を形成するために制御可能な toy サンプルを提供することに注力しています。

 

セットアップ & Imports

このノートブックに含まれるデモを実行するために必要な総てを import しましょう。

###########
# Imports #
###########
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import math as m
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf

from tensorflow_graphics.rendering.reflectance import lambertian
from tensorflow_graphics.rendering.reflectance import phong
from tensorflow_graphics.rendering.camera import orthographic
from tensorflow_graphics.geometry.representation import grid
from tensorflow_graphics.geometry.representation import ray
from tensorflow_graphics.geometry.representation import vector

tf.enable_eager_execution()

 

球体の制御可能なライティング

###########
# Imports #
###########
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import math as m
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf

from tensorflow_graphics.rendering.reflectance import lambertian
from tensorflow_graphics.rendering.reflectance import phong
from tensorflow_graphics.rendering.camera import orthographic
from tensorflow_graphics.geometry.representation import grid
from tensorflow_graphics.geometry.representation import ray
from tensorflow_graphics.geometry.representation import vector

tf.enable_eager_execution()
###############
# UI controls #
###############
#@title Controls { vertical-output: false, run: "auto" }
light_x_position = -0.4  #@param { type: "slider", min: -1, max: 1 , step: 0.05 }
albedo_red = 0.7  #@param { type: "slider", min: 0.0, max: 1.0 , step: 0.1 }
albedo_green = 1  #@param { type: "slider", min: 0.0, max: 1.0 , step: 0.1 }
albedo_blue = 1  #@param { type: "slider", min: 0.0, max: 1.0 , step: 0.1 }
light_red = 1  #@param { type: "slider", min: 0.0, max: 1.0 , step: 0.1 }
light_green = 1  #@param { type: "slider", min: 0.0, max: 1.0 , step: 0.1 }
light_blue = 1  #@param { type: "slider", min: 0.0, max: 1.0 , step: 0.1 }
specular_percentage = 0.25  #@param { type: "slider", min: 0, max: 1 , step: 0.01 }
shininess = 4  #@param { type: "slider", min: 0, max: 10, step: 1 }
diffuse_percentage = 1.0 - specular_percentage
dtype = np.float64
albedo = np.array((albedo_red, albedo_green, albedo_blue), dtype=dtype)

def compute_intersection_normal_sphere(image_width, image_height, sphere_radius,
                                       sphere_center, dtype):
  pixel_grid_start = np.array((0.5, 0.5), dtype=dtype)
  pixel_grid_end = np.array((image_width - 0.5, image_height - 0.5), dtype=dtype)
  pixel_nb = np.array((image_width, image_height))
  pixels = grid.generate(pixel_grid_start, pixel_grid_end, pixel_nb)

  pixel_ray = tf.math.l2_normalize(orthographic.ray(pixels), axis=-1)
  zero_depth = np.zeros([image_width, image_height, 1])
  pixels_3d = orthographic.unproject(pixels, zero_depth)

  intersections_points, normals = ray.intersection_ray_sphere(
      sphere_center, sphere_radius, pixel_ray, pixels_3d)
  intersections_points = np.nan_to_num(intersections_points)
  normals = np.nan_to_num(normals)
  return intersections_points[0, :, :, :], normals[0, :, :, :]

#####################################
# Setup the image, sphere and light #
#####################################
# Image dimensions
image_width = 400
image_height = 300

# Sphere center and radius
sphere_radius = np.array((100.0,), dtype=dtype)
sphere_center = np.array((image_width / 2.0, image_height / 2.0, 300.0),
                         dtype=dtype)

# Set the light along the image plane
light_position = np.array((image_width / 2.0 + light_x_position * image_width,
                           image_height / 2.0, 0.0),
                          dtype=dtype)
vector_light_to_sphere_center = light_position - sphere_center
light_intensity_scale = vector.dot(
    vector_light_to_sphere_center, vector_light_to_sphere_center,
    axis=-1) * 4.0 * m.pi
light_intensity = np.array(
    (light_red, light_green, light_blue)) * light_intensity_scale

################################################################################################
# For each pixel in the image, estimate the corresponding surface point and associated normal. #
################################################################################################
intersection_3d, surface_normal = compute_intersection_normal_sphere(
    image_width, image_height, sphere_radius, sphere_center, dtype)

#######################################
# Reflectance and radiance estimation #
#######################################
incoming_light_direction = tf.math.l2_normalize(
    intersection_3d - light_position, axis=-1)
outgoing_ray = np.array((0.0, 0.0, -1.0), dtype=dtype)
albedo = tf.broadcast_to(albedo, tf.shape(surface_normal))

# Lambertian BRDF
brdf_lambertian = diffuse_percentage * lambertian.brdf(incoming_light_direction, outgoing_ray,
                                  surface_normal, albedo)
# Phong BRDF
brdf_phong = specular_percentage * phong.brdf(incoming_light_direction, outgoing_ray, surface_normal,
                        np.array((shininess,), dtype=dtype), albedo)
# Composite BRDF
brdf_composite = brdf_lambertian + brdf_phong
# Irradiance
cosine_term = vector.dot(surface_normal, -incoming_light_direction)
cosine_term = tf.math.maximum(tf.zeros_like(cosine_term), cosine_term)
vector_light_to_surface = intersection_3d - light_position
light_to_surface_distance_squared = vector.dot(
    vector_light_to_surface, vector_light_to_surface, axis=-1)
irradiance = light_intensity / (4 * m.pi *
                                light_to_surface_distance_squared) * cosine_term
# Rendering equation
zeros = tf.zeros(intersection_3d.shape)
radiance = brdf_composite * irradiance
radiance_lambertian = brdf_lambertian * irradiance
radiance_phong = brdf_phong * irradiance

###############################
# Display the rendered sphere #
###############################
# Saturates radiances at 1 for rendering purposes.
radiance = np.minimum(radiance, 1.0)
radiance_lambertian = np.minimum(radiance_lambertian, 1.0)
radiance_phong = np.minimum(radiance_phong, 1.0)
# Gammma correction
radiance = np.power(radiance, 1.0 / 2.2)
radiance_lambertian = np.power(radiance_lambertian, 1.0 / 2.2)
radiance_phong = np.power(radiance_phong, 1.0 / 2.2)

plt.figure(figsize=(20, 20))

# Diffuse
radiance_lambertian = np.transpose(radiance_lambertian, (1, 0, 2))
ax = plt.subplot("131")
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.grid(False)
ax.set_title("Lambertian")
_ = ax.imshow(radiance_lambertian)

# Specular
radiance_phong = np.transpose(radiance_phong, (1, 0, 2))
ax = plt.subplot("132")
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.grid(False)
ax.set_title("Specular - Phong")
_ = ax.imshow(radiance_phong)

# Diffuse + specular
radiance = np.transpose(radiance, (1, 0, 2))
ax = plt.subplot("133")
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.grid(False)
ax.set_title("Combined lambertian and specular")
_ = ax.imshow(radiance)

 

以上






TensorFlow Graphics : Tutorials : B-スプラインと Slerp 補間

TensorFlow Graphics : Intermediate Tutorials : B-スプラインと Slerp 補間 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/28/2019

* 本ページは、TensorFlow Graphics の github レポジトリの次のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

 

TensorFlow Graphics : Intermediate Tutorials : B-スプラインと slerp 補間

限定的な数の事前定義されたポイントと関連する値が与えられたとき、補間は新しいデータポイントを事前定義されたポイントの範囲内で予測することを可能にします。

下の例で、左のプロットは青いドットで表わされるサンプルを示します。これらのサンプルが滑らかな関数に由来すると仮定すると、これらのドット間のもっともらしい (= plausible) 値を見つけるために利用可能な多くの選択肢があります。最初の選択肢は中央のプロットで観察できるように、隣り合う点の任意のペアを線で結びつける区分線形関数を構築することです。もう一つの広く使用される選択肢はこれらのサンプルに多項式を fit させることです。右のプロットはサンプルに fit された三次多項式を示します。

このノートブックは B-スプラインと球面線形補間を TensorFlow Graphicsを使用してどのように遂行するかを示します。

 

セットアップ & Imports

このノートブックに含まれるデモを実行するために必要な総てを import しましょう。

import matplotlib.pyplot as plt
import tensorflow as tf

from tensorflow_graphics.math.interpolation import bspline
from tensorflow_graphics.math.interpolation import slerp

tf.enable_eager_execution()

 

Slerp 補間

Lerp はポイント間を線形に補間することを可能にする広く使用される補間テクニックです。冒頭で記述された区間線形補間は線形補間のピースから効果的に構成されます。しかしデータが円上または球面に在る場合はどうでしょう?その場合、Lerp は補間するための良い方法を提供しませんが、幸い、Slear は提供するでしょう!Slerp は球面線形補間を表しクォータニオンを補間するコンテキストで導入されました、これは回転の形式化です。

次のデモは 2 つのベクトルを定義することを可能にします。それぞれ円の中心か始まり円上で終わります。これらは間を補間することを望むベクトルを定義し、そしてスライダー「パーセント」は各ベクトルが補間されたベクトルに影響を与える程度を制御します。結果のベクトルはまた円上で終わることに注意してください。

#@title Slerp - Vectors will be normalized first { vertical-output: true, run: "auto"}

vector_1_x = -0.56  #@param { type: "slider", min: -1.0, max: 1.0, step: 0.01}
vector_1_y = -0.39  #@param { type: "slider", min: -1.0, max: 1.0, step: 0.01}
vector_2_x = 0.47  #@param { type: "slider", min: -1.0, max: 1.0, step: 0.01}
vector_2_y = 0.74  #@param { type: "slider", min: -1.0, max: 1.0, step: 0.01}
percent = 0.7  #@param { type: "slider", min: 0.0, max: 1.0, step: 0.01}

vector_1 = tf.constant((vector_1_x, vector_1_y), dtype=tf.float32)
vector_2 = tf.constant((vector_2_x, vector_2_y), dtype=tf.float32)
vector_1 = tf.nn.l2_normalize(vector_1)
vector_2 = tf.nn.l2_normalize(vector_2)
vector_3 = slerp.interpolate(
    vector_1, vector_2, percent, method=slerp.InterpolationType.VECTOR)

v1 = vector_1.numpy()
v2 = vector_2.numpy()
v3 = vector_3.numpy()

plt.figure(figsize=(10, 10))
circle = plt.Circle((0, 0), 1.0, color='g', fill=False)
ax = plt.gca()
ax.add_artist(circle)
plt.arrow(
    0.0, 0.0, v1[0], v1[1], width=0.001, color='k', length_includes_head=True)
plt.arrow(
    0.0, 0.0, v2[0], v2[1], width=0.001, color='b', length_includes_head=True)
plt.arrow(
    0.0, 0.0, v3[0], v3[1], width=0.001, color='r', length_includes_head=True)
plt.axis((-1.1, 1.1, -1.1, 1.1))
plt.show()

 

B-スプライン補間

グローバルサポートを持つ他の補間テクニック (e.g. ベジェ曲線、グローバル多項式補間) とは対照的に、B-スプラインはローカルコントロールを持つ区間多項式関数です。コントールは「ノット」と呼ばれるポイントの位置に由来します。

#@title B-Spline Interpolation { vertical-output: true, run: "auto"}

num_knots = 5
cyclical = True  #@param { type: "boolean" }
degree = 3  #@param { type: "slider", min: 1, max: 4, step: 1}
knot_1_x = -2.5  #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_1_y = -1  #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_2_x = -1.5  #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_2_y = 2  #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_3_x = 0  #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_3_y = -3  #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_4_x = 1.5  #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_4_y = 3  #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_5_x = 3  #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_5_y = 0  #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}

max_pos = num_knots if cyclical else num_knots - degree
knots = tf.constant(((knot_1_x, knot_2_x, knot_3_x, knot_4_x, knot_5_x),
                     (knot_1_y, knot_2_y, knot_3_y, knot_4_y, knot_5_y)))

positions = tf.expand_dims(
    tf.range(start=0.0, limit=max_pos, delta=0.01, dtype=knots.dtype), axis=-1)

spline = bspline.interpolate(knots, positions, degree, cyclical)
spline = tf.squeeze(spline, axis=1)

plt.figure(figsize=(10, 10))
plt.plot(spline[:, 0], spline[:, 1], 'r')
plt.plot(knots[0, :], knots[1, :], 'b.')
plt.axis((-3.5, 3.5, -3.5, 3.5))
plt.show()

 

以上






TensorFlow Graphics : Tutorials : カメラ・キャリブレーション

TensorFlow Graphics : Beginner Tutorials : カメラ・キャリブレーション (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/28/2019

* 本ページは、TensorFlow Graphics の github レポジトリの次のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

 

TensorFlow Graphics : Beginner Tutorials : カメラ・キャリブレーション

カメラは 3D オブジェクトの 2D 画像を捕捉できるハードウェアの複雑な部品群です。実際に、リアルなカメラを正確にモデル化するためには、レンズ・ディストーション (= distortion, 歪曲収差)、ISO、焦点距離そして露出時間を含む多くのパラメータを推定する必要があります。以下では、私達の興味を射影カメラモデルに制限します。

このモデルはしばしば内部パラメータとして参照される 2 つのパラメータから成ります :

  • 主点、これは画像上の光学中心の投影です。理想的には、主点は画像の中心に近いです。
  • 焦点距離、これは光学中心とイメージプレーンの間の距離です。このパラメータはズームのレベルを制御することを可能にします。

このノートブックは射影カメラの内部パラメータを推定するために Tensorflow Graphics をどのように利用するかを示します。これらのパラメータのリカバーは 3D 再構成を含む幾つかのタスクを遂行するために特に重要です。

この Colab では、目標は観察とその観察と現在のソリューションのレンダリングの間の対応が与えられたときにカメラの内部パラメータをリカバーすることです。3D シーンに矩形だけを挿入してそれを最適化の間の対応のソースとして使用することにより単純さが保持されます。最小化はレーベンバーグ・マーカート・アルゴリズムを使用して遂行されます。

 

セットアップ & Imports

このノートブックに含まれるデモを実行するために必要な総てを import しましょう。

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np

#################################
# Imports the necessary modules #
#################################

from tensorflow_graphics.math.optimizer import levenberg_marquardt
from tensorflow_graphics.rendering.camera import perspective  

tf.enable_eager_execution()

 

射影カメラモデルを理解する

このモデルがどのように動作するかを示すため、カメラの前のシーンには矩形以外は何もないと仮定します。最初にこの矩形をカメラにより観測されたものとしてレンダーする関数を定義しましょう。

def render_rectangle(rectangle_vertices, focal, principal_point, image_dimensions):
  """Renders a rectangle on the image plane.

  Args:
    rectangle_vertices: the four 3D corners of a rectangle.
    focal: the focal lengths of a projective camera.
    principal_point: the position of the principal point on the image plane.
    image_dimensions: the dimensions (in pixels) of the image.

  Returns:
    A 2d image of the 3D rectangle.
  """
  image = np.zeros((int(image_dimensions[0]), int(image_dimensions[1]), 3))
  vertices_2d = perspective.project(rectangle_vertices, focal, principal_point)
  vertices_2d_np = vertices_2d.numpy()
  top_left_corner = np.maximum(vertices_2d_np[0, :], (0, 0)).astype(int)
  bottom_right_corner = np.minimum(
      vertices_2d_np[1, :],
      (image_dimensions[1] - 1, image_dimensions[0] - 1)).astype(int)
  for x in range(top_left_corner[0], bottom_right_corner[0] + 1):
    for y in range(top_left_corner[1], bottom_right_corner[1] + 1):
      c1 = float(bottom_right_corner[0] + 1 -
                 x) / float(bottom_right_corner[0] + 1 - top_left_corner[0])
      c2 = float(bottom_right_corner[1] + 1 -
                 y) / float(bottom_right_corner[1] + 1 - top_left_corner[1])
      image[y, x] = (c1, c2, 1)
  return image

次のセルはデフォルト内部パラメータを定義してこれらのパラメータを使用して矩形をレンダーします。


# Sets up the vertices of the rectangle.
rectangle_depth = 1000.0
rectangle_vertices = np.array(
    ((-150.0, -75.0, rectangle_depth), (150.0, 75.0, rectangle_depth)))

# Sets up the size of the image plane.
image_width = 400
image_height = 300
image_dimensions = np.array((image_height, image_width), dtype=np.float64)

# Sets the horizontal and vertical focal length to be the same. The focal length
# picked yields a field of view around 50degrees.
focal_lengths = np.array((image_height, image_height), dtype=np.float64)
# Sets the principal point at the image center.
ideal_principal_point = np.array(
    (image_width, image_height), dtype=np.float64) / 2.0

# Let's see what our scene looks like using the intrinsic paramters defined above.
render = render_rectangle(rectangle_vertices, focal_lengths, ideal_principal_point,
                          image_dimensions)
_ = plt.imshow(render)

焦点距離と光学中心の位置は最終的なイメージ上で非常に異なる効果を持ちます。これらのパラメータの各々の効果を納得するために下のスライダーの異なる configuration を使用してください。

###############
# UI controls #
###############
#@title model parameters { vertical-output: false, run: "auto" }
focal_length_x = 300  #@param { type: "slider", min: 100.0, max: 500.0, step: 1.0 }
focal_length_y = 300  #@param { type: "slider", min: 100.0, max: 500.0, step: 1.0 }
optical_center_x = 200  #@param { type: "slider", min: 0.0, max: 400.0, step: 1.0 }
optical_center_y = 150.0  #@param { type: "slider", min: 0.0, max: 300.0, step: 1.0 }

render = render_rectangle(
    rectangle_vertices,
    np.array((focal_length_x, focal_length_y), dtype=np.float64),
    np.array((optical_center_x, optical_center_y), dtype=np.float64),
    image_dimensions)
_ = plt.imshow(render)

(訳注: 以下はパラメータを変更して得られたイメージです : )

焦点距離 100 焦点距離 500

光学中心 x == 0 光学中心 x == 400

光学中心 y == 0 光学中心 y == 300


 

内部パラメータを最適化する

総てのカメラ (e.g. スマートフォン・カメラ) はそれ自身の内部パラメータのセットを持ちます。他のアプリケーションの中では、3D シーン再構成、ロボティクス、そしてナビゲーションシステムにおいて正確な内部パラメータが使用されます。

内部パラメータを推定するための一般的な方法は既知の 3D オブジェクトを使用することです。内部パラメータの現在の推定を使用して、既知の 3D オブジェクトがどのように「見える」べきかを予測して、そしてそれを実際の観測と比較す比較することができます。

結果をプロットする助けとなる幾つかのヘルパー関数を定義することから始めましょう。

def plot_optimization_step(observation, prediction):
  plt.figure(figsize=(20, 10))
  ax = plt.subplot("131")
  ax.set_title("Observation")
  _ = ax.imshow(observation)
  ax = plt.subplot("132")
  ax.set_title("Prediction using estimated intrinsics")
  _ = ax.imshow(prediction)
  ax = plt.subplot("133")
  ax.set_title("Difference image")
  _ = ax.imshow(np.abs(observation - prediction))
  plt.show()


def print_errors(focal_error, center_error):
  print("Error focal length %f" % (focal_error,))
  print("Err principal point %f" % (center_error,))

さて私達が求めている内部パラメータの値、そしてこれらのパラメータの値の初期推測を定義しましょう。

def build_parameters():
  # Constructs the intrinsic parameters we wish to recover.
  real_focal_lengths = focal_lengths * np.random.uniform(0.8, 1.2, size=(2,))
  real_principal_point = ideal_principal_point + (np.random.random(2) -
                                                  0.5) * image_width / 5.0

  # Initializes the first estimate of the intrinsic parameters.
  estimate_focal_lengths = tf.Variable(real_focal_lengths +
                                       (np.random.random(2) - 0.5) *
                                       image_width)
  estimate_principal_point = tf.Variable(real_principal_point +
                                         (np.random.random(2) - 0.5) *
                                         image_width / 4)
  return real_focal_lengths, real_principal_point, estimate_focal_lengths, estimate_principal_point

前述したように、内部パラメータの現在の推定を使用して 3D オブジェクトがどのように見えるか比較することができます、それを実際の観測と比較することができます。次の関数は私達が最小化することを求めるこれら 2 つのイメージ間の距離を捕捉します。

def residuals(estimate_focal_lengths, estimate_principal_point):
  vertices_2d_gt = perspective.project(rectangle_vertices, real_focal_lengths,
                                       real_principal_point)
  vertices_2d_observed = perspective.project(rectangle_vertices,
                                             estimate_focal_lengths,
                                             estimate_principal_point)
  return vertices_2d_gt - vertices_2d_observed

今では問題を解くための総てのピースが適所にあります; let’s give it a go!

Note: 残差は Levenberg-Marquard を使用して最小化されます、これは特にこの問題のために示されます。ファーストクラスの optimizers (e.g. Adam や勾配降下) もまた使用できます。

# Samples intrinsic parameters to recover and an initial solution.
real_focal_lengths, real_principal_point, estimate_focal_lengths, estimate_principal_point = build_parameters(
)

# Contructs the observed image.
observation = render_rectangle(rectangle_vertices, real_focal_lengths,
                               real_principal_point, image_dimensions)

# Displays the initial solution.
print("Initial configuration:")
print_errors(
    np.linalg.norm(estimate_focal_lengths - real_focal_lengths),
    np.linalg.norm(estimate_principal_point - real_principal_point))
image = render_rectangle(rectangle_vertices, estimate_focal_lengths,
                         estimate_principal_point, image_dimensions)
plot_optimization_step(observation, image)

# Optimization.
_, (estimate_focal_lengths,
    estimate_principal_point) = levenberg_marquardt.minimize(
        residuals, (estimate_focal_lengths, estimate_principal_point), 1)

print("Predicted configuration:")
print_errors(
    np.linalg.norm(estimate_focal_lengths - real_focal_lengths),
    np.linalg.norm(estimate_principal_point - real_principal_point))
image = render_rectangle(rectangle_vertices, estimate_focal_lengths,
                         estimate_principal_point, image_dimensions)
plot_optimization_step(observation, image)

 

以上






AI導入支援 #2 ウェビナー

スモールスタートを可能としたAI導入支援   Vol.2
[無料 WEB セミナー] [詳細]
「画像認識 AI PoC スターターパック」の紹介
既に AI 技術を実ビジネスで活用し、成果を上げている日本企業も多く存在しており、競争優位なビジネスを展開しております。
しかしながら AI を導入したくとも PoC (概念実証) だけでも高額な費用がかかり取組めていない企業も少なくないようです。A I導入時には欠かせない PoC を手軽にしかも短期間で認知度を確認可能とするサービの紹介と共に、AI 技術の特性と具体的な導入プロセスに加え運用時のポイントについても解説いたします。
日時:2021年10月13日(水)
会場:WEBセミナー
共催:クラスキャット、日本FLOW(株)
後援:働き方改革推進コンソーシアム
参加費: 無料 (事前登録制)
人工知能開発支援
◆ クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com