Instructions to use RomanCohort/RNAQwen with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use RomanCohort/RNAQwen with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("RomanCohort/RNAQwen", device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 8,694 Bytes
acd280c | 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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | """权重忠实性验证(长期留存,比 _cmp_* 一次性诊断更值得留)。
为什么需要它
------------
本项目用的两个嵌入模型里,`pubmedbert` 目录下的 `model.safetensors` 与
`tokenizer.json` **是本地生成的**(上游只给 `pytorch_model.bin` + `vocab.txt`)。
而 PROGRESS.md 坑 7 记录过一次"大小完全正确、内容全是坏的"的静默损坏事故。
所以下面这个失败模式必须能排除:
"PubMedBERT 上 semantic F1 地板高到 0.95" —— 是真模型性质,还是权重坏了?
办法:从上游拿原始 `pytorch_model.bin`(ModelScope,带官方 sha256),逐张量比对。
本脚本对每个模型做三件事:
1. 本地文件 sha256 vs ModelScope 声明的 sha256(上游真有的文件);本地生成的文件标注清楚
2. 本地 safetensors vs 上游 pytorch_model.bin 的**逐键 bitwise** 比对
3. 两条加载路径的**前向输出**是否逐位一致
跑法:
python scripts/00d_verify_embedder_weights.py # 两个模型都验
python scripts/00d_verify_embedder_weights.py --models pubmedbert
"""
from __future__ import annotations
import argparse
import hashlib
import json
import sys
import urllib.request
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
import rnaqwen # noqa: E402,F401 —— env.shim
import torch # noqa: E402
from transformers import AutoModel, AutoTokenizer # noqa: E402
MS_API = "https://www.modelscope.cn/api/v1/models/{repo}/repo/files?Revision=master&Recursive=True"
# 模型的仓库名与本地目录名,以及**本地生成**(上游没有)的文件清单
MODELS = {
"pubmedbert": {
"dir": ROOT / "models" / "pubmedbert",
"repo": "microsoft/BiomedNLP-BiomedBERT-base-uncased-abstract-fulltext",
"locally_generated": ["model.safetensors", "tokenizer.json"],
"upstream_bin": ROOT / "models" / "pubmedbert_upstream" / "pytorch_model.bin",
},
"bert": {
"dir": ROOT / "models" / "bert-base-uncased",
"repo": "google-bert/bert-base-uncased",
"locally_generated": [],
"upstream_bin": None, # 上游自带 safetensors,无需比对 .bin
},
}
TEXTS = [
"The RNA molecule is a non-coding RNA that regulates gene expression.",
"The protein adopts an eight-stranded beta-barrel fold.",
"The central bank raised its policy rate by twenty-five basis points.",
"Sear the lamb shoulder on all sides until the fat renders golden.",
]
def sha256(p: Path) -> str:
h = hashlib.sha256()
with p.open("rb") as fh:
for c in iter(lambda: fh.read(1 << 20), b""):
h.update(c)
return h.hexdigest()
def ms_manifest(repo: str) -> dict:
"""ModelScope 官方清单(含官方 sha256)。拿不到就返回空 —— 别让网络问题假装成权重问题。"""
try:
req = urllib.request.Request(MS_API.format(repo=repo), headers={"User-Agent": "rnaqwen/0.1"})
with urllib.request.urlopen(req, timeout=90) as r:
payload = json.loads(r.read().decode())
except Exception as e: # noqa: BLE001
print(f" [warn] 拉不到 ModelScope 清单({type(e).__name__}: {e})—— 跳过 sha256 对照")
return {}
return {
f["Path"]: {"sha256": f.get("Sha256") or "", "size": f.get("Size")}
for f in (payload.get("Data") or {}).get("Files") or []
if f.get("Type") != "tree"
}
def file_check(name: str, cfg: dict) -> list[dict]:
print(f"\n[1] 文件级 sha256 ({name})", flush=True)
man = ms_manifest(cfg["repo"])
rows = []
for f in sorted(cfg["dir"].iterdir()):
if not f.is_file():
continue
local = sha256(f)
ref = man.get(f.name, {}).get("sha256", "")
if f.name in cfg["locally_generated"]:
verdict = "本地生成(上游无此文件)"
elif not ref:
verdict = "上游清单里没有这个文件名"
elif ref.lower() == local:
verdict = "OK sha256 == ModelScope 声明值"
else:
verdict = f"MISMATCH ms={ref[:24]}"
rows.append({"file": f.name, "size": f.stat().st_size, "sha256": local, "verdict": verdict})
print(f" {f.stat().st_size:>12d} B {f.name:<22s} {verdict}", flush=True)
return rows
def tensor_and_forward_check(name: str, cfg: dict) -> dict:
up = cfg["upstream_bin"]
out: dict = {"upstream_bin": str(up) if up else None}
if not up or not up.exists():
print(f"\n[2/3] 跳过逐张量比对(无上游 .bin:{up})", flush=True)
return out
print(f"\n[2] 逐张量比对({name}:本地 safetensors vs 上游 .bin)", flush=True)
from safetensors.torch import load_file
sd_up = torch.load(up, map_location="cpu", weights_only=True)
sd_lo = load_file(str(cfg["dir"] / "model.safetensors"))
def norm(k: str) -> str:
return k[5:] if k.startswith("bert.") else k
HEAD = ("cls.", "pooler.")
up_n = {norm(k): v for k, v in sd_up.items() if not k.startswith(HEAD)}
lo_n = {norm(k): v for k, v in sd_lo.items()}
common = sorted(set(up_n) & set(lo_n))
diffs = {}
shape_bad = []
for k in common:
a, b = up_n[k], lo_n[k]
if a.shape != b.shape:
shape_bad.append(k)
continue
diffs[k] = float((a.float() - b.float()).abs().max())
exact = sum(1 for d in diffs.values() if d == 0.0)
worst = sorted(diffs.items(), key=lambda kv: -kv[1])[:3]
print(f" 上游 {len(sd_up)} 张量(含 MLM 头 {len(sd_up) - len(up_n)})→ 归一后共同键 {len(common)}", flush=True)
print(f" bitwise 相同: {exact}/{len(diffs)} shape 不一致: {len(shape_bad)}", flush=True)
print(f" max|Δ| 最大: " + ", ".join(f"{k}={v:.2e}" for k, v in worst), flush=True)
out.update({
"tensors_upstream": len(sd_up), "tensors_local": len(sd_lo),
"common_keys": len(common), "bitwise_equal": exact,
"max_abs_diff": max(diffs.values()) if diffs else None,
"all_bitwise_equal": exact == len(diffs) and not shape_bad,
})
print(f"\n[3] 前向比对({name})", flush=True)
tok = AutoTokenizer.from_pretrained(str(cfg["dir"]))
enc = tok(TEXTS, return_tensors="pt", padding=True, truncation=True, max_length=512)
m_lo = AutoModel.from_pretrained(str(cfg["dir"])).eval()
m_up = AutoModel.from_config(m_lo.config).eval()
report = m_up.load_state_dict({norm(k): v for k, v in sd_up.items() if not k.startswith("cls.")}, strict=False)
print(f" 上游 .bin -> model: missing={len(report.missing_keys)} unexpected={len(report.unexpected_keys)}", flush=True)
# load_state_dict 是 in-place 的,但漏装任何键都会让下面的比对变成"随机 vs 训练"
assert not report.missing_keys, f"有键没装上,比对无意义: {report.missing_keys[:5]}"
with torch.no_grad():
h_lo = m_lo(**enc).last_hidden_state
h_up = m_up(**enc).last_hidden_state
same = torch.equal(h_lo, h_up)
maxd = float((h_lo - h_up).abs().max())
print(f" last_hidden_state 逐位相同: {same} max|Δ| = {maxd:.3e}", flush=True)
out.update({"forward_bitwise_equal": same, "forward_max_abs_diff": maxd})
return out
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--models", default="bert,pubmedbert")
ap.add_argument("--out", type=Path, default=ROOT / "reports" / "embedder_weight_verification.json")
args = ap.parse_args()
report = {"purpose": "排除'地板高/指标异常'由权重静默损坏导致", "models": {}}
for key in [s.strip() for s in args.models.split(",") if s.strip()]:
cfg = MODELS.get(key)
if not cfg:
print(f"[skip] 未知模型 {key}")
continue
print("=" * 78)
print(f"模型 {key} -> {cfg['repo']}")
print("=" * 78)
report["models"][key] = {
"dir": str(cfg["dir"]),
"repo": cfg["repo"],
"files": file_check(key, cfg),
**tensor_and_forward_check(key, cfg),
}
args.out.parent.mkdir(parents=True, exist_ok=True)
args.out.write_text(json.dumps(report, indent=2, ensure_ascii=False), encoding="utf-8")
print(f"\n[done] -> {args.out}")
ok = all(
m.get("all_bitwise_equal", True) and m.get("forward_bitwise_equal", True)
for m in report["models"].values()
)
print("[OK] 权重忠实" if ok else "[FAIL] 有权重不一致,先别用这些分数")
return 0 if ok else 1
if __name__ == "__main__":
raise SystemExit(main())
|