| import pytest |
|
|
| torch = pytest.importorskip("torch") |
|
|
| from heapr.grouped_model import ( |
| ExpandedGroupedLagunaContext, |
| RepackedExpandedGroupedLagunaContext, |
| default_contiguous_group_indices, |
| full_group_mask, |
| ) |
|
|
|
|
| def test_default_contiguous_group_indices(): |
| indices = default_contiguous_group_indices(16, group_width=4) |
|
|
| assert indices.tolist() == [ |
| [0, 1, 2, 3], |
| [4, 5, 6, 7], |
| [8, 9, 10, 11], |
| [12, 13, 14, 15], |
| ] |
|
|
|
|
| def test_full_group_mask_shape(): |
| mask = full_group_mask(2, 3, 4) |
|
|
| assert mask.shape == (2, 3, 4) |
| assert mask.all() |
|
|
|
|
| class TinyRouter(torch.nn.Module): |
| def __init__(self): |
| super().__init__() |
| self.hidden_dim = 2 |
| self.top_k = 1 |
| self.weight = torch.nn.Parameter(torch.zeros(2, 2)) |
| self.e_score_correction_bias = torch.nn.Parameter(torch.zeros(2), requires_grad=False) |
|
|
| def forward(self, hidden_states): |
| logits = hidden_states @ self.weight.T |
| scores = torch.sigmoid(logits) |
| selected = scores.topk(self.top_k, dim=-1).indices |
| weights = scores.gather(1, selected) |
| weights = weights / weights.sum(dim=-1, keepdim=True) |
| return logits, weights.to(hidden_states.dtype), selected |
|
|
|
|
| class TinyExperts(torch.nn.Module): |
| def __init__(self): |
| super().__init__() |
| self.gate_up_proj = torch.nn.Parameter(torch.zeros(2, 4, 2)) |
| self.down_proj = torch.nn.Parameter(torch.zeros(2, 2, 2)) |
|
|
|
|
| class TinySparseMlp(torch.nn.Module): |
| def __init__(self): |
| super().__init__() |
| self.num_experts_per_tok = 1 |
| self.gate = TinyRouter() |
| self.experts = TinyExperts() |
| self.routed_scaling_factor = 1.0 |
|
|
| def forward(self, hidden_states): |
| return hidden_states |
|
|
|
|
| class TinyLayer(torch.nn.Module): |
| def __init__(self): |
| super().__init__() |
| self.mlp = TinySparseMlp() |
|
|
|
|
| class TinyInner(torch.nn.Module): |
| def __init__(self): |
| super().__init__() |
| self.layers = torch.nn.ModuleList([TinyLayer()]) |
|
|
|
|
| class TinyModel(torch.nn.Module): |
| def __init__(self): |
| super().__init__() |
| self.model = TinyInner() |
| self.config = type("Config", (), {"moe_routed_scaling_factor": 1.0})() |
|
|
|
|
| def test_expanded_grouped_context_patches_and_restores(): |
| model = TinyModel() |
| original_forward = model.model.layers[0].mlp.forward |
| mask = full_group_mask(1, 2, 2) |
| indices = default_contiguous_group_indices(2, group_width=1) |
|
|
| with ExpandedGroupedLagunaContext(model, keep_group_masks=mask, group_indices=indices): |
| assert model.model.layers[0].mlp.forward != original_forward |
|
|
| assert model.model.layers[0].mlp.forward == original_forward |
|
|
|
|
| def test_repacked_expanded_context_rewrites_and_restores_shapes(): |
| model = TinyModel() |
| mlp = model.model.layers[0].mlp |
| mask = full_group_mask(1, 2, 2) |
| indices = default_contiguous_group_indices(2, group_width=1) |
|
|
| with RepackedExpandedGroupedLagunaContext(model, keep_group_masks=mask, group_indices=indices): |
| assert mlp.experts.gate_up_proj.shape == (4, 2, 2) |
| assert mlp.experts.down_proj.shape == (4, 2, 1) |
| assert mlp.gate.weight.shape == (4, 2) |
| assert mlp.gate.top_k == 2 |
|
|
| assert mlp.experts.gate_up_proj.shape == (2, 4, 2) |
| assert mlp.experts.down_proj.shape == (2, 2, 2) |
| assert mlp.gate.weight.shape == (2, 2) |
| assert mlp.gate.top_k == 1 |
|
|
|
|
| def test_repacked_layer_scaled_context_reduces_child_top_k(): |
| model = TinyModel() |
| mlp = model.model.layers[0].mlp |
| mask = full_group_mask(1, 2, 2) |
| mask[0, :, 1] = False |
| indices = default_contiguous_group_indices(2, group_width=1) |
|
|
| with RepackedExpandedGroupedLagunaContext( |
| model, |
| keep_group_masks=mask, |
| group_indices=indices, |
| child_budget_mode="layer-scaled", |
| ): |
| assert mlp.gate.top_k == 1 |
|
|
|
|
| def test_repacked_parent_weighted_context_zeros_children_past_budget(): |
| model = TinyModel() |
| mlp = model.model.layers[0].mlp |
| mlp.gate.e_score_correction_bias.data[:] = torch.tensor([1.0, 0.0]) |
| mask = full_group_mask(1, 2, 2) |
| mask[0, 0, 1] = False |
| indices = default_contiguous_group_indices(2, group_width=1) |
|
|
| with RepackedExpandedGroupedLagunaContext( |
| model, |
| keep_group_masks=mask, |
| group_indices=indices, |
| child_budget_mode="parent-weighted", |
| ): |
| _, routing_weights, selected_children = mlp.gate(torch.zeros(1, 2)) |
|
|
| assert selected_children.tolist() == [[0, 2]] |
| assert routing_weights.tolist() == [[1.0, 0.0]] |
|
|