MRが楽しい

MRやVRについて学習したことを書き残す

Unity AIのドキュメントを読む その59(モデルからの出力を取得する)

本日はUnityの技術調査枠です。
Unity AIのドキュメントを読みながら実際に操作を試して記事に残します。

Unity AI

以下のUnity AIのドキュメントを試しながら実行時のキャプチャをしていきます。
docs.unity3d.com

モデルからの出力を取得する

ここではモデルからの出力を取得する方法を解説します。
テンソルの出力を取得するには以下の2つの方法があります。

  • PeekOutputを使用して出力テンソルへの参照を取得します。
  • CopyOutputを使用してワーカーのスコープ外で管理するテンソルに出力をコピーします。

PeekOutputを使用する

PeekOutputを使用してテンソルの出力への参照を取得します。
PeekOutputはTensorオブジェクトを返すため、通常はTensorまたはTensorにキャストする必要があります。

worker.Schedule(inputTensor);
Tensor<float> outputTensor = worker.PeekOutput() as Tensor<float>;

推論エンジンのワーカーメモリアロケータはPeekOutputから返される参照を所有します。

  • 出力に対してDisposeを使用する必要はありません。
  • 出力を変更するかワーカーを再実行すると、ワーカー出力とPeekOutputのコピーの両方が変更されます。
  • ワーカーに対してDisposeを使用すると、PeekOutputのコピーが破棄されます。

再度Scheduleを呼び出すと、テンソルが上書きされます。

Note

テンソルの出力からデータを読み取る際は注意が必要です。
多くの場合、モデルの実行が完了するまでGPUまたはCPUにデータをダウンロードする前に、ブロッキング待機が発生する可能性があります。
このオーバーヘッドを軽減するにはモデルからの出力を非同期で読み取ることを検討してください。

元のテンソルのデータをダウンロードする

テンソル出力のデータの読み取り専用のNativeArrayまたはArrayコピーへのブロッキングダウンロードを以下の方法で実行できます。

  • PeekOutputを使用した後、テンソルに対してDownloadToNativeArrayを使用します。
  • PeekOutputを使用した後、テンソルに対してDownloadToArrayを使用します。

元のテンソルのデータを待機する

データ取得中にモデルがブロックされるのを回避するには出力テンソルの非同期リードバックを要求できます。

Tensor<float> outputTensor = worker.PeekOutput() as Tensor<float>;
var result = await outputTensor.ReadbackAndCloneAsync();
Tensor<float> outputTensor = worker.PeekOutput() as Tensor<float>;
outputTensor.ReadbackRequest();

// 完了時
outputTensor.ReadbackAndClone(); // ブロックしない

CopyOutputを使用する

CopyOutputを使用すると、ワーカーの出力をワーカーのスコープ外で管理するテンソルにコピーできます。

  • nullを渡すと、推論エンジンはワーカーの出力のコピーを含む新しいテンソルを作成して返します。
  • 既存のテンソルを渡すと、推論エンジンは出力の形状に合わせてテンソルの形状を変更して出力データをそのテンソルにコピーします。
Tensor myOutputTensor;
//...
void Update () {
   worker.Schedule(inputTensor);
   worker.CopyOutput("output", ref myOutputTensor);
}

CopyOutputは計算された出力形状に合わせて指定されたテンソルの形状を変更します。
その際、指定したテンソルの出力容量を確認してください。

// The model outputs a tensor of shape (1, 10)

// CopyOutputは空のテンソル、つまりテンソルデータのないテンソルで動作します。
myOutputTensor = new Tensor<float>(new TensorShape(1, 10), data: null);
worker.CopyOutput("output", ref myOutputTensor);

// CopyOutputIntoはdataOnBackendが十分な容量を持っている限り、異なる形状のテンソルでも動作します。
myOutputTensor = new Tensor<float>(new TensorShape(152));
worker.CopyOutput("output", ref myOutputTensor);
// myOutputTensor now has shape (1, 10) but still has dataOnBackend.maxCapacity == 152

CopyOutputを使用する場合、出力を受け取るテンソルの管理を行ってください。

  • テンソルの使用が終了したらメモリを解放するためにDispose()を呼び出す必要があります。
  • Worker.Scheduleを再度呼び出してもテンソルは自動的に更新されません。最新の出力が必要な場合はモデルのスケジュール設定後にCopyOutputを再度呼び出します。

複数の出力

モデルに複数の出力がある場合、Worker.PeekOutputのパラメーターとして各出力名を使用できます。