reminis
Your model's weights are just data. Store them in a database.
reminis converts any GGUF model into a SQLite database where every tensor becomes a queryable, versionable, diffable row. Convert back to GGUF when you're done. Lossless. Fast.
pip install reminis
Why
The ML world treats model weights as opaque files. You save the whole thing, load the whole thing, and if something goes wrong, you retrain from scratch.
Once weights are in a database, you get — for free — everything that 40 years of database engineering has built: queries, rollback, diffs, branching, merging, audit logs, access control, replication.
Quick Start
# Convert a GGUF model to SQLite
reminis convert model.gguf
# Inspect what's inside
reminis info model.db
# Browse it in your browser
reminis view model.db
# Compare two models, and package the difference
reminis diff base.db finetuned.db -o change.delta.db
# Reconstruct the fine-tune from the base plus the pack
reminis apply base.db change.delta.db -o rebuilt.db
# Convert back to GGUF
reminis export model.db -o model_restored.gguf
Verified Results
Architectures
Every tensor is SHA256-hashed before and after the round-trip. These architectures are confirmed lossless:
| Architecture | Model | Tensors | Size | Convert | Export | Result |
|---|---|---|---|---|---|---|
llama |
Mistral-7B-Instruct-v0.3 Q4_K_M | 291 | 4170 MB | 18.5s | 4.4s | lossless |
llama |
Llama-3.2-1B-Instruct F16 | 147 | 2365 MB | 19.2s | 1.7s | lossless |
qwen2 |
Qwen2.5-0.5B-Instruct FP16 | 291 | 1208 MB | 14.4s | 0.9s | lossless |
granitemoe |
Granite-3.1-1B-A400M Q4_K_M | 242 | 784 MB | 6.5s | 0.5s | lossless |
clip (vision) |
SmolVLM-256M projector F16 | 198 | 181 MB | 1.0s | 0.1s | lossless |
llama |
SmolLM-135M, 9 quantizations | 272 | 84–258 MB | ~2s | ~0.1s | lossless |
The MoE model is the interesting one: 72 of its tensors are 3-dimensional expert stacks (e.g. [512, 1024, 32] in Q6_K), a shape the quantized export path had never seen. It generalizes correctly.
Throughput is roughly linear with size — about 120 MB/s converting, over 1 GB/s exporting.
Diff and apply at scale
Perturbing all 64 attention projections, then reconstructing from the pack:
| Model | Copy DB | Diff | Pack | Apply + verify | Result |
|---|---|---|---|---|---|
| Llama-3.2-1B, 2.4 GB, F16 | 0.57s | 9.4s | 278 MB (11.8%) | 7.9s | exact |
| Mistral-7B, 4.2 GB, Q4_K_M | 1.78s | 14.5s | 15 MB (0.4%) | 12.2s | exact |
The 7B row is quantized, so per-value deltas are not computable — but changes are still detected and encoded byte-exactly through the XOR path, which is why quantized models work at all.
Roughly half of each timing is SHA256 hashing the full model, which is what guarantees a pack cannot be applied to the wrong base. SHA256 is hardware-accelerated here (957 MB/s) and measurably faster than blake2b, so that is already the cheapest safe option.
Quantization coverage
SHA256-verified lossless round-trip across 9 SmolLM-135M variants covering 13 quantization types:
Model Dtypes Tensors GGUF MB DB MB RT MB Conv(s) Exp(s) Result
---------------------------------------------------------------------------------------------------------------------------------------
SmolLM-135M.IQ3_M F32,IQ3_S,IQ4_NL,Q4_K 272 86.0 86.1 84.3 1.73 0.05 PASS
SmolLM-135M.IQ4_XS F32,IQ4_NL,IQ4_XS,Q5_K 272 87.1 87.1 85.4 1.81 0.06 PASS
SmolLM-135M.Q2_K F32,IQ4_NL,Q3_K,Q8_0 272 84.1 84.2 82.4 1.64 0.05 PASS
SmolLM-135M.Q3_K_M F32,IQ4_NL,Q4_K,Q5_0 272 89.2 89.3 87.5 1.67 0.06 PASS
SmolLM-135M.Q4_K_M F32,Q4_K,Q5_0,Q6_K,Q8_0 272 100.6 100.7 98.9 1.71 0.05 PASS
SmolLM-135M.Q5_K_M F32,Q5_1,Q5_K,Q6_K,Q8_0 272 106.9 106.9 105.2 1.83 0.07 PASS
SmolLM-135M.Q6_K F32,Q6_K,Q8_0 272 132.0 132.0 130.3 1.84 0.07 PASS
SmolLM-135M.Q8_0 F32,Q8_0 272 138.1 138.2 136.4 1.92 0.08 PASS
SmolLM-135M.f16 F16,F32 272 258.3 258.4 256.7 2.39 0.14 PASS
---------------------------------------------------------------------------------------------------------------------------------------
9/9 models passed SHA256-verified lossless round-trip
ALL TESTS PASSED - every tensor in every model matches byte-for-byte
Every tensor in every model was hashed with SHA256 before and after the round-trip. Zero data loss.
What's in the Database
$ reminis info model.db
Database: model.db (258.4 MB)
general.name: SmolLM 135M
general.architecture: llama
Metadata fields: 40
Tensors: 272
Parameters: 134,515,008
Weight data: 256.6 MB
Dtype breakdown:
F16 211 tensors 256.5 MB
F32 61 tensors 0.1 MB
Every tensor gets its own row with full metadata:
| Column | Description |
|---|---|
name |
Tensor path (e.g. blk.5.attn_q.weight) |
shape |
Dimensions as JSON (e.g. [576, 576]) |
dtype |
Data type (F16, F32, Q4_K, Q8_0, etc.) |
n_elements |
Number of parameters |
n_bytes |
Storage size in bytes |
data |
Raw weight data as BLOB |
All model metadata (architecture, context length, vocab size, etc.) is stored in a model_meta table.
Query Your Model
Once in SQLite, you can query weights like any database:
-- Largest tensors by parameter count
SELECT name, n_elements, n_bytes / 1024 / 1024 as mb
FROM tensors ORDER BY n_elements DESC LIMIT 5;
-- All attention weights in layer 5
SELECT name, shape, dtype FROM tensors
WHERE name LIKE 'blk.5.attn%';
-- Total size by dtype
SELECT dtype, COUNT(*) as count, SUM(n_bytes) / 1024 / 1024 as total_mb
FROM tensors GROUP BY dtype;
-- Model architecture
SELECT key, value FROM model_meta
WHERE key LIKE '%context_length%' OR key LIKE '%block_count%';
Diffing and Delta Packs
reminis diff compares two models tensor by tensor and can emit a delta pack — a small database that reconstructs the target from the base:
reminis diff base.db instruct.db -o change.delta.db
reminis apply base.db change.delta.db -o rebuilt.db
Packs record the weight hashes of both sides. apply refuses a base that does not match, rather than silently producing a corrupt model, and verifies the result against the recorded target hash.
Encoding is XOR, not arithmetic subtraction. An arithmetic float delta is not exactly reversible — b - a generally is not representable in the tensor's own dtype, so a + delta lands a rounding step away from b. XOR is exact for every dtype, and also works on quantized tensors, whose bytes cannot be subtracted meaningfully at all. Per tensor, whichever of the compressed XOR delta or a compressed full replacement is smaller wins.
How small are packs, really?
It depends entirely on how much of the model the fine-tune touched.
| Scenario | Tensors changed | Pack size |
|---|---|---|
| Targeted change (5 of 272 tensors) | 5 | 1.4% of full model |
| Full fine-tune (SmolLM-135M to its Instruct variant) | 272 of 272 | 58.8% of full model |
The second row is the honest one for full fine-tuning. Every tensor changed, with ~97% of individual values differing in each, so lossless compression has little to exploit — the differing float16 mantissa bits are close to incompressible.
The delta is also only partly low-rank. Ranks needed to capture 90% of the delta's energy:
| Tensor | Shape | Rank for 90% | Full rank |
|---|---|---|---|
blk.0.attn_q.weight |
[576, 576] | 31 | 576 |
blk.18.ffn_gate.weight |
[1536, 576] | 311 | 576 |
blk.29.ffn_down.weight |
[576, 1536] | 291 | 576 |
Attention deltas compress well under a low-rank factorization; FFN deltas do not. That asymmetry is exactly what --lossy exploits.
Low-rank packs for LoRA fine-tunes
A LoRA update is W + BA with BA rank-r by construction, so the delta is genuinely low-rank and needs only r*(m+n) numbers instead of m*n. Opt in with --lossy:
reminis diff base.db lora-merged.db -o change.delta.db --lossy 0.01
The tolerance is the maximum relative error allowed per tensor (default 0.01 = 1%). Measured on a rank-16 LoRA merge:
| Model | Lossless pack | Low-rank pack | Shrink | Worst error |
|---|---|---|---|---|
| SmolLM-135M, 258 MB | 38.3 MB (14.9%) | 3.5 MB (1.4%) | 11.0x | 1.2e-04 |
| Llama-3.2-1B, 2.4 GB | 242.7 MB (10.3%) | 6.4 MB (0.3%) | 37.9x | 1.1e-04 |
Achieved error lands ~100x inside the 1% budget, because rank is chosen by error target rather than fixed.
It decides per tensor, and never makes a pack worse. Low-rank is only used where it both beats the lossless size and stays inside tolerance; otherwise the lossless encoding is kept. On the full fine-tune above, only 1 of 272 tensors qualified and the pack stayed lossless — so --lossy degrades gracefully to "no change" rather than silently hurting quality.
Cost is a slower diff (SVD): roughly 4x on the 1B model. Quantized and non-2D tensors are never low-rank encoded, since their bytes cannot be decomposed.
Lossy packs are still exactly verifiable. The reconstruction is deterministic, so the pack records the hash of what apply will actually produce, and apply checks against it byte-for-byte. What it cannot promise is that this equals the original target — that divergence is carried as a recorded error bound and printed on apply:
Result verified against reconstruction hash
NOTE: this is a lossy pack (120 tensors low-rank encoded). The result is not
byte-identical to the original target; worst per-tensor relative error is 1.16e-04.
Python API
from reminis import gguf_to_sqlite, sqlite_to_gguf
# Convert
db_path = gguf_to_sqlite("model.gguf")
# Query with standard sqlite3
import sqlite3
conn = sqlite3.connect(db_path)
for name, n_elements in conn.execute(
"SELECT name, n_elements FROM tensors ORDER BY n_elements DESC LIMIT 5"
):
print(f"{name}: {n_elements:,} params")
# Export back
sqlite_to_gguf(db_path, "model_restored.gguf")
Supported Formats
All GGUF tensor types are supported and verified, including:
| Type | Description | Verified |
|---|---|---|
| F32, F16 | Full precision | Yes |
| Q4_K, Q5_K, Q6_K | K-quants (4/5/6 bit) | Yes |
| Q8_0 | 8-bit quantized | Yes |
| Q3_K, Q5_0, Q5_1 | Other quants | Yes |
| IQ3_S, IQ4_NL, IQ4_XS | Importance-weighted quants | Yes |
Roadmap
- Publish to PyPI
- GGUF to SQLite converter (lossless, verified across 13 quant types)
- SQLite to GGUF back-converter (lossless, byte-perfect)
- SHA256 verification test suite
- Interactive HTML viewer (
reminis view) - Weight diffing between model versions (
reminis diff) - Delta packs with verified apply (
reminis apply) - Validated to 7B across llama / qwen2 / granitemoe / clip
- Low-rank delta encoding for LoRA fine-tunes (
--lossy) - Safetensors input, for the transformers/peft workflow
- Fine-tune tracking with edit logs
- Surgical rollback of bad training steps
- Model merging via SQL operations
- Inference from database-stored weights
- Unsloth integration
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file reminis-0.3.0.tar.gz.
File metadata
- Download URL: reminis-0.3.0.tar.gz
- Upload date:
- Size: 37.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.31 {"installer":{"name":"uv","version":"0.11.31","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
220c3c233c8fba4d36a1e29bfa19bedffa90bb58307f29377f79129a28bf6765
|
|
| MD5 |
2228cc86c2476b681643190135778451
|
|
| BLAKE2b-256 |
2845690528eb951a58a9c49b35992652cd72a1da54ef416ce590d04c4085bb39
|
File details
Details for the file reminis-0.3.0-py3-none-any.whl.
File metadata
- Download URL: reminis-0.3.0-py3-none-any.whl
- Upload date:
- Size: 34.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.31 {"installer":{"name":"uv","version":"0.11.31","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90b22d281481890e219b547c8da9b21ac62c66681d31b3ec14fb3f3c2c4ee972
|
|
| MD5 |
9d4a639a46e1402e0a59573431bd8553
|
|
| BLAKE2b-256 |
7627d0799d7d58a9df0a4b918da9fbe1e16e63e1ad61dfa34ade5ff603ff15e9
|