Skip to content

Commit 0563015

Browse files
pdufterchanglan
authored andcommitted
add option to add the global grad norm for sub-components of the network to summaries
GitOrigin-RevId: 02f7cfd
1 parent 1b17f81 commit 0563015

2 files changed

Lines changed: 101 additions & 31 deletions

File tree

axlearn/common/learner.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
import dataclasses
1313
import enum
1414
from collections.abc import Mapping, Sequence
15-
from typing import Any, Callable, Optional, cast
15+
from typing import Any, Callable, List, Optional, Union, cast
1616

1717
import jax
1818
import optax
19+
from absl import logging
1920
from jax import numpy as jnp
2021

2122
from axlearn.common.base_layer import ParameterSpec
@@ -47,6 +48,7 @@
4748
Nested,
4849
Tensor,
4950
flatten_items,
51+
get_recursively,
5052
match_regex_rules,
5153
prune_empty,
5254
register_per_param_settings,
@@ -166,9 +168,10 @@ class Config(BaseLearner.Config):
166168
#
167169
# See optimizers.param_ema for more details.
168170
ema: InstantiableConfig = config_for_function(param_ema)
169-
# Whether to add per variable gradient and norm summaries. Enable it will make
170-
# the training slower since the summary is computed for every step.
171-
enable_per_variable_summaries: bool = False
171+
# Whether to add per variable gradient and norm summaries. Enable it will make the training
172+
# slower since the summary is computed for every step. When a list of strings is provided,
173+
# the global norm for each specified component (prefix-matched) is computed.
174+
enable_per_variable_summaries: Union[bool, List[str]] = False
172175
# Optional decorator for the ForwardFn. An example use would be to enable gradient
173176
# accumulation by using `with_minibatch_steps` decorator defined below. E.g.
174177
# learner.forward_fn_transformation = config.config_for_function(with_minibatch_steps).set(
@@ -269,7 +272,7 @@ def _compute_updated_params(
269272
state_updates: Nested[Tensor],
270273
) -> Nested[Tensor]:
271274
cfg = self.config
272-
if cfg.enable_per_variable_summaries:
275+
if cfg.enable_per_variable_summaries is True:
273276
param_rms = jax.tree.map(
274277
lambda p: optax.safe_root_mean_squares(p.value, min_rms=1e-3), opt_params
275278
)
@@ -280,6 +283,20 @@ def _compute_updated_params(
280283
)
281284
for p, g_n in flatten_items(grad_rms):
282285
self.add_summary(f"grad_rms/{p}", g_n)
286+
elif isinstance(cfg.enable_per_variable_summaries, list):
287+
# Expecting a list of strings for which to get grad norm summaries,
288+
# e.g., ["comp_1", "comp_2/weight"]
289+
for variable_prefix in cfg.enable_per_variable_summaries:
290+
try:
291+
variable_grad_norm = get_recursively(gradients, variable_prefix)
292+
except KeyError:
293+
# fail gracefully to not interrupt training
294+
logging.error("Could not get grad norm for %s", variable_prefix)
295+
variable_grad_norm = {}
296+
297+
# Using global norm when subcomponents are used.
298+
variable_grad_norm = optax.global_norm(variable_grad_norm)
299+
self.add_summary(f"grad_norm/{variable_prefix}", variable_grad_norm)
283300

284301
# Set `parameter_updates` to 0 if the param is not updated by the optimizer.
285302
parameter_updates = jax.tree.map(

axlearn/common/learner_test.py

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,11 @@ def loss_fn(params):
586586

587587
self.assertNestedAllClose(dict(v=expected_new_v), updated_params, atol=1e-6)
588588

589-
def test_per_variable_summaries(self):
589+
@parameterized.parameters(
590+
(True,),
591+
(["comp_1", "comp_2/weight"],),
592+
)
593+
def test_per_variable_summaries(self, enable_per_variable_summaries):
590594
sgd_cfg = config_for_function(sgd_optimizer).set(
591595
learning_rate=1.0,
592596
decouple_weight_decay=True,
@@ -596,31 +600,58 @@ def test_per_variable_summaries(self):
596600
args=(config_for_function(clip_by_global_norm), sgd_cfg),
597601
)
598602
cfg = Learner.default_config().set(
599-
name="test", optimizer=optimizer_cfg, enable_per_variable_summaries=True
603+
name="test",
604+
optimizer=optimizer_cfg,
605+
enable_per_variable_summaries=enable_per_variable_summaries,
600606
)
601607
learner: Learner = cfg.instantiate(parent=None)
602608
params = dict(
603-
weight=OptParam(
604-
value=jnp.asarray([0, 2, 2, -3], dtype=jnp.float32),
605-
factorization_spec=None,
606-
weight_decay_scale=1.0,
609+
comp_1=dict(
610+
weight=OptParam(
611+
value=jnp.asarray([0, 2, 2, -3], dtype=jnp.float32),
612+
factorization_spec=None,
613+
weight_decay_scale=1.0,
614+
),
615+
moving_mean=OptParam(
616+
value=jnp.array([0, -1, 0, 0], dtype=jnp.float32),
617+
factorization_spec=None,
618+
weight_decay_scale=0.0,
619+
),
607620
),
608-
moving_mean=OptParam(
609-
value=jnp.array([0, -1, 0, 0], dtype=jnp.float32),
610-
factorization_spec=None,
611-
weight_decay_scale=0.0,
621+
comp_2=dict(
622+
weight=OptParam(
623+
value=jnp.asarray([0, 2, 2, -3], dtype=jnp.float32),
624+
factorization_spec=None,
625+
weight_decay_scale=1.0,
626+
),
627+
bias=OptParam(
628+
value=jnp.array([0, -1, 0, 0], dtype=jnp.float32),
629+
factorization_spec=None,
630+
weight_decay_scale=0.0,
631+
),
612632
),
613633
)
614634
state = learner.init(model_params=params)
615635

616636
def loss_fn(x):
617-
return -jax.nn.log_softmax(x["weight"] + x["moving_mean"])[1]
637+
return -jax.nn.log_softmax(
638+
x["comp_1"]["weight"]
639+
+ x["comp_1"]["moving_mean"]
640+
+ x["comp_2"]["weight"]
641+
+ x["comp_2"]["bias"]
642+
)[1]
618643

619644
loss, grads = jax.value_and_grad(loss_fn)(jax.tree.map(lambda p: p.value, params))
620-
np.testing.assert_allclose(loss, 1.412078, atol=1e-6, rtol=1e-6)
621-
expected_grad = jnp.asarray([0.089629, -0.756364, 0.662272, 0.004462])
645+
np.testing.assert_allclose(loss, 2.142971, atol=1e-6, rtol=1e-6)
646+
expected_grad = jnp.asarray([0.01587562, -0.8826942, 0.86677927, 0.00003935])
622647
self.assertNestedAllClose(
623-
dict(weight=expected_grad, moving_mean=expected_grad), grads, atol=1e-6, rtol=1e-6
648+
dict(
649+
comp_1=dict(weight=expected_grad, moving_mean=expected_grad),
650+
comp_2=dict(weight=expected_grad, bias=expected_grad),
651+
),
652+
grads,
653+
atol=1e-6,
654+
rtol=1e-6,
624655
)
625656
_, output_collection = F(
626657
learner,
@@ -632,22 +663,44 @@ def loss_fn(x):
632663
Updates(
633664
delta_updates=grads,
634665
opt_params=params,
635-
inplace_updates=dict(moving_mean=params["moving_mean"].value + 1),
666+
inplace_updates=dict(
667+
dict(comp_1=dict(moving_mean=params["comp_1"]["moving_mean"].value + 1))
668+
),
636669
)
637670
],
638671
)
672+
expected_summaries = {
673+
"optimizer/learning_rate": 1.0,
674+
"optimizer/lr_schedule_step": 1,
675+
"optimizer/gradient_norm": jnp.sqrt(jnp.sum(4 * expected_grad**2)),
676+
"optimizer/schedule_step": 1,
677+
"optimizer/schedule_scale": -1.0,
678+
}
679+
680+
if enable_per_variable_summaries is True:
681+
expected_summaries = {
682+
**expected_summaries,
683+
**{
684+
"param_rms/comp_1/weight": jnp.sqrt((0 + 4 + 4 + 9) / 4),
685+
"param_rms/comp_1/moving_mean": 0.5,
686+
"param_rms/comp_2/weight": jnp.sqrt((0 + 4 + 4 + 9) / 4),
687+
"param_rms/comp_2/bias": 0.5,
688+
"grad_rms/comp_1/weight": jnp.sqrt(jnp.mean(expected_grad**2)),
689+
"grad_rms/comp_1/moving_mean": jnp.sqrt(jnp.mean(expected_grad**2)),
690+
"grad_rms/comp_2/weight": jnp.sqrt(jnp.mean(expected_grad**2)),
691+
"grad_rms/comp_2/bias": jnp.sqrt(jnp.mean(expected_grad**2)),
692+
},
693+
}
694+
else:
695+
expected_summaries = {
696+
**expected_summaries,
697+
**{
698+
"grad_norm/comp_1": jnp.sqrt(jnp.sum(2 * expected_grad**2)),
699+
"grad_norm/comp_2/weight": jnp.sqrt(jnp.sum(expected_grad**2)),
700+
},
701+
}
639702
self.assertNestedAllClose(
640-
{
641-
"optimizer/learning_rate": 1.0,
642-
"optimizer/lr_schedule_step": 1,
643-
"optimizer/gradient_norm": jnp.sqrt(jnp.sum(2 * expected_grad**2)),
644-
"param_rms/weight": jnp.sqrt((0 + 4 + 4 + 9) / 4),
645-
"param_rms/moving_mean": 0.5,
646-
"grad_rms/weight": jnp.sqrt(jnp.mean(expected_grad**2)),
647-
"grad_rms/moving_mean": jnp.sqrt(jnp.mean(expected_grad**2)),
648-
"optimizer/schedule_step": 1,
649-
"optimizer/schedule_scale": -1.0,
650-
},
703+
expected_summaries,
651704
output_collection.summaries,
652705
)
653706

0 commit comments

Comments
 (0)