
FAI Fully Homomorphic Classification · v1
An image classifier that runs on encrypted images. The model decides whether a photo contains a person or no person while the photo stays encrypted end to end: the party running the model never sees the image, never holds a key that could open it, and never sees the answer. Only the holder of the secret key — the person who encrypted the photo — can read the result.
This is version 1: a working, measured, auditable first release of a fully homomorphic encryption (FHE) image classifier. It demonstrates the complete encrypted path on a real task with published numbers. It is not yet tuned for throughput, and its single demonstrated use is binary person detection on natural photographs. Read the Limitations before building on it.
| Model name | Falconsai/fai_fully_homomorphic_classification |
| Version | 1 (release identifier FAI_FHE_20261015_054859) |
| Task | Binary image classification: person vs no_person |
| Encryption | TFHE (Fully Homomorphic Encryption over the Torus), 128-bit security, compiled with Zama Concrete ML 1.9.0 / concrete-python 2.10.0 |
| Input | 64×64 RGB image |
| Output | 2 logits (no_person, person), decrypted by the client |
| Parameters | 46,968 (integer-quantized: 6-bit inputs, 4-bit weights, 4-bit activations) |
| Developed by | FALCONS.AI |
What this model classifies
The model answers one question about a photograph: is there a person in it?
| Label | Meaning |
|---|---|
person |
At least one human being is visible and occupies roughly 2 % or more of the frame — a pedestrian, a crowd, a portrait, someone in the background of a street scene |
no_person |
No human being of that size is present — landscapes, animals, vehicles, objects, interiors, food, documents, empty rooms |
It was evaluated on natural photographs of everyday scenes (COCO-2017 validation images): streets, homes, sports, animals, vehicles, food, indoor and outdoor settings. That is the domain in which the published numbers hold.
Examples of what v1 is for
- Occupancy and presence gating — "is anyone in this frame?" for cameras whose footage may not leave the premises unencrypted: lobbies, wards, control rooms, home cameras processed by a third party.
- Privacy-preserving photo triage — routing uploaded images that contain people into a different handling path (consent, retention, blurring) without the triage service ever seeing the image.
- Content gating for shared models — a service that must answer "person / no person" for a partner's images without the partner disclosing them.
- A reference workload — an end-to-end encrypted classifier with an audit pack, validation scripts and measured latency, for teams evaluating FHE in their own environment.
What it does not do: it does not count people, locate them, identify them, estimate age or any attribute, or recognise faces. It outputs one of two labels and a confidence.
Encryption: what is protected and what is not
| Scheme | TFHE — lattice-based, post-quantum family; programmable bootstrapping (41,024 bootstraps per image) |
| Security level | 128-bit (parameters selected by the Concrete optimizer); per-operation error probability 8.0 × 10⁻¹³ |
| Circuit | Max intermediate bit width 13; accumulator rounding 7 bits |
| Secret key | Generated and held by the client only. Never transmitted. |
| Evaluation keys | ~2.1 GB, generated by the client from its secret key and registered once with the server; they allow computation on ciphertexts but cannot decrypt |
| Ciphertexts | Input ≈ 295 kB per image; output ≈ 262 kB |
| Key generation | ≈ 17.5 s on the client (measured, 8-core CPU) |
The server learns nothing about the image or the prediction. It learns that a query happened, its size, and its timing. The model weights are held by the server in plaintext; this model protects the data, not the model. Many queries can reveal how the model behaves (model extraction), so rate-limit the API if that matters to you.
Files
| File | Purpose |
|---|---|
fhe/client.zip |
What a client needs to generate keys, encrypt inputs and decrypt outputs for this circuit |
fhe/server.zip |
The compiled FHE circuit (MLIR) the server executes on ciphertexts |
fhe/model_meta.json |
Class names, preprocessing, encryption parameters, measured performance |
fhe/versions.json |
Exact library versions the artifacts were written with |
FAI_FHE.onnx |
The same classifier as a plaintext ONNX graph (quantization-aware), for simulation and comparison — not encrypted |
FAI_FHE.pt |
PyTorch state dict of the plaintext classifier |
validation/ |
verify_model.py (re-run the metrics yourself), metrics_with_ci.json, VALIDATION_REPORT.md, ACCEPTANCE.md, per-image predictions |
audit/ |
Crypto parameters read from the artifacts, compiled circuit, threat model, key-management note, SBOM, provenance with SHA-256 sums |
model_card.md |
The release's own generated card |
Version requirements: the client must run Python 3.12 (the client.zip was written on 3.12 and Concrete ML refuses a different minor); the server is version-independent. Both need concrete-ml==1.9.0.
How to use
Input format
RGB image → bilinear resize to 64×64 (whole image, no crop) → float32, NCHW → x / 255 · 2 − 1
Values in [−1, 1], shape (1, 3, 64, 64). Decode the two output logits with argmax (or softmax for a confidence); index 0 = no_person, index 1 = person.
Encrypted inference (client / server)
# pip install concrete-ml==1.9.0 (Python 3.12 on the client)
import numpy as np
from PIL import Image
from concrete.ml.deployment import FHEModelClient, FHEModelServer
# ---- client side: keys stay here
client = FHEModelClient(path_dir="fhe", key_dir="my_keys") # folder containing client.zip
client.generate_private_and_evaluation_keys()
evaluation_keys = client.get_serialized_evaluation_keys() # send ONCE to the server (~2.1 GB)
img = Image.open("photo.jpg").convert("RGB").resize((64, 64), Image.BILINEAR)
x = (np.asarray(img, dtype=np.float32).transpose(2, 0, 1)[None] / 255.0) * 2.0 - 1.0
encrypted_input = client.quantize_encrypt_serialize(x) # ~295 kB; the photo never leaves in the clear
# ---- server side: never sees x, the keys that decrypt, or the answer
server = FHEModelServer(path_dir="fhe") # folder containing server.zip
server.load()
encrypted_output = server.run(encrypted_input, evaluation_keys) # computation on ciphertext
# ---- client side: open the answer
logits = client.deserialize_decrypt_dequantize(encrypted_output)[0]
label = ["no_person", "person"][int(np.argmax(logits))]
Plaintext check (no encryption, same arithmetic)
import onnxruntime as ort
sess = ort.InferenceSession("FAI_FHE.onnx")
logits = sess.run(None, {sess.get_inputs()[0].name: x})[0]
Use this to confirm preprocessing and to compare against the encrypted result; the FHE circuit reproduces this classifier's integer arithmetic.
Ready-made deployment
A Docker stack (model service + a drop-zone client page) that runs this model with no local installs is available from FALCONS.AI as part of the FALCONS.AI Fully Homomorphic Encryption (FHE) Classification product.
Evaluation
Held-out test set: 4,247 COCO-2017 validation images (2,307 no_person, 1,940 person). Confidence intervals are 95 % bootstrap intervals. "FHE-simulated" reproduces the exact integer arithmetic of the encrypted circuit on all 4,247 images; "encrypted" is the real TFHE execution.
| Accuracy | ROC-AUC | F1 | Balanced acc. | Avg. precision | |
|---|---|---|---|---|---|
| Plaintext (quantized) | 81.42 % (80.2–82.5) | 0.887 (0.877–0.897) | 0.793 (0.779–0.806) | 0.812 | 0.886 |
| FHE-simulated (full test set) | 81.19 % (80.0–82.4) | 0.884 (0.874–0.894) | 0.787 (0.773–0.802) | 0.808 | 0.881 |
| Encrypted (TFHE, 1 image) | 100 % (1 of 1) | – | – | – | – |
- Accuracy drop from plaintext to encrypted arithmetic: 0.24 percentage points.
- Label agreement, FHE-simulated vs plaintext: 95.0 % of images.
- Label agreement, encrypted vs simulated: 100 % on the encrypted sample.
- Calibration (expected calibration error): 0.012 under FHE simulation — the confidence is usable as a probability.
Per-class recall (FHE-simulated): no_person 85.3 %, person 76.3 %.
Operating points (FHE-simulated; threshold on the logit margin person − no_person):
| Threshold | Precision | Recall | False-positive rate |
|---|---|---|---|
| −1 | 0.683 | 0.897 | 0.350 |
| 0 (default, argmax) | 0.814 | 0.763 | 0.147 |
| +1 | 0.912 | 0.577 | 0.047 |
| +2 | 0.960 | 0.379 | 0.013 |
Lower the threshold when missing a person is costly; raise it when false alarms are.
Speed (measured)
| Encrypted inference, 8 CPU cores | 3,762 s per image (≈ 63 min) |
| Plaintext inference | ≈ 3 ms per image |
| Key generation | 17.5 s |
This is the honest cost of full FHE at 13-bit circuit width on CPU. It is suitable for asynchronous, batch or audit workflows, not interactive use. GPU TFHE and narrower circuits are the path to seconds per image; neither is part of v1.
Limitations
- Version 1. One task, one resolution, one circuit width. The release passed its accuracy checks (accuracy drop ≤ 1.5 pp; ROC-AUC ≥ 0.80) and did not pass its own stretch targets for plaintext/FHE label agreement (95.0 % vs 97 % target) or latency (3,762 s vs 300 s target). Those targets define v2.
- 64×64 input. Fine detail is lost; small or distant people (below ~2 % of the frame) are not reliably detected. Performance on stylised, synthetic, medical, document or aerial imagery has not been measured.
- Binary only. No counting, localisation, identity or attributes.
- Evaluation domain. Numbers above are for everyday natural photographs; measure on your own data with
validation/verify_model.pybefore relying on them. - The model is not hidden from the server. FHE protects the image and the answer; whoever runs
server.zipholds the circuit. - Large evaluation keys. ~2.1 GB per client key set must be transferred to the server once.
- Library pinning. Artifacts are bound to concrete-ml 1.9.0 / concrete-python 2.10.0 and a Python 3.12 client.
- Bias. Person detection on COCO-style data inherits that data's distribution of people, places and cameras; no demographic breakdown has been measured for v1.
Intended use and out-of-scope use
Intended: evaluating and deploying encrypted person/no-person classification where the image owner must stay cryptographically blind to the processor; benchmarking FHE inference; building privacy-preserving triage pipelines.
Out of scope: safety-critical decisions (access control, surveillance alerts that act without a human), any identification or profiling of individuals, and any domain other than natural photographs without re-validation.
Citation
@misc{falconsai_fai_fhe_v1_2026,
title = {FAI Fully Homomorphic Classification v1: a TFHE image classifier for person detection on encrypted images},
author = {FALCONS.AI},
year = {2026},
url = {https://huggingface.co/Falconsai/fai_fully_homomorphic_classification}
}
Contact
FALCONS.AI · m.stattelman@falcons.ai
Release FAI_FHE_20261015_054859 · TFHE via Zama Concrete ML 1.9.0 · 128-bit security.
Dataset used to train Falconsai/fai_fully_homomorphic_classification
Evaluation results
- Accuracy (FHE-simulated, 95% CI 0.800–0.824) on COCO-2017 val (held-out, 4,247 images)self-reported0.812
- ROC-AUC (FHE-simulated, 95% CI 0.874–0.894) on COCO-2017 val (held-out, 4,247 images)self-reported0.884
- F1 (FHE-simulated, 95% CI 0.773–0.802) on COCO-2017 val (held-out, 4,247 images)self-reported0.787