// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "LearningArray.h" #include "Templates/SharedPointer.h" #include "LearningNeuralNetwork.generated.h" class UNNEModelData; namespace UE::NNE { class IModelCPU; class IModelInstanceCPU; } namespace UE::Learning { struct FNeuralNetwork; struct FNeuralNetworkInference; } /** * Neural Network Data Object * * This is the UObject which contains the actual data used by a Neural Network. It stores the raw FileData used to construct the network using NNE, * as well as the input and output sizes and a compatibility hash that can be used to quickly check if two networks may be compatible in terms of * inputs and outputs. * * Internally this also stores the various things required to map between NNE style inference and the style of inference used in Learning via * FNeuralNetwork and FNeuralNetworkInference. */ UCLASS(BlueprintType) class LEARNING_API ULearningNeuralNetworkData : public UObject { GENERATED_BODY() public: // Get the FNeuralNetwork object that can be used to do inference. TSharedPtr& GetNetwork(); /** * Initialize the ULearningNeuralNetworkData object given some input and output sizes as well as a compatibility hash and FileData. * * @param InInputSize Network Input Size * @param InOutputSize Network Output Size * @param InCompatibilityHash Compatibility Hash * @param InFileData Network FileData */ void Init( const int32 InInputSize, const int32 InOutputSize, const int32 InCompatibilityHash, const TArrayView InFileData); // Initialize this network from another ULearningNeuralNetworkData object void InitFrom(const ULearningNeuralNetworkData* OtherNetworkData); // Load a snapshot of this network from the given array of bytes bool LoadFromSnapshot(const TArrayView InBytes); // Save a snapshot of this network to the given array of bytes void SaveToSnapshot(TArrayView OutBytes) const; // Number of bytes required to save or load a snapshot of this network int32 GetSnapshotByteNum() const; // If this network is empty or not bool IsEmpty() const; // Gets the network input size int32 GetInputSize() const; // Gets the network output size int32 GetOutputSize() const; // Gets the compatibility hash int32 GetCompatibilityHash() const; // Gets the content hash int32 GetContentHash() const; private: // Calls UpdateNetwork to update the internal representation. virtual void PostLoad() override final; // Uploads the FileData to NNE and updates the internal in-memory representation of the network used for inference. void UpdateNetwork(); // Size of the inputs expected by this network UPROPERTY(VisibleAnywhere, Category = "Network Properties") int32 InputSize = 0; // Size of the output produced by this network UPROPERTY(VisibleAnywhere, Category = "Network Properties") int32 OutputSize = 0; // Compatibility hash used for testing if inputs to one network are compatible with another UPROPERTY(VisibleAnywhere, Category = "Network Properties") int32 CompatibilityHash = 0; // Content hash which can be used to see when the network data has changed UPROPERTY(VisibleAnywhere, Category = "Network Properties") int32 ContentHash = 0; // File data used by NNE UPROPERTY() TArray FileData; // Model Data used by NNE UPROPERTY() TObjectPtr ModelData; // Internal in-memory network representation TSharedPtr Network; }; namespace UE::Learning { /** * Settings object for a neural network instance */ struct LEARNING_API FNeuralNetworkInferenceSettings { // If to allow for multi-threaded evaluation bool bParallelEvaluation = true; // Minimum batch size to use for multi-threaded evaluation uint16 MinParallelBatchSize = 16; }; /** * Neural Network Object */ struct LEARNING_API FNeuralNetwork { /** Create a new inference object for this network with the given maximum batch size and inference settings. */ TSharedRef CreateInferenceObject( const int32 MaxBatchSize, const FNeuralNetworkInferenceSettings& Settings = FNeuralNetworkInferenceSettings()); bool IsEmpty() const; int32 GetInputSize() const; int32 GetOutputSize() const; void UpdateModel(const TSharedPtr& InModel, const int32 InInputSize, const int32 InOutputSize); private: int32 InputSize = 0; int32 OutputSize = 0; TSharedPtr Model; TArray, TInlineAllocator<64>> InferenceObjects; FRWLock Lock; }; /** * Neural Network Inference Object */ struct LEARNING_API FNeuralNetworkInference { /** * Constructs a new network inference object. Generally this should not be called directly and FNeuralNetwork::CreateInferenceObject should * be used instead so that created instances are tracked. * * @param InModel NNE Model * @param InMaxBatchSize Maximum batch size * @param InInputSize Network input size * @param InOutputSize Network output size * @param InSettings Inference settings */ FNeuralNetworkInference( UE::NNE::IModelCPU& InModel, const int32 InMaxBatchSize, const int32 InInputSize, const int32 InOutputSize, const FNeuralNetworkInferenceSettings& InSettings = FNeuralNetworkInferenceSettings()); /** * Evaluate this network. * * @param Output The Output Buffer of shape (<= MaxBatchSize, OutputSize) * @param Input The Input Buffer of shape (<= MaxBatchSize, InputSize) */ void Evaluate(TLearningArrayView<2, float> Output, const TLearningArrayView<2, const float> Input); // This function will re-build the internal Model Instances used for multi-threading. It should be called whenever the given Model is updated. void ReloadModelInstances(NNE::IModelCPU& Model, const int32 InInputSize, const int32 InOutputSize); int32 GetMaxBatchSize() const; int32 GetInputSize() const; int32 GetOutputSize() const; private: int32 MaxBatchSize = 0; int32 InputSize = 0; int32 OutputSize = 0; FNeuralNetworkInferenceSettings Settings; TArray, TInlineAllocator<64>> ModelInstances; }; /** * Neural-network based function object. */ struct LEARNING_API FNeuralNetworkFunction { /** * Constructs a Neural Network Function from the given Neural Network * * @param InMaxInstanceNum Maximum number of instances to evaluate for * @param InNeuralNetwork Neural network to use * @param InInferenceSettings Inference settings */ FNeuralNetworkFunction( const int32 InMaxInstanceNum, const TSharedPtr& InNeuralNetwork, const FNeuralNetworkInferenceSettings& InInferenceSettings = FNeuralNetworkInferenceSettings()); /** * Evaluate this network for the given instances. * * Note: this function takes a lock, so although it can be called from multiple threads if the set of Instances don't overlap, it will not * benefit from multi-threading. This is because to use the NNE interface all the instances that need to be evaluated must be gathered and * scattered into internal buffers. * * @param Output The Output Buffer of shape (<= MaxInstanceNum, OutputSize) * @param Input The Input Buffer of shape (<= MaxInstanceNum, InputSize) * @param Instances The instances to evaluate */ void Evaluate( TLearningArrayView<2, float> Output, const TLearningArrayView<2, const float> Input, const FIndexSet Instances); /** Sets the NeuralNetwork and re-creates the NeuralNetworkInference object. */ void UpdateNeuralNetwork(const TSharedPtr& NewNeuralNetwork); /** Gets the NeuralNetwork associated with this function */ const TSharedPtr& GetNeuralNetwork() const; private: int32 MaxInstanceNum = 0; FRWLock EvaluationLock; TLearningArray<2, float> InputBuffer; TLearningArray<2, float> OutputBuffer; TSharedPtr NeuralNetwork; TSharedPtr NeuralNetworkInference; FNeuralNetworkInferenceSettings InferenceSettings; }; }