Skip to content

Commit 5b0b768

Browse files
madiatorclaude
andcommitted
docs: add Tinker fine-tuning section to README and deduplicate poem example
- Add Fine-Tuning with Tinker section covering basic usage, checkpoint resume, and custom data formats - Add poem fine-tuning example to the examples table - Replace inline duplicate in poem example with call to convert_to_chat_format() Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c94ca77 commit 5b0b768

2 files changed

Lines changed: 72 additions & 19 deletions

File tree

‎README.md‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pip install bespokelabs-curator
6161
| **Product feature extraction** | <a target="_blank" href="https://colab.research.google.com/drive/1YoA23-cBcWpaSErULzBI2bo2LPGo37GQ"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a> | Finetuning a model to identify features of a product |
6262
| **Sentiment analysis** | <a href="https://colab.research.google.com/drive/1Zfl3g7POsqqYQqkzXdyhYRSAymLhZugn?usp=sharing" target="_blank"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a> | Aspect-based sentiment analysis of restaurant reviews and finetuning using Together.ai |
6363
| **RAFT for domain-specific RAG** | <a href="https://github.com/bespokelabsai/curator/tree/main/examples/blocks/raft" target="_blank">Code</a> | Implement Retrieval Augmented Fine-Tuning (RAFT) that processes domain-specific documents, generates questions, and prepares data for fine-tuning LLMs. |
64+
| **Poem generation & LoRA fine-tuning** | <a href="https://github.com/bespokelabsai/curator/blob/main/examples/poem_finetuning_example.py" target="_blank">Code</a> | End-to-end pipeline: curate poem data with Curator, then LoRA fine-tune with TinkerTrainer |
6465

6566
### Data Generation
6667
| **Task** | **Link(s)** | **Goal** |
@@ -295,6 +296,76 @@ See documentation:
295296
* [kluster.ai batch mode](https://docs.bespokelabs.ai/bespoke-curator/how-to-guides/using-kluster.ai-for-batch-inference)
296297
* [Mistral batch mode](https://docs.bespokelabs.ai/bespoke-curator/how-to-guides/using-mistral-for-batch-inference)
297298

299+
## 🔧 Fine-Tuning with Tinker
300+
301+
Curator integrates with the [Tinker SDK](https://github.com/thinking-machines-lab/tinker-cookbook) so you can go from curated data to a LoRA fine-tuned model in a few lines of Python.
302+
303+
```bash
304+
pip install bespokelabs-curator tinker
305+
export TINKER_API_KEY="your-tinker-key"
306+
```
307+
308+
```python
309+
from bespokelabs.curator import TinkerTrainer, TinkerTrainerConfig
310+
311+
# Configure training
312+
config = TinkerTrainerConfig(
313+
base_model="Qwen/Qwen3-8B",
314+
epochs=3,
315+
batch_size=4,
316+
lora_config={"rank": 16, "alpha": 32, "dropout": 0.05},
317+
checkpoint_every_epoch=True,
318+
)
319+
320+
# Training data is a list of chat-format dicts (or a HuggingFace Dataset)
321+
training_data = [
322+
{"messages": [
323+
{"role": "user", "content": "What is Python?"},
324+
{"role": "assistant", "content": "Python is a programming language."},
325+
]},
326+
# ...
327+
]
328+
329+
# Train
330+
trainer = TinkerTrainer(config)
331+
result = trainer.train(training_data)
332+
print(f"Final loss: {result.final_loss:.4f}")
333+
334+
# Sample from the fine-tuned model
335+
response = trainer.sample("Explain recursion in Python")
336+
print(response)
337+
```
338+
339+
### Checkpoint Resume
340+
341+
Training can be resumed from any saved checkpoint. The trainer restores both model weights and optimizer state, then continues from where it left off — no data is replayed.
342+
343+
```python
344+
# Resume from an earlier run's checkpoint
345+
checkpoints = result.checkpoints # list of CheckpointInfo
346+
trainer = TinkerTrainer(config)
347+
trainer.load_checkpoint(checkpoints[-1])
348+
result = trainer.train(training_data) # continues from the checkpoint
349+
```
350+
351+
### Custom Data Formats
352+
353+
Subclass `TinkerTrainer` to handle non-standard data layouts:
354+
355+
```python
356+
class MyTrainer(TinkerTrainer):
357+
def format_example(self, row):
358+
return TrainingExample.from_dict_messages([
359+
{"role": "user", "content": row["question"]},
360+
{"role": "assistant", "content": row["answer"]},
361+
])
362+
363+
trainer = MyTrainer(config)
364+
result = trainer.train([{"question": "What is 2+2?", "answer": "4"}, ...])
365+
```
366+
367+
See the full [poem fine-tuning example](examples/poem_finetuning_example.py) for an end-to-end pipeline that curates data with `curator.LLM` and then fine-tunes with `TinkerTrainer`.
368+
298369
## Bespoke Curator Viewer
299370
The hosted curator viewer is a rich interface to visualize data -- and makes visually inspecting the data much easier.
300371

‎examples/poem_finetuning_example.py‎

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -325,25 +325,7 @@ def main(use_mock: bool = False):
325325
# Step 2: Convert to chat format
326326
print("\n[Step 2] Converting to chat format for fine-tuning...")
327327

328-
# Convert poem data to chat format directly (works with list of dicts)
329-
training_data = []
330-
for row in poem_data:
331-
messages = [
332-
{
333-
"role": "system",
334-
"content": "You are a creative poet who writes beautiful poems in various styles. "
335-
"When asked to write a poem, you provide both the poem and a brief explanation of its meaning.",
336-
},
337-
{
338-
"role": "user",
339-
"content": f"Write a {row['style']} poem about \"{row['theme']}\".",
340-
},
341-
{
342-
"role": "assistant",
343-
"content": f"{row['poem']}\n\n---\n\n**Explanation:** {row['explanation']}",
344-
},
345-
]
346-
training_data.append({"messages": messages})
328+
training_data = convert_to_chat_format(poem_data)
347329

348330
print(f" Training examples: {len(training_data)}")
349331

0 commit comments

Comments
 (0)