Skip to main content
Write your own loop when you need control over the data, sampling, rewards, or loss function that isn’t in a cookbook recipe. Your training loop calls the Together Python SDK to create a model resource and a session, then sends each step’s operations to the resource.
If you already have a Tinker training script, you can migrate it by changing its import line.

Install the SDK

Install the Together Python SDK, version 3.0.0 or later:

Run a minimal loop

This loop trains a low-rank adapter (LoRA) for Qwen3.8 27B on one sample for one step, then saves the adapter as an inference checkpoint. The model resource it creates bills per GPU-minute from when it’s ready until the loop exits:
To build on this loop, learn how to:

Train on a batch

This loop trains on eight of your samples per step, for NUM_STEPS steps. You pass the learning rate on every optim_step, so it can follow a schedule, such as this loop’s linear decay:
For cross-entropy, result.loss is a sum over the batch’s trained tokens, not a mean. Divide it by the number of trained tokens for a per-token loss that stays comparable as the batch’s token count changes. To train with group relative policy optimization (GRPO), build GRPO samples and pass a policy-gradient loss. GrpoLossParams sets the clip band and the beta weight of a Kullback-Leibler (KL) penalty:

Sample from the generator

Pass all your prompts to sample_batch in one call, and set num_samples to request a group of completions for each prompt. In this example, prompts holds each prompt’s token IDs:
Build each completion’s GRPO sample from its tokens and logprobs. Sampling can run at the same time as other sampling and trainer operations, but a synchronous weights_sync waits for in-flight sampling to finish. One SamplingParams applies to every prompt in a sample_batch call. The API reference lists its fields and defaults. Two fields need care:
  • stop: For models without stop-string support, omit stop and truncate completions in your code. A request with an unsupported stop string fails as invalid input.
  • seed: Leave it unset. It’s unsupported and causes an internal error when the generator uses multiple inference engines.

Compute log-probabilities for fixed text

Call session.generator.compute_logprobs(prompt) for one prompt, or compute_logprobs_batch(prompts) for several. If the trainer has stepped since the last sync, call weights_sync first, so the log-probabilities come from the updated weights. Entry 0 of the result is a 0 placeholder, because the first token has no preceding context. sample with prompt_logprobs=True returns the same placeholder.

Session operations

Every operation targets a session. Calls on session.trainer run on the model resource’s trainer. Calls on session.generator run on its generator replicas, which exist only on a resource created with num_generator_replicas above 0. The API reference lists every operation’s fields and defaults. weights_sync takes a weight_sync_type, which defaults to WEIGHT_SYNC_TYPE_SYNCHRONOUS and also accepts the deferred modes used for async training. Each call submits an operation, then waits until the operation finishes or the call’s timeout runs out. When an operation fails on the resource, the SDK raises an operation error.

Call operations asynchronously

Every call has an _async variant, such as forward_backward_async, sample_batch_async, or ModelResourcesClient.create_async. Use the _async variants in async code. The synchronous calls block the calling thread, so they raise RuntimeError when called from code that’s already running an event loop, such as an async application. Jupyter notebooks are the exception: a synchronous call works in a notebook cell but blocks the cell until it returns. The _async variants work in notebooks too. The _async variants also let independent work overlap. For example, one session can sample while another trains, or you can train several adapters at once.

Next steps

For complete loops, see the cookbook’s notebooks and direct preference optimization (DPO) loop, which build samples with the cookbook’s helpers.

SFT notebook

Supervised fine-tuning (SFT) on the No Robots dataset, step by step.

GRPO notebook

GRPO on GSM8K math problems, step by step.

DPO training loop

DPO on chosen and rejected pairs, built on a custom loss function.

Samples and loss masks

Line up a sample’s arrays and mask a chat prompt.

Custom loss functions

Train toward an objective you compute in PyTorch, such as DPO.

Model resources

Choose GPUs, attach to a running resource, and stop it.