mantissa-embed
Learning what "similar" means, with a C engine. A classifier answers which class; an embedder answers how alike — it maps each image to a point in a vector space where same-thing images land close together and different-thing images land far apart. That single geometry powers similarity search, verification ("are these two the same?") and retrieval ("show me the nearest matches"), for classes the model was never trained to name.
mantissa-embed trains that embedding with metric learning — the
contrastive and triplet losses — on top of a
mantissa-cnn trunk: its
Conv2D / MaxPool2D / Flatten / Dense layers, its
mantissa C-engine and pure-numpy
backends, and its dataset loaders are reused, not reimplemented. This package
adds only what metric learning needs on top of a classifier: the two losses,
an Embedder that trains a trunk with them, and the four things embeddings buy
you — fit / embed / retrieve / verify.
The mantissa family
Part of the mantissa family: a low-precision engine written in C, with small Python packages built on top. Each package sits under the one it depends on — ⭐ marks where you are, and every other name links to its repo.
- mantissa — low-precision neural-network engine in C (the core)
- mantissa-perceptron — perceptron & ADALINE, the linear classics
- mantissa-nn — shared neural-net primitives (layers, engine binding)
- mantissa-cnn — convolutional networks for images
- mantissa-auto-encoder — autoencoders for denoising & super-resolution
- mantissa-interpret — CNN interpretability (occlusion, saliency, Grad-CAM)
- ⭐ mantissa-embed — CNN metric learning (image embeddings for similarity & retrieval) (you are here)
- mantissa-mlp — multilayer perceptrons, fully-connected nets
- mantissa-cnn — convolutional networks for images
New to metric learning?
A classifier learns a fixed set of labels and a decision boundary between them. Metric learning learns something more basic and more reusable: a distance. It trains the network so that the Euclidean distance between two images' embeddings is a measure of how similar they are — and it does that using only the relation between examples ("these two are the same kind", "these two are not"), never a class name. Two consequences follow:
- It generalizes to classes it never trained on. Because the loss only ever says "closer" or "farther", a model trained on some identities produces useful distances for identities it has never seen — the basis of face verification, where you cannot retrain for every new person.
- One embedding serves many tasks. Compute it once per image, then: verify by thresholding a distance, retrieve by nearest-neighbour search, cluster by feeding the vectors to any clustering method.
Two classic losses do the training, and this package implements both:
- Contrastive loss works on pairs. A same-class pair pays its squared
distance (pulled together); a different-class pair pays a hinge that is zero
once the pair is at least a
marginapart (pushed apart, but only until far enough) — Hadsell, Chopra & LeCun (2006), "Dimensionality Reduction by Learning an Invariant Mapping", CVPR. - Triplet loss works on triples of (anchor, positive, negative). It asks
only that the negative be farther from the anchor than the positive, by at
least a
margin— a relative constraint, which is often easier to satisfy and to scale than pinning absolute distances — Schroff, Kalenichenko & Philbin (2015), "FaceNet: A Unified Embedding for Face Recognition and Clustering", CVPR.
A Siamese/triplet network is just one network run more than once. The two
legs of a pair (or three of a triplet) share the same weights, so there is no
second network to manage: stack the legs into one batch, run a single forward
pass, compute the loss and its per-row gradient on the resulting embeddings,
and backpropagate that gradient through the one trunk. That is exactly what
Embedder.fit does — the same custom forward/loss/backward loop the rest of the
family uses, with a metric loss where the classifier's softmax would be.
Install
pip install mantissa-embed
Pulls in mantissa-cnn >= 0.2.2 (and transitively mantissa-nn + the
mantissa-core engine). For the demo's plots, pip install mantissa-embed[viz].
From checkouts (works today, no PyPI needed): clone this repo, cnn,
mantissa-nn and mantissa side by
side, build the engine (make dist there), then here:
pip install -e ../mantissa -e ../mantissa-nn -e ../cnn && pip install -e ".[viz]"
mantissa-cnn finds the sibling engine checkout automatically, and its dataset
loaders find a data/ directory via MANTISSA_CNN_DATA (the demo points this
at the sibling cnn/data/ for you).
Quickstart
# datasets are mantissa-cnn's; nothing downloads implicitly — fetch once:
python -m mantissa_cnn.datasets download mnist
from mantissa_cnn import datasets
from mantissa_embed import Embedder, models
Xtr, ytr, Xte, yte = datasets.subset("mnist", 6000, 2000, seed=0)
emb = Embedder(models.small_cnn_embedder(embed_dim=16), loss="triplet") # or "contrastive"
emb.fit(Xtr, ytr, epochs=8, batch_size=64, lr=0.05, verbose=True)
Z = emb.embed(Xte) # (2000, 16) embedding vectors
idx, dist = emb.retrieve(Xte[0], Z, k=5) # 5 nearest test images to query 0
same = emb.verify(Xte[0], Xte[1], threshold=1.0) # same identity? (bool)
Or compose your own trunk from mantissa-cnn's layers — any Conv/Pool/Flatten
stack ending in Dense(embed_dim, act="identity"):
from mantissa_cnn import Conv2D, MaxPool2D, Flatten, Dense
from mantissa_embed import Embedder
emb = Embedder(
[Conv2D(16, 3, pad=1), MaxPool2D(2), Flatten(), Dense(32)], # 32-D embedding
loss="contrastive", margin=1.0, seed=0)
The API
Embedder(layers, loss="triplet"|"contrastive", margin=None, seed=0, backend="mantissa"),
then:
| method | does | returns |
|---|---|---|
fit(X, y, epochs, batch_size, lr, verbose) |
trains the trunk by metric learning; y only decides same/different |
self (history_["loss"] per epoch) |
embed(X) |
one forward pass, chunked | (n, embed_dim) embeddings |
retrieve(query, gallery, k) |
k nearest gallery items in embedding space (numpy argpartition, no sklearn) |
(indices, distances), nearest-first |
verify(a, b, threshold=None) |
embedding distance between a and b; with threshold, a boolean same/different |
distance, or bool |
retrieve and verify accept raw images or a pre-computed (., embed_dim)
matrix, so a fixed gallery is embedded once and reused across queries.
Deliberately minimal, like the rest of the family: NCHW float32 images, plain
SGD, the two classic losses, plain Euclidean distances. No autograd graph, no
optimizer zoo, no ANN index. The trunk's convolutions run in C on zero-copy
float32 buffers; the losses are memory-bound reductions over embedding vectors
and honestly stay in numpy (mantissa_embed.losses, importable and gradient-
checked on their own). Layers allocate scratch once per batch shape and reuse
it.
Results
A small_cnn_embedder (52,528 params, 16-D embedding) trained on a 6k-image
MNIST subset with the triplet loss and the mantissa C engine (8 epochs, triplet
loss 0.090 → 0.016), then embedding the held-out 2k test set. Reproduce with
python examples/embeddings_demo.py.
The embedding separates the digits with no label ever entering the loss — colour is the true digit, shown only for the plot:
And nearest-neighbour retrieval returns same-digit images (green border = a correct match, i.e. the neighbour's true digit equals the query's):
License
MIT — © Tekin Ertekin. Base package: mantissa-cnn; engine: mantissa — same author, MIT.
Metadata
Release files for mantissa-embed 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| mantissa_embed-0.1.0.tar.gz | 16.5 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mantissa_embed-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.0 kB
Release files / mantissa_embed-0.1.0.tar.gz
| Download URL | mantissa_embed-0.1.0.tar.gz |
|---|---|
| Size | 16.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
51ab79c82f9365b6da0a1eae2c87eb96634e0e9abec9cd74dbe044adb43f31d0
|
|
BLAKE2b-256 checksum How to use checksums |
756ce830f32a4cd0a316bdca777bbe415743e043304e839b88b903ee1426b85c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 25, 2026.
Transparency logRelease files / mantissa_embed-0.1.0-py3-none-any.whl
| Download URL | mantissa_embed-0.1.0-py3-none-any.whl |
|---|---|
| Size | 14.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
df931e052c87d423e780b779ff773dc63088c77426287ad414ddee2b776cb26d
|
|
BLAKE2b-256 checksum How to use checksums |
5ab018ae1100690bdcbff8ae61bf2c55ac6584c0ba11ec67458b5e9ca43a9755
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 25, 2026.
Transparency logSecurity for mantissa-embed 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for mantissa_embed-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 25, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.