首页 > 教程攻略 > ai资讯 >使用 ORPO 微调 Llama 3

使用 ORPO 微调 Llama 3

来源:互联网 时间:2026-08-03 13:45:39

ORPO算是近期微调领域一个让人眼前一亮的方案——它把监督式微调和偏好对齐这两个原本分开的阶段,直接揉进了同一个训练过程。这带来的最直接好处就是:训练所需的计算量和时间都明显降下来了。更关键的是,从实际跑下来的结果看,不管是在不同参数规模的模型上,还是各种评测基准里,ORPO的表现都优于其他对齐方法。

下面,我们就通过一个具体案例来演示如何用TRL库和ORPO对最新的Llama 3 8B模型进行微调。配套的代码已经放在了Google Colab和GitHub上(LLM课程中都能找到)。

ORPO 到底怎么工作的?

指令微调和偏好对齐,一直是让大语言模型适配具体任务的两大基本功。传统做法通常分两步走:先通过

监督式微调(SFT)

让模型在目标领域里站稳脚跟,然后再用

偏好对齐方法

(比如RLHF或DPO)来引导模型,让它更倾向于输出用户喜欢的答案,而不是那些被拒掉的回答。

但研究人员后来发现这套流程有个麻烦的地方:SFT虽然能把模型往目标领域里带,可它同时也悄悄

提高了模型生成那些糟糕答案的概率

——好的坏的一起涨。所以后面非得加一个偏好对齐阶段,才能把好答案和坏答案之间的可能性差距重新拉开。

ORPO(由Hong和Lee在2024年提出)提供了一种很巧妙的解决办法:它把指令调整和偏好对齐整合成了一个端到端的训练流程。具体来说,ORPO在标准的语言建模目标上做了改动,把负对数似然损失和一个叫“优势比”(Odds Ratio, OR)的惩罚项结合在了一起。这个OR损失会对那些被拒绝的回答施加轻微的惩罚,同时对偏好的回答给予更强的奖励。这样一来,模型在学会完成任务的同时,也自然而然地跟上了人类的偏好。

目前,ORPO已经落地到了几个主流微调库上,比如TRL、Axolotl和LLaMA-Factory,开发者直接拿来用就行。

用 ORPO 微调 Llama 3 的实操

Llama 3是Meta最新推出的LLM系列,训练数据量是

15万亿个token

(对比之下,Llama 2只有2万亿)。这次发布了两个规格:700亿参数的版本和80亿参数的版本。70B版在MMLU上拿下了82分,HumanEval上也有81.7,成绩相当亮眼。

上下文长度也提升到了8,192个token(Llama 2是4,096),而且通过RoPE还有望扩展到32K。另外,这次还换了一个新的分词器,词表大小到了128K token,同样一段文本编码后token数量能减少15%。这也解释了为什么参数从7B涨到了8B。

准备数据集

ORPO需要一份偏好数据集,里面包含三样东西:一条提示(prompt)、一个被选中的好答案(chosen)、还有一个被拒绝的答案(rejected)。这次我们用 mlabonne/orpo-dpo-mix-40k,它是下面这几个高质量DPO数据集的混合物:

  • argilla/distilabel-capybara-dpo-7k-binarized:高分答案(≥5)摘了2,882条
  • argilla/distilabel-intel-orca-dpo-pairs:高分答案(≥9)且不在GSM8K范围内的,凑了2,299条
  • argilla/ultrafeedback-binarized-preferences-cleaned:高分答案(≥5),22,799条
  • argilla/distilabel-math-preference-dpo:高分答案(≥9),2,181条
  • unalignment/toxic-dpo-v0.2(541条)
  • M4-ai/prm_dpo_pairs_cleaned(7,958条)
  • jondurbin/truthy-dpo-v0.1(1,016条)

这里要感谢 argilla、unalignment、M4-ai 和 jondurbin 贡献了这些源数据。

安装与环境配置

先把需要的库都装上:

pip install -U transformers datasets accelerate peft trl bitsandbytes wandb

接着导入库并登录W&B(这一步可选):

import gc
import os

import torch
import wandb
from datasets import load_dataset
from google.colab import userdata
from peft import LoraConfig, PeftModel, prepare_model_for_kbit_training
from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    BitsAndBytesConfig,
    TrainingArguments,
    pipeline,
)
from trl import ORPOConfig, ORPOTrainer, setup_chat_format

wb_token = userdata.get('wandb')
wandb.login(key=wb_token)

如果你的GPU支持,还可以开Flash Attention,能显著提速:

if torch.cuda.get_device_capability()[0] >= 8:
    !pip install -qqq flash-attn
    attn_implementation = "flash_attention_2"
    torch_dtype = torch.bfloat16
else:
    attn_implementation = "eager"
    torch_dtype = torch.float16

加载模型并配置PEFT

接下来,用bitsandbytes先把Llama 3 8B以4位精度加载进来,然后用PEFT配一套LoRA参数。这里用 setup_chat_format() 来顺手调整模型和分词器,让它支持ChatML——这个函数会自动套用聊天模板、添加特殊标记,同时把模型的嵌入层大小调到和新词表一致。

有一点要注意:访问 meta-llama/Meta-Llama-3-8B 需要提前向Meta提交申请,并在Hugging Face上登录。如果嫌麻烦,也可以换成非门控的版本,比如 NousResearch/Meta-Llama-3-8B

# Model
base_model = "meta-llama/Meta-Llama-3-8B"
new_model = "OrpoLlama-3-8B"

# QLoRA config
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch_dtype,
    bnb_4bit_use_double_quant=True,
)

# LoRA config
peft_config = LoraConfig(
    r=16,
    lora_alpha=32,
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
    target_modules=['up_proj', 'down_proj', 'gate_proj', 'k_proj', 'q_proj', 'v_proj', 'o_proj']
)

# Load tokenizer
tokenizer = AutoTokenizer.from_pretrained(base_model)

# Load model
model = AutoModelForCausalLM.from_pretrained(
    base_model,
    quantization_config=bnb_config,
    device_map="auto",
    attn_implementation=attn_implementation
)
model, tokenizer = setup_chat_format(model, tokenizer)
model = prepare_model_for_kbit_training(model)

处理数据集

模型就位后,开始处理数据。加载 mlabonne/orpo-dpo-mix-40k,然后调用 apply_chat_template() 把“chosen”和“rejected”两列转成ChatML格式。注意,为了演示方便,这里只取了前1,000条——跑完整的数据集时间会长很多。

dataset_name = "mlabonne/orpo-dpo-mix-40k"
dataset = load_dataset(dataset_name, split="all")
dataset = dataset.shuffle(seed=42).select(range(100))

def format_chat_template(row):
    row["chosen"] = tokenizer.apply_chat_template(row["chosen"], tokenize=False)
    row["rejected"] = tokenizer.apply_chat_template(row["rejected"], tokenize=False)
    return row

dataset = dataset.map(
    format_chat_template,
    num_proc= os.cpu_count(),
)
dataset = dataset.train_test_split(test_size=0.01)

训练配置与超参数

训练前,有几个关键超参数得特别注意:

  • learning_rate

    :相比传统的SFT甚至DPO,ORPO用的学习率要低得多。这里的8e-6来自原始论文,大致对应SFT的1e-5和DPO的5e-6。如果做真正的全量微调,建议提到1e-6左右试试。
  • beta

    :对应论文中的 \(\lambda\),默认值0.1。原始论文的附录有消融实验,展示了不同beta值的影响。

其他参数比如max_length和batch size,都按尽量吃满显存来设(这套配置大概用20 GB)。理想情况下,模型应该训练3-5个epoch,这里只跑1个。

orpo_args = ORPOConfig(
    learning_rate=8e-6,
    beta=0.1,
    lr_scheduler_type="linear",
    max_length=1024,
    max_prompt_length=512,
    per_device_train_batch_size=2,
    per_device_eval_batch_size=2,
    gradient_accumulation_steps=4,
    optim="paged_adamw_8bit",
    num_train_epochs=1,
    evaluation_strategy="steps",
    eval_steps=0.2,
    logging_steps=1,
    warmup_steps=10,
    report_to="wandb",
    output_dir="./results/",
)

trainer = ORPOTrainer(
    model=model,
    args=orpo_args,
    train_dataset=dataset["train"],
    eval_dataset=dataset["test"],
    peft_config=peft_config,
    tokenizer=tokenizer,
)
trainer.train()
trainer.sa ve_model(new_model)

在L4 GPU上,训练这1,000条样本大约需要2小时。看看W&B上的曲线:

虽然损失是在下降的,但chosen和rejected之间的差距还不够明显——平均边际和准确率都只比零和0.5高一点点。原始论文里,作者在Anthropic/hh-rlhf数据集(161k条)上跑了10个epoch,比我们这轮演示长得多。他们也用Llama 3做过实验,日志数据已经公开发布了。

最后一步,把QLoRA适配器和基础模型合并,然后推到Hugging Face Hub:

# Flush memory
del trainer, model
gc.collect()
torch.cuda.empty_cache()

# Reload tokenizer and model
tokenizer = AutoTokenizer.from_pretrained(base_model)
model = AutoModelForCausalLM.from_pretrained(
    base_model,
    low_cpu_mem_usage=True,
    return_dict=True,
    torch_dtype=torch.float16,
    device_map="auto",
)
model, tokenizer = setup_chat_format(model, tokenizer)

# Merge adapter with base model
model = PeftModel.from_pretrained(model, new_model)
model = model.merge_and_unload()

model.push_to_hub(new_model, use_temp_dir=False)
tokenizer.push_to_hub(new_model, use_temp_dir=False)

恭喜,到这里我们就完成了对Llama 3的一次快速微调,产出的模型是 mlabonne/OrpoLlama-3-8B。你可以在Hugging Face Space上直接体验它。

虽然这次训练不够充分(从W&B曲线也能看出来),但用LLM AutoEval在Nous的基准套件上跑了一下,结果还挺让人意外的:

ORPO微调后的模型在每一个基准上都有提升,超过了基础版本。这很鼓舞人心,也意味着如果对整个40k数据集做完整微调,效果应该会更可观。

对开源社区来说,现在确实是个好时候。越来越多高质量的开源权重模型在发布,闭源和开源之间的差距在慢慢缩小。而微调,正是让模型在自己场景里发挥最大价值的关键工具。

总结

这篇内容我们聊了ORPO是怎么把SFT和偏好对齐合成一步的,然后通过TRL在自定义数据集上对Llama 3 8B做了一次实战微调。最终模型的结果让人眼前一亮,也展示了ORPO作为微调新范式的潜力。

文章到这里就差不多了,建议你亲自跑一下Colab上的笔记本。下次我们会专门聊聊怎么构造高质量的数据集——这件事其实比模型微调本身还容易被忽略。