機(jī)器學(xué)習(xí)|從0開(kāi)始大模型之模型DPO訓(xùn)練
1、為什么需要DPO
Rafailov等人在2023年發(fā)表了一篇論文《Direct Preference Optimization: Your Language Model is Secretly a Reward Model》,該論文提出了一種新的訓(xùn)練方法,稱為直接偏好優(yōu)化(DPO),該論文介紹:
雖然大規(guī)模無(wú)監(jiān)督語(yǔ)言模型 (LM) 可以學(xué)習(xí)廣泛的世界知識(shí)和一些推理技能,但由于其訓(xùn)練完全無(wú)監(jiān)督,因此很難精確控制其行為。
現(xiàn)有的獲得這種可控性的方法是收集模型生成相對(duì)質(zhì)量的人類標(biāo)簽,并微調(diào)無(wú)監(jiān)督語(yǔ)言模型以符合這些偏好,通常使用從人類反饋中進(jìn)行強(qiáng)化學(xué)習(xí) (RLHF)。
然而,RLHF 是一個(gè)復(fù)雜且通常不穩(wěn)定的過(guò)程,首先要擬合一個(gè)反映人類偏好的獎(jiǎng)勵(lì)模型,然后使用強(qiáng)化學(xué)習(xí)微調(diào)大型無(wú)監(jiān)督語(yǔ)言模型以最大化這個(gè)估計(jì)的獎(jiǎng)勵(lì),而不會(huì)偏離原始模型太遠(yuǎn)。
在該論文中,利用獎(jiǎng)勵(lì)函數(shù)和最優(yōu)策略之間的映射來(lái)表明,這個(gè)受約束的獎(jiǎng)勵(lì)最大化問(wèn)題可以通過(guò)一個(gè)階段的策略訓(xùn)練進(jìn)行精確優(yōu)化,本質(zhì)上是解決人類偏好數(shù)據(jù)的分類問(wèn)題。
由此產(chǎn)生的算法,稱之為直接偏好優(yōu)化 (DPO),穩(wěn)定、高效且計(jì)算量小,無(wú)需擬合獎(jiǎng)勵(lì)模型、在微調(diào)期間從 LM 中采樣或執(zhí)行重大超參數(shù)調(diào)整。
由此可見(jiàn),DPO 主要解決RLHF不穩(wěn)定的問(wèn)題,直接使用人類偏好數(shù)據(jù)訓(xùn)練模型。
2、DPO的訓(xùn)練原理
DPO 的訓(xùn)練原理如下圖所示(出自原論文):
DPO
主要包括兩個(gè)步驟:
- 數(shù)據(jù)收集:收集一個(gè)偏好數(shù)據(jù)集,其中包含給定提示的生成結(jié)果的正負(fù)選擇對(duì);
- 優(yōu)化:直接最大化DPO 損失的對(duì)數(shù)似然函數(shù),該損失函數(shù)是偏好數(shù)據(jù)集上的交叉熵?fù)p失和模型生成結(jié)果的對(duì)數(shù)似然性之間的加權(quán)平均值;
具體公式推導(dǎo)可以參考這篇博客:https://www.cnblogs.com/lemonzhang/p/17910358.html。
3、DPO的代碼實(shí)現(xiàn)
3.1 收集數(shù)據(jù)
DPO 訓(xùn)練器對(duì)數(shù)據(jù)集的格式有具體的要求,包括三個(gè)部分:
- 提示(prompt):提示的格式為:prompt: 文本;
- 選中(chosen):選中文本的格式為:chosen: 文本;
- 拒絕(rejected):拒絕選中文本的格式為:rejected: 文本;
- 示例:
{
"id": 0,
"prompt": "最近,馬其頓流行歌手托瑟·普羅埃斯基在一場(chǎng)巡回展覽中被紀(jì)念。",
"chosen": "\"最近,馬其頓流行歌手托斯·普羅埃斯基在一場(chǎng)展覽中被人們銘記。\" 讓我?guī)湍憷斫?。這意味著他們舉辦了一場(chǎng)特別的展示(我們稱之為展覽),以幫助人們記住馬其頓這個(gè)地方的歌手托斯·普羅埃斯基有多特別。這有點(diǎn)像翻看你的相冊(cè)來(lái)回憶快樂(lè)時(shí)光,但規(guī)模更大,更專注于他成名后的工作和生活。",
"rejected": "哦!你想知道這句話的意思嗎?好的,讓我告訴你!??\n\n所以,“Son d?nem”在英語(yǔ)中意思是“Last period”。而“Makedon”在英語(yǔ)中是“Macedonian”。而“pop ?ark?c?s?”在英語(yǔ)中意思是“pop singer”!??\n\n所以,句子“Son d?nem Makedon pop ?ark?c?s? Tose Proeski gezer sergide an?l?yor”的意思是“Last period Macedonian pop singer Tose Proeski正在舞臺(tái)上表演?!??\n\n希望這有幫助,小伙伴!??"
}
DPO的數(shù)據(jù)可以搜索huggingface的DPO數(shù)據(jù)集,地址為:https://huggingface.co/datasets?sort=trending&search=dpo 。
比如 https://huggingface.co/datasets/Anthropic/hh-rlhf 的數(shù)據(jù)集如下:
hh-rlhf
3.2 TRL
引入 TRL 庫(kù),支持 DPO 訓(xùn)練器,訓(xùn)練樣例代碼:
training_args = DPOConfig(
beta=0.1,
)
dpo_trainer = DPOTrainer(
model,
ref_model,
args=training_args,
train_dataset=train_dataset,
tokenizer=tokenizer, # for visual language models, use tokenizer=processor instead
)
dpo_trainer.train()
dpo_trainer.save_model()
如上訓(xùn)練默認(rèn)是保存 safetensors? 格式的模型,如果想保存 pytorch 格式的模型, 可以改為如下代碼:
training_args = DPOConfig(
beta=0.1,
save_safetensors=False, // 設(shè)置為False,改為保存為pytorch格式的模型
)
dpo_trainer = DPOTrainer(
model,
ref_model,
args=training_args,
train_dataset=train_dataset,
tokenizer=tokenizer, # for visual language models, use tokenizer=processor instead
)
dpo_trainer.train()
dpo_trainer.save_model(
output_dir=f"./out/dpo_sft_xxx.pth"
)
3.3 訓(xùn)練
Transformer?的代碼和前面的一樣,可以參考預(yù)訓(xùn)練的代碼,如下就是初始化模型和 DPO 訓(xùn)練的代碼:
def init_model():
from transformers import AutoTokenizer, AutoModelForCausalLM, AutoConfig
AutoConfig.register(MyPretrainConfig.model_type, MyPretrainConfig)
AutoModelForCausalLM.register(MyPretrainConfig, Transformer)
my_tokenizer = "./my_tokenizer"
tokenizer = AutoTokenizer.from_pretrained(my_tokenizer, trust_remote_code=True, use_fast=False)
ckp = f'./out/full_sft_{lm_config.dim}.pth.{batch_size}'
print(f"lmconfigs: {lm_config.to_json_string()}")
with open(ckp_path + "/config.json", 'w') as f:
f.write(lm_config.to_json_string())
# 拷貝文件到指定的目錄
for item in os.listdir(my_tokenizer):
src_item = os.path.join(my_tokenizer, item)
if os.path.isfile(src_item):
dest_item = os.path.join(ckp_path, item)
shutil.copy2(src_item, dest_item)
shutil.copy2(ckp, ckp_path + "/pytorch_model.bin")
model = AutoModelForCausalLM.from_pretrained(ckp_path, trust_remote_code=True).to(device)
def count_parameters(model):
return sum(p.numel() for p in model.parameters() if p.requires_grad)
tokenizer.pad_token = tokenizer.eos_token
print(f'LLM總參數(shù)量:{count_parameters(model) / 1e6:.3f} 百萬(wàn)')
model = model.to(device)
return model, tokenizer
if __name__ == '__main__':
lm_config = MyPretrainConfig()
max_seq_len = lm_config.max_seq_len
out_dir = 'out'
epochs = 20 # 訓(xùn)練輪數(shù)
batch_size = 8 # batch_size
learning_rate = 1e-5 # 學(xué)習(xí)率
device = 'cuda:0' # or cpu
dtype = 'bfloat16'
ckp_path = f'./my_checkpoint'
if not os.path.exists(ckp_path):
os.makedirs(ckp_path)
model, tokenizer = init_model()
training_config = DPOConfig(
output_dir=ckp_path,
per_device_train_batch_size=1,
remove_unused_columns=False,
report_to="none",
save_steps=2000,
learning_rate=learning_rate,
save_safetensors=False,
)
# 下載訓(xùn)練圖片:https://huggingface.co/datasets/jingyaogong/minimind_dataset/tree/main/dpo
dataset_path = f'{basepath}/dpo_train_data.json'
train_dataset = load_dataset('json', data_files=dataset_path)
dpo_trainer = DPOTrainer(
model,
ref_model=None,
args=training_config,
beta=0.1,
train_dataset=train_dataset['train'],
tokenizer=tokenizer,
max_length=512,
max_prompt_length=512
)
dpo_trainer.train()
dpo_trainer.save_model(
output_dir=f"./out/dpo_sft_{lm_config.dim}.pth.{batch_size}"
)
- init_model 函數(shù)主要是注冊(cè)和加載預(yù)訓(xùn)練的模型,并將tokeinzer 的一些配置文件都拷貝到./my_checkpoint 方便后續(xù)的訓(xùn)練;
- DPOConfig 主要是配置訓(xùn)練的一些參數(shù),比如保存的模型路徑、學(xué)習(xí)率等;
- DPOTrainer? 是DPO 訓(xùn)練器,將模型載入后調(diào)用train 進(jìn)行訓(xùn)練,參數(shù)說(shuō)明如下:
model: transformers.PreTrainedModel,預(yù)訓(xùn)練模型
ref_model: transformers.PreTrainedModel,參考模型
args: DPOConfig,用于訓(xùn)練的 DPO 配置參數(shù)
train_dataset: datasets.Dataset,訓(xùn)練數(shù)據(jù)集
tokenizer: transformers.PreTrainedTokenizerBase,分詞器
model_init: 用于訓(xùn)練的模型初始化器,如果指定為 None,則將使用默認(rèn)的模型初始化器
optimizer: torch.optim.Optimizer,優(yōu)化器
callbacks: 用于訓(xùn)練的回調(diào)函數(shù)
- dpo_trainer.save_model? 保存模型,傳入output_dir 參數(shù),指定保存的模型路徑
4、總結(jié)
至此,訓(xùn)練系列按照步驟寫完了,現(xiàn)在總結(jié)訓(xùn)練流程:
模型訓(xùn)練流程
不過(guò)驗(yàn)證下來(lái),訓(xùn)練效果不是很好,這個(gè)也是從0開(kāi)始訓(xùn)練會(huì)遇到的問(wèn)題,因此接下來(lái)會(huì)完成幾個(gè)事項(xiàng):
- 模型迭代優(yōu)化,解決訓(xùn)練效果不好的問(wèn)題;
- 模型嘗試新的模型和解決方案,解決訓(xùn)練速度問(wèn)題;
- 加入多模態(tài)訓(xùn)練集,將語(yǔ)言大模型改進(jìn)為多模態(tài)模型;
- 最后將整個(gè)模型訓(xùn)練完成后,將代碼開(kāi)源;
