Sign in Get started

Train a model

Create an export and train a model in one call - detection, segmentation, keypoint, or classification, on a managed GPU.

View as Markdown

A training run is always on an export, never on a dataset directly. Create the export, check what went into it, then train it. Nothing is created behind your back, and an export you can see is an export you can re-train from.

Source: resources/training.py

from pictograph import Client, TrainingRun

client = Client()

client.exports.create(
    dataset_name="road-signs",
    name="road-signs-v1",
    format="pictograph",
    include_images=True,
    wait=True,
)

run: TrainingRun = client.training.create(
    dataset_name="road-signs",
    export_name="road-signs-v1",
    pipeline_type="yolox",
    name="road-signs-detector",
    gpu_type="a10g",
    config={"epochs": 50, "batch_size": 16},
)

if run.model_id:
    client.models.download(
        model_id=run.model_id,
        output_path="./yolox.onnx",
    )

With wait=True (the default) the call blocks until the run reaches a terminal status and run.model_id is populated.

Signature

client.training.create(
    dataset_name: str,
    export_name: str,
    *,
    pipeline_type: PipelineType,
    name: str,
    config: dict[str, Any] | str | Path | None = None,
    gpu_type: GpuType = "a10g",
    gpu_count: int = 1,
    version_of_model_id: str | None = None,
    wait: bool = True,
    poll_interval: float = 5.0,
    timeout: float = 7200.0,
) -> TrainingRun
Argument Default Purpose
dataset_name required Dataset the export belongs to
export_name required A completed export - training is always on an export
pipeline_type required Which pipeline to train
name required Run name
config {} Hyperparameters (epochs, batch_size, learning_rate, image_size)
gpu_type a10g a10g / a100 / h100 / auto
gpu_count 1 GPUs per run
version_of_model_id None Train a new version of an existing model
wait True Block until the run terminates
poll_interval 5.0 Seconds between polls
timeout 7200 Max seconds to wait (2 hours)

Pipelines

pipeline_type Task Best for
yolox Object detection (boxes) Speed, edge deployment, small datasets
sm_pytorch Semantic segmentation Pixel-wise class maps
classification Image classification Tag-style labels with no geometry
rfdetr_detection Object detection Higher mAP than YOLOX on harder data
rfdetr_segmentation Instance segmentation (polygons + masks) Best per-instance mask accuracy
rfdetr_keypoint Keypoint / pose detection A structured joint set per instance

GPU tiers

gpu_type When
a10g (default) YOLOX, classification, RF-DETR detection
a100 Large RF-DETR, big batch sizes
h100 Last resort, only when A100 runs out of memory
auto Let the platform pick the cheapest tier the config fits

How much data you need

There is no fixed image minimum. The matched images are split into train / val / test with at least one image in train and one in val; below three images everything goes into train. classification needs at least two classes; every other pipeline needs one.

The practical floor is data, not policy: a handful of images trains, but it will not generalise.

Async usage

Pass wait=False to fire-and-forget:

run = client.training.create(
    dataset_name="road-signs",
    export_name="road-signs-v1",
    pipeline_type="yolox",
    name="road-signs-detector",
    wait=False,
)
print("queued:", run.id)

# Poll yourself later.
run = client.training.get(
    run_id=run.id,
)
if run.status == "completed":
    model = client.models.get(
        model_id=run.model_id,
    )

Hyperparameters

config keys are pipeline-specific. Common ones across pipelines:

Key Type Typical
epochs int 30–100
batch_size int 8 / 16 / 32
learning_rate float 0.0010.01
image_size int 640 (YOLOX), 1024 (segmentation)

Unsupported keys are ignored.

Keypoint (pose) training

rfdetr_keypoint trains on keypoint annotations grouped into objects: a set of joints per object, not a box or a mask. Three things are worth knowing first.

Grouping is the supervision signal. RF-DETR Keypoint is query-based and top-down, so it needs to know which points belong to the same object. That is what instance_id carries; the editor assigns it per class as you draw, so a pose is simply every joint sharing an instance_id. Points with no instance_id train as single-joint objects.

Name left and right explicitly. A joint’s class name travels into training, and RF-DETR derives its horizontal-flip pairs by matching left_* to right_*. Without that pairing, flip augmentation mirrors the image but keeps the joint identities, teaching the model that a left wrist is a right one. Joint names with no counterpart map to identity.

Every object still needs a box. The box is derived from each instance’s placed joints automatically, so you never draw one. An object of a keypoint class with no joints placed yet still trains as a plain detection, with its keypoints masked out of the loss rather than dropped from the dataset.

run = client.training.create(
    dataset_name="gymnasts",
    export_name="gymnasts-v1",
    pipeline_type="rfdetr_keypoint",
    name="gymnast-pose",
    gpu_type="a10g",
    config={"epochs": 30},
)
print(run.metrics["OKS"])   # keypoint AP is measured over OKS, not box IoU

Predictions come back as keypoint annotations carrying instance_id, the same shape the editor writes, so they render, export and re-open for correction like hand-drawn poses. Accuracy is reported as OKS (object keypoint similarity) rather than mAP; the two are not comparable, which is why they have separate names.

Errors

Status Exception Cause
404 NotFoundError Dataset missing or has no status_filter-matching images
422 ValidationError Pipeline or GPU invalid, or too few classes for the pipeline
402 PaymentRequiredError Balance below the minimum needed to launch a GPU run
408 PollTimeoutError wait=True and timeout elapsed (the run continues; poll later)
5xx ApiError Training run failed - inspect run.error_message

A run is charged for the GPU minutes it actually used, once it succeeds - there is no up-front charge and no refund to chase if it fails. The 402 is a start-time balance floor, not a pre-charge.

See also

  • Training - lower-level create / list / get / cancel primitives
  • Models - download the trained weights
  • Credits - estimate("training_<gpu>") for the live USD price
  • Local inference - run the model you just trained
  • Deployments - serve it behind a URL
Copied to clipboard