# Supervised Learning

The cookbook provides a pipelined SFT training loop with configurable datasets, evaluation, and checkpointing.

## Architecture

```
train.Config
├── model_name            which model to fine-tune
├── dataset_builder       SupervisedDatasetBuilder → SupervisedDataset
├── learning_rate          optimizer config (+ lr_schedule, adam_*)
├── evaluator_builders    what to measure during training
├── save_every / eval_every    checkpoint and eval cadence
└── lora_rank             LoRA adapter size

SupervisedDatasetBuilder
└── __call__() → (train_dataset, eval_dataset)
     │
     ▼
SupervisedDataset
├── get_batch(index) → list[tinker.Datum]
├── __len__() → number of batches
└── set_epoch(seed) → shuffle for next epoch
```

## Key Components

### tinker_cookbook.supervised.train.Config

The central configuration. Defines model, data, hyperparameters, and evaluation.

```
from tinker_cookbook.supervised import train

config = train.Config(
    log_path="~/logs/my-sft-run",
    model_name="Qwen/Qwen3-8B",
    dataset_builder=my_dataset_builder,
    learning_rate=1e-4,
    lora_rank=32,
    num_epochs=1,
    save_every=20,
    eval_every=10,
)
asyncio.run(train.main(config))
```

### SupervisedDataset / SupervisedDatasetBuilder

A `SupervisedDatasetBuilder` is a config that constructs a `SupervisedDataset`. The dataset returns batches of `tinker.Datum` objects.

```
from tinker_cookbook.supervised.types import SupervisedDatasetBuilder, SupervisedDataset

class MyDatasetBuilder(SupervisedDatasetBuilder):
    def __call__(self) -> tuple[SupervisedDataset, SupervisedDataset | None]:
        return train_data, eval_data  # eval_data is optional
```

**Built-in builders:**

| Builder | Use case |
| --- | --- |
| `SupervisedDatasetFromHFDataset` | Wrap a HuggingFace dataset with `map_fn` or `flatmap_fn` |
| `StreamingSupervisedDatasetFromHFDataset` | Stream from HuggingFace (lower memory) |
| `FromConversationFileBuilder` | Load from a JSONL file of chat conversations |
| `ChatDatasetBuilder` | Base class for chat data — adds tokenizer + renderer |

### ChatDatasetBuilder

For chat-formatted data. Adds `tokenizer` and `renderer` properties from `ChatDatasetBuilderCommonConfig`:

```
from tinker_cookbook.supervised.types import ChatDatasetBuilder, ChatDatasetBuilderCommonConfig

class MyChats(ChatDatasetBuilder):
    common_config = ChatDatasetBuilderCommonConfig(
        model_name_for_tokenizer="Qwen/Qwen3-8B",
        renderer_name="qwen3",
        batch_size=8,
        max_length=4096,
    )
```

## Training Loop

`train.main(config)` handles the full pipeline:

1. Create `ServiceClient` → `TrainingClient`
2. Build dataset from `config.dataset_builder`
3. For each epoch and batch:
   - `forward_backward` with the batch
   - `optim_step`
   - Run evaluators at `eval_every` intervals
   - Save checkpoints at `save_every` intervals
4. Save final checkpoint

The loop pipelines requests for throughput — see [Clock Cycles & Pipelining](https://tinker-docs.thinkingmachines.ai/tinker/under-the-hood/).

## Next Steps

- [sl_loop.py](https://github.com/thinking-machines-lab/tinker-cookbook/blob/main/tinker_cookbook/recipes/sl_loop.py) — minimal training loop without abstractions
- [SL Hyperparameters](https://tinker-docs.thinkingmachines.ai/tutorials/advanced/sl-hyperparams/) — choosing learning rate, rank, batch size
- [Tutorials: Your First SFT](https://tinker-docs.thinkingmachines.ai/tutorials/basics/first-sft/) — interactive walkthrough
