rlhf
Set up and run the full RLHF pipeline (SFT, reward model training, RL from reward model) using the Tinker API. Use when the user wants to do RLHF, train a reward model, or run the full preference-base
Files1
View on GitHubSKILL.mdtinker-cookbook/.claude/skills/rlhf/SKILL.md
GitHubRL from Human Feedback (RLHF) Pipeline
Help the user set up and run the full 3-stage RLHF pipeline using the Tinker API.
Overview
RLHF is a multi-stage pipeline:
- SFT Stage — Fine-tune base model on instruction data
- Reward Model (RM) Stage — Train a reward model on preference comparisons
- RL Stage — Optimize the SFT policy using the reward model
Step 1: Understand the request
Ask the user (if not already specified):
- Base model: Which model to start from (e.g.,
meta-llama/Llama-3.2-3B) - Preference data: Which comparison dataset (HHH, HelpSteer3, UltraFeedback, or custom)
- Which stages to run: All 3, or skip SFT/RM if checkpoints exist
- LoRA rank: Typically 64 for RLHF
Step 2: Reference existing recipes
Read these files:
tinker_cookbook/recipes/preference/rlhf/rlhf_pipeline.py— Complete 3-stage pipelinetinker_cookbook/rl/preference_envs.py— Preference-based RL environmentstinker_cookbook/preference/types.py— PreferenceModelBuildertinker_cookbook/preference/comparison_policy_evaluator.py— RM evaluationdocs/preferences/rlhf-example.mdx— RLHF guide
Step 3: Configure each stage
Stage 1: SFT
Standard supervised fine-tuning (see /sft skill). Key settings:
- Dataset: NoRobots or similar instruction data
sft_learning_rate: 2e-4 (LoRA)train_on_what:TrainOnWhat.ALL_ASSISTANT_MESSAGES
Stage 2: Reward Model
Train on preference comparisons:
- Uses
ChatDatasetBuilderFromComparisonswith a comparison builder (e.g.,HHHComparisonBuilder) rm_learning_rate: 3e-4- Produces a reward model checkpoint used in Stage 3
Stage 3: RL from Reward Model
Optimize SFT policy using RM scores:
- Load SFT checkpoint as starting policy
- Load RM weights for scoring
PreferenceModelBuilderFromChatRendererwraps the RMPairwisePreferenceRLDatasetBuildercreates the RL environmentrl_learning_rate: 1e-5 (much lower than SFT)tournament_pattern:ALL_PAIRS_BOTH_WAYSfor pairwise comparison
Typical Hyperparameters
@chz.chz
class CLIConfig:
base_model: str = "meta-llama/Llama-3.2-3B"
lora_rank: int = 64
batch_size: int = 256
max_length: int = 16384
sft_learning_rate: float = 2e-4
rm_learning_rate: float = 3e-4
rl_learning_rate: float = 1e-5
rl_max_tokens: int = 1024
rl_group_size: int = 4
Step 4: Write the training script
Follow the pipeline pattern from rlhf_pipeline.py:
import asyncio
import os
import chz
from tinker_cookbook import checkpoint_utils, model_info
from tinker_cookbook.preference.types import PreferenceModelBuilderFromChatRenderer
from tinker_cookbook.rl import preference_envs, train
from tinker_cookbook.supervised import train as supervised_train
# Stage 1: SFT
def sft_stage(log_path, base_model, ...):
# Standard SFT config + supervised_train.main()
...
# Stage 2: Reward Model
def train_rm(log_path, base_model, ...):
# Train on preference comparisons
...
# Stage 3: RL
async def train_rl(log_path, sft_log_path, rm_log_path, base_model, ...):
sft_checkpoint = checkpoint_utils.get_last_checkpoint(sft_log_path)["state_path"]
rm_weights = checkpoint_utils.get_last_checkpoint(rm_log_path)["sampler_path"]
preference_model_builder = PreferenceModelBuilderFromChatRenderer(
renderer_name=renderer_name,
model_name=base_model,
rm_weights_path=rm_weights,
)
rl_dataset_builder = preference_envs.PairwisePreferenceRLDatasetBuilder(
comparison_builder=comparison_builder,
preference_model_builder=preference_model_builder,
batch_size=batch_size,
group_size=group_size,
tournament_pattern=preference_envs.TournamentPattern.ALL_PAIRS_BOTH_WAYS,
...
)
config = train.Config(
load_checkpoint_path=sft_checkpoint,
dataset_builder=rl_dataset_builder,
learning_rate=1e-5,
...
)
await train.main(config)
Step 5: Run
# Full pipeline
python -m tinker_cookbook.recipes.preference.rlhf.rlhf_pipeline
# Skip SFT (already have checkpoint)
python -m tinker_cookbook.recipes.preference.rlhf.rlhf_pipeline run_sft=False
# Skip SFT and RM
python -m tinker_cookbook.recipes.preference.rlhf.rlhf_pipeline run_sft=False run_rm=False
Step 6: Add tests
If you created a new RLHF recipe, add a smoke test:
# tests/recipes/test_recipe_<name>.py
import pytest
from tests.helpers import run_recipe
@pytest.mark.integration
def test_<recipe_name>():
run_recipe(
"tinker_cookbook.recipes.<recipe_name>.train",
["behavior_if_log_dir_exists=delete"],
)
run_recipe() automatically passes max_steps=2 so the recipe runs 2 training steps and exits. See tests/recipes/test_recipe_rlhf_pipeline.py for the existing example.
Common pitfalls
- RL learning rate must be much lower than SFT (1e-5 vs 2e-4)
- Checkpoints flow between stages: SFT → RL policy init, RM → RL reward scoring
- Use
checkpoint_utils.get_last_checkpoint()to find checkpoints from previous stages - RM quality directly impacts RL — validate RM before running Stage 3
group_sizein RL stage affects variance of reward estimates