微调大型语言模型:根据您的需求自定义 Llama 3 8B

2024/9/20 21:03:37

本文主要是介绍微调大型语言模型:根据您的需求自定义 Llama 3 8B,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

由LLM生成的关于LLM微调的图像(GPT:左和中,Gemini:右)

自2022年11月发布以来,ChatGPT引发了关于大型语言模型(LLMs)和AI能力的广泛讨论。现在很少能找到没听说过ChatGPT或没尝试过它的人了。虽然像GPT、Gemini或Claude这样的工具拥有数百亿(甚至数千亿)的参数,并且在庞大的文本语料库上进行了预训练,非常强大,但它们并非无所不能。在某些特定任务上,这些模型会表现出不足。然而,我们并非没有解决这些问题的方法。我们可以利用较小的开源模型的力量,将它们适应到我们特定的问题上。

这篇博客旨在简要介绍几个较小的开源LLM,并解释LLM微调中的两个重要概念:量化和LoRA。此外,我们还将介绍一些最流行的微调库,并附上代码示例,以便您能够快速将这些概念应用于您的用例。让我们开始微调之旅。

如果你想要跳过理论部分直接查看代码,请点击这里。

目录
  1. “小型”大型语言模型
  2. 量化
  3. 低秩适应(LoRA)
  4. Unsloth
  5. 监督微调训练器(SFT)
  6. 比率偏好优化(ORPO)
  7. 结论
  8. 参考资料
“小”型大型语言模型

Llama 3与其他模型的比较

微调大型语言模型(LLMs)可能会非常昂贵,特别是对于参数数量较多的模型。一般来说,参数少于100亿的模型通常可以进行微调而不会遇到显著的基础设施挑战。然而,对于更大的模型,如Llama 3 70B,需要大量的资源。微调一个像Llama 3这样的700亿参数模型大约需要1.5太字节的GPU虚拟内存(vRAM)。从另一个角度来看,这相当于大约20个Nvidia A100 GPU的集群,每个GPU有80GB的vRAM。这样的配置成本大约为40万美元,假设硬件甚至可以买到。

或者,也可以使用像 AWS、Azure 或 GCP 这样的云提供商,但这种方法也很昂贵。例如,在 AWS 上使用 8 个 Nvidia A100 GPU 一小时的成本为 40 美元。如果你要用 20 个 GPU 对 70B 参数的模型进行微调 5 天,大约需要花费 12,000 美元。

由于这些成本,大多数从业者主要使用参数少于100亿的较小的LLM。这些模型可以更经济地训练,只需要16GB到24GB的vRAM(对于更大的批次大小和更快的训练)。例如,我使用Nvidia A10在AWS上将Mistral 7B微调为塞尔维亚语,这花费不到10小时且成本不到20美元。

当然,仍然需要的是,一个7B模型在没有量化的情况下无法适应并训练在那么多的vRAM上,具体来说是量化到4位。

Quantization

使用完整的32位参数,我们仍然需要大量的vRAM(以普通人的标准来看是荒谬的)来训练LLM——大约需要150GB。

将 FP32 转换为 INT8

量化通过将模型参数转换为低精度的数据类型(如8位或4位),显著减少了内存消耗并提高了执行速度。其概念很简单:所有可能的32位值都被映射到一个较小范围的有限值(例如,8位转换为256个值)。这个过程可以被可视化为将高精度值围绕几个固定点进行分组,这些固定点代表其附近的值。

低秩适配(LoRA)

LoRA 是一种通过使用矩阵降维来更新模型权重的技术。这项技术特别相关,因为广泛用于大规模语言模型(LLM)的变压器高度依赖于矩阵。LoRA 工作原理的详细解释可以在 Jay Alammar 的一篇博客文章中找到。

常规微调

当更新模型的权重时,需要调整这些矩阵中的参数。从概念上讲,这种调整可以看作是将一个权重更新矩阵添加到原始矩阵中:W’ = W + ΔW。LoRA 通过将这个更新矩阵分解为两个较小的矩阵来引入了一种新的方法,这两个较小的矩阵相乘可以近似于更新矩阵。在微调过程中,LoRA 直接创建这两个较小的矩阵进行相乘,而不是先创建再分解更新矩阵。

一个关于常规微调和使用LoRA微调之间的对比示例可以在下面的图片中看到,这些图片改编自Sebastian Raschka的博客文章。

与使用LoRA进行微调(右)相比的常规微调(左)的另一种表述形式

LoRA 的关键优势在于,虽然近似值略显不精确,但它显著提高了内存和计算效率。例如,考虑一个包含 1000x1000 参数的矩阵,总共有 100 万个参数。通过使用 1000x100 和 100x1000 矩阵的分解(且略显不精确)版本,参数数量减少到仅 20 万个,从而减少了 80% 的参数。

量化和LoRA通常结合使用,形成所谓的QLoRA。

不_slope(Unsloth)

如果我要重新开始大规模语言模型的微调,我会选择 Unsloth Python 库。Unsloth 提供了多种针对大规模语言模型微调的优化,并支持包括 Mistral、Llama 3、Gemma 等在内的多种流行的大规模语言模型。例如,他们的免费层级包含了针对 Mistral 的 12 种不同的微调优化,提供了显著的 2.2 倍加速。

Unsloth 微调优化

以下是一些代码片段,展示了如何使用 Unsloth 库对 Llama 3 8B 进行微调。所有这些代码块均来自 Unsloth GitHub,完整的微调笔记本可以在这里找到:此处。

导入4位模型:

    model, tokenizer = FastLanguageModel.from_pretrained(  
        model_name = "unsloth/llama-3-8b-bnb-4bit",  
        max_seq_length = max_seq_length,  
        dtype = dtype,  
        load_in_4bit = load_in_4bit,  
        # token = "hf_...", # 如果使用带有门控的模型(如meta-llama/Llama-2-7b-hf),可以使用token  
    )

设置LoRA:

    model = FastLanguageModel.get_peft_model(  
        model,  
        r = 16, # 选择任何大于0的数字!建议选择8, 16, 32, 64, 128  
        target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",  
                          "gate_proj", "up_proj", "down_proj",],  
        lora_alpha = 16,  
        lora_dropout = 0, # 支持任何值,但=0是最优的  
        bias = "none",    # 支持任何值,但="none"是最优的  
        # [NEW] "unsloth" 使用30%更少的VRAM,适合2倍大的批次大小!  
        use_gradient_checkpointing = "unsloth", # True或"unsloth"适用于非常长的上下文  
        random_state = 3407,  
        use_rslora = False,  # 我们支持秩稳定的LoRA  
        loftq_config = None, # 以及LoftQ  
    )

初始化Hugging Face的Supervised Finetuning Trainer:

    trainer = SFTTrainer(  
        model = model,  
        tokenizer = tokenizer,  
        train_dataset = dataset,  
        dataset_text_field = "text",  
        max_seq_length = max_seq_length,  
        dataset_num_proc = 2,  
        packing = False, # 可以使短序列的训练速度提高5倍。  
        args = TrainingArguments(  
            per_device_train_batch_size = 2,  
            gradient_accumulation_steps = 4,  
            warmup_steps = 5,  
            max_steps = 60,  
            learning_rate = 2e-4,  
            fp16 = not torch.cuda.is_bf16_supported(),  
            bf16 = torch.cuda.is_bf16_supported(),  
            logging_steps = 1,  
            optim = "adamw_8bit",  
            weight_decay = 0.01,  
            lr_scheduler_type = "linear",  
            seed = 3407,  
            output_dir = "outputs",  
        ),  
    )

训练模型:

    trainer_stats = trainer.train()
监督微调训练器 (SFT)

预训练一个大语言模型之后,下一个关键步骤是监督微调。这个过程对于开发一个能够理解和生成连贯响应的模型至关重要,而不仅仅是完成句子。

像 Hugging Face 提供的 SFT(Supervised Finetuning Trainer)和 PEFT(Parameter Efficient Finetuning)这样的工具,以及 Tim Dettmers 开发的 BitsAndBytes,显著简化了将 LoRA、量化和微调等技术应用到模型中的过程。这些库使得实现高级优化方法更加简便和高效,无论是开发者还是研究人员都能从中受益。

下面你会发现Unsloth、SFT和ORPO的代码非常相似。这种相似性源于这些库背后的基本理念大体相同,区别主要在于库本身以及可能的一些超参数。

导入4位模型:

    # Hugging Face 模型ID  
    model_id = "meta-llama/Meta-Llama-3-8B"  
    model_id = "mistralai/Mistral-7B-v0.1"  

    # BitsAndBytesConfig 4位配置  
    bnb_config = BitsAndBytesConfig(  
        load_in_4bit=True,  
        bnb_4bit_use_double_quant=True,  
        bnb_4bit_quant_type="nf4",  
        bnb_4bit_compute_dtype=torch.bfloat16 if use_flash_attention2 else torch.float16  
    )  

    # 加载模型和分词器  
    model = AutoModelForCausalLM.from_pretrained(  
        model_id,   
        quantization_config=bnb_config,   
        use_cache=False,   
        device_map="auto",  
        token=os.environ["HF_TOKEN"], # 如果模型像 llama 或 mistral 一样受保护  
        attn_implementation="flash_attention_2" if use_flash_attention2 else "sdpa"  
    )  
    model.config.pretraining_tp = 1  

    tokenizer = AutoTokenizer.from_pretrained(  
        model_id,  
        token=os.environ["HF_TOKEN"], # 如果模型像 llama 或 mistral 一样受保护  
    )  
    tokenizer.pad_token = tokenizer.eos_token  
    tokenizer.padding_side = "right"

设置LoRA:

    # 基于QLoRA论文的LoRA配置  
    peft_config = LoraConfig(  
            lora_alpha=16,  
            lora_dropout=0.1,  
            r=64,  
            bias="none",  
            task_type="CAUSAL_LM",  
            target_modules=[  
                "q_proj",  
                "k_proj",  
                "v_proj",  
                "o_proj",  
                "gate_proj",   
                "up_proj",   
                "down_proj",  
            ]  
    )  

    # 准备模型进行训练  
    model = prepare_model_for_kbit_training(model)

初始化Hugging Face的Supervised Finetuning Trainer:

    args = TrainingArguments(  
        output_dir="mistral-int4-alpaca",  
        num_train_epochs=1,  
        per_device_train_batch_size=6 if use_flash_attention2 else 2, # 根据你的硬件情况可以调整批处理大小  
        gradient_accumulation_steps=4,  
        gradient_checkpointing=True,  
        optim="paged_adamw_8bit",  
        logging_steps=10,  
        save_strategy="epoch",  
        learning_rate=2e-4,  
        bf16=use_flash_attention2,  
        fp16=not use_flash_attention2,  
        tf32=use_flash_attention2,  
        max_grad_norm=0.3,  
        warmup_steps=5,  
        lr_scheduler_type="linear",  
        disable_tqdm=False,  
        report_to="none"  
    )  

    model = get_peft_model(model, peft_config)
    trainer = SFTTrainer(  
        model=model,  
        train_dataset=dataset,  
        peft_config=peft_config,  
        max_seq_length=2048,  
        tokenizer=tokenizer,  
        packing=True,  
        formatting_func=format_instruction,   
        args=args,  
    )

训练模型:

    trainer.train()

完整的笔记本可以在这里找到:here。

比率偏好优化(ORPO)

在这篇博客文章中,我们重点介绍了大型语言模型(LLMs)的预训练和监督微调。然而,所有最先进的大型语言模型都会经历另一个关键步骤:偏好对齐。这个步骤发生在预训练和微调之后,通过告知模型哪些生成的输出是可取的,哪些不是。偏好对齐的流行方法包括从人类反馈中学习的强化学习(RLHF)和直接偏好优化(DPO)。

一种名为 Odds Ratio Preference Optimization (ORPO) 的新方法于2024年3月出现,结合了监督微调和偏好对齐。

传统的大语言模型微调 vs ORPO 大语言模型微调

对于 ORPO 的详细解释,包括代码示例和概述,参见 Maxime Labonne 的见解深刻的 博客文章。

这里有一部分使用ORPO进行微调和偏好对齐的代码。完整代码可在此处获取 here。

导入4位模型:

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

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

    # 加载分词器  
    tokenizer = AutoTokenizer.from_pretrained(base_model)  

    # 加载模型  
    model = AutoModelForCausalLM.from_pretrained(  
        base_model,  
        quantization_config=bnb_config,  
        device_map="auto",  
        attn_implementation=attn_implementation  
    )

设置LoRA:

    # LoRA配置  
    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']  
    )  

    model = prepare_model_for_kbit_training(model)

初始化Hugging Face的ORPO Trainer:

    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()
结论

虽然像GPT、Gemini或Claude这样的大型语言模型(LLMs)非常强大,但它们的庞大体积和资源需求使得它们在许多任务中不太实用。为了解决这个问题,可以使用量化和低秩适应(LoRA)等技术对较小的开源LLMs进行微调和定制,以满足特定需求。这些技术可以减少内存消耗并提高计算效率,使得训练模型更加经济,尤其是对于参数少于100亿的模型。

像 Unsloth、Supervised Finetuning Trainer (SFT) 和 Odds Ratio Preference Optimization (ORPO) 这样的工具简化了微调过程,使其更加易于访问。例如,Unsloth 提供了可以显著加速训练的优化,而 ORPO 结合了监督微调与偏好对齐,以提升模型性能。

通过利用这些技术和工具,开发人员和研究人员可以将大型语言模型(LLMs)调整到他们特定的需求上,而无需承担训练大型模型的高昂成本。这种方法使高级语言模型的访问更加民主化,并且能够支持不同领域的广泛应用。

参考资料

[1] 模型内存实用工具: https://huggingface.co/spaces/hf-accelerate/model-memory-usage

[2] AWS EC2 P4d 实例定价: https://aws.amazon.com/ec2/instance-types/p4/#:~:text=to%20learn%20more%20%C2%BB-,产品详情,-实例大小

[3] 量化详细解释: https://huggingface.co/docs/optimum/concept_guides/quantization

[4] 使用 NVIDIA TensorRT 进行量化感知训练以实现 INT8 推理的 FP32 准确性:https://developer.nvidia.com/blog/achieving-fp32-accuracy-for-int8-inference-using-quantization-aware-training-with-tensorrt/

[5] 图解Transformer: https://jalammar.github.io/illustrated-transformer/

[6] LoRA: 大型语言模型的低秩适应:https://arxiv.org/abs/2106.09685

[7] 基于低秩适配(LoRA)的参数高效LLM微调:https://sebastianraschka.com/blog/2023/llm-finetuning-lora.html

[8] QLoRA:量化LLM的高效微调:https://arxiv.org/abs/2305.14314

[9] Unsloth: https://unsloth.ai/

[10] 监督微调训练器: https://huggingface.co/docs/trl/sft_trainer

[11] ORPO:无需参考模型的单体偏好优化:https://arxiv.org/abs/2403.07691

[12] 使用 ORPO 微调 Llama 3: https://huggingface.co/blog/mlabonne/orpo-llama-3



这篇关于微调大型语言模型:根据您的需求自定义 Llama 3 8B的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程