コンテンツにスキップ

Udon で計算する ONNX モデル(試験中)

この章は試験中です。 [Onnx("…")] は、入力と出力を配列で渡し、結果を数として読み出します。

モデルのファイルJudge.cs と同じフォルダの gesture.onnx
入力pose — float32 の要素が 12 個(形は [1,12])
出力scores — float32 の要素が 4 個(形は [1,4])
生成されるファイルJudge.Gesture.Model.cs
組み込む手順組み込む手順のとおりです
using Tsukimi;
using UnityEngine;
// 属性の引数は、このスクリプトから見たモデルのファイルの経路です。
// 属性を 2 つ以上書くと、モデルごとに名前が生えます。
[Onnx("gesture.onnx")]
public partial class Judge : TsukimiBehaviour
{
// 生成されたファイルがこのクラスの残りになるので、partial が必要です。
// 入力に詰める値の取り出し元。Inspector で 12 個挿します。
public Transform[] joints;
// 入力と出力をまとめて持つ値。型は生成されたファイルが宣言します。
private GestureIO io;
// 選ばれた番号。他の Behaviour から読めます。
public int chosen;
void Start()
{
io = new GestureIO();
// 配列の確保は 1 度だけです。以後は同じ配列の中身を書き換えます。
// 要素数は生成された定数を使います(モデルを差し替えると値も変わります)。
// 配列の型はモデルの要素の型で決まります(このモデルは float32 なので float[])。
io.In0 = new float[SizeIn0];
io.Out0 = new float[SizeOut0];
}
public void Decide()
{
// 入力は全部詰めます。詰め残した要素は前回の値のまま計算されます。
for (int i = 0; i < SizeIn0; i++) io.In0[i] = joints[i].localPosition.y;
// 計算します。返るまでに全部の層が進みます。
RunGesture(io);
// 結果は io.Out0 に入ります。ここでは最も大きい成分の番号を選びます。
chosen = 0;
for (int i = 1; i < SizeOut0; i++)
if (io.Out0[i] > io.Out0[chosen]) chosen = i;
}
}

重みと層の中身を省いた抜き書きです。

// This file was generated from an ONNX model. Manual edits are lost on the next generation.
// Source model: gesture
using UnityEngine;
// 入力と出力をまとめた型。もとのテンソルの名前と形がコメントに残ります。
public struct GestureIO
{
public float[] In0; // pose [1,12]
public float[] Out0; // scores [1,4]
}
public partial class Judge
{
// 配列を確保するときに使う要素数。コンパイルの時点で決まっています。
public const int SizeIn0 = 12; // [1,12]
public const int SizeOut0 = 4; // [1,4]
// private なので、入力を詰めずに外から呼ぶことはできません。
private void RunGesture(GestureIO io)
{
float[] In0 = io.In0; // pose
float[] Out0 = io.Out0; // scores
// 学習した重みは、配列への代入として並びます。
float[] c0 = new float[12];
c0[0] = 0.0002085293f;
c0[1] = 0.71990794f;
// …(重みの数だけ続きます)…
// 層と層の間に挟む配列。4 つずつまとめられる層は Vector4 になります。
float[] t5 = new float[12]; // centered [1,12]
Vector4[] t7 = new Vector4[6]; // g0 [1,24]
// 層は、もとのモデルでの名前と演算つきで 1 つずつ並びます。
// center: Elementwise pose mean -> centered
for (int i = 0; i < 12; i++)
{
t5[i] = In0[i] - c0[i];
}
// …(層の数だけ続きます)…
// out: Copy g2 -> scores
for (int i = 0; i < 4; i++)
{
Out0[i] = t11[i];
}
}
}
生成される名前説明
GestureIO入力と出力を持つ型。中身はモデルの要素の型の配列で(要素の型)、In0・Out0 のように並びます
SizeIn0 / SizeOut0配列ごとの要素数。配列は Start で 1 度だけ確保します
RunGesture(io)その値の入力で計算し、出力を同じ値へ書き戻します
In0 / In1 …番号はモデルの入力の並び順です。出力も同じ規則です
呼び出せる場所計算するメソッドは外部から呼び出せません。入力を代入しないまま計算することを防ぐためです
費用計算は Udon の上で 1 つずつ進みます。毎フレーム計算する前にプロファイラで計測してください
重みの格納先重みはコードの中の定数です。モデルのファイルは配布物に含める必要がありません
計算しない出力どのノードも計算しない出力(モデルに保存した定数・出力にも挙げた入力)を持つモデルはエラーです。その定数や入力の配列は、自分のコードで読みます
モデルの要素の型入力と出力の配列
float32float[]
float64double[]
uint8 / int8byte[] / sbyte[]
uint16 / int16ushort[] / short[]
uint32 / int32uint[] / int[]
uint64 / int64ulong[] / long[]
boolbool[]
float16・bfloat16・float8・4 ビットの整数・複素数・文字列Udon に無い型なので、入力か重みに 1 つでもあれば、型を名指してコンパイルのエラーになります(float16・bfloat16 のモデルは float32 に変換してから書き出します)

計算はその型のまま行い、整数は桁あふれで折り返します(uint8 の 0 - 1 は 255・演算ごとに受け取る型はONNX モデルの演算(試験中)にあります)。