@@ -219,7 +219,9 @@ def _evaluation_metrics(self, rollouts):
219219 # Actions are (time, n_agents, n_factors)
220220 per_agent = torch .cat (
221221 [
222- rollout .get ((group , "action" ))[:, agent_index ].reshape (- 1 , len (self ._factor_sizes )).long ()
222+ rollout .get ((group , "action" ))[:, agent_index ]
223+ .reshape (- 1 , len (self ._factor_sizes ))
224+ .long ()
223225 for rollout in rollouts
224226 ]
225227 )
@@ -352,7 +354,9 @@ def _get_policy_for_loss(self, group, model_config, continuous, _orig=original):
352354
353355 n_agents = len (self .group_map [group ])
354356 actor_module = model_config .get_model (
355- input_spec = _Composite ({group : self .observation_spec [group ].clone ().to (self .device )}),
357+ input_spec = _Composite (
358+ {group : self .observation_spec [group ].clone ().to (self .device )}
359+ ),
356360 output_spec = _Composite (
357361 {
358362 group : _Composite (
@@ -395,6 +399,51 @@ def _get_policy_for_loss(self, group, model_config, continuous, _orig=original):
395399_ADVANTAGE_AGENT_DIM = - 2
396400
397401
402+ def _enable_per_agent_advantage_normalization (
403+ loss_module , group : str , n_agents : int
404+ ) -> None :
405+ """
406+ BenchMARL hardcodes `normalize_advantage=False` and doesn't expose it on the config,
407+ which deviates loss_obj from entropy bonus.
408+
409+ `normalize_advantage_exclude_dims` (in TorchRL) is what we want, but it's not implemented in BenchMARL.
410+ The flag will be added in this PR: https://github.com/facebookresearch/BenchMARL/pull/256.
411+ """
412+ for attribute in ("normalize_advantage" , "normalize_advantage_exclude_dims" ):
413+ if not hasattr (loss_module , attribute ):
414+ raise RuntimeError (
415+ f"{ type (loss_module ).__name__ } has no { attribute !r} . Check TorchRL version"
416+ )
417+
418+ loss_module .normalize_advantage = True
419+ loss_module .normalize_advantage_exclude_dims = (_ADVANTAGE_AGENT_DIM ,)
420+
421+ # check -2 is agent dimension
422+ advantage_key = loss_module .tensor_keys .advantage
423+ inner_forward = loss_module .forward
424+ state = {"verified" : False }
425+
426+ def forward (tensordict , * args , ** kwargs ):
427+ if not state ["verified" ]:
428+ advantage = tensordict .get (advantage_key , None )
429+ if advantage is None :
430+ raise RuntimeError (
431+ f"[{ group } ] { advantage_key } absent before the loss forward"
432+ )
433+ shape = tuple (advantage .shape )
434+ if advantage .ndim < 3 or shape [- 1 ] != 1 or shape [- 2 ] != n_agents :
435+ raise RuntimeError (
436+ f"[{ group } ] expected advantage of shape (..., { n_agents } , 1) so "
437+ f"that dim { _ADVANTAGE_AGENT_DIM } holds agents, got { shape } ."
438+ )
439+ print (
440+ f"[adv-norm] { group } : (advantage { shape } , agent dim { _ADVANTAGE_AGENT_DIM } , { n_agents } agents)" ,
441+ flush = True ,
442+ )
443+ state ["verified" ] = True
444+ return inner_forward (tensordict , * args , ** kwargs )
445+
446+ loss_module .forward = forward
398447
399448
400449class VizdoomExperiment (Experiment ):
@@ -404,8 +453,18 @@ class VizdoomExperiment(Experiment):
404453 - job_type : algorithm name
405454 - group : "<environment>/<task>"
406455 - id / name : "<algo>_<task>_<N>agents_seed<S>_<timestamp>"
456+
457+ and turns on per-agent advantage normalization.
407458 """
408459
460+ def _setup_algorithm (self ):
461+ super ()._setup_algorithm ()
462+ for group , loss_module in self .losses .items ():
463+ if hasattr (loss_module , "normalize_advantage" ):
464+ _enable_per_agent_advantage_normalization (
465+ loss_module , group , len (self .group_map [group ])
466+ )
467+
409468 def _setup_logger (self ):
410469 num_agents = sum (len (v ) for v in self .group_map .values ())
411470 run_id = self .task .config .get ("run_id" )
@@ -423,7 +482,9 @@ def _setup_logger(self):
423482 "name" : run_id ,
424483 }
425484
426- action_spec = self .test_env .input_spec ["full_action_spec" , next (iter (self .group_map )), "action" ]
485+ action_spec = self .test_env .input_spec [
486+ "full_action_spec" , next (iter (self .group_map )), "action"
487+ ]
427488 original = self .config .wandb_extra_kwargs
428489 self .config .wandb_extra_kwargs = {** original , ** extra }
429490 try :
0 commit comments