Hello. I faced a problem working with inclusive union actions when implementing Imitation Learning recording. I am working with stock UE 5.7.2.
So my action schema currently looks like this:
- root: exclusive union (EU) of 3 top level actions: locomotion, combat, NULL (when inference decides that nothing must be done by an agent, which is a desirable optional behavior)
- combat: EU of attack and dodge
- locomotion: EU of blocking locomotion (jumps, mantles) and non-blocking locomotion (various combineable locomotion actions)
- non blocking locomotion: INCLUSIVE UNION of move, rotate, set speed, upper body animations (which itself is an EU)
In my current setup I have a player imitator character, and for each relevant imitator player action (move, rotate, etc) I push these actions to IL recording actions buffer to be gathered by LA controller (in ULearningAgentsController::EvaluateAgentController_Implementation) on next controller update (each 0.1s). And non-blocking locomotion actions are combineable in this actions buffer, e.g. if durung 100ms imitator moved and rotated right after it then harvested action would be IU of move + rotate. But it can also be that there’s only move or rotate or there’s no pending IU non-blocking locomotion action at all. When IL controller gathers actions, I use ULearningAgentsActions::Make_X_Action functions to make the FLearningAgentsActionObjectElement instances.
THE PROBLEM:
On each IL recording manager update, there is a stacktrace of calls:
UE::Learning::Action::SetVectorFromObject
ULearningAgentsController::EvaluateController
ULearningAgentsController::RunController
and in UE::Learning::Action::SetVectorFromObject for IU action case there’s a check
check(SubElementOffset + SchemaParameters.Elements.Num() == OutActionVector.Num());
and this check gets triggered for my locomotion IU every time. The only way I found to prevent this check from being triggered is to pass all subactions of locomotion IU no matter if imitator did all of them or not. For that I updated my IU to wrap all subactions into optional because I believe it would be easier to distinguish non-taken action as a null optional action rather than an actual action with “null” value. But it kind of makes things ugly. Like, when I specify IU subactions I provide base probabilities for each of them. And then, when I wrap each action into an optional I also provide a probability for each of them, but the probability of each IU subaction element must always be 1 since it is now optional action that does the coin flip.
Another idea that I had is to basically remove the check from UE::Learning::Action::SetVectorFromObject, but
- idk if it’s a good idea. Like, I don’t understand ML and all of its implications that much to be certain that this is a bug and/or not intended behavior and it can be safely removed without any other errors arise later on during training/inference.
- It cannot be easily done as majority of things to get to that check are either not virtual or not exposed by module API or both, which means I would have to basically copy-paste the whole LA framework to my project from engine folder just to change 1 line (and, frankly, get better debugging support).
I also check logs including LogLearning category each debug session and, as far as I can tell, there are no other issues with my observations and actions schemas. (there were, but I addressed all of them)
THE QUESTION:
Is it exactly the way IU is expected to work? I mean I assumed that IU means “this action can contain SOME of these subactions but they all are probabilistic, i.e. they can be present or absent in whatever combination”. But if I must always provide all subactions of IU as an imitator then I might as well change it to a struct action of multiple optionals. It would at least make more sense as in this case I would only have probabilities of optionals and not optionals AND IU subactions.