Paper · 2022-09-15 · cs.CV, cs.LG
Test-Time Training with Masked Autoencoders
For each test image, a masked autoencoder (ViT-L encoder, ViT-B head) is adapted by 20 SGD steps on masked-patch reconstruction of that image alone, then predicts; weights reset per input. On ImageNet-C level 5 it beats the fixed ViT-probing baseline on all 15 corruptions (author-reported; e.g. JPEG 51.2% to 63.0%, mean not stated).
Test-time trainingMasked image modelingSelf-supervised learningDomain adaptationtest-time-trainingmasked-autoencoderdistribution-shiftself-supervisedimagenet-c
arXivPDFCodeMarkdown11 min read
AI-compiled by claude-haiku-5-5 on 2026-10-18, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
A model trained on clean photos often gets worse on photos that look different: blurry, noisy, foggy or compressed. The usual fix is to train on many kinds of damaged photos in advance, but nobody can list every damage in advance.
This paper takes a different route. When a new photo arrives, the model gets a short private training session on that one photo before it answers. The training task needs no labels: hide most of the photo and ask the model to redraw the hidden parts. Doing that forces the model to learn how the picture is built, and that adjusted model is then used for the classification. After the answer is given, the model is reset to its original state and the next photo gets its own session.
The authors use a masked autoencoder for the hide-and-redraw task, because it works well on images and needs no labels. They report that this simple recipe helps on several benchmarks of shifted images.
TL;DR
Test-time training (TTT) with a masked autoencoder (MAE) instead of the rotation task of the original TTT method. Per test image: start from the pretrained weights, run 20 SGD steps that reconstruct masked patches of that image, predict on the unmasked image, then discard the adapted weights. The main evidence is ImageNet-C level 5, where every corruption improves over a fixed ViT-probing baseline. Results are author-reported; the method runs 20 SGD steps per test image. Relevant if you need robustness to unseen shifts and can afford per-input adaptation.
Problem
Models are usually fixed at deployment, so they must be robust to every shift that might occur §1. The paper argues that adapting to each test input is an alternative: each input is a one-sample learning problem where the label is missing but a self-supervised label can be made from the input itself §1.
The earlier TTT method §2 uses rotation prediction as its self-supervised task. The paper argues that rotation is sometimes too easy (outdoor scenes, where the horizon gives the answer) or too hard (top-down views where every rotation looks plausible) §2, and that on rotation-invariant classes it hurts accuracy tab. 1. The assumption behind the method is that the shift is unknown in advance and that the test input is the only information about it §1, §5.
Method
The method replaces the self-supervised head of the Y-shaped TTT network with
MAE reconstruction §3. The network has one encoder f (the MAE encoder) that
feeds two heads: a reconstruction decoder g (the MAE decoder) and a main-task
head h for classification. Encoder and decoder are both ViTs, and the paper
keeps them unchanged so the comparison with MAE stays clean §3, Architecture.
Training happens in three steps §3, Training setup:
- Pretrain the MAE on ImageNet-1k (encoder
f0, decoderg0); this is taken from the MAE authors' checkpoint §3, Training setup §4.1. - Train only the main head
h0on the labelled ImageNet training set while the encoder stays frozen ("ViT probing"), with a ViT-Base head instead of the linear head used by MAE eq. 1 §3. - Keep
f0,g0andh0as the starting point for test time.
The paper compares fine-tuning, probing and joint training of the main head and selects ViT probing as default: fine-tuning makes the encoder specialise for recognition, which the test-time step then undoes; joint training performed worse on ImageNet validation §3, Training setup tab. 2.
At test time, each input x is handled independently §3, Test-time training:
- Reset the encoder and decoder to
f0andg0. - Minimise the reconstruction loss of
xalone, with a random mask per copy eq. 2 §4.1. - Predict with the adapted encoder and the fixed head
h0, on the unmasked input §3. - Discard the adapted weights §3.
The reconstruction loss is the pixel-wise mean squared error between decoded and original patches §3, eq. 2. Only the masked patches enter the loss in the code models_mae_shared.py; the paper's equation does not spell that out.
# TTT-MAE for one test image [§3] [eq. 2]
# Input: crop of shape (224, 224, 3); pretrained encoder0, decoder0, head0
# Output: predicted class
def predict(crop, encoder0, decoder0, head0):
# Start from the pretrained weights; discard them after this input [§3]
encoder = copy(encoder0)
decoder = copy(decoder0)
# Batch of 128 identical copies of the crop [§4.1]
# [code: tt_image_folder.py#L37-L39]
batch = repeat(crop, 128) # (128, 224, 224, 3)
for step in range(20):
# Each copy gets its own random 75% patch mask [§4.1]
# [code: models_mae_shared.py#L155-L162]
masked = random_mask(batch, ratio=0.75)
# Reconstruct the removed patches; loss on those patches only [§3]
# [eq. 2]
loss = mse_on_removed(decoder(encoder(masked)), batch)
# SGD with momentum 0.9 and fixed lr 5e-3 at batch 128 [§4.1]
# Paper also states weight decay 0.2; the code does not apply it
# [code: engine_test_time.py#L64-L65]
encoder, decoder = sgd_step(encoder, decoder, loss)
# Predict on the unmasked crop with the adapted encoder and the fixed head [§3]
features = encoder(crop) # (1, 768)
return argmax(head0(features))Parameter scope: the paper updates encoder and decoder, plus the class token and
mask token §4.1. The released command sets --finetune_mode encoder, which
freezes the decoder engine_test_time.py README.md. The
appendix reports the difference as negligible app. tab:encoder_decoder.
Method details used in the experiments §4.1:
- Only non-masked tokens go to the encoder during test time; the decoder receives all tokens §4.1.
- Test-time augmentation: none beyond random masking. Every TTT step uses the same 224×224 centre crop used for the prediction §4.1.
- Pixel targets with or without per-patch normalisation; normalised targets are slightly better and are the default in the released command §4.1 [app. tab:pix_norm] README.md.
Figures in words: fig. 1 shows one test image being reconstructed over 500 SGD steps, with the recognition loss still falling while reconstruction continues fig. 1. fig. 2 compares AdamW and SGD for test-time steps: AdamW improves then needs early stopping, while SGD keeps improving over 20 steps; the paper therefore uses SGD fig. 2 §3, Optimizer for TTT.
Theory §5: in a linear PCA setting with known covariance and an orthogonal corruption, the paper shows that a test-time update that mixes the training covariance with the single test sample strictly reduces expected risk for some mixing weight above zero (Theorem 1, proof in the appendix). This is a result about linear PCA, not about the MAE used in the experiments.
Hyperparameters
Test-time training §4.1 main_test_time_training.py README.md. Values in the README command are what the released code runs.
| Name | Value | Where | Notes |
|---|---|---|---|
| encoder pretraining | MAE ViT-Large, 800 epochs, ImageNet-1k | §4.1 | pretrained checkpoint from the MAE authors; not retrained here |
| head architecture | ViT-Base (12 layers, width 768, 12 heads) | §3 §4.1 engine_test_time.py | linear layer between encoder and head §4.1 |
| head training (probing) | not stated in the paper | §4.1 | README: 20 epochs, base lr 1e-3, weight decay 0.2, normalised pixels README.md |
| test-time optimizer | SGD, momentum 0.9 | §4.1 | code default sgd with momentum 0.9 main_test_time_training.py |
| test-time learning rate | 5e-3, fixed, no schedule | §4.1 | code sets lr = blr × batch / 256 with blr 1e-2, i.e. 5e-3 at batch 128 main_test_time_training.py |
| test-time steps | 20 | §4.1 | the 10-step runs in the appendix are not the main results app. |
| batch size | 128 identical copies of one image | §4.1 | code default is 256, README uses 128 main_test_time_training.py |
| weight decay | 0.2 | §4.1 | not applied in the code's test-time optimizer engine_test_time.py; the flag defaults to 0.05 and is unused there main_test_time_training.py |
| gradient clipping | not stated | §4.1 | code default clip_grad=None misc.py |
| mask ratio (test time) | 75% | §4.1 app. tab:masking | 50% and 90% are reported in the appendix; 75% is best or tied on 8 of 15 corruptions there |
| crop | resize 256, centre crop 224 | §4.1 | no random crop or flip at test time main_test_time_training.py |
| loss target | per-patch normalised pixels (default in the command) | §4.1 app. tab:pix_norm | normalised targets are slightly better |
| trainable parameters | encoder and decoder, class and mask tokens | §4.1 | released command freezes the decoder engine_test_time.py |
| reset policy | reset to pretrained weights before every test input | §3 engine_test_time.py | |
| probing batch / augmentation | not stated for the probe in the paper | §3 | only crop and horizontal flip for training-time training; no RandAugment-style augmentation §3, Augmentations |
Results
Author-reported. Setup: ImageNet-C, severity level 5, 15 corruptions, each with 50,000 images §4.2 engine_test_time.py. Model: MAE ViT-L encoder, ViT-B head, ViT probing trained on the original ImageNet training set, no augmentation from the corruptions §4.1 §4.2. Baseline: the same model with no test-time training §4.2.
Main ImageNet-C numbers. fig. 3 is the bar graph; the table below gives the same numbers as the paper's own table tab. 2.
| Method | brigh | cont | defoc | elast | fog | frost | gauss | glass | impul | jpeg | motn | pixel | shot | snow | zoom |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Joint Train (ViT, not TTT) | 62.3 | 4.5 | 26.7 | 39.9 | 25.7 | 30.0 | 5.8 | 16.3 | 5.8 | 45.3 | 30.9 | 45.9 | 7.1 | 25.1 | 31.8 |
| Fine-Tune (not TTT) | 67.5 | 7.8 | 33.9 | 32.4 | 36.4 | 38.2 | 22.0 | 15.7 | 23.9 | 51.2 | 37.4 | 51.9 | 23.7 | 37.6 | 37.1 |
| ViT Probe (baseline, no TTT) | 68.3 | 6.4 | 24.2 | 31.6 | 38.6 | 38.4 | 17.4 | 18.4 | 18.2 | 51.2 | 32.2 | 49.7 | 18.2 | 35.9 | 32.2 |
| TTT-MAE (this paper) | 69.1 | 9.8 | 34.4 | 50.7 | 44.7 | 50.7 | 30.5 | 36.9 | 32.4 | 63.0 | 41.9 | 63.0 | 33.0 | 42.8 | 45.9 |
Source: tab. 2. Accuracy in percent, ImageNet-C level 5, 20 TTT steps.
Other reported numbers (author-reported, same hyperparameters as above):
- Level 5 is the main result. The appendix gives all five severities with 10 steps app.. Most entries improve, but not all: at level 2 elastic falls from 51.0 to 49.7 and at level 4 brightness from 73.1 to 72.7 [app. tab:acc_2] app. tab:acc_4. The level-5 10-step numbers sit below the 20-step main table app. tab:acc_5.
- Rotation-invariant classes (5 classes chosen with a ResNet-18 filter [§4.2 footnote]): TTT with rotation hurts on these classes, while TTT-MAE still helps on the four corruptions shown tab. 1.
- ImageNet-A and ImageNet-R, with the same hyperparameters as ImageNet-C: ImageNet-A accuracy 15.3 (baseline) → 21.3 (TTT-MAE); ImageNet-R 31.3 → 38.9 tab. 3.
- Portraits (gender classification, trained on three year-splits of a yearbook dataset and tested on the fourth; the paper reports accuracy per training split): baseline 76.1, 76.5, 78.2 → TTT-MAE 76.4, 76.7, 79.4 tab. 4 §4.4.
Paper vs code
Official repository, linked from the project page
[https://yossigandelsman.github.io/ttt_mae/index.html] and the paper's own
README. The GitHub owner name matches an author (Yossi Gandelsman); the
project page does not state the owner explicitly. Cloned at code_commit. Code paths below are relative
to the repository root.
Discrepancies, each with both citations:
- Weight decay 0.2 is stated for test-time training §4.1. The test-time SGD optimizer is built without a weight-decay argument [code: engine_test_time.py#L64-L65]. The flag exists with default 0.05 but is not used there main_test_time_training.py.
- The paper says encoder and decoder are both optimised §4.1. The released
command passes
--finetune_mode encoder, which freezes the decoder [code: README.md] engine_test_time.py. - The paper's main results use 20 steps §4.1. The appendix tables use 10 steps and say so app.. The README uses 20 steps README.md.
- Probing for the main head: the paper says ViT-Base head, probing on the training set, but the epochs and learning rate are not stated §4.1. The README gives 20 epochs, base lr 1e-3 and weight decay 0.2 and loads a checkpoint described as using "slightly different parameters" [code: README.md]. The probing optimiser has an option between LARS and AdamW [code: main_prob.py#L288-L291].
- Learning rate: the paper states 5e-3 fixed at batch 128 §4.1. The code derives lr from a base rate and the batch size, so a different batch changes the step size main_test_time_training.py.
- Reconstruction loss: the paper's equation averages over all patches [§3, eq. 2]. The code averages over the removed patches only [code: models_mae_shared.py#L265-L282].
- No gradient clipping is set in the released command [code: main_test_time_training.py] misc.py.
- The main head and the ViT-Base head share one name in the paper's figures: "Joint Train" in fig. 3 is the ResNet-16 TTT-Rot baseline from the prior paper, while "Joint Train" in tab. 2 is this paper's ViT joint training. Do not mix them.
Other checks: the paper's ViT-Base dimensions (12 layers, 768 width, 12 heads) match engine_test_time.py. Masking ratio 75% and centre-crop handling match main_test_time_training.py [code: main_test_time_training.py#L171-L176]. Per-sample random masks match [code: models_mae_shared.py#L155-L162]. Reset to the pretrained weights before every test input matches engine_test_time.py.
Licence of the code: no LICENSE file was found in the cloned repository's root
or in the README, so code_license is "not stated". No code is quoted here.
Pitfalls
- Per test image the code runs 20 steps × 128 copies of the crop through the encoder and decoder; the paper itself says test time is slower than the fixed baseline and gives no per-image wall-clock figure [Limitations] [code: engine_test_time.py#L121]. Budget for that, not for the single forward pass of the baseline.
- Weight decay 0.2 is in the text but absent from the test-time optimiser engine_test_time.py. An implementer who follows the text will use a different optimiser from the one that produced the numbers.
- Freezing vs training the decoder: the appendix measures the gap as small app. tab:encoder_decoder, but the text says both are trained §4.1. Pick one and state it.
- Learning rate depends on batch size in the code [code: main_test_time_training.py#L195-L197]. The paper gives only the product result (5e-3 at batch 128).
- Steps: 20 in the main table, 10 in the appendix. Compare like with like.
- Early stopping is what the paper avoids: with AdamW, accuracy peaks and then falls, which a test-time setting cannot tune for §3, Optimizer for TTT fig. 2. The SGD choice rests on this single comparison.
- Test images are not paired with labels, so the 20 SGD steps cannot be checked against accuracy during deployment. The paper's accuracy curves use labels only in evaluation §4.1 engine_test_time.py.
- Normalised pixel targets: the choice changes the baseline too, so the gain over baseline is what matters, not the absolute number app. tab:pix_norm.
- The paper says the corruptions must not be used for training or for corruption-specific choices §4.2. The rotation-invariant class list was chosen with a ResNet-18 on the validation set §4.2 footnote, which is a selection step outside the test-time procedure.
- Theorem 1 is for linear PCA with a known covariance and an orthogonal corruption §5. It does not transfer to the deep model by itself; the paper says so §5, Remark on the assumptions.
- Authors list limitations: no guarantee that reconstruction produces useful features for every main task, only one main task (object recognition) and a handful of benchmarks were tested [Limitations].
- Portraits data was accessed only by the Berkeley authors §4.4. The yearbook dataset itself is not redistributed by this card.
Lineage
- Builds on: §1 the TTT method of Sun et al. (rotation-prediction test-time training; no card in this wiki). This paper keeps the Y-shaped network and the per-input reset, and replaces the self-supervised task with MAE reconstruction §3.
- Builds on: masked autoencoders (MAE) of He et al., used as the pretrained encoder and decoder without modification §3 §4.1. No card in this wiki.
- Compared against: fixed models trained with ViT probing, fine-tuning and joint training tab. 2; the rotation-prediction TTT baseline from Sun et al. fig. 3 tab. 1. Also ImageNet-A, ImageNet-R and Portraits tab. 3 tab. 4.
- Superseded by: not stated in the paper; not known from content already in this wiki.
Source and license
Adapted from “Test-Time Training with Masked Autoencoders” by Yossi Gandelsman, Yu Sun, Xinlei Chen, Alexei A. Efros (arXiv:2209.07522), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/yossigandelsman/test_time_training_mae at commit 81edb0768fa8 (not stated). Any quoted code is shown under that license.