当前位置: 代码网 > 科技>人工智能>aigc > 微调llama 3 — PEFT微调和全量微调

微调llama 3 — PEFT微调和全量微调

2024年08月02日 aigc 我要评论
高效微调llama3

1. llama 3 微调基础

1.1 llama 3 简介

官方blog
llama 3 目前有两个版本:8b版和70b版。8b版本拥有8.03b参数,其尺寸较小,可以在消费者硬件上本地运行。

llama 3与llama 2具有相同的架构,但词汇表要大得多,包含128k entries,而llama 2只有32k entries,根据meta的说法,词汇表的扩展显著提高了模型表现。llama 3的预训练数据包含5%的高质量非英语数据。注意:meta在model card中仍然提到llama 3更适合用于英语任务。

另一方面,词汇表的扩展意味着token embeddings需要更多的数据才能被训练的更准确。meta在15t tokens上训练llama 3。相比之下,llama 2只在2t tokens上训练,google gemma在6t tokens训练,这在当时似乎已经很多了。

模型的性能表现如下图所示:
在这里插入图片描述

1.2 llama 3 8b fully fine-tuning内存占用分析

fully fine-tuning an llm需要更新其所有参数,这种微调需要大量的内存。

  • 模型需要被完全加载到 gpu 内存中
  • 此外,通常用于微调 llms 的优化器 adamw 会为模型中的每个参数创建并存储 2 个参数在 gpu 内存中
  • 并且我们还需要存储在微调过程中创建的张量,即激活值,以便在反向传播过程中用于更新模型参数的梯度。

对llama 3 8b进行微调,例如,批量大小为8,序列长度为512,将消耗128.87gb的显存。注意:这个内存消耗是一个估计值,没有考虑任何的优化,比如梯度检查点和张量并行。

modelloading the modeloptimizer statesactivationstotal
llama 3 8b14.96gb59.83gb54.08gb128.87gb

估算大型语言模型(llm)内存消耗的计算方法

幸运的是,我们可以很容易地减少这三种参数的内存消耗:

  • optimizer states:默认情况下,adamw 的参数为 float32,每项占用 4 字节。adamw-8bit 是另一种不错的选择,它将参数量化为 8 位,即减少了内存消耗从 59.8 gb 到 15 gb。如果使用的框架不复制模型参数,内存消耗会大大减少。
  • model:我们可以将模型量化为4位。它将内存消耗分成近4份,即从15 gb到4 gb。在实践中,为了保持其性能,并不是所有的llm模块都会被量化。
  • activations:我们需要存储激活来计算梯度。然而,使用gradient checkpointing,我们可以在反向传播过程中动态地重新计算激活值,而不是在整个训练过程中都存储这些激活值。它大大减少了激活的内存消耗,从54gb减少到10 gb。

在应用了所有这些优化措施之后,微调过程需要29gb的内存。虽然这仍然太多,但至少现在可以使用两个24gb的gpu来对模型进行微调了。

1.3 llama 3 8b peft fine-tuning内存占用分析

使用peft方法,如lora,我们可以在模型顶部微调一个适配器,不需要完全重新训练模型。为了进一步降低内存消耗。

  1. 使用lora,需要一个带有24 gb ram的gpu来微调llama 3;
  2. 使用qlora,只需要一个带有16 gb ram的gpu。

2. peft方法微调llama 3

1、qlora 是量化的 lora 与 llms 的结合。要使用这种方法对 llama 3 8b 进行微调,我们需要安装

pip install --upgrade bitsandbytes transformers peft accelerate datasets trl

2、然后导入需要的pkgs

import torch, os
from datasets import load_dataset
from peft import loraconfig, prepare_model_for_kbit_training
from transformers import (
    automodelforcausallm,
    autotokenizer,
    bitsandbytesconfig,
    trainingarguments,
)
from trl import sfttrainer

3、如果你拥有较新的gpu,就可以使用bfloat16数据类型以获得更好的训练稳定性,并使用flashattention来减少处理长序列时的内存消耗。下面的代码会自动检测gpu是否兼容bfloat16flashattention

#use bf16 and flashattention if supported
if torch.cuda.is_bf16_supported():
  os.system('pip install flash_attn')
  compute_dtype = torch.bfloat16
  attn_implementation = 'flash_attention_2'
else:
  compute_dtype = torch.float16
  attn_implementation = 'sdpa'

4、然后,我们需要初始化并配置tokenizer。通常,llms在预训练时不包含pad_token。然而,在微调过程中,由于我们的训练示例长度不相同,我们需要将其填充到batch中。我们可以创建并添加一个pad_token到词汇表中,但更简单的选择是将eos_token指定为pad_token。

model_name = "meta-llama/meta-llama-3-8b"
#tokenizer
tokenizer = autotokenizer.from_pretrained(model_name, add_eos_token=true, use_fast=true)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.pad_token_id =  tokenizer.eos_token_id
tokenizer.padding_side = 'left'

注意,我们使用的是左边填充。如果想使用flash_attention,右填充是不兼容的。

5、至于微调数据集,可以选择了 timdettmers/openassistant-guanaco,因为这个数据集足够小。

6、然后,我们创建bnb_config并加载模型:

bnb_config = bitsandbytesconfig(
        load_in_4bit=true,
        bnb_4bit_quant_type="nf4",
        bnb_4bit_compute_dtype=compute_dtype,
        bnb_4bit_use_double_quant=true,
)
model = automodelforcausallm.from_pretrained(
          model_name, quantization_config=bnb_config, device_map={"": 0}, attn_implementation=attn_implementation
)

7、bnb_config定义了在4位精度下加载模型,并对量化常数进行量化(即双重量化)。在前向传递过程中,如果你的gpu支持bfloat16数据类型,则将创建bfloat16张量。请注意:如果你的gpu不支持bfloat16,则笔记本将使用float16。然而,这可能会导致训练不稳定。如果你发现训练损失降至0或nan,请将compute_dtype更改为torch.float32。

8、为了减少激活的内存消耗,我们还需要启用梯度检查点,这是通过

model = prepare_model_for_kbit_training(model)

9、对于 lora 的配置,可以使用:

peft_config = loraconfig(
        lora_alpha=16,
        lora_dropout=0.05,
        r=16,
        bias="none",
        task_type="causal_lm",
        target_modules= ['k_proj', 'q_proj', 'v_proj', 'o_proj', "gate_proj", "down_proj", "up_proj"]
)

可以增加rank来获得更好的结果。增加rank也会增加内存消耗,因为rank增大,适配器的参数也会增加。

10、接下来,定义训练参数和超参数:

training_arguments = trainingarguments(
        output_dir="./llama3_8b_qlora",
        evaluation_strategy="steps",
        do_eval=true,
        optim="paged_adamw_8bit",
        per_device_train_batch_size=8,
        gradient_accumulation_steps=4,
        per_device_eval_batch_size=8,
        log_level="debug",
        save_strategy="epoch",
        logging_steps=100,
        learning_rate=1e-4,
        fp16 = not torch.cuda.is_bf16_supported(),
        bf16 = torch.cuda.is_bf16_supported(),
        eval_steps=100,
        num_train_epochs=3,
        warmup_ratio=0.1,
        lr_scheduler_type="linear",
)

11、使用"paged_adamw_8bit",会在需要时将一些优化器状态存储到cpu ram中,以进一步减少gpu内存消耗。

对于批量大小,随机选择了一个批量大小为32(每个设备的批量大小为8,梯度累积步骤为4(8x4=32)的配置)。该配置消耗了16.6 gb的gpu内存。如果你只有16 gb的gpu,请将每个设备的批量大小减少到4。

12、最后,开始微调时,运行以下命令:

trainer = sfttrainer(
        model=model,
        train_dataset=ds['train'],
        eval_dataset=ds['test'],
        peft_config=peft_config,
        dataset_text_field="text",
        max_seq_length=512,
        tokenizer=tokenizer,
        args=training_arguments,
)

trainer.train()

13、使用google colab的l4实例完成这3个epoch大约需要10个小时。

3. 将微调后的adapter集成到llama 3中

为了避免每次使用时都加载adapter,你可以将其合并到 llama 3 中。当适配器已经使用 qlora 进行微调时,必须小心进行合并,以保持adapter的大部分准确性。我们必须遵循以下步骤:

  1. 加载并量化llama 3
  2. dequantize llama 3 to the compute dtype used during qlora fine-tuning
  3. merge the adapter into the dequantized model
  4. save the resulting model
    最后得到一个没有量化的模型。我们不能像微调那样用bitsandbytes量化它,否则会严重降低模型的性能。使用awq或gptq来代替即可。

4. 使用awq对llama 3进行4位量化

awq是一种量化方案,它保留了模型的重要权重。awq很准确,也受到高效的推理核的支持。首先需要安装autoawq:

pip install autoawq

然后,用几行代码执行量化,例如,要量化前一节合并后得到的模型:

from awq import autoawqforcausallm
from transformers import autotokenizer

tokenizer_path = "meta-llama/meta-llama-3-8b"
model_path = './dqz_merge/'
quant_path = 'llama-3-oasstguanaco3e-awq-4bit'
quant_config = { "zero_point": true, "q_group_size": 128, "w_bit": 4, "version": "gemm" }

# load model and tokenizer
model = autoawqforcausallm.from_pretrained(model_path, safetensors=true)
tokenizer = autotokenizer.from_pretrained(tokenizer_path, use_fast=true)

# quantize
model.quantize(tokenizer, quant_config=quant_config)

# save quantized model with safetensors
model.save_quantized("./"+quant_path, safetensors=true)
tokenizer.save_pretrained("./"+quant_path)

这将把量化模型保存到一个名为“llama-3-oasstguanaco3e-awq-4bit”的目录中。

5. 完全微调模型

qlora和lora只是微调适配器。如果你真的想微调整个模型,你可以尝试galore。galore将梯度投影到低秩子空间,以显著减少它们的维数,从而减少它们的内存消耗。虽然galore大大降低了优化器状态的内存需求,但你仍然需要48gb的gpu ram。

code

具体的notebook代码可以在github仓库中拿到。

notebook中包含了4个部分:

  1. qlora fine-tuning
  2. merging the fine-tuned adapter into the base model
  3. quantization the llama 3 with awq
  4. appendices: lora and galore fine-tuning
(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com