Skip to main content
Docs

Search guides and API endpoints, for example “Idempotency-Key” or “submit job”.

    Tools · Custom Modeling

    Protein Fitness Models

    Learn a sequence-to-property model from a few dozen measured variants on protein language model embeddings, compare three model types by cross-validation, and map every single substitution.

    Updated October 28, 2026

    On this page

    Prices, workflows, and method papersOpen in the app

    Protein Fitness Models turns a table of measured variants of one protein into a model that predicts the property you measured (activity, stability, binding, expression) for variants you have not made. Each sequence is embedded with a protein language model, three model types are compared on those embeddings by cross-validation, and the best is saved as a model you can predict with. If you give the wild-type sequence, you also get an in silico saturation map: a predicted value for each of the 19 substitutions at every residue.

    Built with ESM. Predictions use ESM C 300M (Cambrian Open License) or ESM-2 650M (MIT). The larger ESM C sizes are not offered.

    How the model is built

    1. Embed. Every training sequence goes through the language model, and the per-residue vectors are pooled into one vector per sequence: the mean over the whole sequence, the mean over the mutated positions only (this needs the wild type), or both. ESM C 300M is the default and reads sequences up to 2,046 residues; ESM-2 650M reads up to 1,022. Each embedder is pinned to a fixed revision, and a model refuses to score with a different one.
    2. Hold out. A test set is held out first. random splits variants at random. position keeps every variant at a given position on one side, so the test set measures prediction at positions the model never saw. mutation_count trains on single substitutions and tests on combinations. The last two need the wild type, and a position split is the one to trust if you plan to propose substitutions at new sites.
    3. Compare. Three model types are scored by k-fold cross-validation on the rest, each over a small fixed set of settings so run time stays predictable:
    Model typeWhat it is
    Ridge regressionA linear model on the embeddings
    Random forestAn ensemble of decision trees
    Gaussian processA probabilistic model with calibrated uncertainty (an RBF kernel on standardized embeddings)
    1. Keep the best. The model type with the best cross-validated metric (Spearman rank correlation by default, which is what variant ranking is judged on; RMSE, MAE and R² are the alternatives) is refitted on all non-test variants and saved. Every model type is also scored on the test set, so the comparison shows how much each one learned.

    Inputs

    • Measured variants, any one of these ways:
      • full sequences, each with one value;
      • a wild-type sequence plus mutation strings in the Sequence Mutator syntax (T315I, joined with + for combined variants, WT for the wild type itself), each with one value;
      • a table with a sequence or a mutation column and a value column.
    • Wild type (a sequence or one FASTA record). Optional for random splits and mean pooling, required for position and mutation-count splits, mutated-position pooling and the saturation map. first_residue_number lines literature numbering up with the construct.

    At least 12 and at most 5,000 variants; repeated variants are rejected. A trained model whose embedder is ESM C is saved with an ESM- prefix on the name you chose.

    Predictions

    Predictions come in the units of your assay, one per sequence or mutation string (mutations are read against the model's wild type). Each carries:

    • a 90% prediction interval (split-conformal, from cross-validation errors), and a standard deviation from the random forest and Gaussian process models;
    • the similarity of the sequence to its nearest training sequence, so you can tell when a prediction is far from the model's experience.

    A model fitted on one embedder revision is only ever scored with that revision. To score a whole library inside a workflow, use the Saved Model Scoring job on the MPNN Models page; the Sequence Mutator feeds it candidate variants, and the ranked list can go straight to a selection step.

    Outputs

    FileContents
    model/The saved model, with its model card: the embedder and revision, licence notice, split, metrics and the conditions it was trained under (no pickled code)
    comparison.csvEvery model type's metric on every fold and on the test set
    test_predictions.csvObserved values and each model type's test-set predictions
    saturation_heatmap.csvThe predicted value of every single substitution at every residue (wild types up to 1,000 residues)
    training_summary.jsonThe embedder, the winner, the split and the per-model-type metrics

    The saved model appears in My Models, with the comparison, the base-model notice and a per-position summary of the saturation map.

    What the evidence supports

    In our benchmark on four public deep mutational scans, models on protein language model embeddings ranked unseen variants better than one-hot features at small training sizes (24 to 96 variants). The gain was largest for ESM C 300M. It was thin for ESM-2 650M on the mutation-count split, and one-hot features won on one assay (GB1) there. Four assays is a small sample, so this does not guarantee a gain for any one protein: read the cross-validation and test-set comparison for your own data before trusting a ranking.

    Honest limits

    • Predictions and the saturation map are in silico estimates built from your measurements. They support prioritization; express and measure the variants you care about.
    • A model learns the property you measured on the protein you trained it on. It does not transfer to another protein, and predictions far from the training variants (see the similarity) deserve less trust.
    • Epistasis across many simultaneous mutations is hard for every method; the mutation-count split exists to show how well your data supports it.
    • The saturation map covers substitutions only, not insertions or deletions.

    Generate the variants to measure or score with the Sequence Mutator, choose the next round of variants to measure with Active Learning, or train a model on small-molecule data with QSAR Models.

    Run it from the API

    Submit with Submit a job and the job_type below. Price it first with Estimate job reservation cost: submitting reserves that amount from your wallet, and the charge settles at the actual runtime.

    Protein Fitness Model Training train-protein-fitness

    Job type
    train-protein-fitness
    Hardware
    t4l4a10 (default)
    Typical runtime
    15 min on Nvidia A10

    Payload

    Payload fields
    FieldTypeDescription
    model_namerequiredstring

    Limits: min length 1

    wild_typestring | artifactRef
    first_residue_numberinteger

    Default: 1Limits: ≥ -10000, ≤ 100000

    sequences[]string[]

    Limits: min items 12, max items 5000

    mutations[]string[]

    Limits: min items 12, max items 5000

    values[]number[]

    Limits: min items 12, max items 5000

    activity_tablestring | object[] | artifactRef
    sequence_columnstring

    Default: "sequence"Limits: min length 1

    mutation_columnstring

    Limits: min length 1

    target_columnstring

    Default: "value"Limits: min length 1

    embedderstring

    Default: "esmc_300m"One of: "esmc_300m", "esm2_650m"

    poolingstring

    Default: "mean"One of: "mean", "mutated", "both"

    heads[]string[]

    One of: "ridge", "random_forest", "gaussian_process"Limits: min items 1

    selection_metricstring

    Default: "spearman"One of: "spearman", "rmse", "mae", "r2"

    split_methodstring

    Default: "random"One of: "random", "position", "mutation_count"

    cv_foldsinteger

    Default: 5Limits: ≥ 3, ≤ 10

    test_sizenumber

    Default: 0.2Limits: ≥ 0, ≤ 0.5

    heatmapboolean
    random_seedinteger

    Limits: ≥ 0

    unitstring
    model_descriptionstring
    tags[]string[]

    Example

    from cognichem_client import CogniChem
    
    client = CogniChem.from_env()  # reads COGNICHEM_API_KEY
    payload = {
        "model_name": "ESM-protein-fitness-tiny",
        "wild_type": "MTYKLILNGKTLKGETTTEAVDAATAEKVF",
        "mutations": ["WT", "E15I", "K10M", "G14V", "E15T", "… 31 more"],
        "values": [0, 0.76, -0.411, -0.526, -0.253, "… 31 more"],
        "embedder": "esmc_300m",
        "pooling": "mean",
        "split_method": "position",
        "cv_folds": 3,
        "test_size": 0.2,
        "random_seed": 1226,
        "heatmap": True,
        "unit": "relative fitness (synthetic)",
    }
    
    estimate = client.jobs.estimate(job_type="train-protein-fitness", payload=payload, resource="a10")
    print(f"Reserves ${estimate.cost:.2f}")
    
    job = client.jobs.submit(
        job_name="my-train-protein-fitness-run",
        job_type="train-protein-fitness",
        payload=payload,
        resource="a10",
    )
    status = client.jobs.wait(job.process_id)
    if status.status == "completed":
        client.jobs.result(job.process_id, save_path=".")

    Sample data from the job catalog; long values are shortened here. Each job_name must be unique among your jobs.

    Workflow inputs

    • Table (list) · optionalCSV, TSV, JSON
    • Protein sequences · optionalFASTA, RAW

    Workflow outputs

    • ArchiveZIP
    • TableCSV
    • TableCSV
    • Trained modelOPAQUE