Concrete ML: FHE Inference Wrapped in a scikit-learn API
Concrete ML: Privacy Preserving ML framework using Fully Homomorphic Encryption (FHE), built on top of Concrete, with bindings to traditional ML frameworks.
At a glance
- What is it?
- Concrete ML turns quantized machine learning models into fully homomorphic encryption equivalents, with a compile step that must run before any encrypted prediction. It is a good fit when a model can be quantized and a representative data set is available, and a poor fit when neither holds.
- Who is it for?
- Adopt Concrete ML if your model is already expressible as a scikit-learn estimator or a quantizable PyTorch or Keras model, and if you can supply the representative data set that compile() needs. Do not adopt it if you need floating-point precision, if your model cannot be quantized, or if you expect encrypted training to behave like plaintext training.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 42 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The gap Concrete ML is trying to close
Fully homomorphic encryption lets a server compute on ciphertext without holding the key. The problem is that the arithmetic FHE supports is not the arithmetic most models are written in. A data scientist who wants encrypted inference normally has to learn ciphertext parameters, noise budgets and circuit representation before writing a single line of model code. Concrete ML exists to remove that step. The README states the goal directly: it "simplifies the use of fully homomorphic encryption (FHE) for data scientists so that they can automatically turn machine learning models into their homomorphic equivalents, and use them without knowledge of cryptography." The audience is therefore narrow and specific: people who already build models in scikit-learn, XGBoost or PyTorch and who now need those models to run on data they cannot see. It is not aimed at cryptographers, and it is not a general privacy toolkit. Nothing in the material suggests it handles secure aggregation, differential privacy or secure multi-party computation. The privacy property comes from FHE alone.
Two conversion paths, and why the second one matters
Concrete ML offers built-in models and custom models, and the split determines how much work you do. Built-in models are described as "Ready-to-use FHE-friendly models with a user interface that is equivalent to their the scikit-learn and XGBoost counterparts." You import them from concrete.ml.sklearn and use fit and predict as usual. The second path is for models the user develops in PyTorch or Keras/TensorFlow with quantization-aware training, imported into Concrete ML through ONNX. That second path is where the real constraint lives. A model is not converted by inspection; it is converted after quantization-aware training, which means the training loop itself has to be adapted before the model is ever handed to Concrete ML. The library sits on top of Concrete, a separate Zama project that handles the FHE compilation itself. Concrete ML is the layer that maps model operations onto what Concrete can compile. If a layer has no FHE-friendly equivalent, the ONNX import is where you find out, not at prediction time.
The compile step is the part people miss
The example in the README is short, and the line that matters is easy to skim past. After fitting a LogisticRegression with n_bits=8, the code calls model.compile(X_train) with the comment "We then compile on a representative set." Only after that does predict accept fhe="execute". This is not a formality. Compilation needs a representative data set to derive the parameters under which the circuit will run, and the quality of that set determines whether the compiled model behaves like the plaintext one. The README's own example reports a similarity of 100 percent between the clear and FHE predictions, but that is on a synthetic make_classification data set with class_sep=2 and 30 features, which is about as forgiving as a test case gets. Model.compile is a method call; the representative set behind it is a data engineering obligation. If you cannot produce a sample that looks like production traffic, the compiled model is being tuned against the wrong distribution.
Installation, and the Windows gap
Two channels are documented. Docker: docker pull zamafhe/concrete-ml:latest. Pip: pip install -U pip wheel setuptools followed by pip install concrete-ml. The installation table is the part to read carefully before promising a timeline. Linux, Windows Subsystem for Linux, macOS 11+ on Intel and macOS 11+ on Apple Silicon are all listed as available on pip. Windows is listed as available on Docker only, with pip marked No. macOS on Apple Silicon is the inverse: pip is available, Docker is marked "Coming soon." Python support is limited to 3.8 through 3.12. The README also notes that Concrete ML can be installed on Kaggle and on Google Colab, which matters if you want to try it without provisioning a machine. The practical consequence is that a Windows developer without WSL is pushed into a container, and a Mac developer on Apple Silicon cannot use the container path at all. Neither is a blocker, but both change how the project is packaged for a team.
Quantization is the ceiling on accuracy
The n_bits=8 parameter in the example is not decoration. Concrete ML models are quantized, and the number of bits is a knob the user sets. Fewer bits make the FHE circuit cheaper to evaluate; more bits push accuracy closer to the floating-point model at a higher computational cost. The README does not quantify that trade-off, and I would not guess at it. What can be said from the material is that the trade-off is exposed to the user rather than hidden, which is the honest design. The limitation follows from it. Any model whose accuracy depends on fine-grained floating-point behaviour, or on operations that do not quantize cleanly, will lose something in conversion, and the loss is not something the library can recover for you. The README's claim of 100 percent similarity on the logistic regression example should not be generalized to a deep network with many layers, because each layer is an opportunity for quantization error to accumulate. The documentation is where the per-model numbers live; the README does not carry them.
Where it is the wrong tool
Three cases stand out. First, if you need the model to train on encrypted data in the way you train in the clear, Concrete ML is not that. It supports inference on encrypted data, and the README mentions "additional options to those models allow them to run inference or training on encrypted data with FHE," but the worked example is inference, and the built-in models are described as having a scikit-learn-equivalent interface, which is a statement about API shape rather than about encrypted training throughput. Treat encrypted training as something to confirm against the documentation for your specific model class. Second, if your model uses operations with no FHE-friendly equivalent, the ONNX import path will reject it or force a rewrite, and the rewrite is on you. Third, if the latency budget is tight, note that FHE inference is not a drop-in replacement for a plaintext forward pass. The library makes the code look the same; it does not make the cost the same. Anyone evaluating Concrete ML should measure the compiled model on their own hardware before treating the API similarity as a performance promise.
The alternative: run the model on plaintext behind a trust boundary
The obvious alternative is not another FHE library. It is to keep the model in the clear and move the trust boundary instead, either by running inference inside the data owner's environment or by contracting the computation to a party that is already permitted to see the data. The difference in approach is fundamental. With Concrete ML, the server never holds the key and the computation happens on ciphertext, so the privacy guarantee is cryptographic. With the trust-boundary approach, the server sees plaintext and the guarantee is contractual and operational. Concrete ML costs you quantization, a compile step and FHE evaluation time; the trust-boundary approach costs you the assumption that the operator is trustworthy and the controls that enforce it. Which is cheaper depends entirely on whether the regulatory or contractual situation actually forbids the operator from seeing the data. If it does not forbid it, the FHE machinery is buying a property you were not required to have. If it does forbid it, the trust-boundary approach is not an alternative at all. That question should be answered before the first model is compiled, not after.
Licence, releases and what to check first
The repository metadata reports the licence as NOASSERTION, while the README carries a BSD-3-Clause-Clear badge. Those two signals do not agree, and the discrepancy is worth resolving with the LICENSE file in the repository before any redistribution decision. I am not in a position to give legal advice, and the licence text is the authority, not a badge or a metadata field. On maintenance, the release cadence visible in the material is roughly quarterly: v1.7.0 in September 2024, v1.8.0 in January 2025, v1.9.0 in April 2025, with the last push to the default branch dated August 2026. The project is not archived. The upgrade cost is tied to the built-in model classes and the compile step rather than to a service, so a version bump can change how a model compiles without changing your training code. Pin the version, and re-run compile() against your representative set after every upgrade rather than assuming the old parameters still hold. The first thing to verify is whether your model class exists as a built-in; the second is whether your platform appears in the installation table for the channel you plan to use.
Editorial conclusion
Adopt Concrete ML if your model is already expressible as a scikit-learn estimator or a quantizable PyTorch or Keras model, and if you can supply the representative data set that compile() needs. Do not adopt it if you need floating-point precision, if your model cannot be quantized, or if you expect encrypted training to behave like plaintext training. Before committing, verify three things: that your model class is listed in the built-in models, that your platform appears in the installation table for the channel you intend to use, and that compile() runs on a representative sample of your own data.
Community notes