|
10 | 10 | from deepspeed.utils import safe_get_local_grad, safe_set_local_grad |
11 | 11 | from deepspeed.accelerator import get_accelerator |
12 | 12 | from unit.simple_model import SimpleModel |
| 13 | +from unit.common import DistributedTest |
13 | 14 | import os |
14 | 15 |
|
15 | 16 |
|
@@ -48,6 +49,38 @@ def get_config(precision, clip_value, offload_device="cpu"): |
48 | 49 | return config |
49 | 50 |
|
50 | 51 |
|
| 52 | +@pytest.mark.parametrize("zero_stage", [1, 2, 3]) |
| 53 | +@pytest.mark.parametrize("norm_type", [1, 2, 3]) |
| 54 | +class TestZeroGradNormPNorm(DistributedTest): |
| 55 | + world_size = 1 |
| 56 | + |
| 57 | + def test_matches_flat_norm(self, zero_stage, norm_type): |
| 58 | + # get_grad_norm_direct returns the norm of the gradients viewed as a single vector, |
| 59 | + # so on one rank with no model parallelism it must equal the p-norm of the |
| 60 | + # concatenation. norm_type 2 is the control: it is right on both sides. |
| 61 | + config = { |
| 62 | + "train_batch_size": 1, |
| 63 | + "optimizer": { |
| 64 | + "type": "Adam", |
| 65 | + "params": { |
| 66 | + "lr": 1e-4 |
| 67 | + } |
| 68 | + }, |
| 69 | + "zero_optimization": { |
| 70 | + "stage": zero_stage |
| 71 | + }, |
| 72 | + } |
| 73 | + model = SimpleModel(hidden_dim=4, nlayers=2) |
| 74 | + engine, optimizer, _, _ = deepspeed.initialize(model=model, model_parameters=model.parameters(), config=config) |
| 75 | + |
| 76 | + gradients = [torch.Tensor([3.0, -4.0]), torch.Tensor([2.0])] |
| 77 | + params = list(model.parameters())[:len(gradients)] |
| 78 | + expected = torch.cat([g.reshape(-1) for g in gradients]).norm(float(norm_type)) |
| 79 | + |
| 80 | + actual = optimizer.get_grad_norm_direct(gradients, params, norm_type=norm_type) |
| 81 | + assert torch.allclose(torch.as_tensor(actual).float().cpu(), expected.float().cpu()) |
| 82 | + |
| 83 | + |
51 | 84 | @pytest.mark.parametrize("precision,clip_value,offload_device", [ |
52 | 85 | ("fp16", 0.5, "cpu"), |
53 | 86 | ("bf16", 0.05, "cpu"), |
|
0 commit comments