QLoRA(量化低阶适应)结合了 4 个要素,可以在不牺牲模型性能的情况下充分利用机器的有限内存。我将简要总结每个要点。更多详细信息请参阅 QLoRA 论文。
QLoRA 使用了被称为4 位 NormalFloat的特殊数据类型,这种数据类型仅用 4 位对数字进行编码。虽然这意味着我们只有 2⁴ (= 16) 个块来表示模型参数,但 4 位 NormalFloat 使用特殊技巧来从有限的信息容量中获取更多信息。
量化一组数字的简单方法就是我们之前看到的,即我们将数字分成等距的块。然而,更有效的方法是使用相同信息量的块。这两种方法之间的差异如下图所示。
更具体地说,4 位 NormalFloat 对正态分布数据采用信息理论上最优的量化策略。由于模型参数往往聚集在 0 附近,因此这是量化LLM 参数的有效策略。
双量化,顾名思义,就是对已经量化的模型参数再次执行量化。考虑以下量化过程。给定 FP32 张量,量化它的简单方法是使用下面的数学公式。
这里我们将 FP32格式的数字转换为 [-127, 127] 范围内的 Int8(8 位整数)表示。这个过程需要调整张量XFP32X^{FP32}XFP32的值,将它四舍五入到最接近的整数。同时,我们也可以化简量化公式,通过引入量化常数cFP32c^{FP32}cFP32简化方程,将计算过程简化为张量XFP32X^{FP32}XFP32与量化参数cFP32c^{FP32}cFP32相乘。
虽然这种幼稚的量化方法并不是在实践中实现的(记住我们在 4 位 NormalFloat 中看到的技巧),但它确实说明了量化需要一些计算开销才能将结果常量存储在内存中。
我们可以通过只执行一次这个过程来最小化这种开销。换句话说,为所有模型参数计算一个量化常数。然而,这并不理想,因为它对极值(非常)敏感。换句话说,由于c^FP32中的absmax()函数,一个相对较大的参数值会扭曲所有其他参数值。
或者,我们可以将模型参数划分为更小的块以进行量化。这减少了大值扭曲其他值的可能性,但会带来更大的内存占用。
为了减轻这种内存成本,我们可以(再次)采用量化,但现在采用这种逐块方法生成的常量。对于 64 的块大小,FP32 量化常数增加 0.5 位/参数。通过进一步量化这些常量(例如 8 位),我们可以将此占用空间减少到 0.127 位/参数 [4]。
标准量化与块量化的视觉比较
使用 Nvidia 提供的特性来避免训练期间出现的内存不足错误。当 GPU存储容量达到极限时,它会将内存从 GPU 传输到 CPU。由于LLM训练期间可能会出现间歇性内存峰值,可能会终止训练进程,因此这个特性很重要。
LoRA(低秩适应)是一种参数高效微调(PEFT)方法。关键思想是 LoRA 不重新训练所有模型参数,而是在保持原始参数固定的情况下添加相对少量的可训练参数。它可以将可训练的数量减少100~1000倍,但不会显著牺牲模型性能。
现在我们已经了解了 QLoRA 的所有要素,让我们看看如何将它们组合在一起。
首先,考虑一个10B模型标准的微调过程,其中包括重新训练每个模型参数。假如使用 FP16 作为模型参数和梯度(4 个字节/每个参数,40GB),使用 FP32 作为优化器状态,例如动量、方差、参数(12 个字节/参数,120GB)。因此,10B 参数模型将需要大约 160GB 内存来进行微调。
使用 LoRA,我们可以通过减少可训练参数的数量来立即降低计算成本,Lora冻结了LLM的原始参数并添加一组适配器(Adapter)来作为可训练参数。模型参数和梯度的计算成本与以前相同( 4 字节/每个参数,40GB)。节省来自优化器状态。如果我们的可训练参数减少 100 倍(这些参数全来自于adapter),那么对于0.1B的adapter模型,它的参数更新需要4 个字节/每个参数,优化器状态更新需要12个字节/每个参数(总计1.6GB)。因此,10B 参数模型将需要大约 41.6GB 内存来进行微调。虽然可以节省大量成本,但依然不能在消费级别硬件上运行。
QLoRA通过使用要素1和要素2 ,量化原始模型参数来更进一步。这将训练成本从 4 字节/参数减少到大约 1 字节/参数。然后,通过以同样的方式再次使用 LoRA,这将增加0.16 字节/参数的消耗。因此,10B 模型只需 11.6GB 内存即可进行微调!它可以轻松地在消费类硬件上运行,例如 Google Colab 上的免费 T4 GPU。
下图显示了这 3 种方法的直观比较 。
现在,使用QLora来微调Misttral-7B!该代码支持在谷歌的Colab上运行。
从Hugging Face 的Transforms、Peft和datasets库导入模块。
transformers,,pipeline peftprepare_model_for_kbit_training peft,get_peft_model datasetsload_dataset transformers
安装以下依赖项:
!pip install auto-gptq
!pip install optimization
!pip install bitsandbytes
接下来,从 Hugging Face 加载量化模型,使用The Block发布的Mistral-7B-Instruct model(链接:https://huggingface.co/TheBloke/Mistral-7B-Instruct-v0.2-GPTQ),这是一个经过4-bit量化的Mistral-7b模型。
除了指定要下载的模型存储库之外,还设置以下参数:device_map、trust_remote_code和revision。device_map让该方法自动找出如何最好地分配计算资源以在机器上加载模型,trust_remote_code=False会阻止自定义模型文件在您的计算机上运行。最后,revision指定我们要使用存储库中的模型版本。
model_name modelAutoModelForCausalLM.from_pretrained model_name, device_map, trust_remote_code, revision
加载后,我们看到 7B 参数模型仅占用4.16GB 内存,可以轻松装入 Colab 上免费提供的 CPU 或 GPU 内存。
接下来,加载模型的tokenizer:
=AutoTokenizer.from_pretrained(model_name,use_fast=)
接下来,测试这个模型:输入模型的文本是“Great content, thank you!”,一个模仿youtube的评论。
model.() comment= prompt=f inputs=(prompt,return_tensors=) outputs=model.(input_ids=inputs[].(), max_new_tokens=) (tokenizer.(outputs)[])
模型的响应如下所示。虽然它有一个好的开头,但这种回复有些啰嗦,听起来不像一个真人博主的回复!
I topicsyou help. themeantime,I contentI you up--information. Thanksreading,Ilookforwardhelpingyouanyquestionsyou mayhave!
让我们看看如何通过微调来提高模型的性能。首先,启用梯度检查点,梯度检查点是一种节省内存的技术,可以清除特定的激活并在向后传递期间重新计算它们:
model.train() model.gradient_checkpointing_enable() model=prepare_model_for_kbit_training(model)
接下来,通过配置config设置 LoRA 训练。打印可训练参数的数量,我们观察到减少了 100 倍以上。
config=LoraConfig( r=8, lora_alpha=32, target_modules=[], lora_dropout=0.05, bias=, task_type= ) model=get_peft_model(model,config) model.print_trainable_parameters()
现在,我们可以导入训练数据。此处使用的数据集可在 HuggingFace Dataset Hub获取。我使用我YouTube 频道[1]的评论和回复生成了此数据集。生成该数据集的代码见链接(https://github.com/ShawhinT/YouTube-Blog/blob/main/LLMs/qlora/create-dataset.ipynb)
=load_dataset()
接下来,准备训练数据集。这需要数据具有适当的长度,并且被处理为tokenization:
(): text=examples[] tokenizer.truncation_side= tokenized_inputs=tokenizer( text, return_tensors=, truncation=, max_length= ) tokenized_inputs tokenized_data=data.(tokenize_function,batched=) tokenizer.pad_token=tokenizer.eos_token data_collator=Transformers.DataCollatorForLanguageModeling(tokenizer, mlm=)
在下面的代码块中,定义了模型训练的超参数。
lr=2e-4 batch_size=4 num_epochs=10 training_args=transformers.TrainingArguments( output_dir=, learning_rate=lr, per_device_train_batch_size=batch_size, per_device_eval_batch_size=batch_size, num_train_epochs=num_epochs, weight_decay=0.01, logging_strategy=, evaluation_strategy=, save_strategy=, load_best_model_at_end=True, gradient_accumulation_steps=4, warmup_steps=2, fp16=True, optim=, )
虽然这里列出了几个,但我想在 QLoRA 上下文中强调的两个是fp16和optim。fp16=True使训练器在训练过程中使用 FP16 值,与标准 FP32 相比,这会显着节省内存。optim=”paged_adamw_8bit”启用前面讨论的要素3(即分页优化器)。
设置完所有超参数后,我们可以使用下面的代码运行训练过程。
trainerTransformers.Trainer modelmodel, train_datasettokenized_data, eval_datasettokenized_data, argstraining_args, data_collatordata_collator model.config.use_cache trainer.train model.config.use_cache
由于我们只有 50 个训练样本,因此该训练过程大约需要 10 分钟。
最终模型可在HF hub(https://huggingface.co/shawhin/shawgpt-ft)上获得,如果想跳过训练过程直接加载,可以使用下面的代码:
peftimportPeftModel,PeftConfig fromTransformersimportAutoModelForCausalLM model_name= model=AutoModelForCausalLM.from_pretrained(model_name, device_map=, trust_remote_code=False, revision=) config=PeftConfig.from_pretrained() model=PeftModel.from_pretrained(model,) tokenizer=AutoTokenizer.from_pretrained(model_name,use_fast=True)
可以对微调模型进行推理,这是微调模型对之前相同的评论“Great content, thank you!”的回复:
Gladyouenjoyedit!knowyouhaveanyquestions.
与微调前相比,这样的回复更像我自己,同时也更简短而恰当。
QLoRA 是一种微调技术,使得训练自己的大语言模型变得更加容易。在这里,我概述了该方法的工作原理,并分享了一个使用 QLoRA 创建 YouTube 评论回复器的具体示例。虽然经过微调的模型在模仿我的响应风格方面做得很好,但它在理解专业数据科学知识方面存在一些局限性,这可以通过RAG来改善。
| 欢迎光临 链载Ai (https://www.lianzai.com/) | Powered by Discuz! X3.5 |