Instructions to use SlayerLab/NERGAL with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use SlayerLab/NERGAL with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("token-classification", model="SlayerLab/NERGAL")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForTokenClassification tokenizer = AutoTokenizer.from_pretrained("SlayerLab/NERGAL") model = AutoModelForTokenClassification.from_pretrained("SlayerLab/NERGAL", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download names/convert.py from SlayerLab/NERGAL: direct link, hf CLI and curl.
- Browser
- Download file 4.48 kB
-
https://huggingface.co/SlayerLab/NERGAL/resolve/main/names/convert.py
- Command line
-
hf download hf://SlayerLab/NERGAL/names/convert.py
-
curl -L -o convert.py https://huggingface.co/SlayerLab/NERGAL/resolve/main/names/convert.py
4.48 kB
| """FastPDN-PII FP32 ONNX -> model.safetensors for BertForTokenClassification (plan Task 4a Step 2, Review Gate 14 A). | |
| Named initializers keep their torch names. Each linear weight is the MatMul input that feeds the Add or BiasGelu taking | |
| its named bias, transposed from ONNX's [in, out] to torch's [out, in]. Every weight is used once, the state dict loads | |
| strictly, and no unused float tensor is left in the graph; anything else stops. Weights are copied, never changed. | |
| Usage: python convert.py # writes model.safetensors and convert.json here; create-only | |
| """ | |
| from pathlib import Path | |
| import hashlib, json | |
| import numpy as np | |
| from onnx import numpy_helper | |
| HERE = Path(__file__).resolve().parent | |
| REPO, REVISION = 'ArkadiuszPawlak/fastpdn-ner-polish-pii', '636c57fe77b0afad8f065e7c76243dc051437035' | |
| SNAPSHOT = Path.home()/'.cache/huggingface/hub/models--ArkadiuszPawlak--fastpdn-ner-polish-pii/snapshots'/REVISION | |
| ONNX_SHA256 = '11924bfd20eb478ee614e5e1184536825db4747753d341af7c04b940efa84645' # model.onnx, LFS pin at REVISION | |
| CONFIG_SHA256 = '3cef51cd7d184478adbdec9f68730db279386fe145cb32473a0622edea923578' | |
| NAMED = ('bert.', 'classifier.') | |
| BUFFERS = ('bert.embeddings.position_ids',) # non-persistent in transformers: checked, not saved | |
| def sha(path): | |
| h = hashlib.sha256() | |
| with open(path, 'rb') as f: | |
| for block in iter(lambda: f.read(1 << 20), b''): | |
| h.update(block) | |
| return h.hexdigest() | |
| def weights(graph): | |
| """({torch name: array}, used initializer names).""" | |
| init = {i.name: numpy_helper.to_array(i) for i in graph.initializer} | |
| producer = {o: n for n in graph.node for o in n.output} | |
| state = {k: v for k, v in init.items() if k.startswith(NAMED)} | |
| used = set(state) | |
| for n in graph.node: | |
| bias = [x for x in n.input if x.endswith('.bias') and 'LayerNorm' not in x and x in init] | |
| if n.op_type not in ('Add', 'BiasGelu') or not bias: | |
| continue | |
| (b,), (other,) = bias, [x for x in n.input if x not in bias] | |
| matmul = producer.get(other) | |
| assert matmul is not None and matmul.op_type == 'MatMul' and matmul.input[1] in init, b | |
| w, key = matmul.input[1], b[:-len('bias')] + 'weight' | |
| assert w not in used and key not in state, (w, key) | |
| state[key] = init[w].T.copy() | |
| used.add(w) | |
| return state, used | |
| def leftovers(init, used): | |
| """Unused float tensors with more than one value: a weight the mapping missed. Scalars and int shapes pass.""" | |
| return sorted(k for k, v in init.items() if k not in used and np.asarray(v).dtype.kind == 'f' and np.size(v) > 1) | |
| def main(): | |
| import onnx, torch | |
| from safetensors.torch import save_file | |
| from transformers import BertConfig, BertForTokenClassification | |
| out = HERE/'model.safetensors' | |
| assert not out.exists(), 'create-only' | |
| assert sha(SNAPSHOT/'model.onnx') == ONNX_SHA256 and sha(SNAPSHOT/'config.json') == CONFIG_SHA256 | |
| graph = onnx.load(SNAPSHOT/'model.onnx').graph | |
| init = {i.name: numpy_helper.to_array(i) for i in graph.initializer} | |
| state, used = weights(graph) | |
| left = leftovers(init, used) | |
| assert not left, f'{len(left)} unused float tensors' | |
| for k in BUFFERS: | |
| v = state.pop(k) | |
| assert np.array_equal(v, np.arange(v.shape[-1]).reshape(v.shape)), k | |
| tensors = {k: torch.from_numpy(np.ascontiguousarray(v)) for k, v in sorted(state.items())} | |
| model = BertForTokenClassification(BertConfig.from_pretrained(SNAPSHOT)) | |
| expected = model.state_dict() | |
| assert {k: tuple(v.shape) for k, v in tensors.items()} == {k: tuple(v.shape) for k, v in expected.items()}, 'keys/shapes' | |
| assert all(t.dtype == torch.float32 for t in tensors.values()) | |
| model.load_state_dict(tensors, strict=True) | |
| save_file(tensors, out, metadata=dict(source=f'{REPO}@{REVISION}/model.onnx', onnx_sha256=ONNX_SHA256, | |
| note='converted from ONNX, weights unchanged')) | |
| report = dict(repo=REPO, revision=REVISION, onnx_sha256=ONNX_SHA256, config_sha256=CONFIG_SHA256, | |
| initializers=len(init), used=len(used), tensors=len(tensors), | |
| transposed=len(used) - sum(k.startswith(NAMED) for k in used), | |
| unused_constants=len(init) - len(used), safetensors_sha256=sha(out), convert_sha256=sha(__file__)) | |
| (HERE/'convert.json').write_text(json.dumps(report, indent=1) + '\n') | |
| return report | |
| if __name__ == '__main__': | |
| print(json.dumps(main())) | |