Sign in Get started

Training

Low-level primitives for spawning, polling, and cancelling training runs.

View as Markdown

The training resource manages the lifecycle of a single run against a pre-built export. For the end-to-end “give me an ONNX model from this dataset” call, use train_pipeline instead.

Every example below shows the Python SDK call and the equivalent raw REST request. The REST examples authenticate with an X-API-Key header; set PICTOGRAPH_API_KEY in your shell to copy-and-run them.

from pictograph import Client
client = Client()  # reads PICTOGRAPH_API_KEY

Pipelines

pipeline_typeOutput
yoloxObject detection (boxes)
sm_pytorchSemantic segmentation
classificationImage classification
rfdetr_detectionObject detection (RT-DETR)
rfdetr_segmentationInstance segmentation (RT-DETR)

GPU tiers

gpu_typeApprox. costPick for
a10g (default)~$0.30/hrYOLOX, classification, RF-DETR-detection
a100~$2/hrLarge RF-DETR, big batches
h100~$4/hrLast resort — only when A100 OOMs

create

Spawn a run against an existing (completed) export.

ArgTypeDefaultNotes
dataset_namestrrequiredSource project
export_namestrrequiredPre-built export
pipeline_typePipelineTyperequiredSee table above
namestrrequiredHuman-readable label (1-100 chars)
configdict{}epochs, batch_size, learning_rate, image_size
gpu_typeGpuType"a10g"
waitboolTrueWhen False, returns immediately with status="queued"
poll_intervalfloat5.0Seconds between polls
timeoutfloat7200.0Max poll seconds (2 hours)
run = client.training.create(
    dataset_name="road-signs",
    export_name="road-signs-20260512-120000",
    pipeline_type="yolox",
    name="yolox-run-1",
    config={"epochs": 50},
    gpu_type="a10g",
    wait=True,
    poll_interval=5.0,
    timeout=7200.0,
)
curl -s -X POST "https://api.pictograph.io/api/v1/developer/training/" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dataset_name": "road-signs", "export_name": "road-signs-20260512-120000", "pipeline_type": "yolox", "name": "yolox-run-1", "config": {"epochs": 50}, "gpu_type": "a10g"}'

Returns TrainingRun.

list

Single-page list of training runs in your organization.

ArgTypeDefaultNotes
dataset_namestr | NoneNoneRestrict to one dataset
statusTrainingStatus | NoneNonee.g. "running"
limitint50Backend cap: 100
offsetint0Page offset
runs = client.training.list(limit=20, status="running")
for run in runs:
    print(run.id, run.status, run.progress)
curl -s "https://api.pictograph.io/api/v1/developer/training/?limit=20&status=running" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

iter

Auto-paging iterator across every training run in your org.

for run in client.training.iter(page_size=50):
    print(run.id, run.status, run.progress)
# Page manually with limit + offset until fewer than `limit` rows return.
curl -s "https://api.pictograph.io/api/v1/developer/training/?limit=50&offset=0" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

get

Fetch a single run’s current status, metrics, and progress, addressed by name (or run_id= UUID). status is one of {"pending", "queued", "running", "completed", "failed", "cancelled"}. Because run names are not unique, a by-name lookup returns the most recent run of that name.

run = client.training.get("Swift Falcon")     # by name (latest of that name)
run = client.training.get(run_id="run-uuid")   # by id
print(run.status, run.progress, run.current_epoch, "/", run.total_epochs)
# By name:
curl -s "https://api.pictograph.io/api/v1/developer/training/by-name/Swift%20Falcon" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"
# By UUID:
curl -s "https://api.pictograph.io/api/v1/developer/training/run-uuid" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

Returns {"data": TrainingRun}.

cancel

Cancel a running job, addressed by name (latest of that name) or run_id= UUID. The backend Modal-cancels the GPU job in-flight and marks the run cancelled. Under charge-on-success a run cancelled before it finishes is never charged (a legacy pre-charged run is refunded once). Member+ API key.

client.training.cancel("Swift Falcon")     # by name
client.training.cancel(run_id="run-uuid")   # by id
curl -s -X POST "https://api.pictograph.io/api/v1/developer/training/by-name/Swift%20Falcon/cancel" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

bulk_cancel

Cancel many runs in one org-scoped call. Already-terminal, foreign, or missing ids land in not_found rather than failing the call. Each run is Modal-cancelled in-flight; a run cancelled before it finishes is never charged (a legacy pre-charged run is refunded once).

result = client.training.bulk_cancel(["run-1", "run-2"])
print(result.count, "cancelled;", result.not_found, "skipped")
curl -s -X POST "https://api.pictograph.io/api/v1/developer/training/bulk-cancel" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"run_ids": ["run-1", "run-2"]}'

Returns BulkActionResult with succeeded, not_found, and count.

wait_for_completion

If you created with wait=False, you can block later:

run = client.training.wait_for_completion("run-uuid", timeout=3600.0)
if run.status == "completed":
    model = client.models.get(model_id=run.model_id)
# Poll the get endpoint until "status" reaches a terminal value.
curl -s "https://api.pictograph.io/api/v1/developer/training/run-uuid" \
  -H "X-API-Key: $PICTOGRAPH_API_KEY"

Minimum dataset size

Training requires at least 5 images matching the export’s status_filter so the worker can split into train / val / test. Below that, training fails with a validation error.

ds = client.datasets.get("my-dataset")
assert ds.completed_image_count >= 5

Cost estimation

estimate = client.credits.estimate("training_a10g", quantity=30)
if not estimate.sufficient:
    raise RuntimeError(f"Need ${estimate.total_usd:.2f}, have ${estimate.remaining_usd:.2f}")

Training is billed per GPU-minute in USD; estimate returns the live total_micro_usd (integer micro-USD, 1 USD = 1,000,000 µUSD).

Billing is charge-on-success: a run is charged once, after it completes, for the actual GPU minutes used — a failed, OOM, or cancelled run is never charged (there is no up-front deduction to refund).

Errors

StatusExceptionCause
402PaymentRequiredErrorInsufficient credits
404NotFoundErrorDataset or export missing
422ValidationErrorPipeline / GPU invalid, dataset too small
408PollTimeoutErrorwait=True exceeded timeout (run keeps going)

See also

  • train_pipeline — end-to-end workflow (recommended starting point)
  • Models — download trained ONNX weights
  • Creditsestimate("training_<gpu>") for the live USD price
Copied to clipboard