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: 6,093 Bytes
cddc722 | 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 | """路线 C 的定方向实验:修公式 + 换解码器。
两个改动:
① 修 FSQ 公式(v3 的 l=2 恒等输出零,是 bug 不是发现)
v3: q = round(tanh(z) * (l-1)/2) / ((l-1)/2) -> l=2 时 tanh(z)*0.5 ∈(-0.5,0.5),round 恒 0
v4: k = round((tanh(z)+1)/2 * (l-1)) -> k ∈ {0..l-1},正好 l 档
q = k/(l-1)*2 - 1 -> 落在 [-1,1]
② 解码器 Linear -> MLP
假设:"err 0.64 是解码器太弱,不是词表不够大"
若换 MLP 后 err 大降 -> 瓶颈是解码器(好消息,词表可以小)
若几乎不动 -> 真的是词表容量不足,路线要重估
判据(决定路线成不成):
err —— 能不能留住信息(< 1 才比"输出均值"好)
词内方差 —— 同一个码对应的输入有多像(越低 = 码字越有意义)
最大码占比 —— 塌缩检测(RiboSphere: 有选择性的稀疏是好的,塌是坏的)
"""
from __future__ import annotations
import json
import math
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
import rnaqwen # noqa: F401,E402
import torch # noqa: E402
import torch.nn as nn # noqa: E402
import torch.nn.functional as F # noqa: E402
from rnaqwen.models.encoder import RnaEncoder # noqa: E402
DEV = "cuda" if torch.cuda.is_available() else "cpu"
N_SEQ, STEPS = 64, 2000
DATA = ROOT / "data/processed/rnaqa_n6_merged.jsonl"
CONFIGS = [
(4096, 6, 4), (4096, 4, 8),
(65536, 8, 4), (65536, 4, 16),
]
def fsq(z, levels):
"""v4 公式:正好 levels 档,l=2 时是 {0,1},不退化。"""
k = torch.round((torch.tanh(z) + 1) / 2 * (levels - 1))
q = k / (levels - 1) * 2 - 1
q = q + ((torch.tanh(z) + 1) / 2 * (levels - 1) - k).detach() * 0 # 纯 STE
q = q + (((torch.tanh(z) + 1) / 2 * (levels - 1)) - ((torch.tanh(z) + 1) / 2 * (levels - 1)).detach())
return q, k.long()
class AE(nn.Module):
def __init__(self, dim, latent, levels, dec="linear"):
super().__init__()
self.enc = nn.Linear(dim, latent)
self.levels = levels
if dec == "linear":
self.dec = nn.Linear(latent, dim)
else:
self.dec = nn.Sequential(
nn.Linear(latent, 128), nn.GELU(),
nn.Linear(128, 256), nn.GELU(),
nn.Linear(256, dim))
def forward(self, x):
q, k = fsq(self.enc(x), self.levels)
return self.dec(q), k
def main() -> int:
recs = []
with open(DATA, encoding="utf-8") as f:
for line in f:
if line.strip():
recs.append(json.loads(line))
recs.sort(key=lambda r: len(r.get("sequence") or ""))
pick = [recs[int(i * (len(recs) - 1) / (N_SEQ - 1))] for i in range(N_SEQ)]
seqs = [r["sequence"] for r in pick if r.get("sequence")]
enc = RnaEncoder(str(ROOT / "models/ernierna"), dtype=torch.float32).to(DEV).eval()
with torch.no_grad():
o = enc(seqs)
x = o.hidden.float().to(DEV)[o.mask.to(DEV).squeeze(-1) > 0]
N, D = x.shape
var_tot = float(x.var(dim=0).sum())
print(f"[v4] {len(seqs)} 条序列,位置 {N},维度 {D}")
res = []
for vocab, latent, levels in CONFIGS:
assert levels ** latent == vocab
for dec in ("linear", "mlp"):
torch.manual_seed(0)
ae = AE(D, latent, levels, dec).to(DEV)
opt = torch.optim.AdamW(ae.parameters(), lr=3e-3)
t0 = time.time()
for _ in range(STEPS):
p = torch.randperm(N, device=DEV)[:512]
xb = x[p]
rec, _ = ae(xb)
loss = F.mse_loss(rec, xb) * D
opt.zero_grad(); loss.backward(); opt.step()
ae.eval()
with torch.no_grad():
rec, k = ae(x)
err = float(((rec - x) ** 2).sum(1).mean() / var_tot)
code = torch.zeros(N, dtype=torch.long, device=DEV)
for d_ in range(latent):
code = code * levels + k[:, d_]
uniq, inv, cnt = torch.unique(code, return_inverse=True, return_counts=True)
sums = torch.zeros(uniq.numel(), D, device=DEV)
sums.index_add_(0, inv, x)
means = sums / cnt.unsqueeze(1)
within = float(((x - means[inv]) ** 2).sum(1).mean() / var_tot)
p_ = cnt.float() / N
ent = float(-(p_ * (p_ + 1e-12).log()).sum()) / math.log(vocab)
top1 = float(cnt.max()) / N
res.append({"vocab": vocab, "latent": latent, "levels": levels, "dec": dec,
"err": err, "codes_used": int(uniq.numel()),
"util": float(uniq.numel()) / vocab, "within_var": within,
"entropy_norm": ent, "top1_share": top1, "sec": round(time.time()-t0)})
print(f" vocab {vocab:>6} latent={latent:>2} l={levels:>2} dec={dec:<6} "
f"err {err:.4f} 码字 {int(uniq.numel()):>5} ({100*uniq.numel()/vocab:>5.1f}%) "
f"词内方差 {within:.4f} 归一熵 {ent:.3f} 最大码 {100*top1:.2f}% {time.time()-t0:.0f}s")
out = ROOT / "reports/fsq_v4_decoder.json"
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(res, indent=2, ensure_ascii=False), encoding="utf-8")
print(f"\n[v4] -> {out}")
print()
print(" === 解码器是不是瓶颈 ===")
for vocab, latent, levels in CONFIGS:
a = next(r for r in res if r["vocab"] == vocab and r["dec"] == "linear")
b = next(r for r in res if r["vocab"] == vocab and r["dec"] == "mlp")
drop = 1 - b["err"] / max(a["err"], 1e-9)
print(f" vocab {vocab:>6} latent={latent} l={levels}: "
f"linear {a['err']:.4f} -> mlp {b['err']:.4f} ({100*drop:+.1f}%)")
print()
print(" === l=2 是否还退化(修公式的验证)===")
return 0
if __name__ == "__main__":
raise SystemExit(main())
|