Download code/tests/test_loader_guard.py from maxpicy/mamba-cot-tts: direct link, hf CLI and curl.
- Browser
- Download file 10.3 kB
-
https://huggingface.co/maxpicy/mamba-cot-tts/resolve/main/code/tests/test_loader_guard.py
- Command line
-
hf download hf://maxpicy/mamba-cot-tts/code/tests/test_loader_guard.py
-
curl -L -o test_loader_guard.py https://huggingface.co/maxpicy/mamba-cot-tts/resolve/main/code/tests/test_loader_guard.py
10.3 kB
| # test_loader_guard.py: The checkpoint loader refuses silent drops (LoRA, plan tensors, speaker table). | |
| import sys | |
| from pathlib import Path | |
| import pytest | |
| import torch | |
| import torch.nn as nn | |
| sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) | |
| from train import (LoRALinear, _remap_plain_keys_for_lora, checkpoint_speaker_rows, | |
| load_checkpoint) | |
| class TinyPlain(nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.in_proj = nn.Linear(4, 8, bias=False) | |
| self.out_proj = nn.Linear(8, 4, bias=False) | |
| self.embedding = nn.Embedding(10, 4) | |
| class TinyLora(nn.Module): | |
| def __init__(self, rank=2): | |
| super().__init__() | |
| self.in_proj = LoRALinear(nn.Linear(4, 8, bias=False), rank=rank, | |
| alpha=rank, dropout=0.0) | |
| self.out_proj = LoRALinear(nn.Linear(8, 4, bias=False), rank=rank, | |
| alpha=rank, dropout=0.0) | |
| self.embedding = nn.Embedding(10, 4) | |
| def _save_ckpt(tmp_path, sd, name="ckpt.pt", step=123): | |
| path = tmp_path / name | |
| torch.save({"step": step, "model_state_dict": sd}, path) | |
| return str(path) | |
| def test_remap_plain_projection_keys(): | |
| plain_sd = TinyPlain().state_dict() | |
| lora_sd = TinyLora().state_dict() | |
| remapped, n = _remap_plain_keys_for_lora(plain_sd, lora_sd) | |
| assert n == 2, f"expected both projections remapped, got {n}" | |
| assert "in_proj.original.weight" in remapped | |
| assert "out_proj.original.weight" in remapped | |
| assert "in_proj.weight" not in remapped | |
| assert "embedding.weight" in remapped | |
| torch.testing.assert_close(remapped["in_proj.original.weight"], | |
| plain_sd["in_proj.weight"]) | |
| def test_remap_leaves_matching_and_unknown_keys_alone(): | |
| lora_model_sd = TinyLora().state_dict() | |
| sd = { | |
| "embedding.weight": torch.zeros(10, 4), | |
| "totally.unknown.weight": torch.zeros(2, 2), | |
| } | |
| remapped, n = _remap_plain_keys_for_lora(sd, lora_model_sd) | |
| assert n == 0 | |
| assert set(remapped) == set(sd) | |
| def test_fullft_ckpt_loads_exactly_into_lora_model(tmp_path): | |
| src = TinyPlain() | |
| with torch.no_grad(): | |
| src.in_proj.weight += 1.0 | |
| path = _save_ckpt(tmp_path, src.state_dict()) | |
| dst = TinyLora() | |
| step = load_checkpoint(path, dst, device="cpu") | |
| assert step == 123 | |
| torch.testing.assert_close(dst.in_proj.original.weight, src.in_proj.weight) | |
| x = torch.randn(3, 4) | |
| torch.testing.assert_close(dst.in_proj(x), src.in_proj(x)) | |
| def test_lora_ckpt_into_plain_model_still_errors(tmp_path): | |
| path = _save_ckpt(tmp_path, TinyLora().state_dict()) | |
| with pytest.raises(RuntimeError, match="LoRA adapters"): | |
| load_checkpoint(path, TinyPlain(), device="cpu") | |
| def test_dropped_projection_weight_is_a_hard_error(tmp_path): | |
| sd = TinyPlain().state_dict() | |
| sd["mixer.in_proj.weight"] = torch.zeros(8, 4) | |
| path = _save_ckpt(tmp_path, sd) | |
| with pytest.raises(RuntimeError, match="silently dropped"): | |
| load_checkpoint(path, TinyPlain(), device="cpu") | |
| class TinyNeoX(nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.attention = nn.Module() | |
| self.attention.query_key_value = nn.Linear(4, 12, bias=False) | |
| self.attention.dense = nn.Linear(4, 4, bias=False) | |
| self.embedding = nn.Embedding(10, 4) | |
| def test_neox_projection_unexpected_is_a_hard_error(tmp_path): | |
| sd = TinyNeoX().state_dict() | |
| sd["layers.0.attention.query_key_value.weight"] = torch.zeros(12, 4) | |
| path = _save_ckpt(tmp_path, sd) | |
| with pytest.raises(RuntimeError, match="silently dropped"): | |
| load_checkpoint(path, TinyNeoX(), device="cpu") | |
| def test_missing_projection_is_a_hard_error(tmp_path): | |
| sd = TinyNeoX().state_dict() | |
| del sd["attention.query_key_value.weight"] | |
| path = _save_ckpt(tmp_path, sd) | |
| with pytest.raises(RuntimeError, match="received NO value"): | |
| load_checkpoint(path, TinyNeoX(), device="cpu") | |
| def test_hybrid_graft_missing_attention_is_allowed(tmp_path): | |
| class TinyHybrid(nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.backbone = nn.Module() | |
| self.backbone.backbone = nn.Module() | |
| self.backbone.backbone.layers = nn.ModuleDict({ | |
| "0": nn.ModuleDict({"mixer": nn.ModuleDict( | |
| {"in_proj": nn.Linear(4, 12, bias=False)})}), | |
| }) | |
| self.hybrid_replaced_layers = [0] | |
| model = TinyHybrid() | |
| sd = {k: v for k, v in model.state_dict().items() | |
| if not k.endswith("in_proj.weight")} | |
| path = _save_ckpt(tmp_path, sd) | |
| load_checkpoint(path, model, device="cpu") | |
| def test_architecture_mismatched_projection_shape_is_a_hard_error(tmp_path): | |
| sd = TinyPlain().state_dict() | |
| sd["in_proj.weight"] = torch.zeros(24, 4) | |
| path = _save_ckpt(tmp_path, sd) | |
| with pytest.raises(RuntimeError, match="different SHAPE"): | |
| load_checkpoint(path, TinyPlain(), device="cpu") | |
| def test_shape_mismatched_speaker_table_still_only_warns(tmp_path): | |
| sd = TinyPlain().state_dict() | |
| sd["embedding.weight"] = torch.zeros(20, 4) | |
| path = _save_ckpt(tmp_path, sd, step=5) | |
| assert load_checkpoint(path, TinyPlain(), device="cpu") == 5 | |
| def test_depth_arch_swap_is_allowed(tmp_path): | |
| class GruDepth(nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.depth_module = nn.Module() | |
| self.depth_module.gru = nn.Linear(4, 4, bias=False) | |
| self.depth_module.h_proj = nn.Linear(4, 4, bias=False) | |
| class TfDepth(nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.depth_module = nn.Module() | |
| self.depth_module.depth_tf = nn.Module() | |
| self.depth_module.depth_tf.self_attn = nn.Module() | |
| self.depth_module.depth_tf.self_attn.out_proj = nn.Linear(4, 4, bias=False) | |
| self.depth_module.h_proj = nn.Linear(4, 4, bias=False) | |
| path = _save_ckpt(tmp_path, GruDepth().state_dict(), step=9) | |
| assert load_checkpoint(path, TfDepth(), device="cpu") == 9 | |
| def test_gru_to_mamba2_depth_swap_is_allowed(tmp_path): | |
| class GruDepth(nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.depth_module = nn.Module() | |
| self.depth_module.gru = nn.Linear(4, 4, bias=False) | |
| class SsmDepth(nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.depth_module = nn.Module() | |
| self.depth_module.depth_ssm = nn.Module() | |
| self.depth_module.depth_ssm.in_proj = nn.Linear(4, 4, bias=False) | |
| self.depth_module.depth_ssm.out_proj = nn.Linear(4, 4, bias=False) | |
| path = _save_ckpt(tmp_path, GruDepth().state_dict(), step=11) | |
| assert load_checkpoint(path, SsmDepth(), device="cpu") == 11 | |
| def test_same_arch_missing_depth_projection_still_raises(tmp_path): | |
| class TfDepth(nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.depth_module = nn.Module() | |
| self.depth_module.depth_tf = nn.Module() | |
| self.depth_module.depth_tf.self_attn = nn.Module() | |
| self.depth_module.depth_tf.self_attn.out_proj = nn.Linear(4, 4, bias=False) | |
| sd = TfDepth().state_dict() | |
| del sd["depth_module.depth_tf.self_attn.out_proj.weight"] | |
| path = _save_ckpt(tmp_path, sd) | |
| with pytest.raises(RuntimeError, match="received NO value"): | |
| load_checkpoint(path, TfDepth(), device="cpu") | |
| def test_matched_load_roundtrip_unaffected(tmp_path): | |
| src = TinyLora() | |
| path = _save_ckpt(tmp_path, src.state_dict(), step=7) | |
| dst = TinyLora() | |
| step = load_checkpoint(path, dst, device="cpu") | |
| assert step == 7 | |
| torch.testing.assert_close(dst.in_proj.lora_A, src.in_proj.lora_A) | |
| class TinyDelay(nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.in_proj = nn.Linear(4, 8, bias=False) | |
| self.delay_heads = nn.ModuleList([nn.Linear(8, 16, bias=False)]) | |
| class TinyDepth(nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.in_proj = nn.Linear(4, 8, bias=False) | |
| self.depth_gru = nn.GRU(8, 8, batch_first=True) | |
| def test_delay_ckpt_into_depth_model_warm_start(tmp_path): | |
| src = TinyDelay() | |
| path = _save_ckpt(tmp_path, src.state_dict(), step=100000) | |
| dst = TinyDepth() | |
| before = {k: v.clone() for k, v in dst.state_dict().items() | |
| if k.startswith("depth_gru")} | |
| load_checkpoint(path, dst, device="cpu") | |
| torch.testing.assert_close(dst.in_proj.weight, src.in_proj.weight) | |
| for k, v in before.items(): | |
| torch.testing.assert_close(dst.state_dict()[k], v) | |
| class TinySpeaker(nn.Module): | |
| def __init__(self, num_speakers): | |
| super().__init__() | |
| self.speaker_encoder = nn.Module() | |
| self.speaker_encoder.embedding = nn.Embedding(num_speakers, 4) | |
| self.in_proj = nn.Linear(4, 8, bias=False) | |
| def test_speaker_table_mismatch_is_a_hard_error(tmp_path): | |
| path = _save_ckpt(tmp_path, TinySpeaker(16).state_dict()) | |
| with pytest.raises(RuntimeError, match=r"Speaker table.*--num_speakers 16"): | |
| load_checkpoint(path, TinySpeaker(8), device="cpu") | |
| def test_speaker_table_reinit_only_when_asked(tmp_path): | |
| src = TinySpeaker(16) | |
| with torch.no_grad(): | |
| src.in_proj.weight += 1.0 | |
| path = _save_ckpt(tmp_path, src.state_dict()) | |
| dst = TinySpeaker(8) | |
| load_checkpoint(path, dst, device="cpu", allow_speaker_table_reinit=True) | |
| torch.testing.assert_close(dst.in_proj.weight, src.in_proj.weight) | |
| assert dst.speaker_encoder.embedding.weight.shape[0] == 8 | |
| def test_matching_speaker_table_loads_exactly(tmp_path): | |
| src = TinySpeaker(16) | |
| path = _save_ckpt(tmp_path, src.state_dict()) | |
| dst = TinySpeaker(16) | |
| load_checkpoint(path, dst, device="cpu") | |
| torch.testing.assert_close(dst.speaker_encoder.embedding.weight, | |
| src.speaker_encoder.embedding.weight) | |
| def test_checkpoint_speaker_rows_reads_the_table_size(tmp_path): | |
| assert checkpoint_speaker_rows( | |
| _save_ckpt(tmp_path, TinySpeaker(16).state_dict())) == 16 | |
| assert checkpoint_speaker_rows( | |
| _save_ckpt(tmp_path, TinyPlain().state_dict(), name="plain.pt")) is None | |
| assert checkpoint_speaker_rows(str(tmp_path / "absent.pt")) is None | |