Skip to main content
You have a folder of images sorted into class directories and want a model that says which class a picture belongs to. Three commands:
Native PyTorch 2.14.0, its own runtime. It is the platform’s only supervised method: labelled input/target pairs, trained from initialisation, with no base model anywhere in the spec. A runnable example lives in the sample project tuneplane-examples, under experiments/pytorch-imgcls_simple-cnn_digit-images_v1.

What the data looks like

The directory name is the class name. No label file, no CSV:
That is the torchvision.datasets.ImageFolder convention. Put the number of classes in config.yaml as num_classes and not a line of code changes. val/ is required and must not overlap train/ — the accuracy the platform reports is measured on it, and mixing training images in makes that number mean nothing.

Create

You get train.py and config.yaml, both yours. In train.py only build_model and build_datasets are meant to be edited; the rest is the platform contract — how the arguments arrive, reporting from rank zero, where artifacts are written.

Configure

config.yaml is flat, with no inheritance. Every key is declared by the recipe, so a typo or an out-of-range value fails at tp validate rather than after queueing for a GPU:
--set overrides the same keys and is checked against the same declaration:
Every tunable parameter and its range: tp methods pytorch/image-classification.
A torchvision architecture (resnet18 and friends) needs three-channel input. A single-channel greyscale pipeline is refused explicitly, with the two config keys that disagree named — rather than by the first convolution raising an error that mentions only a tensor shape.

Where the data comes from

An image corpus is a directory, so unlike the text frameworks this method is handed a mount point rather than a file. Two ways: A platform dataset — immutable and versioned, read from the shared cache with no egress:
A volume — a directory maintained in place, no versions, mounted read-only:
Which one: material that keeps changing and growing belongs in a volume; material you need to reproduce from, and to say which version was trained on, belongs in a dataset. See datasets and volumes. Declare neither and input_pipeline: imagefolder reads data/{train,val} under the working directory, which is for local debugging; mnist / cifar10 try to download instead — fine on a laptop, and usually no route out of a cluster node. Declare one of the two for a real run.

Launch

The adapter compiles torchrun from the topology the server billed. One card is enough; with several, batch_size is per process. Two machines or more become a process group with a rendezvous, one launcher per machine — the experiment never reads the GPU count, because RANK, LOCAL_RANK and WORLD_SIZE are already set when train.py starts.
Multi-node needs the kuberay or slurm backend, the same as every other process-group framework here.

After training

  • Console curves: train/loss, validation/loss, validation/accuracy, train/lr
  • checkpoints/best.pt, plus a snapshot of every evaluated epoch
  • The accuracy is correct / total over val/
This method has no tp export and no tp eval, stated rather than omitted: a classifier has no HuggingFace export that would mean anything, and there is no benchmark harness here to score it against. tp submit --then export is refused at compile.

Bringing your own model

Two functions in train.py, and that is all:
Leave the rest alone — it is what lets the console draw the curves and the artifact registry find your weights.

Why not custom/custom

A shell script can call torchrun too, so the difference is worth naming: Reach for custom when your trainer is genuinely not this shape.

Image (an operator does this once)

pytorch-2.14.0 is built by the deployment; the catalog embeds no address:
Then name it in the console: Settings → Runtime → Images → “Default PyTorch image”. A tag is enough and it takes effect without a restart. TUNEPLANE_IMAGE_PYTORCH sets the same field from the environment and is the baseline a saved value overrides. Without it a submission fails with runtime_id='pytorch-2.14.0' has no built-in OCI source.