ニューラル ネットワーク エンジン (NNE) は、さまざまなニューラル ネットワーク ランタイム (別名、実行プロバイダ) にアクセスし、ランタイム固有のコーディングを必要とすることなく、ニューラル ネットワークを評価するための共通の API を提供します。Unreal Engine は個別のランタイムを搭載しており、対応するプラグインを有効にすることでプロジェクトに追加できます。使用するモデルやターゲット ハードウェアに応じて、プロジェクトの NNE アセットからニューラル ネットワークを作成して実行するために最適なランタイムを動的に選択できます。
NNE は、ランタイムが実装するさまざまなインターフェースを提供することで、幅広いユース ケースに対応します。これらのユース ケースはそれぞれ、異なる方法でニューラル ネットワークを実行する必要があります。これらのインターフェースは、CPU 上で実行するか、GPU 上で実行するか、フレームのレンダリングに合わせて GPU 上で実行するかを決定します。この実装により、プロジェクトでニューラル ネットワーク モデルを実行するかどうか、またどのように実行するかを最大限に制御できます。
NNE を使用してリアルタイム推論を実行し、人工知能 (AI) でゲームを拡張したり、アセット操作、クエリ、アーティスト支援ツールとしてエディタベースの機能を実装することができます。
以下は、NNE を構成する重要な要素です。
- インターフェース:NNE ランタイム プラグインのアクセス可能な部分です。
- ランタイム:プロジェクトで 1 つ以上のインターフェースを実装するために使用するプラグインです。
- アセット:ランタイムが有効な場合に Unreal Engine にインポートできるニューラル ネットワーク モデル アセットです。
- モデル:ニューラル ネットワーク モデル アセット内に格納されたデータです。
インターフェース
インターフェースは、NNE Runtime プラグインのアクセス可能な部分です。API をクリーンでわかりやすく保つために、ランタイムに直接アクセスするのではなく、ランタイムが実装するインターフェース クラスを使用し、当該インターフェースがそのランタイムの API を公開します。各インターフェースは明確に定義された 1 つのユースケースを対象としており、ランタイムはそのユースケースのタイプを評価できるかどうかに基づいてインターフェースを実装します。
ユースケースの例としては、CPU 上で同期推論呼び出しを呼び出し、INNERuntimeCPU
および INNERuntimeGPU
を使用して GPU 上のフレーム レンダリングとモデルの評価を整合させることが挙げられます。INNERuntimeRDG
は、レンダリング依存関係グラフ (FRDGBuilder) を使用して、非同期にエンキューされます。NNE は、API をクリーンでわかりやすいものに保つために、それぞれが単一の種類のユースケースに対応できるように設計された、異なるインターフェースを公開しています。
この一貫性により、ランタイム デベロッパーは各インターフェースに対応する機能のサポートに集中することができます。後方互換性を維持しながら、新しい種類のユースケースに対応するために、将来的に新しいインターフェースを追加することができます。
使用可能なインターフェースは次のとおりです。
インターフェース名 | 説明 |
---|---|
INNERuntimeCPU |
このインターフェースは、推論が CPU 上で実行される必要のあるすべてのユースケースに対応します。入力テンソルと出力テンソルは CPU 上で提供され、他のデバイスへのメモリ転送は行われません。GPU にバジェットがない場合や、GPU メモリとの同期が計算の高速化を正当化しない場合に適しています。このインターフェースによって作成されたモデル インスタンスは、呼び出し元がスレッドの安全性とモデルのメモリ存続期間 (入力と出力の両方のメモリを含む) に注意する限り、ゲーム スレッド (同期)、非同期タスク、またはその他のスレッドで実行できます。 |
INNERuntimeGPU |
このインターフェースは GPU 上でニューラル ネットワークを評価するランタイムに対応します。入力テンソルと出力テンソルは CPU メモリとして提供され、ランタイムがそれらをアップロード/ダウンロードする GPU との同期が必要です。推論はフレームのレンダリングとは無関係に実行されるものの、GPU リソースのレンダリング パイプラインと競合します。このインターフェースは、通常、追加の GPU 同期とリソースの競合がパフォーマンスに影響しないエディタのみのユースケースに対応します。INNERuntimeCPU と同様に、このインターフェースによって作成されたモデル インスタンスは、呼び出し元がスレッドの安全性とメモリ存続期間に注意する限り、どのスレッドからでも実行できます。 |
INNERuntimeRDG |
このインターフェースは、レンダリング依存関係グラフ (RDG) の一部としてニューラル ネットワークの評価に対応し、提供される RDG グラフ ビルダーにモデル評価を追加します。このインターフェースは、ニューラル ネットワークがフレームのレンダリング中に使用されるリソースを消費したり、生成したりするときに使用されます。推論は GPU 上で実行され、入力テンソルと出力テンソルは RDG バッファとして提供される必要があります。推論呼び出しはレンダリング スレッドから呼び出され、モデルの作成と設定は通常ゲーム スレッドで実行されます。これにより、エンジン リソースとの緊密な統合が可能になります。 |
ランタイム
ランタイム は NNE インターフェースを実装するプラグインです。ランタイムは通常、起動時に自身を NNE に登録します。
関数 TArray<FString>UE::NNE::GetAllRuntimeNames()
を使用すると、実装しているインターフェースに関係なく、利用可能なすべてのランタイムの名前を取得できます。
テンプレート化された関数 TArray<FString>UE::NNE::GetAllRuntimeNames<T>()
を使用すると、指定されたインターフェースを実装するランタイム名の事前にフィルタされたリストを取得することができます。たとえば、CPU 上で実行されるインターフェースを実装するすべてのランタイムを取得するには、UE::NNE::GetAllRuntimeNames<INNERuntimeCPU>()
を使用します。
ランタイムを取得するには、TWeakInterfacePtr<INNERuntime>UE::NNE::GetRuntime(const FString& Name)
関数を使用します。または、TWeakInterfacePtr<T>UE::NNE::GetRuntime<T>(const FString& Name)
を使用して、対応するテンプレート化された関数を取得することもできます。
対応するプラグインが有効になっていても、すべてのランタイムがすべてのプラットフォームで利用できるわけではありません。ランタイムが利用できなかったり、利用できるものの、テンプレート化された関数で渡されたインターフェースを実装していない場合、返されるウイーク ポインターは null になります。ランタイムは自身をアンロードできるため、使用する前にウイーク ポインターの有効性をテストする必要があります。
ランタイムは、通常、関連するプラグインおよびモジュールとともに、ランタイム自身の登録/登録解除、ロード/アンロードを行います。ただし、ランタイムの存続期間と登録は、固有の実装次第です。
アセット
NNE プラグインを使用すると、Runtime プラグインがそのファイル タイプをサポートしている場合に、ニューラル ネットワーク モデル ファイルを Unreal Engine に直接インポートすることができます。Unreal Engine では、インポートされたニューラル ネットワーク モデル用の NNE モデル データ アセットがコンテンツ ブラウザに作成されます。
すべてのランタイムがすべてのファイル形式をサポートしているわけではありません。ファイルの種類が正常にインポートされたと表示されても、その特定のランタイムに対応するモデルの作成に失敗することがあります。サポートしているファイル形式については、使用しているランタイムを参照してください。
インポートに成功すると、エンジンは UNNEModelData アセットを作成します。コンテンツ ブラウザからこのアセットを開き、プロジェクトに実装されている特定のランタイムに対してモデルを有効または無効にします。不要なランタイムを削除すると、パッケージング速度が向上し、プロジェクトのパッケージ サイズが削減されます。各モデルはデフォルトで各ランタイムに対して最適化されています。ただし、使用する予定のモデルがあるランタイムのみを選択することをお勧めします。
Unreal Engine では、NNE モデルのデータ アセットは他の UE アセットと同じ方法でロードされます。たとえば、UPROPERTY デコレータを持つ UNNEModelData
型のパブリック クラス変数がアクタ内部で定義され、エディタでモデルがそれに割り当てられると、アクタがスポーンされたときに自動的にロードされます。または、コンテンツ パスが既知である場合は、Unreal Engine の関数 LoadObject
を使用してプログラムでアセットをロードすることもできます。
モデル
モデル は、インターフェース内に含まれ、ロードされた UNNEModelData アセットを使用してランタイムによって実装されます。以下の関数を使用して、アセットからモデルを作成できます。
TSharedPtr<UE::NNE:IModelCPU> INNERuntimeCPU::CreateModelCPU(const TObjectPtr<UNNEModelData> ModelData)
TSharedPtr<UE::NNE:IModelGPU> INNERuntimeGPU::CreateModelGPU(const TObjectPtr<UNNEModelData> ModelData)
TSharedPtr<UE::NNE:IModelRDG> INNERuntimeRDG::CreateModelRDG(const TObjectPtr<UNNEModelData> ModelData)
すべてのランタイムが任意の NNE モデル データ アセットからモデルを作成できるわけではありません。ランタイムは Unreal Editor 内でクックに必要な要素として利用できますが、モデルは現在のプラットフォーム上で推論を実行できない場合があります。次の関数を使用すると、モデルを作成できるかどうかを示すステータスを返すことができます。
ECanCreateModelCPUStatus INNERuntimeCPU::CanCreateModelCPU(const TObjectPtr<UNNEModelData> ModelData) const
ECanCreateModelGPUStatus INNERuntimeGPU::CanCreateModelGPU(const TObjectPtr<UNNEModelData> ModelData) const
ECanCreateModelRDGStatus INNERuntimeRDG::CanCreateModelRDG(const TObjectPtr<UNNEModelData> ModelData) const
これらの関数はモデルを作成する前に呼び出す必要があります。結果がモデルを作成できることを示していても、内部エラーによって実際の作成に失敗することがあります。モデルを使用する前に、返されたモデルへの共有ポインタの妥当性を常に確認する必要があります。
作成されたモデルには、通常、モデルの重みとパラメータとして、モデルの異なるインスタンス間で共有される不変のバッファが含まれています。モデルが内部的に必要なデータへのポインタを保持していることを確認するため、モデル作成後に UNNEModelData を解放することができます。
モデル インスタンス
次の関数のいずれかを使用してニューラル ネットワーク モデルから モデル インスタンス を作成することで、推論を実行することができます。
TSharedPtr<UE::NNE::IModelInstanceCPU> UE::NNE::IModelCPU::CreateModelInstanceCPU()
TSharedPtr<UE::NNE::IModelInstanceGPU> UE::NNE::IModelGPU::CreateModelInstanceGPU()
TSharedPtr<UE::NNE::IModelInstanceRDG> UE::NNE::IModelRDG::CreateModelInstanceRDG()
モデル インスタンスには、通常、内部ステートと中間バッファとしてセッション固有のデータが含まれます。重みとパラメータとしてモデルの不変データを共有する単一のモデルから、複数のインスタンスを作成することができます。インスタンスは必要な共有データへの参照を保持するため、インスタンスが作成された後にモデルを解放することができます。
最初の推論呼び出しの前に、モデルが適切にサイズ設定された内部バッファを割り当てられるように、次の関数を呼び出す必要があります。
UE::NNE::ESetInputTensorShapesStatus SetInputTensorShapes(TConstArrayView<UE::NNE::FTensorShape> InInputShapes)
ランタイムは、インポート時やクック時に捕捉できなかった潜在的なエラーを報告するため、返されたステータス コードを確認する必要があります。この関数は、入力形状が変更されるたびに再度呼び出す必要があります。計算リソースのオーバーヘッドを低く保つために、不必要な呼び出しの繰り返しは回避します。
呼び出し元は NNE モデル インスタンスの入出力メモリを所有します。呼び出し元は、完全な推論呼び出しの間、入力テンソルと出力テンソルのメモリが有効なままで、変更されていないことを確認する必要があります。これはスレッド化されたユースケースでは特に注意が必要です。
ティックまたはフレームごとに複数回モデル インスタンスを評価する必要がある場合、複数の個別の呼び出しを実行するよりもバッチを評価する方がパフォーマンスが高いため、入力データのバッチ処理を推奨します。バッチ処理によって複数の呼び出しを同期させることができない場合、NNE が要求するスレッドの安全性に違反することなく、複数のインスタンスが同時に推論を実行できます。
最小限の NNE の例
次の必要なポイントとコード スニペットの例を使用して、CPU 上でニューラル ネットワークを使い始めることができます。
簡単で読みやすくするために、この例では入力テンソルおよび出力テンソルの準備や設定方法について詳しく説明しません。またこの例では、実際のユースケースで実行する必要のある結果のチェックも行っていません。
使用方法の詳細については、「ニューラル ネットワーク エンジンのクイック スタート ガイド」を参照してください。
使用を開始するには、次の手順を実行します。
- Unreal Engine の Plugins ブラウザから NNE Runtime プラグインを有効にします。
- プロジェクトの
.Build.cs
ファイルに依存関係としてNNE
モジュールを追加します。 - コンテンツ ブラウザからニューラル ネットワーク ファイル (
*.onnx
ファイル タイプなど) を Unreal Engine にインポートします。
次のコードでモデルをロードして実行します。
// NNE ヘッダをインクルードする
#include “NNE.h”
#include “NNERuntimeCPU.h”
#include “NNEModelData.h”
// ニューラル ネットワーク モデルのデータ アセットからモデルを作成する
TObjectPtr<UNNEModelData> ModelData = LoadObject<UNNEModelData>(GetTransientPackage(), TEXT("/path/to/asset"));
TWeakInterfacePtr<INNERuntimeCPU> Runtime = UE::NNE::GetRuntime<INNERuntimeCPU>(FString("NNERuntimeORTCpu"));
TSharedPtr<UE::NNE::IModelInstanceCPU> ModelInstance = Runtime->CreateModelCPU(ModelData)->CreateModelInstanceCPU();
// TODO:入力テンソルおよび出力テンソル、テンソル バインディング、および対応する入力形状を設定します。
// 特定の入力サイズが指定されたモデルを準備する
ModelInstance->SetInputTensorShapes(InputShapes);
// 呼び出し元が所有する CPU メモリを渡してモデルを実行する
ModelInstance->RunSync(Inputs, Outputs);