-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathverify_adapter_files.py
More file actions
87 lines (70 loc) · 2.99 KB
/
Copy pathverify_adapter_files.py
File metadata and controls
87 lines (70 loc) · 2.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
"""Verify a downloaded LoRA adapter directory without loading torch.
Parses the safetensors header directly, so this runs on the laptop, in CI, or on
a judge's machine with nothing installed but Python. Confirms the file is intact,
reports its SHA-256, and counts the LoRA tensors and parameters.
python scripts/verify_adapter_files.py models/adapters/<run>/checkpoint-1590
"""
from __future__ import annotations
import argparse
import hashlib
import json
import struct
import sys
from pathlib import Path
EXPECTED_SHA256_PREFIX = "3b625d63ed4f28640a6fb14072486305"
EXPECTED_TENSORS = 696
EXPECTED_PARAMS = 37_152_768
def read_header(path: Path) -> dict:
"""safetensors layout: 8-byte little-endian header length, then JSON."""
with open(path, "rb") as fh:
(n,) = struct.unpack("<Q", fh.read(8))
return json.loads(fh.read(n))
def main() -> int:
ap = argparse.ArgumentParser(description="Verify a LoRA adapter directory")
ap.add_argument("adapter_dir", help="Directory holding adapter_model.safetensors")
ap.add_argument("--expect-sha256", default=EXPECTED_SHA256_PREFIX,
help="Expected SHA-256 prefix ('' to skip the check)")
args = ap.parse_args()
d = Path(args.adapter_dir)
weights = d / "adapter_model.safetensors"
config = d / "adapter_config.json"
if not weights.exists():
print(f"FAIL: {weights} not found")
return 1
header = read_header(weights)
tensors = {k: v for k, v in header.items() if k != "__metadata__"}
params = 0
dtypes = set()
for meta in tensors.values():
n = 1
for dim in meta["shape"]:
n *= dim
params += n
dtypes.add(meta["dtype"])
digest = hashlib.sha256(weights.read_bytes()).hexdigest()
print(f"adapter dir : {d}")
print(f"weights : {weights.name} {weights.stat().st_size:,} bytes")
print(f"sha256 : {digest}")
print(f"tensors : {len(tensors):,}")
print(f"parameters : {params:,}")
print(f"dtypes : {sorted(dtypes)}")
if config.exists():
cfg = json.loads(config.read_text(encoding="utf-8"))
print(f"base model : {cfg.get('base_model_name_or_path')}")
print(f"lora : r={cfg.get('r')} alpha={cfg.get('lora_alpha')} "
f"dropout={cfg.get('lora_dropout')}")
print(f"targets : {', '.join(sorted(cfg.get('target_modules') or []))}")
else:
print("WARN: adapter_config.json missing -- PEFT cannot load this directory")
ok = True
if args.expect_sha256 and not digest.startswith(args.expect_sha256):
print(f"FAIL: sha256 does not start with {args.expect_sha256}")
ok = False
if len(tensors) != EXPECTED_TENSORS:
print(f"WARN: expected {EXPECTED_TENSORS} tensors, found {len(tensors)}")
if params != EXPECTED_PARAMS:
print(f"WARN: expected {EXPECTED_PARAMS:,} parameters, found {params:,}")
print("OK" if ok else "MISMATCH")
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())