6、ipex-llm(原bigdl-llm)大模型微调

ipex-llm环境配置及模型下载

QLORA是一种高效微调方法,可以将内存使用降低到足以在单个48GB GPU上微调一个拥有65B参数的模型,同时保持完整的16位微调任务性能。QLORA通过一个冻结的、4位量化的预训练语言模型将梯度反向传播到低秩适配器(Low Rank Adapters,简称LoRA)

1、安装需要的包

pip install transformers==4.34.0
pip install peft==0.5.0
pip install accelerate==0.23.0

2、QLoRA 微调

2.1 低精度加载模型
model = AutoModelForCausalLM.from_pretrained(pretrained_model_name_or_path = "meta-llama/Llama-2-7b-hf",
                                             load_in_low_bit="nf4",
                                             optimize_model=False,
                                             torch_dtype=torch.float16,
                                             modules_to_not_convert=["lm_head"])
model = model.to('xpu')

指定 load_in_low_bit=“nf4” 以应用 4 位 NormalFloat 优化。 根据 QLoRA 论文,使用 “nf4” 量化比 “int4” 能达到更优的模型效果。

2.2 准备训练模型

可以应用ipex_llm.transformers.qlora中的prepare_model_for_kbit_training对模型进行预处理,以进行训练的准备。

from ipex_llm.transformers.qlora import prepare_model_for_kbit_training
model.gradient_checkpointing_enable() #可以进一步减少内存使用但速度较慢
model = prepare_model_for_kbit_training(model)

从预处理后的模型中创建一个 PEFT 模型并配置它的参数,如下所示:

from ipex_llm.transformers.qlora import get_peft_model
from peft import LoraConfig

config = LoraConfig(r=8, 
                    lora_alpha=32, 
                    target_modules=["q_proj", "k_proj", "v_proj"], 
                    lora_dropout=0.05, 
                    bias="none", 
                    task_type="CAUSAL_LM")
model = get_peft_model(model, config)

从ipex_llm.transformers.qlora导入与IPEX-LLM兼容的 PEFT 模型,替代原先使用 bitandbytes 库和 cuda 的from peft import prepare_model_for_kbit_training, get_peft_model用法。其使用方法和用peft库进行 QLoRA 微调方法相同。

2.3 加载数据集

加载通用数据集 english quotes 来根据英语名言来微调我们的模型。

from datasets import load_dataset
data = load_dataset("Abirate/english_quotes")
data = data.map(lambda samples: tokenizer(samples["quote"]), batched=True)
2.5加载Tokenizer

分词器可以在 LLM 训练和推理中实现分词和去分词过程。您可以使用 Huggingface Transformers API来加载 LLM 推理需要的分词器,它可以与 IPEX-LLM 加载的模型无缝配合使用。对于Llama 2,对应的tokenizer类为LlamaTokenizer。

from transformers import LlamaTokenizer
tokenizer = LlamaTokenizer.from_pretrained(pretrained_model_name_or_path="meta-llama/Llama-2-7b-chat-hf", trust_remote_code=True)
tokenizer.pad_token_id = 0
tokenizer.padding_side = "left"
2.6 训练
import transformers
trainer = transformers.Trainer(
    model=model,
    train_dataset=data["train"],
    args=transformers.TrainingArguments(
        per_device_train_batch_size=4,
        gradient_accumulation_steps= 1,
        warmup_steps=20,
        max_steps=200,
        learning_rate=2e-4,
        save_steps=100,
        fp16=True,
        logging_steps=20,
        output_dir="outputs", # 这里指定你自己的输出路径
        optim="adamw_hf", # 尚不支持 paged_adamw_8bit优化
        # gradient_checkpointing=True, # #可以进一步减少内存使用但速度较慢
    ),
    data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False),
)
model.config.use_cache = False  # 消除警告,进行推理时应重新启用
result = trainer.train()

最终的 LoRA 模型权重和配置文件会保存到 o u t p u t d i r / c h e c k p o i n t − m a x s t e p s / a d a p t e r m o d e l . b i n 和 {output_dir}/checkpoint-{max_steps}/adapter_model.bin和 outputdir/checkpointmaxsteps/adaptermodel.bin{output_dir}/checkpoint-{max_steps}/adapter_config.json。

3、合并模型

微调模型后,可以将QLoRA模型权重和基本模型合并并导出为Hugging Face格式。

3.1 加载预训练模型
base_model = AutoModelForCausalLM.from_pretrained(
        "meta-llama/Llama-2-7b-hf",
        torch_dtype=torch.float16,
        device_map={"": "cpu"},
    )

在合并状态下,应删除 load_in_low_bit=“nf4”,因为我们需要加载原始模型作为基本模型。

3.2 合并权重
from ipex_llm.transformers.qlora import PeftModel
adapter_path = "./outputs/checkpoint-200"
lora_model = PeftModel.from_pretrained(
        base_model,
        adapter_path,
        device_map={"": "cpu"},
        torch_dtype=torch.float16,
    )

我们用import PeftModel from ipex_llm.transformers.qlora替代from peft import PeftModel来导入 IPEX-LLM 兼容的 PEFT 模型。

为了验证LoRA权重和预训练模型权重有效合并,我们提取第一层权重(在 llama2模型中为attention中的queries)来对比其差异。

first_weight = base_model.model.layers[0].self_attn.q_proj.weight
first_weight_old = first_weight.clone()
lora_weight = lora_model.base_model.model.model.layers[0].self_attn.q_proj.weight
assert torch.allclose(first_weight_old, first_weight)

通过merge_and_unlaod方法可以将微调后的模型与预训练的模型进行合并,并通过assert声明来验证权重是否发生变化。

lora_model = lora_model.merge_and_unload()
lora_model.train(False)
assert not torch.allclose(first_weight_old, first_weight)

如果返回一下输出并没有报错,则说明模型合并成功。

Using pad_token, but it is not set yet.
Using pad_token, but it is not set yet.

最后,我们可以将合并的模型保存在指定的本地路径中。

output_path = ./outputs/checkpoint-200-merged
lora_model_sd = lora_model.state_dict()
deloreanized_sd = {
        k.replace("base_model.model.", ""): v
        for k, v in lora_model_sd.items()
        if "lora" not in k
    }
base_model.save_pretrained(output_path, state_dict=deloreanized_sd)
tokenizer.save_pretrained(output_path)

4、使用微调模型进行推理

model_path = "./outputs/checkpoint-200-merged"
model = AutoModelForCausalLM.from_pretrained(pretrained_model_name_or_path = model_path,load_in_4bit=True)
model = model.to('xpu')
tokenizer = LlamaTokenizer.from_pretrained(pretrained_model_name_or_path = model_path)

可以验证微调模型在增添新的数据集训练后是否能做出富有哲理的回答。

with torch.inference_mode():
    input_ids = tokenizer.encode('The paradox of time and eternity is', 
                                  return_tensors="pt").to('xpu')
    output = model.generate(input_ids, max_new_tokens=32)
    output = output.cpu()
    output_str = tokenizer.decode(output[0], skip_special_tokens=True)
    print(output_str)

仅替换model_path参数来使用预训练模型重复该过程,将预训练模型的答案与微调模型的答案进行比较:
预训练模型:

The paradox of time and eternity is that time is not eternal, but eternity is. nobody knows how long time is.
The paradox of time and eternity is

微调模型:

The paradox of time and eternity is that, on the one hand, we experience time as linear and progressive, and on the other hand, we experience time as cyclical. And the

可以看到微调模型的推理结果与新增数据集中有相同的词汇以及近似的文本风格。基于 IPEX-LLM 的优化,我们可以在仅仅几分钟的训练之内达到这个效果。

  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值