Skip to content

Commit 9b95475

Browse files
authored
Stabilize reduced-precision InstanceNorm (#4230)
1 parent 18672fb commit 9b95475

2 files changed

Lines changed: 28 additions & 6 deletions

File tree

python/mlx/nn/layers/normalization.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,19 @@ def __call__(self, x: mx.array) -> mx.array:
6464
f"InstanceNorm expects inputs with at least 3 dimensions"
6565
f" (N, ..., C) but the input has {x.ndim} dimensions."
6666
)
67-
reduction_axes = tuple(range(1, x.ndim - 1))
68-
# Compute stats
69-
mean = mx.mean(x, axis=reduction_axes, keepdims=True)
70-
var = mx.var(x, axis=reduction_axes, keepdims=True)
71-
# Normalize
72-
x = (x - mean) * mx.rsqrt(var + self.eps)
67+
batch_size, features = x.shape[0], x.shape[-1]
68+
spatial_shape = x.shape[1:-1]
69+
channels_first = mx.transpose(x, (0, x.ndim - 1, *range(1, x.ndim - 1)))
70+
x = mx.fast.layer_norm(
71+
channels_first.reshape(batch_size, features, -1),
72+
None,
73+
None,
74+
self.eps,
75+
)
76+
x = mx.transpose(
77+
x.reshape(batch_size, features, *spatial_shape),
78+
(0, *range(2, len(spatial_shape) + 2), 1),
79+
)
7380
# Scale and shift if necessary
7481
return (self.weight * x + self.bias) if "weight" in self else x
7582

python/tests/test_nn.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,21 @@ def test_instance_norm(self):
693693
]
694694
self.assertTrue(x.shape == y.shape)
695695
self.assertTrue(np.allclose(y, expected_y, atol=1e-5))
696+
# Reduced-precision statistics must not overflow for finite feature maps.
697+
checkerboard = np.indices((4, 4, 4)).sum(axis=0) % 2
698+
x = mx.array(
699+
np.stack(
700+
[
701+
np.where(checkerboard, -512, 512),
702+
np.where(checkerboard, -256, 256),
703+
],
704+
axis=-1,
705+
).astype(np.float16)
706+
)[None]
707+
y = nn.InstanceNorm(dims=2)(x)
708+
self.assertEqual(y.dtype, mx.float16)
709+
self.assertTrue(mx.allclose(y.min(), mx.array(-1.0, dtype=mx.float16)))
710+
self.assertTrue(mx.allclose(y.max(), mx.array(1.0, dtype=mx.float16)))
696711
# Test repr
697712
self.assertTrue(str(inorm) == "InstanceNorm(3, eps=1e-05, affine=False)")
698713
# Raise for inputs without spatial dimensions

0 commit comments

Comments
 (0)