| import einops |
| import torch |
| from jaxtyping import Float |
| from torch.nn import Linear |
|
|
| from .types import Stats |
|
|
|
|
| def unit_norm_decoder(decoder: Linear | None) -> None: |
| """Unit-normalize the decoder weight vectors.""" |
| if decoder is None: |
| return |
|
|
| decoder.weight.data /= decoder.weight.data.norm(dim=0) |
|
|
|
|
| |
| @torch.no_grad() |
| def unit_norm_decoder_gradient(decoder: Linear | None) -> None: |
| """ |
| Remove the component of the gradient parallel to the decoder weight vectors. |
| Assumes that the decoder weight vectors are unit-normalized. |
| NOTE: Without `@torch.no_grad()`, this causes a memory leak! |
| """ |
| if decoder is None: |
| return |
| |
| if decoder.weight.grad is None: |
| return |
| scalar = einops.einsum( |
| decoder.weight.grad, |
| decoder.weight, |
| "... n_latents n_inputs, ... n_latents n_inputs -> ... n_inputs", |
| ) |
| vector = einops.einsum( |
| scalar, |
| decoder.weight, |
| "... n_inputs, ... n_latents n_inputs -> ... n_latents n_inputs", |
| ) |
| decoder.weight.grad -= vector |
|
|
|
|
| def standardize( |
| x: Float[torch.Tensor, "... n_inputs"], eps: float = 1e-5 |
| ) -> tuple[Float[torch.Tensor, "... n_inputs"], Stats]: |
| """Standardize the inputs to zero mean and unit variance.""" |
|
|
| mu = x.mean(dim=-1, keepdim=True) |
| x = x - mu |
| std = x.std(dim=-1, keepdim=True) |
| x = x / (std + eps) |
| return x, Stats(mu, std) |
|
|