
StackLLaMA : RLHFã§LLaMAãåŠç¿ããããã®å®è·µã¬ã€ã
以äžã®èšäºãé¢çœãã£ãã®ã§ãç°¡åã«ãŸãšããŸããã
1. ã¯ããã«
ãã®èšäºã§ã¯ããSFTããRMããRLHFãã®çµã¿åããã§ããStack Exchangeãã®è³ªåã«çãããStackLLaMAãã®åŠç¿ã®å šã¹ãããã玹ä»ããŸãã
ã»SFT (Supervised Fine-tuning) : æåž«ãããã¡ã€ã³ãã¥ãŒãã³ã°
ã»RM (Reward / preference modeling) : å ±é ¬ / å奜ã¢ããªã³ã°
ã»RLHF (Reinforcement Learning from Human Feedback) : ãã¥ãŒãã³ãã£ãŒãããã¯ããã®åŒ·ååŠç¿

ãStackLLaMAãã¯ã以äžã§ãã¢ã詊ãããšãã§ããŸãã
ããŒã¹ã¢ãã«ãšããŠãLLaMA 7BããããŒã¿ã»ãããšããŠãStackExchangeãããŒã¿ã»ããã䜿çšããŠããŸãã
2. Stack Exchange ããŒã¿ã»ãã
ãStackExchangeãããŒã¿ã»ããã¯ããStackExchangeããã©ãããã©ãŒã ã®è³ªåå¿çã®ããŒã¿ã»ããã§ããè³æç¥šã®æ°ããšãåãå ¥ããããåçããå«ãŸããŠããŸãã
Askell et al.2021 ã«åŸããååçã«ã¹ã³ã¢ãä»äžããŠããŸãã
score = log2 (1 + upvotes) rounded to the nearest integer, plus 1 if the questioner accepted the answer (we assign a score of â1 if the number of upvotes is negative).ãå ±é ¬ã¢ãã«ãã§ã¯ãæ¯èŒããããã«1åã«ã€ã2ã€ã®çããåžžã«å¿ èŠã§ãã1åãããæå€§10åã®åçãã¢ã§ãµã³ããªã³ã°ããŸããããããŠãã¢ãã«åºåãããèªã¿ãããããããã«ãHTMLãMarkdownã«å€æããŠããŸãã
3. å¹ççãªåŠç¿æŠç¥
å€§èŠæš¡ãªã¢ãã«ã®åŠç¿ã«ã¯ãèšå€§ãªéã®ã¡ã¢ãªãå¿ èŠã§ããå粟床åŠç¿ãªã©ãããã€ãã®ããªãã¯ã䜿ãã°ãã¡ã¢ãªãç¯çŽã§ããŸãããé ããæ©ãã䜿ãæãããŠããŸããŸãã
3-1. PEFT
ã¡ã¢ãªå¯Ÿçã®1ã€ãšããŠã8bitã§èªã¿èŸŒãã ã¢ãã«ã«å¯ŸããŠãLoRAããå®è¡ã§ãããPEFTããå©çšããæ¹æ³ããããŸãã

ã¢ãã«ã8bitã§ããŒããããšãéã¿ã®ãã©ã¡ãŒã¿ããšã«1ãã€ãããæ¶è²»ããªããããã¡ã¢ãªãå€§å¹ ã«åæžãããŸã (LlaMa 7Bã®ã¡ã¢ãªã¯ 7GB)ã å ã®éã¿ãçŽæ¥åŠç¿ãã代ããã«ãLoRA ã¯ããã€ãã®ç¹å®ã®ã¬ã€ã€ãŒã®äžã«å°ããªã¢ããã¿ãŒã¬ã€ã€ãŒã远å ããŸãããã®ãããåŠç¿å¯èœãªãã©ã¡ãŒã¿ã®æ°ã¯å€§å¹ ã«æžå°ããŸãã
3-2. 䞊ååŠç
å€§èŠæš¡ãªã¢ãã«ã1ã€ã®GPUã«åããããšãã§ããããã«ãªããŸããããåŠç¿ã¯äŸç¶ãšããŠé ããªãåŸåããããŸãããã®ã·ããªãªã®æãåçŽãªæŠç¥ã¯ã䞊ååŠçãã§ããåãåŠç¿ãå¥ã ã®GPUã«è€è£œããç°ãªãããããåGPUã«æž¡ããŸããããã«ãããã¢ãã«ã®åæ¹/åŸæ¹ãã¹ã䞊ååããGPUã®æ°ã«å¿ããŠæ¡åŒµã§ããŸãã

ããã§ã¯ããtransformers.TrainerããŸãã¯ãAccelerateãã®ããããã䜿çšããŸããã©ã¡ããããtorchrunããŸãã¯ãAccelerate launchãã§ã¹ã¯ãªãããåŒã³åºããšãã«åŒæ°ãæž¡ãã ãã§ãã³ãŒãã倿Žããã«äžŠååŠçããµããŒãããŸãã
accelerate launch --multi_gpu --num_machines 1 --num_processes 8 my_accelerate_script.py
torchrun --nnodes 1 --nproc_per_node 8 my_torch_script.py4. æåž«ãããã¡ã€ã³ãã¥ãŒãã³ã°
ãRLHFããè¡ãåã«ã¢ãã«ããã¡ã€ã³ãã¥ãŒãã³ã°ããããšã¯ãç¹å¥ãªããšã§ã¯ãããŸãããäºååŠç¿ã«ããå æé¢ä¿èšèªã¢ããªã³ã°ã®ç®çãé©çšããŠããã ãã«ãªããŸããããŒã¿ãå¹ççã«äœ¿çšããããã«ããããã³ã°ãšåŒã°ããææ³ã䜿çšããŠããŸãããããå ã®ãµã³ãã«ããšã«1ã€ã®ããã¹ããçšæããæé·ããã¹ãã«ããã£ã³ã°ãã代ããã«ãEOSããŒã¯ã³ãæãã§å€ãã®ããã¹ããé£çµããããã£ã³ã°ãªãã§ããããåããããã«ã³ã³ããã¹ããµã€ãºã®ãã£ã³ã¯ãåãåããŸãã

ãã®ã¢ãããŒãã§ã¯ãã¢ãã«ãééããåããŒã¯ã³ãåŠç¿ããããããåŠç¿ãããå¹ççã«è¡ãããŸãã
ãããã³ã°ã¯ãConstantLengthDatasetãã«ãã£ãŠåŠçãããPEFTã§ã¢ãã«ãããŒãããåŸã«Trainerã䜿çšã§ããŸããã¢ãã«ãint8ã§ããŒãããåŠç¿çšã«æºåããŠãããLoRAã¢ããã¿ã远å ããŸãã
# 8bitã¢ãã«ã®ããŒã
model = AutoModelForCausalLM.from_pretrained(
args.model_path,
load_in_8bit=True,
device_map={"": Accelerator().local_process_index}
)
model = prepare_model_for_int8_training(model)
# LoRAã¢ããã¿ã®è¿œå
lora_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(model, config)5. å ±é ¬ / å奜ã¢ããªã³ã°
人éã®ã¢ãããŒã·ã§ã³ããã®ãŸãŸäœ¿ã£ãŠãRLHFãã䜿ã£ãã¢ãã«ã®ãã¡ã€ã³ãã¥ãŒãã³ã°ãè¡ãããšãã§ããŸãããããããã®å Žåãæé©åã®ç¹°ãè¿ãåŸã«ãããã€ãã®ãµã³ãã«ã人éã«éããè©äŸ¡ãåããå¿ èŠããããŸããããã¯ãé«äŸ¡ã§æéãããããŸãã
çŽæ¥çãªãã£ãŒãããã¯ã®ä»£ããã«æå¹ãªã®ãã人éã®ã¢ãããŒã·ã§ã³ãããšã«åŠç¿ãããå ±é ¬ã¢ãã«ãã䜿çšããæ¹æ³ã§ãããå ±é ¬ã¢ãã«ãã¯ãããã³ãã x ãš2ã€ã®åçåè£ (y_k, y_j) ããã人éã®ã¢ãããŒã·ã§ã³ã«ãã£ãŠã©ã¡ããé«ãè©äŸ¡ãããããäºæž¬ããŸãã
ããã¯ãæ¬¡ã®æå€±é¢æ°ã§è¡šçŸã§ããŸãã

ãStackExchangeãããŒã¿ã»ããã䜿çšãããšãã¹ã³ã¢ãããšã«2ã€ã®åçã®ãã¡ã©ã¡ãããŠãŒã¶ãŒã«å¥œãŸããããæšæž¬ã§ããŸãããã®æ å ±ãšäžã§å®çŸ©ããæå€±ã䜿çšããŠãã«ã¹ã¿ã æå€±é¢æ°ã远å ããŠãtransformers.Trainerãã倿Žã§ããŸãã
class RewardTrainer(Trainer):
def compute_loss(self, model, inputs, return_outputs=False):
rewards_j = model(input_ids=inputs["input_ids_j"], attention_mask=inputs["attention_mask_j"])[0]
rewards_k = model(input_ids=inputs["input_ids_k"], attention_mask=inputs["attention_mask_k"])[0]
loss = -nn.functional.logsigmoid(rewards_j - rewards_k).mean()
if return_outputs:
return loss, {"rewards_j": rewards_j, "rewards_k": rewards_k}
return loss100,000çµã®åè£ã®ãµãã»ãããå©çšããä¿æããã50,000çµã®åè£ã§è©äŸ¡ããŸãã é©åºŠãªããããµã€ãº4 ã§ãBF16 粟床ã®Adamãªããã£ãã€ã¶ã䜿çšããŠåäžãšããã¯ã«å¯ŸããŠLoRA PEFTã¢ããã¿ã䜿çšã㊠LLaMA ã¢ãã«ãåŠç¿ããŸãã
LoRA ã®æ§æã¯æ¬¡ã®ãšããã§ãã
peft_config = LoraConfig(
task_type=TaskType.SEQ_CLS,
inference_mode=False,
r=8,
lora_alpha=32,
lora_dropout=0.1,
)åŠç¿ã¯8-A100 GPU ã§æ°æéããããã¢ãã«ã¯æçµç²ŸåºŠ67%ãéæããŸãããããã¯ã¹ã³ã¢ãäœãããã«æããŸããããã®ã¿ã¹ã¯ã¯äººéã«ãšã£ãŠãéåžžã«é£ãããã®ã«ãªããŸãã
6. 人éã®ãã£ãŒãããã¯ããã®åŒ·ååŠç¿
ãRLHFãã®ã¹ãããã¯ã次ã®ãšããã§ãã
(1) ããã³ããããå¿çãçæã
(2) å ±é ¬ã¢ãã«ã䜿çšããŠå¿çãè©äŸ¡ã
(3) è©äŸ¡ã䜿çšããŠåŒ·ååŠç¿ããªã·ãŒæé©åã¹ããããå®è¡ã

ã¯ãšãªãšå¿çã®ããã³ããã¯ãããŒã¯ã³åãããŠã¢ãã«ã«æž¡ãããåã«ã次ã®ããã«ãã³ãã¬ãŒãåãããŸãã
Question: <Query>
Answer: <Response>ãSFTããRMããRLHFãã«ã¯åããã³ãã¬ãŒãã䜿çšããŠããŸãã
匷ååŠç¿ã䜿çšããŠèšèªã¢ãã«ãåŠç¿ããå Žåã®äžè¬çãªåé¡ã¯ãã¢ãã«ãæå³äžæãªå 容ãçæããããšã§é«ãå ±é ¬ãåŸãæ¹æ³ãåŠç¿ããŠããŸãããšã§ãããã®å¯Ÿçã®ãããå ±é ¬ã«ããã«ãã£ã远å ããŸãã
åŠç¿ããŠããªãã¢ãã«ã®åç §ãä¿æãããKL-divergenceããèšç®ããããšã§æ°ããã¢ãã«ã®çæãåç §ã¢ãã«ãšæ¯èŒããŸãã

ããã§ãr ã¯å ±é ¬ã¢ãã«ããã®å ±é ¬ãKL(x,y) ã¯çŸåšã®ããªã·ãŒãšåç §ã¢ãã«éã®ãKL-divergenceãã§ãã
ããäžåºŠãèšæ¶å¹çã®é«ãåŠç¿PEFTãå©çšããŸããããã¯ãRLHF ã®ã³ã³ããã¹ãã§ãããªãå©ç¹ãæäŸããŸããããã§ãåç §ã¢ãã«ãšããªã·ãŒã¯åãããŒã¹ã§ããSFTã¢ãã«ãå ±æããŠãããããã8bitã§ããŒãããåŠç¿äžã«ããªãŒãºããŸãã åºæ¬ã¢ãã«ã®éã¿ãå ±æããªãããPPOã䜿çšããŠããªã·ãŒã® LoRA éã¿ãæä»çã«æé©åããŸãã
for epoch, batch in tqdm(enumerate(ppo_trainer.dataloader)):
question_tensors = batch["input_ids"]
# ããªã·ãŒãããµã³ããªã³ã°ããŠå¿çãçæ
response_tensors = ppo_trainer.generate(
question_tensors,
return_prompt=False,
length_sampler=output_length_sampler,
**generation_kwargs,
)
batch["response"] = tokenizer.batch_decode(response_tensors, skip_special_tokens=True)
# ææ
ã¹ã³ã¢ãèšç®
texts = [q + r for q, r in zip(batch["query"], batch["response"])]
pipe_outputs = sentiment_pipe(texts, **sent_kwargs)
rewards = [torch.tensor(output[0]["score"] - script_args.reward_baseline) for output in pipe_outputs]
# PPOã¹ãããã®å®è¡
stats = ppo_trainer.step(question_tensors, response_tensors, rewards)
# çµ±èšã W&B ã«èšé²
ppo_trainer.log_stats(stats, batch, rewards)3x8 A100-80GB GPUã§20 æéåŠç¿ããŸããã

ã¢ãã«ã®æ§èœã¯1000ã¹ãããçšåºŠã§é æã¡ã«ãªããŸãã
åŠç¿åŸã®ã¢ãã«ã¯ã©ããªããšãã§ããã®ãèŠãŠã¿ãŸãã

ã¢ããã€ã¹ã¯ãŸã ä¿¡çšã§ããŸãããããã®åçã¯éŠå°Ÿäžè²«ããŠãããGoogleãžã®ãªã³ã¯ãçšæãããŠããããšãããããŸãã
7. 課é¡
ãLLMãã®åŒ·ååŠç¿ã¯ãåžžã«é 颚æºåžãšããããã§ã¯ãããŸãããä»å玹ä»ããã¢ãã«ã¯ãå€ãã®å®éšã倱æããã€ããŒãã©ã¡ãŒã¿èª¿æŽã®çµæã§ããããã§ãããã®ã¢ãã«ã¯å®ç§ãšã¯èšãé£ãã§ãã
以äžã§ã¯ããã®ã¢ãã«ãäœãéçšã§ééãã芳å¯çµæãé çã®çš®ãããã€ã玹ä»ããŸãã
7-1. å ±é ¬ãé«ããšããããšã¯ãããã©ãŒãã³ã¹ãé«ããšããããšïŒ

匷ååŠç¿ã§ã¯ãã§ããã ãé«ãå ±é ¬ãåŸãããšèããŸãããRLHFãã§ã¯ãå ±é ¬ã¢ãã«ãã䜿çšããŠããŸãããããã¯äžå®å šãªãã®ã§ããã£ã³ã¹ãããã°PPOã¯ãã®äžå®å šããå©çšããããšããŸããããã¯ãå ±é ¬ã®æ¥æ¿ãªå¢å ãšããŠçŸããŸããçæãããããã¹ããèŠããšãã»ãšãã©ã ``` ãšããæååã®ç¹°ãè¿ãã®å ŽåããããŸãããã³ãŒããå«ãåçã¯ããªããã®ãããéåžžé«ãã©ã³ã¯ãããããšãããå ±é ¬ã¢ãã«ããçºèŠããã®ã§ãã幞ããªããšã«ããã®çš®ã®åé¡ã¯ããŸã芳å¯ããããKLããã«ãã£ã®å¯Ÿçãå¹ããŠããããã§ãã
7-2. KLã¯åžžã«ãã©ã¹ã®å€ïŒ
åè¿°ããããã«ãã¢ãã«ã®åºåãåºæ¬æ¹éã®åºåã«è¿ã¥ããããã«ãKLããã«ãã£ã䜿çšãããŸããäžè¬ã«ããKL-divergenceãã¯2ã€ã®ååžéã®è·é¢ã枬å®ããåžžã«æ£ã§ãããããããtrlãã§ã¯KLã®æšå®å€ã䜿çšããæåŸ å€ãšããŠå®éã®ãKL-divergenceããšçãããªããŸãã

æããã«ãããŒã¯ã³ããSFTãã¢ãã«ãããäœã確çã§ããªã·ãŒãããµã³ããªã³ã°ãããå Žåãããã¯è² ã®KLããã«ãã£ã«ã€ãªãããŸãããå¹³åçã«ã¯æ£ã®å€ã«ãªããŸããããããçææŠç¥ã«ãã£ãŠã¯ãäžéšã®ããŒã¯ã³ã匷å¶çã«çæãããããäžéšã®ããŒã¯ã³ãæå¶ãããããããšãã§ããŸããäŸãã°ããããã§çæããå Žåã宿ããã·ãŒã±ã³ã¹ã¯ããã£ã³ã°ãããæå°é·ãèšå®ããå ŽåãEOSããŒã¯ã³ã¯æå¶ãããŸããã¢ãã«ã¯ããããã®ããŒã¯ã³ã«éåžžã«é«ã確çãäœã確çãå²ãåœãŠãããšãã§ãããããè² ã®KLã«ã€ãªãããPPOã¢ã«ãŽãªãºã ã¯å ±é ¬ãæé©åããããããããã®è² ã®ããã«ãã£ãè¿œãæ±ããããšã«ãªããäžå®å®ã«ãªããŸãã

7-3. ç¶ç¶çãªèª²é¡
çŸåšããããæ·±ãçè§£ãã解決ããŠãããªããã°ãªããªãåé¡ãæ°å€ãååšããŸããäŸãã°ãæå€±ãæ¥å¢ããããšããããããããããªãäžå®å®ããåŒãèµ·ããå¯èœæ§ããããŸãã
