Predicting Model Performance › Practical Forecasting Workflows

Michael BrenndoerferNovember 2, 20259 min read
Language AI HandbookAll chapters
Part 23 of 67Scaling Laws
Chapter 168Predicting Model Performance › Practical Forecasting Workflows
Reading section 6 of 10 · Practical Forecasting Workflows · 9 minSection 6 of 10 · 9 min

Book contents

Section 6 of 10 · 9 min read

Predicting Model Performance › Practical Forecasting WorkflowsOne section · Sections in this chapter6 / 10 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Given these techniques and caveats, how should practitioners approach performance prediction in real projects? The goal is to balance prediction value against its costs and integrate predictions into decision-making rather than treating them as ground truth.

The Ladder Approach

A practical workflow uses progressively larger "stepping stone" models. The idea is to invest a small fraction of the total training budget in preliminary runs that provide data for fitting scaling laws with decreasing extrapolation distance. Each rung of the ladder gives us more confidence in our prediction for the final run.

In[25]:
Code
import numpy as np


def plan_scaling_ladder(
    target_params, target_tokens, min_runs=5, budget_fraction=0.1
):
    """
    Plan a series of training runs to predict target model performance.

    Args:
        target_params: Target model size in millions
        target_tokens: Target training tokens in billions
        min_runs: Minimum number of scaling runs
        budget_fraction: Fraction of target compute for ladder

    Returns:
        Dict with planned runs, compute requirements, and ladder fraction
    """
    # Target compute (rough approximation: 6ND for forward+backward passes)
    target_compute = 6 * target_params * 1e6 * target_tokens * 1e9
    ladder_budget = target_compute * budget_fraction

    # Distribute runs geometrically
    runs = []
    n_runs = min_runs

    # Start at 0.1% of target scale, end at 3% (10-30x extrapolation)
    scale_fractions = np.geomspace(0.001, 0.03, n_runs)

    for frac in scale_fractions:
        # Compute-optimal scaling: N proportional to sqrt(C), D proportional to sqrt(C)
        run_params = target_params * np.sqrt(frac)
        run_tokens = target_tokens * np.sqrt(frac)
        run_compute = 6 * run_params * 1e6 * run_tokens * 1e9

        runs.append(
            {
                "params_m": run_params,
                "tokens_b": run_tokens,
                "compute_petaflops": run_compute / 1e15,
            }
        )

    total_ladder_compute = sum(r["compute_petaflops"] for r in runs)

    return {
        "runs": runs,
        "total_compute_pf": total_ladder_compute,
        "target_compute_pf": target_compute / 1e15,
        "ladder_fraction": total_ladder_compute / (target_compute / 1e15),
    }


# Plan ladder for a 7B parameter model trained on 2T tokens
ladder = plan_scaling_ladder(
    target_params=7000, target_tokens=2000, min_runs=5, budget_fraction=0.05
)
Out[26]:
Console
Scaling ladder plan for 7B model on 2T tokens:
Target compute: 84000000.0 PF
Ladder compute: 4337439.4 PF (5.2% of target)

Planned training runs:
--------------------------------------------------
  Run 1: 221M params, 63B tokens (84000.00 PF)
  Run 2: 339M params, 97B tokens (196589.17 PF)
  Run 3: 518M params, 148B tokens (460086.95 PF)
  Run 4: 793M params, 226B tokens (1076763.26 PF)
  Run 5: 1212M params, 346B tokens (2520000.00 PF)

This ladder approach invests a small fraction of the total compute budget in preliminary runs that inform the final training decision. The geometric spacing ensures coverage across scales while keeping total ladder cost bounded. Each run provides data points for fitting scaling laws with decreasing extrapolation distance. By the time we've completed the ladder, we're extrapolating perhaps 30× rather than 1000×.

The ladder also provides early warning of problems. If the scaling relationship looks anomalous at intermediate scales, perhaps due to architectural issues or data problems, we discover this cheaply before committing to the full run.

Out[27]:
Visualization
Log-log scatter plot showing five ladder training runs clustered in the bottom-left corner and the red-star target model in the top-right, illustrating the scale gap between preliminary runs and the final training goal.
Scaling Ladder: Run Distribution
Bar chart showing compute in petaFLOPs for each of the five ladder runs, with values increasing geometrically and percentage labels showing each run's cost as a fraction of the target training budget.
Compute per Ladder Run

Decision Framework

With predictions and uncertainties in hand, teams can make informed decisions. The goal is to translate raw predictions into information that supports go/no-go decisions and resource allocation:

In[28]:
Code
from scipy.stats import norm


def forecast_decision(
    target_N, target_D, params, covariance, loss_target, capability_thresholds
):
    """
    Generate decision recommendation based on forecasts.

    Combines loss prediction with capability estimation to provide
    information for training decisions.

    Args:
        target_N: Target model size in millions of parameters
        target_D: Target training data in billions of tokens
        params: Fitted scaling law parameters
        covariance: Parameter covariance matrix
        loss_target: Desired loss threshold
        capability_thresholds: Dict mapping task names to calibration params

    Returns:
        Dict with predicted loss, confidence intervals, probability of
        hitting loss target, and expected capability scores
    """
    result = predict_with_uncertainty(target_N, target_D, params, covariance)

    # Probability of hitting loss target
    if result["std"] > 0:
        p_hit_loss = norm.cdf(loss_target, result["mean"], result["std"])
    else:
        p_hit_loss = 1.0 if result["mean"] <= loss_target else 0.0

    # Expected capability scores
    expected_capabilities = {}
    for task, calib in capability_thresholds.items():
        score = loss_to_accuracy(
            result["mean"], calib["baseline"], calib["ceiling"], calib["floor"]
        )
        expected_capabilities[task] = score

    return {
        "predicted_loss": result["mean"],
        "loss_ci": (result["ci_lower"], result["ci_upper"]),
        "p_hit_loss_target": p_hit_loss,
        "expected_capabilities": expected_capabilities,
    }
Out[29]:
Console
=== Forecast-Based Decision Report ===

Target: 7B parameters, 2T tokens
Loss target: 1.95

Predicted loss: 1.843
95% CI: [1.065, 2.109]
P(loss ≤ target): 65.7%

Expected benchmark performance:
  hellaswag: 73.3%
  mmlu: 81.8%
  arc_challenge: 79.4%

This framework translates raw predictions into actionable information: the probability of achieving targets, expected benchmark performance, and confidence levels. Decision-makers can then weigh these forecasts against compute costs and alternative investments. A 60% probability of hitting a loss target might justify a go-ahead for a moderate-cost run but not for an extremely expensive one. The framework makes trade-offs explicit and quantitative.