В этом руководстве мы реализуем сквозной рабочий процесс прямой оптимизации предпочтений (Direct Preference Optimization, DPO), чтобы настроить большую языковую модель в соответствии с человеческими предпочтениями без использования модели вознаграждения.
Этапы работы
1. Настройка среды выполнения и установка всех необходимых библиотек для DPO, PEFT и квантованного обучения.
2. Определение всех глобальных гиперпараметров, ограничений набора данных и настроек оптимизации в одном месте.
3. Инициализация генератора случайных чисел и подтверждение доступности GPU для обеспечения воспроизводимости запусков.
Код для настройки среды
«`python
import os
import math
import random
import torch
!pip -q install -U «transformers>=4.45.0» «datasets>=2.19.0» «accelerate>=0.33.0» «trl>=0.27.0» «peft>=0.12.0» «bitsandbytes>=0.43.0» «sentencepiece» «evaluate»
SEED = 42
random.seed(SEED)
torch.manual_seed(SEED)
torch.cuda.manualseedall(SEED)
MODELNAME = os.environ.get(«MODELNAME», «Qwen/Qwen2-0.5B-Instruct»)
DATASETNAME = «HuggingFaceH4/ultrafeedbackbinarized»
OUTPUTDIR = «dpoultrafeedback_qlora»
MAXTRAINSAMPLES = 8000
MAXEVALSAMPLES = 200
MAXPROMPTLEN = 512
MAXCOMPLETIONLEN = 256
BETA = 0.1
LR = 2e-4
EPOCHS = 1
PERDEVICEBS = 2
GRAD_ACCUM = 8
LOGGING_STEPS = 10
SAVE_STEPS = 200
device = «cuda» if torch.cuda.is_available() else «cpu»
print(«Device:», device, «GPU:», torch.cuda.getdevicename(0) if device == «cuda» else «None»)
«`
Загрузка токенизатора и базовой языковой модели
«`python
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(
loadin4bit=True,
bnb4bitquant_type=»nf4″,
bnb4bitusedoublequant=True,
bnb4bitcomputedtype=torch.bfloat16 if torch.cuda.isavailable() and torch.cuda.getdevicecapability(0)[0] >= 8 else torch.float16,
)
tokenizer = AutoTokenizer.frompretrained(MODELNAME, use_fast=True)
if tokenizer.pad_token is None:
tokenizer.padtoken = tokenizer.eostoken
model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME,
quantizationconfig=bnbconfig,
torchdtype=torch.bfloat16 if torch.cuda.isavailable() and torch.cuda.getdevicecapability(0)[0] >= 8 else torch.float16,
device_map=»auto»,
)
model.config.use_cache = False
«`
Настройка LoRA-адаптеров
«`python
from peft import LoraConfig, getpeftmodel
lora_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
bias=»none»,
tasktype=»CAUSALLM»,
targetmodules=[«qproj», «kproj», «vproj», «oproj», «upproj», «downproj», «gateproj»],
)
model = getpeftmodel(model, lora_config)
model.printtrainableparameters()
model.gradientcheckpointingenable()
«`
Загрузка набора данных
«`python
from datasets import load_dataset
ds = loaddataset(DATASETNAME)
trainsplit = «trainprefs» if «train_prefs» in ds else («train» if «train» in ds else list(ds.keys())[0])
testsplit = «testprefs» if «test_prefs» in ds else («test» if «test» in ds else None)
trainraw = ds[trainsplit]
testraw = ds[testsplit] if test_split is not None else None
print(«Splits:», ds.keys())
print(«Using train split:», trainsplit, «size:», len(trainraw))
if test_raw is not None:
print(«Using test split:», testsplit, «size:», len(testraw))
«`
Настройка формата примеров
«`python
def extractlastuserand_assistant(messages):
lastuseridx = None
lastasstidx = None
for i, m in enumerate(messages):
if m.get(«role») == «user»:
lastuseridx = i
if m.get(«role») == «assistant»:
lastasstidx = i
if lastuseridx is None or lastasstidx is None:
return None, None
promptmessages = messages[: lastuser_idx + 1]
assistanttext = messages[lastasst_idx].get(«content», «»)
return promptmessages, assistanttext
def format_example(ex):
chosen_msgs = ex[«chosen»]
rejected_msgs = ex[«rejected»]
promptmsgsc, chosentext = extractlastuserandassistant(chosen_msgs)
promptmsgsr, rejectedtext = extractlastuserandassistant(rejected_msgs)
if promptmsgsc is None or promptmsgsr is None:
return {«prompt»: None, «chosen»: None, «rejected»: None}
prompttext = tokenizer.applychat_template(
promptmsgsc, tokenize=False, addgenerationprompt=True
)
return {
«prompt»: prompt_text,
«chosen»: chosen_text.strip(),
«rejected»: rejected_text.strip(),
}
«`
Обучение модели
«`python
from trl import DPOTrainer, DPOConfig
usebf16 = torch.cuda.isavailable() and torch.cuda.getdevicecapability(0)[0] >= 8
usefp16 = torch.cuda.isavailable() and not use_bf16
training_args = DPOConfig(
outputdir=OUTPUTDIR,
beta=BETA,
perdevicetrainbatchsize=PERDEVICEBS,
gradientaccumulationsteps=GRAD_ACCUM,
numtrainepochs=EPOCHS,
learning_rate=LR,
lrschedulertype=»cosine»,
warmup_ratio=0.05,
loggingsteps=LOGGINGSTEPS,
savesteps=SAVESTEPS,
savetotallimit=2,
bf16=use_bf16,
fp16=use_fp16,
optim=»pagedadamw8bit»,
maxlength=MAXPROMPTLEN + MAXCOMPLETION_LEN,
maxpromptlength=MAXPROMPTLEN,
report_to=»none»,
)
trainer = DPOTrainer(
model=model,
args=training_args,
processing_class=tokenizer,
traindataset=trainds,
evaldataset=evalds,
)
trainer.train()
trainer.savemodel(OUTPUTDIR)
tokenizer.savepretrained(OUTPUTDIR)
print(«Saved to:», OUTPUT_DIR)
«`
Генерация текста
«`python
from peft import PeftModel
from transformers import pipeline
def generatetext(modelforgen, prompt, maxnew_tokens=180):
modelforgen.eval()
inputs = tokenizer(prompt, returntensors=»pt», truncation=True, maxlength=MAXPROMPTLEN).to(modelforgen.device)
with torch.no_grad():
out = modelforgen.generate(
inputs,
maxnewtokens=maxnewtokens,
do_sample=True,
temperature=0.7,
top_p=0.95,
padtokenid=tokenizer.eostokenid,
)
return tokenizer.decode(out[0], skipspecialtokens=True)
basemodel = AutoModelForCausalLM.frompretrained(
MODEL_NAME,
quantizationconfig=bnbconfig,
torchdtype=torch.bfloat16 if usebf16 else torch.float16,
device_map=»auto»,
)
basemodel.config.usecache = True
dpomodel = PeftModel.frompretrained(basemodel, OUTPUTDIR)
dpomodel.config.usecache = True
samplepool = evalds if evalds is not None and len(evalds) > 0 else train_ds
samples = [samplepool[i] for i in random.sample(range(len(samplepool)), k=min(3, len(sample_pool)))]
for i, ex in enumerate(samples, 1):
prompt = ex[«prompt»]
print(«\n» + «=»*90)
print(f»Sample #{i}»)
print(«- Prompt:\n», prompt)
baseout = generatetext(base_model, prompt)
dpoout = generatetext(dpo_model, prompt)
print(«\n- Base model output:\n», base_out)
print(«\n- DPO (LoRA) output:\n», dpo_out)
print(«\nDone.»)
«`
В этом руководстве мы продемонстрировали, как DPO обеспечивает стабильную и эффективную альтернативу RLHF, напрямую оптимизируя пары предпочтений с простой, чётко определённой целью. Мы показали, что параметрически эффективная настройка с помощью LoRA и 4-битного квантования позволяет проводить практические эксперименты даже при ограниченных вычислительных ресурсах. Мы качественно подтвердили согласование, сравнив генерации до и после обучения DPO, подтвердив, что модель учится предпочитать более качественные ответы, оставаясь при этом лёгкой и развёртываемой.
1. Какие этапы включает в себя процесс настройки большой языковой модели в соответствии с человеческими предпочтениями с помощью прямой оптимизации предпочтений (DPO)?
В статье описан процесс настройки большой языковой модели с помощью DPO, который включает в себя:
* настройку среды выполнения и установку всех необходимых библиотек для DPO, PEFT и квантованного обучения;
* определение всех глобальных гиперпараметров, ограничений набора данных и настроек оптимизации в одном месте;
* инициализацию генератора случайных чисел и подтверждение доступности GPU для обеспечения воспроизводимости запусков;
* загрузку токенизатора и базовой языковой модели;
* настройку LoRA-адаптеров;
* загрузку набора данных;
* настройку формата примеров;
* обучение модели;
* генерацию текста.
2. Какие библиотеки и инструменты используются для настройки среды и обучения модели в этом руководстве?
В этом руководстве используются следующие библиотеки и инструменты:
* `transformers` для работы с языковыми моделями;
* `datasets` для загрузки и обработки наборов данных;
* `accelerate` для ускорения обучения;
* `trl` для реализации DPO;
* `peft` для настройки LoRA-адаптеров;
* `bitsandbytes` для квантования моделей;
* `sentencepiece` для токенизации текста;
* `evaluate` для оценки моделей.
3. Какие параметры и настройки оптимизации используются в этом руководстве для обучения модели?
В этом руководстве используются следующие параметры и настройки оптимизации:
* `BETA` — параметр, который определяет баланс между оптимизацией предпочтений и сохранением качества модели;
* `LR` — скорость обучения;
* `EPOCHS` — количество эпох обучения;
* `PERDEVICEBS` — размер пакета для обучения на одном устройстве;
* `GRAD_ACCUM` — количество шагов накопления градиента;
* `LOGGING_STEPS` — количество шагов между логированием прогресса обучения;
* `SAVE_STEPS` — количество шагов между сохранением модели;
* `usebf16` и `usefp16` — параметры, которые определяют использование 16-битной или 32-битной точности вычислений;
* `optim` — оптимизатор для обучения модели.