Views
No views yet
⚠️ Experimental / early-stage research artifact. This HEF is a quantized port ofsentence-transformers/all-MiniLM-L6-v2to the Hailo-10H accelerator.Update (2026-05-30): we now have a real retrieval benchmark. Despite a modest ~0.72 cosine vs FP32, on MTEB/BEIR SciFact (300 queries, real qrels) the INT8 HEF drops nDCG@10 0.717 → 0.664 (−7.4%) and Recall@10 0.843 → 0.786 — i.e. it keeps ~93% of FP32 retrieval quality. So the cosine number badly understates usability: this is a usable retrieval embedding (not "broken"), though not FP32-equivalent. We then swept the DFC accuracy levers to close that ~7% — QAT, compression, in-domain calibration, 16-bit, larger calibration — and none meaningfully helped (QAT/in-domain hurt); the gap is intrinsic to per-tensor INT8 here. Details below + in the research log.
mercury) using DFC 5.3.0, compiled with a no-attention-mask cut following the RuVector recipe.| HEF | minilm-l6-ruvector.hef (11.21 MiB) |
| Target | Hailo-10H (hailo10h / mercury) |
| DFC | 5.3.0 |
| Source model | sentence-transformers/all-MiniLM-L6-v2 (Apache-2.0, 22.7M params, 6 BERT layers, hidden=384) |
| Sequence length | 128 (static) |
| Quantization | INT8 (with ew_add* raised to a16_w16) |
| Cut topology | Single-input, no attention mask. Cut start_node=/embeddings/Add_1 (post-embedding-sum), end_node=last_hidden_state. Host-side computes embeddings + LayerNorm in FP32 (cheap, int64 Gather), and mean-pools the encoder output with the real attention_mask post-NPU. |
| Cosine vs FP32 | ~0.72 mean cosine on emulator (SDK_QUANTIZED vs SDK_FP_OPTIMIZED) — a poor proxy for retrieval (see below) |
| Retrieval vs FP32 | SciFact nDCG@10 0.664 vs 0.717 (−7.4%); Recall@10 0.786 vs 0.843 — INT8 keeps ~93% of FP32 |
multiproc_policy=disabled + drop-mask-input ONNX surgery) adapted from Hailo-8 / DFC 3.x to Hailo-10H / DFC 5.x. Original recipe: RuVector — compile-encoder-hef.py, MIT-licensed, Copyright (c) 2025 rUv.attention_mask in the mean-pool step downstream. A mask-aware variant exists in our research log but has the same or worse cosine number — the mask input is functionally inert at the DFC cut topology we have available. See the source repo's PLAN.md for the 18-iteration negative result.make_bert_assets.py. We tested in-domain (SciFact-corpus) calibration — it hurt (−16% nDCG; the distribution is too narrow). Diverse general-domain calibration (≈256 WikiText paragraphs) was marginally best. Calibration diversity matters more than domain-match here.optimization_level=2, compression 0) made it worse (−31% nDCG, overfits the small calib); in-domain calibration worse (−16%, too narrow); compression worse; 16-bit (a16_w16 on matmul/conv/softmax) doesn't fit Hailo-10H (AccelerasUnsupportedError); per-channel weights are not a DFC knob (conv is already per-channel; A8W4 group-wise QuaROT/GPTQ ships only in Hailo's genai LLM path). The only non-backfire was larger diverse calibration: 256 WikiText samples nudged nDCG 0.664→0.670 (gap −7.4%→−6.5%, near noise), while 1024 overshot to −8.8%. Conclusion: the ~7% gap is intrinsic to per-tensor INT8 of this model on this stack. Full sweep + numbers: PLAN.md "Session 5".[PAD] token; the attention noise above applies.ew_add* layers raised to 16-bit activations + 16-bit weights to keep residual paths numerically stable. Other layers are INT8.hailo_platform (the runtime, separate from DFC)1import numpy as np
2from hailo_platform import (HEF, VDevice, FormatType, HailoStreamInterface,
3 InputVStreamParams, OutputVStreamParams,
4 InferVStreams, ConfigureParams)
5
6# Tokenize + FP32 embed + LayerNorm on host (small cost)
7def host_prep(text, tokenizer, embed_layer, layernorm):
8 enc = tokenizer(text, padding="max_length", truncation=True,
9 max_length=128, return_tensors="np")
10 emb = embed_layer(enc.input_ids) # [1, 128, 384]
11 h0 = layernorm(emb + token_type_embed + # add positions/segments
12 position_embed)
13 return h0.astype(np.float32), enc.attention_mask
14
15# Configure and run the HEF
16hef = HEF("minilm-l6-ruvector.hef")
17with VDevice() as dev:
18 cfg_params = ConfigureParams.create_from_hef(hef, interface=HailoStreamInterface.PCIe)
19 network_group = dev.configure(hef, cfg_params)[0]
20 in_params = InputVStreamParams.make(network_group, format_type=FormatType.FLOAT32)
21 out_params = OutputVStreamParams.make(network_group, format_type=FormatType.FLOAT32)
22 with network_group.activate(network_group.create_params()):
23 with InferVStreams(network_group, in_params, out_params) as pipe:
24 x, mask = host_prep("Hello world", tokenizer, embed, layernorm)
25 # HEF expects [1, 1, 128, 384] NCHW — reshape if needed
26 out = pipe.infer({list(in_params)[0]: x[:, None, :, :]})
27
28last_hidden = list(out.values())[0] # [1, 1, 128, 384] NCHW
29# Mean-pool with real mask, then L2-normalize
30last_hidden = last_hidden[:, 0] # [1, 128, 384]
31m = mask.astype(np.float32)[..., None]
32pooled = (last_hidden * m).sum(axis=1) / np.clip(m.sum(axis=1), 1e-9, None)
33embedding = pooled / np.clip(np.linalg.norm(pooled, axis=-1, keepdims=True), 1e-12, None)transformers model — copy out just bert.embeddings.{word,position,token_type}_embeddings + bert.embeddings.LayerNorm, frozen to FP32.1# 0. Set up Python 3.10 venv with DFC
2python3.10 -m venv dfcvenv
3. dfcvenv/bin/activate
4pip install hailo_dataflow_compiler-5.3.0-py3-none-linux_x86_64.whl
5pip install numpy onnx onnxruntime onnxsim transformers sentence-transformers
6
7# 1. Build calibration + eval NPZs from MS-MARCO-style prompts
8python recipe/make_bert_assets.py \
9 --out-dir work/ --seq 128 --calib-n 50 --eval-n 16
10
11# 2. Compile (uses the included source ONNX, no mask)
12python recipe/compile_minilm_ruvector.py \
13 --onnx source/minilm-l6-encoder-only-seq128.onnx \
14 --calib work/bert-calib-seq128.npz \
15 --hef-out minilm-l6-ruvector.hef \
16 --har-out minilm-l6-ruvector.har \
17 --hw-arch hailo10h \
18 --opt-level 0 --compression-level 0
19
20# 3. Verify cosine on INT8 emulator (no hardware needed, ~15 min wall)
21python recipe/eval_ruvector_int8.py \
22 --har minilm-l6-ruvector.har \
23 --eval work/bert-eval-seq128.npzopt-level=0.minilm-l6-ruvector.hef ← compiled HEF (11.21 MiB)
source/
minilm-l6-encoder-only-seq128.onnx ← post-no-mask-surgery ONNX (40.77 MiB)
recipe/
minilm_l6_nomask.alls ← DFC alls script
compile_minilm_ruvector.py ← compile driver (RuVector monkey-patch)
make_bert_assets.py ← calib/eval NPZ generator
eval_ruvector_int8.py ← INT8 emulator eval (cosine vs FP32)minilm_l6_nomask.alls) in full:model_optimization_config(calibration, batch_size=16, calibset_size=50)
model_optimization_config(globals, multiproc_policy=disabled)
pre_quantization_optimization(ew_add_fusing, policy=disabled)
model_optimization_flavor(optimization_level=0, compression_level=0)
pre_quantization_optimization(matmul_correction, layers={matmul*}, correction_type=zp_comp_block)
quantization_param({ew_add*}, precision_mode=a16_w16)
quantization_param({conv*}, precision_mode=a16_w16)
pre_quantization_optimization(layer_norm_decomposition, equalization=disabled, bit_decomposition_mode=uniform_precision)
allocator_param(spatial_defuse_legacy=True)compile_minilm_ruvector.py, which omits conv* a16_w16 because that variant failed at Unsupported layers for the target mercury: precision_change11 on Hailo-10H. The cfg/minilm_l6_nomask.alls shipped here documents the Hailo-8 reference variant.)sentence-transformers/all-MiniLM-L6-v2 — Apache-2.0. Originally from Microsoft (Wang et al., 2020, MiniLM paper) and fine-tuned by sentence-transformers (Reimers & Gurevych, 2019).compile-encoder-hef.py — MIT, Copyright (c) 2025 rUv. The Keras-serializable monkey-patch + multiproc_policy=disabled pattern is the key insight; we adapted it from Hailo-8/DFC 3.x to Hailo-10H/DFC 5.x.cstr/Kokoro-82M-encoder-hailo10h — sister HEF for the Kokoro-82M ALBERT phoneme encoder, same recipe family, same cosine plateau. Uses the matching tools/make_kokoro_encoder_only.py + tools/replace_pow3_with_mul.py ONNX surgery for ALBERT-specific Pow(x, 3.0) and embedding-dim projection.1@misc{minilm-l6-hailo10h,
2 title = {all-MiniLM-L6-v2 Hailo-10H HEF (experimental)},
3 author = {CrispHailo project},
4 year = {2026},
5 howpublished = {\url{https://huggingface.co/cstr/all-MiniLM-L6-v2-hailo10h}}
6}
7
8@inproceedings{wang2020minilm,
9 title = {MiniLM: Deep Self-Attention Distillation for Task-Agnostic Compression of Pre-Trained Transformers},
10 author = {Wang, Wenhui and Wei, Furu and Dong, Li and Bao, Hangbo and Yang, Nan and Zhou, Ming},
11 booktitle = {NeurIPS},
12 year = {2020}
13}
14
15@inproceedings{reimers2019sbert,
16 title = {Sentence-{BERT}: Sentence Embeddings using {S}iamese {BERT}-Networks},
17 author = {Reimers, Nils and Gurevych, Iryna},
18 booktitle = {EMNLP-IJCNLP},
19 year = {2019}
20}optimization_level=0. Emulator eval wall: ~15 min for 16 samples on Kaggle CPU (DFC SDK_QUANTIZED is ~22× slower than SDK_FP_OPTIMIZED). Session 5 added a real MTEB/BEIR SciFact retrieval benchmark + a full quant-lever sweep (see PLAN.md).sentence-transformers.apache-2.0. This repository redistributes under the same terms; it grants no rights the upstream licence does not.