File size: 6,289 Bytes
25e8424 fb58c6c 25e8424 fb58c6c 25e8424 fb58c6c 25e8424 fb58c6c 25e8424 fb58c6c 25e8424 | 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 | """On-device accuracy for NewBERT embedding models (gte-*-en-v1.5, gte-multilingual-base): CLS embeddings of the
patched model vs the stock Hugging Face model on CPU in fp32 (exact GELU), and vs stock PyTorch Native.
Gates: mean per-text cosine >= min(0.9999, stock bf16 mean) (patched vs CPU fp32; stock itself is below 0.9999 at L=1024); min per-text cosine within COS_MIN_SLACK of stock
bf16 PyTorch Native's min (an absolute 0.9999 floor on the minimum is not met by stock bf16 itself);
retrieval top-1 per query equal to the CPU fp32 ranking at least as often as stock Native; padding isolation
bit-exact; fused ops actually traced.
GTE_TEST_MODEL=Alibaba-NLP/gte-large-en-v1.5 NEURON_LOGICAL_NC_CONFIG=2 python -m pytest -m neuron -s tests/test_device_embed.py
"""
import json
import os
import sys
import pytest
from conftest import load_package
from test_device import TOPICS, _pairs
torch = pytest.importorskip("torch")
pytest.importorskip("torch_neuronx")
transformers = pytest.importorskip("transformers")
pytestmark = pytest.mark.neuron
MODEL = os.environ.get("GTE_TEST_MODEL", "Alibaba-NLP/gte-large-en-v1.5")
RESULTS = os.environ.get("GTE_RESULTS", os.path.join(os.path.dirname(os.path.abspath(__file__)),
"device_results.jsonl"))
SHAPES = [(256, 32), (128, 32), (512, 3), (1024, 8), (768, 3)]
COS_MEAN_MIN = 0.9999
ATTN_LENGTHS = tuple(int(v) for v in os.environ.get("GTE_ATTN_LENGTHS", "128,256,384,512,768,1024").split(","))
COS_MIN_SLACK = 1e-3 # worst single text may trail stock's worst by this much (bf16 noise over 24 layers)
def _load(dtype):
m = transformers.AutoModel.from_pretrained(MODEL, trust_remote_code=True, attn_implementation="eager").eval()
if dtype is not None:
for mod in m.modules():
for cn, c in mod.named_children():
if "GELU" in type(c).__name__:
setattr(mod, cn, torch.nn.GELU(approximate="tanh"))
m = m.to(dtype)
return m
class _CLS(torch.nn.Module):
def __init__(self, m):
super().__init__()
self.m = m
def forward(self, ids, msk):
return self.m(input_ids=ids, attention_mask=msk).last_hidden_state[:, 0]
@pytest.fixture(scope="module")
def setup():
import torch._dynamo as dyn
dyn.config.cache_size_limit = 64
pkg = load_package()
to = sys.modules["gte_encoder_neuron_kernels.torch_ops"]
calls = {}
for name in ("attn_block", "gated_mlp_ln", "res_ln", "ln"):
def wrap(f, name=name):
def g(*a, **k):
calls[name] = calls.get(name, 0) + 1
return f(*a, **k)
return g
setattr(to, name, wrap(getattr(to, name)))
tok = transformers.AutoTokenizer.from_pretrained(MODEL, trust_remote_code=True)
cpu = _CLS(_load(None))
dev = torch.device("neuron")
stock = _CLS(_load(torch.bfloat16)).to(dev)
p = _load(torch.bfloat16)
n = pkg.patch_model(p, attn_lengths=ATTN_LENGTHS)
patched = _CLS(p).to(dev)
return dict(pkg=pkg, tok=tok, cpu=cpu, dev=dev, stock=stock, patched=patched, n_layers=n, calls=calls)
def _emb(model, ids, msk, bs, dev):
cm = torch.compile(model, backend="neuron", dynamic=False)
with torch.no_grad():
out = [cm(ids[i:i + bs].to(dev), msk[i:i + bs].to(dev)).float().cpu() for i in range(0, ids.shape[0], bs)]
return torch.cat(out), cm
def _cos(a, b):
return torch.nn.functional.cosine_similarity(a, b, dim=-1)
@pytest.mark.parametrize("L,bs", SHAPES)
def test_embed_accuracy_and_isolation(setup, L, bs):
tok, cpu, dev = setup["tok"], setup["cpu"], setup["dev"]
per = 8
Q, Ps = _pairs(L, per)
queries = list(TOPICS)
texts = queries + Ps # 8 queries + 64 passages
n = len(texts) - len(texts) % bs
texts = texts[:n]
e = tok(texts, return_tensors="pt", max_length=L, padding="max_length", truncation=True)
ids, msk = e["input_ids"], e["attention_mask"]
with torch.no_grad():
ref = torch.cat([cpu(ids[i:i + 8], msk[i:i + 8]).float() for i in range(0, n, 8)])
e_stock, _ = _emb(setup["stock"], ids, msk, bs, dev)
setup["calls"].clear()
e_nki, cm = _emb(setup["patched"], ids, msk, bs, dev)
traced = dict(setup["calls"])
cos_nki = _cos(e_nki, ref)
cos_stock = _cos(e_stock, ref)
def top1(E):
En = torch.nn.functional.normalize(E, dim=-1)
q = En[:len(queries)]
p = En[len(queries):]
return (q @ p.T).argmax(-1)
t_ref, t_stock, t_nki = top1(ref), top1(e_stock), top1(e_nki)
agree_stock = float((t_stock == t_ref).float().mean())
agree_nki = float((t_nki == t_ref).float().mean())
b1 = ids[:bs].clone(), msk[:bs].clone()
if bs > 1:
perm = torch.arange(n - 1, n - bs, -1)
b1[0][1:], b1[1][1:] = ids[perm], msk[perm]
with torch.no_grad():
y0 = cm(ids[:bs].to(dev), msk[:bs].to(dev))[0].float().cpu()
y1 = cm(b1[0].to(dev), b1[1].to(dev))[0].float().cpu()
isolated = bool(torch.equal(y0, y1))
lens = msk.sum(-1).numpy()
worst = torch.argsort(cos_nki)[:4].tolist()
rec_worst = [dict(i=int(i), len=int(lens[i]), cos_nki=float(cos_nki[i]), cos_stock=float(cos_stock[i]))
for i in worst]
rec = dict(model=MODEL, kind="embed", lnc=setup["pkg"].LNC, L=L, bs=bs, texts=n,
real_len=[int(lens.min()), int(lens.max())], layers_patched=setup["n_layers"],
cos_min_stock=float(cos_stock.min()), cos_mean_stock=float(cos_stock.mean()),
cos_min_nki=float(cos_nki.min()), cos_mean_nki=float(cos_nki.mean()),
top1_agree_stock=agree_stock, top1_agree_nki=agree_nki, padding_isolated=isolated,
traced_ops=traced, worst=rec_worst)
print(json.dumps(rec), flush=True)
with open(RESULTS, "a") as f:
f.write(json.dumps(rec) + "\n")
nl = setup["n_layers"]
if L in ATTN_LENGTHS:
assert traced.get("attn_block", 0) >= nl, rec
assert traced.get("gated_mlp_ln", 0) >= nl, rec
assert float(cos_nki.mean()) >= min(COS_MEAN_MIN, float(cos_stock.mean())), rec
assert float(cos_nki.min()) >= float(cos_stock.min()) - COS_MIN_SLACK, rec
assert agree_nki >= agree_stock, rec
assert isolated, rec
|