This repository contains the reference implementation of NTK-LoRA, a lightweight and practical method for calibrating LoRA-fine-tuned Vision Transformers using the Gaussian Process view of the Laplace approximation.
NTK-LoRA is a simple, post-hoc calibration method for LoRA-fine-tuned Transformer-based Models.
It leverages the Gaussian Process (GP) view of the Laplace approximation and requires only:
- ~25 lines of code
- Standard libraries (
PyTorch,scikit-learn) - No expertise in Bayesian inference
- No covariance factorizations or heavy Laplace machinery
Across CelebA and CUB tasks, NTK-LoRA achieves:
- Better calibration (lower NLL / ECE) than Temperature Scaling and Laplace
- Higher accuracy for the top-k% most confident predictions
- More stable performance across epochs
- In some cases, significantly shorter training
Although this repository evaluates NTK-LoRA on Vision Transformers (ViT/DeiT), the method is model-agnostic. It can be applied to LLMs and other Transformer architectures, since it only requires model with attention layers fine-tuned using LoRA.
NTK-LoRA has two stages. First, build the Jacobian feature vector for each
input — the gradient of every output logit with respect to the calibrated (LoRA)
parameters, via torch.autograd:
def compute_grad(output, calibrated_params):
grads = torch.autograd.grad(output, calibrated_params,
create_graph=True)
return torch.cat([g.reshape([-1]) for g in grads])
def compute_jacobians(x_sample, model, calibrated_params):
out = model(x_sample[None])
gradients = [compute_grad(out[0, i], calibrated_params)
for i in range(out.shape[1])]
return torch.stack(gradients, axis=1)Then, fit a Gaussian Process classifier on those Jacobian features
(j_train / j_test) and read off calibrated probabilities — just
scikit-learn, no Bayesian machinery:
def fit_ntk_lora(j_train, y_train):
scale = ConstantKernel(
constant_value=1.0,
constant_value_bounds=(1e-5, 1e5))
noise_kernel = WhiteKernel(
noise_level=1e-3,
noise_level_bounds=[1e-7, 1e5])
kernel = scale * DotProduct() + noise_kernel
gpc = GaussianProcessClassifier(kernel=kernel)
scaler = preprocessing.StandardScaler()
j_train_scaled = scaler.fit_transform(j_train)
gpc.fit(j_train_scaled, y_train)
return gpc, scaler
def predict_with_ntk_lora(j_test, gpc, scaler, return_logit_variance=False):
j_test_scaled = scaler.transform(j_test)
pred = gpc.predict_proba(j_test_scaled)
# Optionally, return the whole predictive distribution on logits
if return_logit_variance:
z_mean, z_var = gpc.latent_mean_and_variance(j_test_scaled)
return pred, z_mean, z_var
return predFine-tuning Transformers with LoRA is efficient, but it often makes the model overconfident—it outputs very high probabilities even when it is wrong. This can be problematic in domains like medical imaging, autonomous systems, or any application where decisions must be reliable.
NTK-LoRA is a lightweight post-hoc calibration method (i.e. does not require any changes in the model training) that fixes this by learning a better probability model on top of the existing fine-tuned network. No retraining or architectural changes are required.
NTK-LoRA does not modify or retrain the base network.
It focuses only on the LoRA parameters (and optionally the final classification layer), since those are the weights that were actually updated during fine-tuning.
We use the Jacobian of the model’s output logit with respect to the LoRA parameters.
This tells us:
“If the LoRA weights changed slightly, how much would the prediction change?”
Each input image produces a high-dimensional “sensitivity fingerprint,” which acts as a feature vector describing how the model behaves locally.
Instead of trusting the raw logits from the Transformer, we feed the Jacobian features into a simple Gaussian Process Classifier (GPC) from scikit-learn.
The GP learns:
- how confident the model should be
- patterns of uncertainty in the neighborhood of each input
- how logits relate to true labels
This requires only a few lines of code and no Bayesian background.
After fitting the GP, predictions look like:
- 0.75 instead of 0.99 when the model is uncertain
- 0.99 when predictions are consistently correct
- plus uncertainty estimates via logit variance
The result is trustworthy probability estimates rather than overconfident ones.
Neural networks, including Transformers, behave almost like linear models in a small neighborhood around their fine-tuned weights.
This means:
- Jacobians capture the local behavior well
- A linear kernel GP can model confidence accurately
- Calibration improves dramatically
The method avoids Hessians, large matrices, or complex Laplace machinery.
- No Bayesian expertise required
- No large covariance matrices
- No dependencies on unstable libraties, only PyTorch + scikit-learn
- Works with any LoRA-finetuned Transformer
- Core method <30 lines of code
Although the experiments in this repository focus on ViT/DeiT, the method is model-agnostic.
Since it relies only on Jacobians of LoRA parameters, the method can in principle be applied to:
- LLMs (e.g., LLaMA)
- multimodal Transformers
- other PEFT methods
LLM experiments are conceptually straightforward but not yet included in this repository (work in progress).
This repository accompanies my upcoming paper: NTK-LoRA: Calibrating Fine-Tuned Vision Transformers Using Gaussian Processes
The paper provides a deeper and more formal explanation of the method, including:
- the connection between NTK-LoRA and the Laplace approximation
- how Jacobians of LoRA parameters form a Gaussian Process kernel
- derivations and theoretical background
- full experimental results on CelebA and CUB
- comparisons with Temperature Scaling and classical Laplace methods
- limitations and extensions (including applicability to LLMs)
A public preprint (arXiv link) will be added here once available.
The code has been tested on the following systems:
- Linux: Ubuntu 22.04, CUDA 12.1, Python 3.10
- macOS: macOS 14.3 (Apple Silicon), Python 3.10
- Memory requirements depend on the size of the pretrained model (DeiT-Tiny fits easily on CPUs/Apple Silicon)
-
Clone the repository
git clone <this git repo> cd CalibratedVisionLora
-
Run the setup script
bash setup_env.sh
This will:
- create a Python virtual environment
- install all required dependencies
- install this project as an editable package (
pip install -e .), so its modules are importable from anywhere
- Run Training
source venv/bin/activate python train.py
This will:
- activate the Python virtual environment
- fine-tune a ViT model
- calibrate after every epoch
- visualize the results
You're ready to fine-tune a ViT with LoRA and run NTK-LoRA calibration.