NeuroRVQ β€” Safetensors Weights

Pre-converted safetensors weights for the NeuroRVQ multi-scale biosignal tokenizer, ready for use with neurorvq-rs (pure-Rust inference on Burn 0.20) or any framework that supports safetensors.

Weights are converted from the official PyTorch .pt checkpoints published at ntinosbarmpas/NeuroRVQ.

Model Files

Tokenizers (encoder β†’ RVQ β†’ decoder)

File Modality Params Size Embed Patch RVQ
NeuroRVQ_EEG_tokenizer_v1.safetensors EEG 76.0 M 304 MB 200 200 8 levels
NeuroRVQ_ECG_tokenizer_v1.safetensors ECG 68.1 M 272 MB 40 40 8 levels
NeuroRVQ_EMG_tokenizer_v1.safetensors EMG 143.6 M 574 MB 200 200 16 levels

Foundation Models (encoder only)

File Modality Params Size Depth
NeuroRVQ_EEG_foundation_model_v1.safetensors EEG 58.6 M 234 MB 12 blocks
NeuroRVQ_EMG_foundation_model_v1.safetensors EMG 111.2 M 445 MB 12 blocks

Config Flags

File Description
flags/NeuroRVQ_EEG_v1.yml EEG β€” 103 channels, patch=200, embed=200
flags/NeuroRVQ_ECG_v1.yml ECG β€” 15 channels, patch=40, embed=40
flags/NeuroRVQ_EMG_v1.yml EMG β€” 16 channels, patch=200, embed=200

Quick Start β€” Rust

# Install
cargo add neurorvq-rs

# Download weights + config
huggingface-cli download eugenehp/NeuroRVQ \
    NeuroRVQ_EEG_tokenizer_v1.safetensors \
    flags/NeuroRVQ_EEG_v1.yml \
    --local-dir weights/

# Run tokenization
cargo run --release --bin infer -- \
    --config weights/flags/NeuroRVQ_EEG_v1.yml \
    --weights weights/NeuroRVQ_EEG_tokenizer_v1.safetensors

Library API

use neurorvq_rs::{NeuroRVQEncoder, Modality, data, channels};
use std::path::Path;

let (model, _ms) = NeuroRVQEncoder::<B>::load_with_modality(
    Path::new("flags/NeuroRVQ_EEG_v1.yml"),
    Path::new("NeuroRVQ_EEG_tokenizer_v1.safetensors"),
    Modality::EEG,
    device,
)?;

// Tokenize β†’ 4 branches Γ— 8 RVQ levels of discrete indices
let tokens = model.tokenize(&batch)?;
for (br, levels) in tokens.branch_tokens.iter().enumerate() {
    for (lv, indices) in levels.iter().enumerate() {
        println!("Branch {} Level {}: {} tokens", br, lv, indices.len());
    }
}

Foundation Model API

use neurorvq_rs::{NeuroRVQFoundationModel, Modality};

let (fm, _ms) = NeuroRVQFoundationModel::<B>::load(
    Path::new("flags/NeuroRVQ_EEG_v1.yml"),
    Path::new("NeuroRVQ_EEG_foundation_model_v1.safetensors"),
    Modality::EEG,
    device,
)?;

let features = fm.encode(&batch)?;       // 4 branch feature vectors
let pooled = fm.encode_pooled(&batch)?;   // Mean-pooled for classification

Quick Start β€” Python

from safetensors.torch import load_file

state_dict = load_file("NeuroRVQ_EEG_tokenizer_v1.safetensors")
model.load_state_dict(state_dict, strict=False)

Architecture

Raw Signal [B, N, T]
    β”‚
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Multi-Scale Temporal Conv           β”‚  4 parallel branches
β”‚  EEG/ECG: k=21,15,9,5               β”‚  modality-specific kernels
β”‚  EMG:     k=51,17,8,5               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    β”‚ Γ—4 branches
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Transformer Encoder                 β”‚  12 blocks, 10 heads
β”‚  + spatial / temporal pos. embeds    β”‚  shared weights across branches
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    β”‚ Γ—4 branches
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Encode Heads                        β”‚  Linear β†’ Tanh β†’ Linear
β”‚  embed_dim β†’ code_dim (128)          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    β”‚ Γ—4 branches
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Residual Vector Quantization        β”‚  8 levels (EEG/ECG)
β”‚  L2-norm codebook lookup             β”‚  16 levels (EMG)
β”‚  codebook: 8192 Γ— 128               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    β”‚ Γ—4 branches        ← discrete token indices
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Transformer Decoder                 β”‚  3 blocks
β”‚  per-branch PatchEmbed (1Γ—1 conv)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    β”‚ concat 4 branches
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Decode Heads                        β”‚  Amplitude (GELU)
β”‚  4Γ—embed_dim β†’ decoder_out_dim       β”‚  Sin/Cos phase (Tanh)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    β”‚
    β–Ό
  Inverse FFT β†’ Reconstructed Signal

Numerical Parity (Rust vs Python)

Verified against the official PyTorch reference implementation:

Layer Max Abs Error Notes
Encoder features < 8 Γ— 10⁻³ 12 transformer layers, f32 accumulation
Encode heads < 2 Γ— 10⁻³ After Tanh squashing
RVQ quantized vectors β‰ˆ 0 ΒΉ Exact with random-init codebooks
Token indices 99.3% exact Β² Pretrained weights
Decode outputs < 8 Γ— 10⁻¹ ΒΉ Dominated by ≀0.7% boundary tokens

ΒΉ Differences stem from the ≀0.7% of tokens near codebook decision boundaries β€” a natural consequence of f32 arithmetic differences between frameworks.

Β² With random-init weights: 100% match (all "mismatches" resolve to identical codebook vectors, i.e., ties).

Benchmarks

Platform: Apple M4 Pro, 64 GB RAM, macOS 15 (arm64)

Tokenize Latency β€” All Backends

Configuration Modality PyTorch CPU Rust NdArray Rust wgpu (GPU)
EEG 4ch Γ— 64t EEG 179 ms 661 ms 51 ms
EEG 8ch Γ— 32t EEG 180 ms 662 ms 60 ms
EEG 16ch Γ— 16t EEG 180 ms 664 ms 62 ms
EEG 32ch Γ— 8t EEG 178 ms 664 ms 65 ms
EEG 64ch Γ— 4t EEG 179 ms 664 ms 68 ms
ECG 4ch Γ— 150t ECG 272 ms 1881 ms 92 ms
ECG 8ch Γ— 75t ECG 273 ms 1874 ms 92 ms
ECG 12ch Γ— 50t ECG 272 ms 1877 ms 93 ms
ECG 15ch Γ— 40t ECG 272 ms 1878 ms 93 ms
EMG 4ch Γ— 64t EMG 255 ms 998 ms 90 ms
EMG 8ch Γ— 32t EMG 255 ms 998 ms 88 ms
EMG 16ch Γ— 16t EMG 254 ms 1001 ms 90 ms

Tokenize Latency: NdArray vs wgpu vs PyTorch

Tokenize Comparison

Encode Latency: NdArray vs wgpu vs PyTorch

Encode Comparison

Rust β€” Tokenize Latency by Configuration

Tokenize Latency

Rust β€” EEG Scaling by Channel Count

EEG Scaling

Rust β€” Model Construction Time

Construction Time

Backend Comparison Summary

Comparison Result
wgpu vs NdArray wgpu is ~12Γ— faster (GPU acceleration)
wgpu vs PyTorch CPU wgpu is ~3Γ— faster for EEG/EMG/ECG
NdArray vs PyTorch CPU PyTorch is ~3.7Γ— faster (optimized BLAS)

Key Observations

  • wgpu (GPU) is the fastest backend β€” 51–93 ms across all configurations
  • PyTorch CPU uses Apple Accelerate/AMX BLAS and fused operators, making it faster than Rust NdArray on CPU
  • Latency scales with total patch count, not the channel/time decomposition β€” EEG (256 patches) < EMG (256 patches, 16 RVQ) < ECG (600 patches)
  • Construction time is ~13 ms (warm) / ~54 ms (cold start for EMG with larger kernels)
  • Standard deviation < 1% β€” highly stable inference latency

Why Rust?

Python + PyTorch Rust + Burn
Dependencies pip, torch, numpy, einops, ... Zero (single static binary)
GPU support CUDA, MPS wgpu (Metal, Vulkan, WebGPU)
Deployment Interpreter + venv Single binary, WASM, embedded
Memory GC pauses Deterministic, no GC
Latency (GPU) β€” 51–93 ms (wgpu Metal)

Conversion

These weights were converted from the official .pt files:

import torch
from safetensors.torch import save_file

state_dict = torch.load("model.pt", map_location="cpu")
converted = {k: v.float().contiguous() for k, v in state_dict.items()
             if isinstance(v, torch.Tensor) and v.is_floating_point()}
save_file(converted, "model.safetensors")

Or use the included script:

python scripts/convert_pt_to_safetensors.py \
    --input NeuroRVQ_EEG_tokenizer_v1.pt \
    --output NeuroRVQ_EEG_tokenizer_v1.safetensors

Citation

@article{barmpas2024neurorvq,
  title={NeuroRVQ: Joint Neurophysiological Multi-Scale Temporal Tokenization and Reconstruction},
  author={Barmpas, Konstantinos and others},
  year={2024}
}

License

Apache-2.0 β€” same as the original NeuroRVQ release.

Links

Downloads last month
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support