Can You Prove an AI Ran Correctly? — ZK Proofs Meet Machine Learning
Part 3 of 3 in the Zero-Knowledge Proofs series. The frontier where cryptography meets AI — and why it's harder than anyone admits.
Can You Prove an AI Ran Correctly? — ZK Proofs Meet Machine Learning
Part 3 of 3 in the Zero-Knowledge Proofs series. The frontier where cryptography meets AI — and why it's harder than anyone admits.
We've come a long way.
In Part 1, we learned what Zero-Knowledge Proofs are — the Ali Baba cave, the three properties, the simulator argument, Fiat-Shamir.
In Part 2, we learned how to prove arbitrary computations — circuits, R1CS, QAP, SNARKs vs STARKs, ZK rollups.
Now we're at the edge of what's actually solved.
Here's the question Part 3 answers:
Can you prove that an AI model ran correctly — without revealing the model?
Prove that a specific neural network, given a specific input, produced a specific output. Without revealing the model's weights. Without revealing the input if it's private. With a cryptographic proof that anyone can verify.
This is called zkML — Zero-Knowledge Machine Learning. And it's simultaneously one of the most important ideas in AI and one of the hardest engineering problems in cryptography.
Let me show you exactly why — and exactly how far we've gotten.
Why This Problem Matters
Before the technical deep dive, let's establish why anyone should care.
The AI Trust Crisis
Right now, when an AI system makes a decision that affects your life — a loan approval, a medical diagnosis, a content moderation call — you have exactly zero ability to verify it.
The company says: "Our AI decided X."
You have no way to verify:
That their AI actually made that decision (vs a human overriding it)
That the model they used is the model they claim to use
That the model wasn't manipulated between training and deployment
That the decision was actually based on the inputs they say it was
This is a trust problem. And trust problems are exactly what ZK proofs were designed to solve.
The Specific Use Cases
Use Case 1 — AI API Verification
Provider claims: "We ran GPT-X on your input"
ZK proof proves: This specific model produced this output
Without revealing: The model weights (proprietary)
Use Case 2 — Fair AI Compliance
Bank claims: "Our loan model didn't use race as a feature"
ZK proof proves: The model's decision path never touched protected attributes
Without revealing: The model itself (trade secret)
Use Case 3 — On-Chain AI Agents
Agent claims: "I computed this action off-chain"
ZK proof proves: The computation was correct
Without revealing: Private input data
Use Case 4 — Private Medical AI
Hospital claims: "AI diagnosed this condition"
ZK proof proves: The diagnosis came from a certified model
Without revealing: Patient data (HIPAA) or model weights
In every case, the pattern is the same: prove correctness without revealing secrets.
That's exactly what ZK proofs do. The problem is that neural networks are spectacularly hostile to ZK proof systems.
Why Neural Networks Hate ZK Circuits
Remember from Part 2 that ZK proofs work by converting computations into arithmetic circuits — graphs of addition and multiplication gates over a finite field.
Neural networks, on the surface, seem like they'd map nicely to circuits. They're just matrix multiplications and activation functions, right?
Wrong. Here's where it falls apart.
Problem 1: The Floating Point Catastrophe
ZK proof systems work over finite fields — integers modulo a large prime number p. Every value in the computation must be an integer in the range [0, p-1].
Neural networks use floating point arithmetic — IEEE 754 floats with 32-bit or 16-bit representations, sign bits, exponents, mantissas.
Neural network weight: 0.3847291... (float32)
ZK circuit expects: an integer in [0, p-1]
These are fundamentally different number systems.
To make a neural network ZK-provable, you must quantize it — convert all weights and activations to fixed-point integers.
Float weight: 0.3847291
Scale by 2^16: 25,217 (fixed-point integer)
Now it's ZK-compatible. But...
Every quantization introduces error. The quantized model produces slightly different outputs than the original. And that difference matters — because your ZK proof proves the quantized model ran correctly, not necessarily that it matches the original float model's behavior.
For small models on classification tasks, this error is acceptable. For large language models where token probabilities matter enormously — it's a serious problem.
Problem 2: The Activation Function Nightmare
The core operation of a neural network layer is:
output = activation(W · x + b)
Where W · x + b is a matrix multiplication — perfectly fine in ZK circuits, just a lot of multiply-add constraints.
The activation function is the problem.
ReLU: f(x) = max(0, x)
To prove ReLU in a ZK circuit, you need to prove a comparison: x < 0 or x ≥ 0. Comparisons require bit decomposition — proving the bit representation of a number, then checking the sign bit.
Proving x ≥ 0 in a ZK circuit:
1. Decompose x into n bits: x = b₀ + 2b₁ + 4b₂ + ... + 2^(n-1)b_{n-1}
2. Prove each bᵢ ∈ {0,1}: bᵢ * (1 - bᵢ) = 0 (one constraint per bit)
3. Prove the decomposition is correct: sum = x (one constraint)
4. Check sign bit
For a 32-bit number: ~34 constraints per ReLU
A small ResNet has ~500,000 ReLU activations
Total: ~17,000,000 constraints just for activations
GELU: f(x) = x · Φ(x) where Φ is the Gaussian CDF
Even worse. GELU involves a non-algebraic function (the error function). There's no exact circuit representation — you need polynomial approximations, which add approximation error on top of quantization error.
Softmax: f(xᵢ) = e^xᵢ / Σe^xⱼ
Exponentials. Division. Both catastrophically expensive in finite field arithmetic.
The fundamental problem: neural network operations were designed for hardware that's excellent at floating point arithmetic. ZK circuits are algebraic structures designed for integer arithmetic in finite fields. These two worlds were not designed to work together.
Problem 3: The Scale Problem
Let's talk numbers.
GPT-2 (small, 117M parameters):
Layers: 12
Attention heads per layer: 12
Each forward pass: ~10^9 multiply-accumulate operations
ZK prover speed (modern hardware): ~10^6 constraints/second
Estimated proving time for one GPT-2 forward pass:
~10^9 / 10^6 = ~1,000 seconds = ~17 minutes
For GPT-3 (175B parameters):
~25,000× larger than GPT-2
Estimated proving time: months
This isn't a software optimization problem. It's a fundamental scaling challenge.
Current ZK systems can practically prove:
✅ Models with < 10M parameters
✅ Specific layers in isolation
✅ Quantized INT8/INT4 models with simplified activations
✅ Tree-based models (decision trees, random forests)
✅ Linear and logistic regression
⚠️ Small CNNs (ResNet-18: ~30 seconds on M2 Mac)
❌ Transformer models at GPT-2 scale
❌ Any frontier LLM
Problem 4: The Memory Wall
STARK provers need to hold the entire execution trace in memory during proof generation.
Execution trace size ≈ (number of steps) × (number of registers)
For a small transformer layer:
Steps: ~10^6
Registers: ~10^3
Trace size: ~10^9 entries × 32 bytes = ~32 GB RAM
For a full GPT-2 forward pass:
Trace size: Multiple terabytes
You'd need a machine with terabytes of RAM just to generate the proof. That's not a data center configuration — it's a fantasy configuration.
What's Actually Working Today
Okay, enough about what doesn't work. Here's the honest picture of what does.
EZKL — The Most Production-Ready zkML Library
EZKL (Easy Zero-Knowledge for Machine Learning) is the most mature zkML framework available today. It converts ONNX models (the standard ML model format) directly into Halo2 circuits.
# The zkML workflow with EZKL
import ezkl
import torch
import json
# Step 1: Export your model to ONNX
model = YourSmallModel()
dummy_input = torch.randn(1, 28, 28) # e.g., MNIST image
torch.onnx.export(model, dummy_input, "model.onnx")
# Step 2: Define circuit settings
# This is where you configure quantization precision
settings = ezkl.PyRunArgs()
settings.input_visibility = "public" # input is public
settings.output_visibility = "public" # output is public
settings.param_visibility = "private" # weights are private ← the key
# Step 3: Calibrate (analyze the model's numerical range)
# EZKL figures out the optimal quantization scale
await ezkl.calibrate_settings("model.onnx", "input.json", "settings.json")
# Step 4: Compile the model into a ZK circuit
await ezkl.compile_circuit("model.onnx", "model.compiled", "settings.json")
# Step 5: Generate proving and verification keys
# (This is the setup phase — done once per model)
await ezkl.setup("model.compiled", "vk.key", "pk.key")
# Step 6: Generate a witness (the private computation trace)
await ezkl.gen_witness("input.json", "model.compiled", "witness.json")
# Step 7: Generate the proof
await ezkl.prove("witness.json", "model.compiled", "pk.key", "proof.json")
# Step 8: Verify the proof
result = await ezkl.verify("proof.json", "settings.json", "vk.key")
print(f"Proof valid: {result}") # True
What EZKL handles for you:
Automatic quantization of float weights to fixed-point integers
Conversion of activation functions to polynomial approximations
Circuit compilation with Halo2
Solidity verifier export for on-chain verification
Real performance numbers (as of 2026):
Model | Params | Prove Time | Proof Size
--------------------|---------|------------|------------
Logistic regression | ~1K | <1 sec | ~50 KB
Small MLP (MNIST) | ~100K | ~2 sec | ~200 KB
ResNet-18 | 11M | ~30 sec | ~800 KB
MobileNet-V2 | 3.4M | ~15 sec | ~500 KB
BERT-tiny | 4.4M | ~3 min | ~2 MB
GPT-2 (full) | 117M | Not feasible | —
The sweet spot today: models under ~10M parameters on specialized hardware.
The Working Example: A ZK MNIST Classifier
Let me show you what a complete zkML proof actually looks like end-to-end.
The model — a small CNN that classifies handwritten digits:
import torch
import torch.nn as nn
class MNISTClassifier(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 16, 3, padding=1) # 16 filters
self.conv2 = nn.Conv2d(16, 32, 3, padding=1) # 32 filters
self.fc1 = nn.Linear(32 * 7 * 7, 128)
self.fc2 = nn.Linear(128, 10)
self.relu = nn.ReLU()
self.pool = nn.MaxPool2d(2)
def forward(self, x):
x = self.pool(self.relu(self.conv1(x))) # 28x28 → 14x14
x = self.pool(self.relu(self.conv2(x))) # 14x14 → 7x7
x = x.view(-1, 32 * 7 * 7)
x = self.relu(self.fc1(x))
return self.fc2(x) # logits for 10 classes
# Parameters: ~408K — within EZKL's practical range
What the ZK proof proves:
Public inputs:
→ Input image (28×28 pixels, normalized)
→ Output logits (10 class scores)
→ Model commitment (hash of the weights)
Private inputs (never revealed):
→ The model weights (~408K float32 values)
→ All intermediate activations
The proof certifies:
"A model with this specific commitment, given this image,
produced these exact logits — computed correctly according
to the circuit constraints."
The Solidity verifier (auto-generated by EZKL):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MNISTVerifier {
// Verification key embedded at compile time
// (contains the model commitment implicitly)
function verifyProof(
bytes calldata proof,
uint256[] calldata publicInputs
) public view returns (bool) {
// publicInputs contains:
// [0..783]: flattened image pixels (public)
// [784..793]: output logits (public)
// Runs Halo2 verification logic
// Returns true iff the proof is valid
return _verify(proof, publicInputs);
}
}
A smart contract can now trustlessly verify that a specific neural network produced a specific classification — without ever seeing the model weights.
The Circom Approach: Building ZK Circuits Manually
For simpler ML operations, you can write ZK circuits directly in Circom. This gives you more control but requires deep understanding of the constraints.
Here's a ZK proof for a simple dot product — the core operation in a linear layer:
pragma circom 2.0.0;
// Prove: public_output = sum(weights[i] * input[i])
// Without revealing: weights (private)
// Public: input, output
template DotProduct(n) {
signal input weights[n]; // private: model weights
signal input inputs[n]; // public: user input
signal output out; // public: layer output
signal products[n];
signal running_sum[n+1];
running_sum[0] <== 0;
for (var i = 0; i < n; i++) {
// Each multiplication becomes one R1CS constraint
products[i] <== weights[i] * inputs[i];
running_sum[i+1] <== running_sum[i] + products[i];
}
out <== running_sum[n];
}
// A 3-neuron layer
component main {public [inputs]} = DotProduct(3);
For input [1, 2, 3] with private weights [0.5, 0.3, 0.2] (quantized to [5, 3, 2] with scale 10):
products[0] = 5 * 1 = 5
products[1] = 3 * 2 = 6
products[2] = 2 * 3 = 6
out = 5 + 6 + 6 = 17 (divide by scale: 1.7)
The proof verifies this output was computed with some weights satisfying the circuit — without revealing [5, 3, 2].
Now add a ReLU:
pragma circom 2.0.0;
include "comparators.circom";
include "bitify.circom";
// ZK ReLU: prove output = max(0, input)
template ReLU(bits) {
signal input in;
signal output out;
signal isNegative;
// Decompose into bits to check sign
component n2b = Num2Bits(bits);
n2b.in <== in + (1 << (bits-1)); // offset to handle negatives
// The most significant bit tells us the sign
isNegative <== n2b.out[bits-1];
// out = in * (1 - isNegative)
// If negative: out = in * 0 = 0
// If positive: out = in * 1 = in
out <== in * (1 - isNegative);
}
template LinearLayerWithReLU(n, bits) {
signal input weights[n];
signal input inputs[n];
signal output out;
// First compute dot product
component dot = DotProduct(n);
for (var i = 0; i < n; i++) {
dot.weights[i] <== weights[i];
dot.inputs[i] <== inputs[i];
}
// Then apply ReLU
component relu = ReLU(bits);
relu.in <== dot.out;
out <== relu.out;
}
component main {public [inputs]} = LinearLayerWithReLU(3, 32);
This is what EZKL automates for an entire neural network. The circuit can have millions of these constraints. The proof certifies all of them hold simultaneously.
The Research Frontier: What's Being Worked On
The gap between "works for small models" and "works for GPT-4" is enormous. Here's what researchers are actively attacking:
Recursive Proof Aggregation
Instead of proving an entire model in one massive proof, prove each layer independently and then recursively combine the proofs:
Layer 1 proof: π₁ (proves layer 1 ran correctly)
Layer 2 proof: π₂ (proves layer 2 ran correctly, given layer 1's output)
...
Layer N proof: πₙ
Aggregation proof: π_final
Proves: "π₁, π₂, ..., πₙ are all valid"
Size: same as one proof
Verify time: same as one proof
This allows parallelizing the proving work across many machines. Each machine proves one layer. A final aggregation step combines them.
Halo2 and Plonky2 are designed for efficient recursive proof aggregation. This is probably the most promising path to practical zkML for larger models.
Hardware Acceleration
Current bottleneck: the mathematical operations in ZK provers (multi-scalar multiplication, number theoretic transforms) are computationally intensive.
CPU prover: baseline
GPU prover: 10-50× faster (parallelizing field arithmetic)
FPGA prover: 50-100× faster (custom circuits for ZK operations)
ASIC prover: 100-1000× faster (purpose-built silicon)
Companies building ZK hardware accelerators (Ingonyama, Cysic, and others) are making proving times drop fast. What takes 30 minutes today on a CPU might take 30 seconds on a ZK ASIC in three years.
ZK-Friendly Neural Architectures
Instead of forcing existing neural network architectures through ZK circuits — design architectures specifically for ZK provability:
ZK-unfriendly activations: ReLU, GELU, Softmax (expensive comparisons)
ZK-friendly activations: Polynomial activations like x², x³
(pure algebraic — zero extra constraints)
Trade-off: Polynomial activations are slightly less expressive
than ReLU, but massively cheaper to prove
Research into architectures that sacrifice a small amount of accuracy for orders-of-magnitude cheaper proving is genuinely promising. For many applications (classification, regression), the accuracy trade-off is acceptable.
Optimistic zkML
A hybrid approach: run the model normally, commit to the output on-chain. Only generate a ZK proof if someone challenges the output.
Normal case (99.9% of the time):
→ Run model off-chain
→ Post commitment to output
→ No proof generated
→ Save enormous compute
Challenge case (0.1% of the time):
→ Challenger disputes an output
→ Prover generates ZK proof for disputed computation
→ Smart contract verifies proof
→ Honest party wins, dishonest party loses stake
This is essentially the optimistic rollup model applied to ML inference. Much cheaper in the common case. Weaker guarantee (outputs aren't immediately final). But practical today for many use cases.
The Honest State of zkML in 2026
Let me be direct about where things actually stand:
What works today:
✅ Small classification models (MNIST, CIFAR-10 scale)
✅ Logistic regression and linear models
✅ Tree-based models (gradient boosting, random forests)
✅ Embedding lookups (proving a specific embedding was retrieved)
✅ Specific transformer components in isolation (one attention head)
✅ Quantized INT4 models on specialized hardware
What's coming (1-3 years):
🔄 Small transformers (BERT-tiny, DistilBERT scale)
🔄 zkML with hardware accelerators (GPU/FPGA provers)
🔄 Recursive proof aggregation for larger models
🔄 ZK-friendly neural architectures with acceptable accuracy
What's a decade away (maybe):
⏳ GPT-2 scale models in practical time
⏳ Any frontier LLM (GPT-4, Claude, Gemini scale)
⏳ Real-time zkML inference at production scale
The fundamental physics haven't changed. Proving a 175B parameter model requires proving trillions of constraints. Even with 1000× hardware improvement, we're looking at a decade before that's practical.
But here's the thing: zkML doesn't need to work for frontier models to be transformative. The use cases that matter most — fair lending models, medical diagnosis verification, on-chain AI agents, compliance verification — mostly involve models with millions of parameters, not billions.
The 10M parameter sweet spot is achievable today. And that covers an enormous amount of real-world AI.
The Complete ZK Proofs Picture
We started this series with a cave analogy. We end at the frontier of cryptography and AI.
Here's the full map of everything we covered:
PART 1 — FOUNDATIONS
├── Why ZK proofs exist (the information gap)
├── Ali Baba's Cave (the intuition)
├── Three properties: Completeness, Soundness, Zero-Knowledge
├── The Simulator Argument (the formal definition)
├── Sigma Protocols + Schnorr (the math)
└── Fiat-Shamir (making it non-interactive)
PART 2 — PROVING COMPUTATION
├── Arithmetic Circuits (programs as math)
├── R1CS (formalizing constraints)
├── QAP (constraints as polynomials)
├── KZG Commitments (the heart of SNARKs)
├── Trusted Setup (SNARKs' original sin)
├── STARKs (the trustless alternative)
├── SNARKs vs STARKs (the full trade-off)
├── ZK Rollups (the full architecture)
└── ZK vs Optimistic Rollups
PART 3 — ZK MEETS AI
├── Why zkML matters (the AI trust crisis)
├── Why it's hard (float, activations, scale, memory)
├── EZKL (the production zkML library)
├── Working MNIST classifier proof
├── Circom circuits for neural operations
├── Research frontier (recursion, hardware, architecture)
└── Honest state of the field in 2026
The One Thing To Remember From This Series
Zero-Knowledge Proofs solve a problem that seems impossible: convince someone you know something without telling them what you know.
They do it by making cheating statistically impossible, and by proving that fake transcripts are indistinguishable from real ones — which means real transcripts contain nothing worth extracting.
Applied to blockchain: you can verify millions of transactions with a 200-byte proof.
Applied to AI: you can prove a model ran correctly without revealing the model.
We're at the beginning of understanding what that means. The math has been there for decades. The engineering is catching up. And the applications — trustless AI, private computation, verifiable systems — are going to matter more as AI becomes more powerful and more central to decisions that affect people's lives.
If you've read all three parts: you now understand ZK proofs at a depth that most engineers — including most blockchain engineers — don't have.
That's the moat.
I'm Niranjan — Full Stack & Web3 Developer at Alkimi Exchange, M.Sc. student in Data Science & Generative AI. Writing about what I'm actually learning, not what sounds impressive.
Follow me on X / Twitter | Read the full series on Hashnode
This concludes the Zero-Knowledge Proofs series. Part 1: The Proof That Reveals Nothing Part 2: SNARKs vs STARKs: The Great ZK Showdown Part 3: Can You Prove an AI Ran Correctly?
