Fine-tune

What it does

Updates a model’s parameters on labelled cells, following the recipe its authors published, then predicts labels for new cells. Unlike transfer, this changes the model — and so needs meaningfully more data, GPU time, and care.

Only methods whose official adaptation actually updates weights are offered here. Models that are adapted by training a classifier on frozen embeddings — SCimilarity, GenePT, UCE and scPRINT — are not listed under finetune; run transfer --classifier mlp instead, which is the same post-hoc head for every method. The one deliberate exception is scCello, whose official recipe is linear probing on the frozen encoder; it is kept here because that is what its authors call fine-tuning.

Supported methods

scfoundry list methods --task finetune:

cellama cellfm cellplm geneformer langcell scbert sccello scfoundation scgpt

Cell2Sentence fine-tuning is not yet supported.

Inputs

Two files satisfying the standard input contract:

  • Reference — the training set. Must carry labels in obs["cell_type"] (or --label-key). Any discrete column works.

  • Query — the cells to predict. Labels not required.

Warning

Input must be raw counts over the full transcriptome. Passing log-normalised values, or an object already subset to highly variable genes, does not raise an error — it produces a plausible-looking embedding that is silently wrong. See Input data format.

Every example on this page runs against the four demo files, downloaded once into a demo/ directory of the workspace as shown in Demo datasets:

demo/colon_1000.h5ad            1,000 human colon cells, labelled, 7 batches
demo/colon_50.h5ad                 50 human colon cells, labelled
demo/liver_1shot_support.h5ad   one labelled cell per class (5 cells)
demo/liver_1shot_query.h5ad     75 liver cells to annotate

The colon pair is designed for this task: fine-tune on colon_1000.h5ad, predict the held-out colon_50.h5ad.

Running it

Note

Run scfoundry commands from inside a workspace created by scfoundry init (any subdirectory works — the workspace is found by walking upwards, like a git repository), or pass --workspace DIR. Weights, the image cache, results and run logs all live there. See The workspace.

scfoundry finetune --method scgpt \
  --reference demo/colon_1000.h5ad \
  --query demo/colon_50.h5ad
[PROCESS 4a/8c2e10] FINETUNE:finetune_by_scgpt (colon_1000)
[PROCESS b0/17d3f9] FINETUNE:predict_by_scgpt (colon_50)

[SUCCESS] completed=2 failed=0 cached=0

The fine-tuned weights are saved, so they can be reused later.

scfoundry finetune --method scgpt --reference demo/colon_1000.h5ad
results/finetune/finetuned_models/scgpt/colon_1000/

Fine-tuning is the expensive half. Split the stages when one trained model will be applied to several query sets.

scfoundry finetune --method scgpt \
  --query demo/colon_50.h5ad \
  --fitted results/finetune/finetuned_models/scgpt/colon_1000

--fitted is always the directory written by the fine-tuning stage, whatever the method.

Parameters

Option

Type

Default

Description

--method

string

required

Which model to fine-tune.

--reference

path

none

Labelled training set. Required to fine-tune.

--query

path

none

Cells to predict. Required to predict.

--fitted

path

none

Fine-tuned model directory. Required when --reference is absent.

--label-key

string

cell_type

obs column holding training labels.

--epochs

integer

per method

Training epochs.

--batch-size

integer

per method

Training batch size. Lower it first when you hit out-of-memory.

--model

string

per method

Pretrained checkpoint to start from, under data/model_weights/.

Two more training parameters have per-method defaults and are forwarded as pipeline parameters: --finetune_eval_size (the fraction of the reference held out for validation) and --predict_batch_size. scFoundation additionally exposes --scfoundation_lr, --scfoundation_scheduler and --scfoundation_grad_clip; CELLama exposes --cellama_finetuned_top_k.

Per-method training defaults

Epochs and batch sizes follow each model’s published fine-tuning recipe, which is why they vary so widely; the validation fraction is 0.2 for every method. These are the settings of the paper’s fine-tuning benchmark, so a run with no options reproduces its protocol. Changing them without a reason usually makes results worse rather than better.

Method

--epochs

--batch-size

--finetune_eval_size

--predict_batch_size

scgpt

10

32

0.2

64

geneformer

1

4

0.2

16

langcell

20

16

0.2

16

sccello

20

24

0.2

64

scfoundation

20

32

0.2

64

scbert

100

8

0.2

32

cellfm

30

8

0.2

64

cellplm

2000

not used

0.2

not used

cellama

20

32

not used (see below)

not used

Note

Two of these are not what they look like. scBERT’s 100 epochs is an upper bound — early stopping with patience 10 governs in practice, matching the official recipe. And CELLama is a two-stage method: its sentence backbone is fine-tuned on 10,000 generated cell sentences with CELLama’s own training routine, which holds out a fixed 1,000 of them for evaluation and never sees a cell-type label; the labels are then used by a post-hoc classifier on the fine-tuned embeddings, which holds out 0.2 of the cells like every other method. --finetune_eval_size therefore has nothing to change and is ignored.

Outputs

results/finetune/
├── finetuned_models/<method>/<reference>/
│   ├── ...                        the model's own files
│   ├── base_model/                CELLama only: the fine-tuned sentence backbone
│   └── posthoc_classifier/        CELLama only: the head trained on its embeddings
└── prediction/<method>/
    ├── <query>_predicted_labels.tsv
    └── <query>_predicted_probs.tsv

For the commands above:

results/finetune/finetuned_models/scgpt/colon_1000/     args.json, best_model.pt, label_map.json, vocab.json
results/finetune/prediction/scgpt/colon_50_predicted_labels.tsv
results/finetune/prediction/scgpt/colon_50_predicted_probs.tsv

Model directories are copied out of the Nextflow work directory, not symlinked, so they survive the cleanup that follows a successful run.

Both TSVs are indexed by barcode. _predicted_labels.tsv has a single predicted_label column; _predicted_probs.tsv has one column per class.

Reading results in Python

import anndata as ad
import pandas as pd

labels = pd.read_csv(
    "results/finetune/prediction/scgpt/colon_50_predicted_labels.tsv",
    sep="\t", index_col=0,
)
truth = ad.read_h5ad("demo/colon_50.h5ad").obs["cell_type"].reindex(labels.index)

print(f"accuracy: {(labels['predicted_label'] == truth).mean():.1%}")

A per-class breakdown is usually more informative than a single accuracy, especially with imbalanced cell types:

from sklearn.metrics import classification_report

print(classification_report(truth, labels["predicted_label"], zero_division=0))

Gotchas

Label sets must match. The classifier can only predict classes it saw during training. Cell types present in the query but not the reference are forced into some training class — quietly, with no warning. If you expect novel types, this task is the wrong tool.

Fine-tuning needs real data. The demo files exist to show the mechanics, not to produce a usable classifier. A thousand cells across seven classes will overfit almost any backbone.

Fine-tuned weights are large. A fine-tuned model directory is comparable in size to the original checkpoint — 205 MB for scGPT, 261 MB for CellPLM. Fine-tuning several models on several datasets adds up quickly.

Batch-size flags are inert for some methods. CellPLM ignores --batch-size and --predict_batch_size; CELLama ignores --predict_batch_size. Passing them is accepted but has no effect.

Note

Every launch gets its own run directory under runs/<task>/, and the workspace nextflow.config sets cleanup = true, so the task work directory is deleted once the run succeeds. After a failure it is kept: fix the cause, add --resume to the same command, and Nextflow reuses every task that already completed. scfoundry runs lists the run directories with their status; scfoundry runs --task <task> narrows the list.

Next steps

  • Transfer — when you have few labels, or a model without a fine-tuning recipe.

  • Embed — the representation post-hoc classifiers train on.