FakeRocket543/GLiNER-Forge

Multi-platform GLiNER inference: macOS MLX + NVIDIA CUDA + RK3588S ONNX, with fp16 quantization bug fix

0

stars

15

commits

Python

primary language

Jun 5, 2026

updated

README

GLiNER-Forge — Multi-Platform GLiNER Inference

Zero-shot NER (Named Entity Recognition) via GLiNER2, benchmarked across macOS (Apple Silicon), NVIDIA CUDA, and ARM edge (RK3588S).

Two Tracks

TrackPurposeRuntimeUse case
python/Research & iterationMLX Python / ONNX RuntimeFast experiments, model conversion, accuracy validation
native/Production FFIMLX C++ → dylibSwift/Dart app embedding

Quick Start

Python (MLX)

cd python
pip install -r requirements.txt
python gliner_mlx/infer.py

Python (ONNX)

python gliner_onnx/infer.py

Native build

cd native && ./build.sh
# → dist/libgliner_wrapper.dylib

Model

Using gliner2-multi-v1 (mDeBERTa-v3-base, 100+ languages).

FormatSourceSize
MLX safetensorsOpenMed/gliner-relex-base-v1.0-mlx~900 MB
ONNX fp16SemplificaAI/gliner2-multi-v1-onnx~645 MB
ONNX fp32same~1.2 GB

Quantization Status

MethodNERRelation ExtractionNotes
FP16Recommended, lossless accuracy
INT8✅ Works⚠️ Pending3-step fp16→fp32→int8 conversion, see convert/fix_fp16.py
INT4/Q4❌ UntestedDeBERTa encoder may crash
GGUF❌ Not feasiblellama.cpp does not support encoder-only models

INT8 on RK3588S (NanoPi M6)

VariantSizeEncoder TimeSpeedup
fp16531MB1878ms1.00×
int8322MB853ms2.20×

See reports/rk3588s_benchmark.md

⚠️ INT8 quantization pitfall: Running quantize_dynamic directly on fp16 ONNX will fail. You must first strip 153+ hidden fp16 tensors (Constant node attributes, ConstantOfShape, Cast targets). Use python/convert/fix_fp16.py to handle this.

Directory Layout

GLiNER-Forge/
├── python/
│   ├── gliner_mlx/      # MLX Python inference (OpenMed)
│   ├── gliner_onnx/     # ONNX Runtime inference
│   │   ├── infer.py             # macOS (CoreML)
│   │   └── infer_crossplatform.py # macOS + Linux ARM
│   ├── convert/         # to_onnx, to_mlx, quantize, fix_fp16
│   │   ├── fix_fp16.py          # fp16→fp32 cleanup (required before int8)
│   │   ├── quantize.py
│   │   ├── to_mlx.py
│   │   └── to_onnx.py
│   └── requirements.txt
├── native/
│   ├── src/             # gliner_wrapper.h/.cpp (MLX C++)
│   ├── dart/            # Dart FFI binding
│   ├── swift/           # Swift bridge
│   └── build.sh
├── bench/               # speed & accuracy benchmarks
│   ├── bench_rk3588s.py         # NanoPi M6 (RK3588S) benchmark
│   └── ...
├── reports/
│   └── rk3588s_benchmark.md     # RK3588S detailed report
├── models/              # weights (gitignored)
└── README.md

CUDA Results (Quadro T2000, 4GB VRAM)

See bench/results_cuda.md for full details.

ModelVRAMAvg ms/articleNotes
small-v2.5675 MB94 msBest speed/VRAM ratio
medium-v2.5836 MB112 msMarginal improvement
large-v2.51838 MB236 msBest precision
ONNX (CUDA)162 msSlower than PyTorch on CUDA

Key finding: ONNX wins on Apple Silicon, PyTorch wins on CUDA.

Cross-Platform Comparison

PlatformHardwareBackendBest ModelLatencyVRAM/RAM
Apple SiliconM-series (MPS)ONNX FP32small-v2.513ms~900MB
NVIDIA CUDAQuadro T2000 (4GB)PyTorch FP32small-v2.594ms675MB
ARM EdgeRK3588S (NanoPi M6)ONNX INT8gliner2-multi-v1853ms322MB

INT8 Precision Note

PlatformINT8 ImpactNotes
Apple Silicon (MPS)❌ Destructive (recall drops to 6.6-51.5%)ONNX quant on MPS breaks mDeBERTa
NVIDIA CUDA⚠️ Not testedPyTorch native faster anyway
ARM Edge (RK3588S)Zero loss (18/18 cases, 10 langs, 100% recall)Full pipeline verified: encoder + span_rep + classifier

RK3588S INT8 accuracy validated with Wikipedia Taiwan multilingual test data (18 cases, 10 languages: zh-TW, en, ja, ko, ru, fr, es, ar, th, vi). All cases achieve 100% recall at threshold 0.3. See bench/bench_rk3588s_accuracy.py.

Pipeline Recommendation by Platform

PlatformModelBackendQuantization
Apple Siliconsmall-v2.5ONNX FP32 (th=0.1)None
NVIDIA GPUsmall-v2.5PyTorch CUDA (th=0.3)None
ARM Edge (RK3588S)gliner2-multi-v1ONNX INT8Dynamic int8 via fix_fp16.py

References

Contributors

FakeRocket543

15 commits

FakeRocket543/GLiNER-Forge

Multi-platform GLiNER inference: macOS MLX + NVIDIA CUDA + RK3588S ONNX, with fp16 quantization bug fix

0

stars

15

commits

Python

primary language

Jun 5, 2026

updated

README

GLiNER-Forge — Multi-Platform GLiNER Inference

Zero-shot NER (Named Entity Recognition) via GLiNER2, benchmarked across macOS (Apple Silicon), NVIDIA CUDA, and ARM edge (RK3588S).

Two Tracks

TrackPurposeRuntimeUse case
python/Research & iterationMLX Python / ONNX RuntimeFast experiments, model conversion, accuracy validation
native/Production FFIMLX C++ → dylibSwift/Dart app embedding

Quick Start

Python (MLX)

cd python
pip install -r requirements.txt
python gliner_mlx/infer.py

Python (ONNX)

python gliner_onnx/infer.py

Native build

cd native && ./build.sh
# → dist/libgliner_wrapper.dylib

Model

Using gliner2-multi-v1 (mDeBERTa-v3-base, 100+ languages).

FormatSourceSize
MLX safetensorsOpenMed/gliner-relex-base-v1.0-mlx~900 MB
ONNX fp16SemplificaAI/gliner2-multi-v1-onnx~645 MB
ONNX fp32same~1.2 GB

Quantization Status

MethodNERRelation ExtractionNotes
FP16Recommended, lossless accuracy
INT8✅ Works⚠️ Pending3-step fp16→fp32→int8 conversion, see convert/fix_fp16.py
INT4/Q4❌ UntestedDeBERTa encoder may crash
GGUF❌ Not feasiblellama.cpp does not support encoder-only models

INT8 on RK3588S (NanoPi M6)

VariantSizeEncoder TimeSpeedup
fp16531MB1878ms1.00×
int8322MB853ms2.20×

See reports/rk3588s_benchmark.md

⚠️ INT8 quantization pitfall: Running quantize_dynamic directly on fp16 ONNX will fail. You must first strip 153+ hidden fp16 tensors (Constant node attributes, ConstantOfShape, Cast targets). Use python/convert/fix_fp16.py to handle this.

Directory Layout

GLiNER-Forge/
├── python/
│   ├── gliner_mlx/      # MLX Python inference (OpenMed)
│   ├── gliner_onnx/     # ONNX Runtime inference
│   │   ├── infer.py             # macOS (CoreML)
│   │   └── infer_crossplatform.py # macOS + Linux ARM
│   ├── convert/         # to_onnx, to_mlx, quantize, fix_fp16
│   │   ├── fix_fp16.py          # fp16→fp32 cleanup (required before int8)
│   │   ├── quantize.py
│   │   ├── to_mlx.py
│   │   └── to_onnx.py
│   └── requirements.txt
├── native/
│   ├── src/             # gliner_wrapper.h/.cpp (MLX C++)
│   ├── dart/            # Dart FFI binding
│   ├── swift/           # Swift bridge
│   └── build.sh
├── bench/               # speed & accuracy benchmarks
│   ├── bench_rk3588s.py         # NanoPi M6 (RK3588S) benchmark
│   └── ...
├── reports/
│   └── rk3588s_benchmark.md     # RK3588S detailed report
├── models/              # weights (gitignored)
└── README.md

CUDA Results (Quadro T2000, 4GB VRAM)

See bench/results_cuda.md for full details.

ModelVRAMAvg ms/articleNotes
small-v2.5675 MB94 msBest speed/VRAM ratio
medium-v2.5836 MB112 msMarginal improvement
large-v2.51838 MB236 msBest precision
ONNX (CUDA)162 msSlower than PyTorch on CUDA

Key finding: ONNX wins on Apple Silicon, PyTorch wins on CUDA.

Cross-Platform Comparison

PlatformHardwareBackendBest ModelLatencyVRAM/RAM
Apple SiliconM-series (MPS)ONNX FP32small-v2.513ms~900MB
NVIDIA CUDAQuadro T2000 (4GB)PyTorch FP32small-v2.594ms675MB
ARM EdgeRK3588S (NanoPi M6)ONNX INT8gliner2-multi-v1853ms322MB

INT8 Precision Note

PlatformINT8 ImpactNotes
Apple Silicon (MPS)❌ Destructive (recall drops to 6.6-51.5%)ONNX quant on MPS breaks mDeBERTa
NVIDIA CUDA⚠️ Not testedPyTorch native faster anyway
ARM Edge (RK3588S)Zero loss (18/18 cases, 10 langs, 100% recall)Full pipeline verified: encoder + span_rep + classifier

RK3588S INT8 accuracy validated with Wikipedia Taiwan multilingual test data (18 cases, 10 languages: zh-TW, en, ja, ko, ru, fr, es, ar, th, vi). All cases achieve 100% recall at threshold 0.3. See bench/bench_rk3588s_accuracy.py.

Pipeline Recommendation by Platform

PlatformModelBackendQuantization
Apple Siliconsmall-v2.5ONNX FP32 (th=0.1)None
NVIDIA GPUsmall-v2.5PyTorch CUDA (th=0.3)None
ARM Edge (RK3588S)gliner2-multi-v1ONNX INT8Dynamic int8 via fix_fp16.py

References

Contributors

FakeRocket543

15 commits

Languages

Python

82.3%

C++

15.3%

Shell

1.4%