提供LoRA微调和全量参数微调代码,训练数据为data/train_sft.csv,验证数据为data/dev_sft.csv,数据格式为"<s>Human: "+问题+"\n</s><s>Assistant: "+答案。本文主要介绍Llama-2-7b模型LoRA微调以及4bit量化的实践过程。

1.LoRA微调脚本

  LoRA微调脚本train/sft/finetune_lora.sh如下所示:

output_model=save_folder
# 需要修改到自己的输入目录
if [ ! -d ${output_model} ];then  
    mkdir ${output_model}
fi
cp ./finetune.sh ${output_model}
CUDA_VISIBLE_DEVICES=0,1 deepspeed --num_gpus 2  finetune_clm_lora.py \              # 用于训练的脚本
    --model_name_or_path meta-llama/Llama-2-7b-chat-hf \                             # 预训练模型路径
    --train_files ../../data/train_sft.csv \                                         # 训练数据
                ../../data/train_sft_sharegpt.csv \                                  # 训练数据
    --validation_files  ../../data/dev_sft.csv \                                     # 验证数据
                         ../../data/dev_sft_sharegpt.csv \                           # 验证数据
    --per_device_train_batch_size 1 \                                                # 每个设备的训练批次大小
    --per_device_eval_batch_size 1 \                                                 # 每个设备的验证批次大小
    --do_train \                                                                     # 是否训练
    --do_eval \                                                                      # 是否验证
    --use_fast_tokenizer false \                                                     # 是否使用快速分词器
    --output_dir ${output_model} \                                                   # 输出目录
    --evaluation_strategy  steps \                                                   # 评估策略
    --max_eval_samples 800 \                                                         # 最大验证样本数
    --learning_rate 1e-4 \                                                           # 学习率
    --gradient_accumulation_steps 8 \                                                # 梯度累积步数
    --num_train_epochs 10 \                                                          # 训练轮数
    --warmup_steps 400 \                                                             # 预热步数
    --load_in_bits 4 \                                                               # 加载位数
    --lora_r 8 \                                                                     # lora_r表示秩的大小
    --lora_alpha 32 \                                                                # lora_alpha表示控制模型对原始预训练参数的更新程度
    --target_modules q_proj,k_proj,v_proj,o_proj,down_proj,gate_proj,up_proj \       # 目标模块
    --logging_dir ${output_model}/logs \                                             # 日志目录
    --logging_strategy steps \                                                       # 日志策略
    --logging_steps 10 \                                                             # 日志步数
    --save_strategy steps \                                                          # 保存策略
    --preprocessing_num_workers 10 \                                                 # 预处理工作数
    --save_steps 20 \                                                                # 保存步数
    --eval_steps 20 \                                                                # 评估步数
    --save_total_limit 2000 \                                                        # 保存总数限制
    --seed 42 \                                                                      # 种子
    --disable_tqdm false \                                                           # 禁用tqdm
    --ddp_find_unused_parameters false \                                             # ddp_find_unused_parameters
    --block_size 2048 \                                                              # 块大小
    --report_to tensorboard \                                                        # 报告到tensorboard
    --overwrite_output_dir \                                                         # 覆盖输出目录
    --deepspeed ds_config_zero2.json \                                               # deepspeed配置文件
    --ignore_data_skip true \                                                        # 忽略数据跳过
    --bf16 \                                                                         # bf16
    --gradient_checkpointing \                                                       # 梯度检查点
    --bf16_full_eval \                                                               # bf16_full_eval
    --ddp_timeout 18000000 \                                                         # ddp_timeout
    | tee -a ${output_model}/train.log                                               # 日志输出

    # --resume_from_checkpoint ${output_model}/checkpoint-20400 \                    # 恢复检查点

2.LoRA微调代码

  LoRA微调具体实现代码train/sft/finetune_clm_lora.py参考文献[3]。这里要说明下HuggingFace开源的一个高效微调大模型的PEFT库,目前支持很多方法和模型,详见参考文献[4][5]。LoRA(Low-Rank Adaptation)的本质就是奇异值分解,使用包含矩阵能量的秩来近似和还原原始矩阵,这样就可以将平方复杂度转换为线性复杂度了。本人读研期间做了很长时间的概率矩阵分解,对此有所理解。核心代码如下所示:

# 步骤1:导入peft库中Lora相关模块
from peft import (
    LoraConfig,
    PeftModel,
    get_peft_model,
    get_peft_model_state_dict,
    prepare_model_for_int8_training,
    prepare_model_for_kbit_training,
    set_peft_model_state_dict,
)

# 步骤2:lora配置
lora_config = LoraConfig(  # lora配置
        r = model_args.lora_r,  # r表示秩
        lora_alpha = model_args.lora_alpha,  # alpha表示缩放因子
        # target_modules = ["query_key_value"], # 目标模块
        # target_modules =  ['q_proj', 'k_proj', 'v_proj', 'o_proj'], # 目标模块
        target_modules = model_args.target_modules,  # 目标模块
        fan_in_fan_out = False,  # 是否使用fan_in_fan_out
        lora_dropout = 0.05,  # lora_dropout
        inference_mode = False,  # 是否使用推理模式
        bias = "none",  # 偏置
        task_type = "CAUSAL_LM",  # 任务类型
    )

# 步骤3:加载model
model = AutoModelForCausalLM.from_pretrained( # 从预训练模型中加载模型
    model_args.model_name_or_path, # 模型名或路径
    from_tf = bool(".ckpt" in model_args.model_name_or_path), # 是否从tensorflow加载
    config = config, # 配置
    cache_dir = model_args.cache_dir, # 缓存目录
    revision = model_args.model_revision, # 模型版本
    use_auth_token = True if model_args.use_auth_token else None, # 是否使用token
    torch_dtype = torch_dtype, # torch数据类型
    device_map = {"": int(os.environ.get("LOCAL_RANK") or 0)} # 设备映射
)

# 步骤4:获取peft模型
model = get_peft_model(model, lora_config)

# 步骤5:初始化Trainer
trainer = Trainer( # 训练器
    model = model, # 模型
    args = training_args, # 训练参数
    train_dataset = train_dataset if training_args.do_train else None, # 训练数据集
    eval_dataset = eval_dataset if training_args.do_eval else None, # 评估数据集
    tokenizer = tokenizer, # tokenizer
    # 数据收集器将默认为DataCollatorWithPadding,因此我们将其更改
    data_collator = transformers.DataCollatorForSeq2Seq( # 数据收集器
        tokenizer, pad_to_multiple_of=8, return_tensors="pt", padding=True # tokenizer,填充到8的倍数,返回张量,填充
    ),
    compute_metrics=compute_metrics if training_args.do_eval and not is_torch_tpu_available() else None, # 计算指标
    preprocess_logits_for_metrics=preprocess_logits_for_metrics if training_args.do_eval and not is_torch_tpu_available() else None, # 为指标预处理logits
    callbacks=([SavePeftModelCallback] if isinstance(model, PeftModel) else None), # 回调
)

3.加载LoRA微调模型

  加载LoRA微调模型需要通过PEFT加载预训练模型参数和微调模型参数,base_model_name_or_path为预训练模型参数保存路径,finetune_model_path为微调模型参数保存路径。核心代码如下所示:

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
from peft import PeftModel,PeftConfig

# 例如: finetune_model_path='Llama2-Chinese-7b-LoRA'
finetune_model_path='' #微调模型参数保存路径

# 例如: base_model_name_or_path='meta-llama/Llama-2-7b'
base_model_name_or_path='' #为预训练模型参数保存路径

tokenizer = AutoTokenizer.from_pretrained(base_model_name_or_path,use_fast=False)
tokenizer.pad_token = tokenizer.eos_token
model = AutoModelForCausalLM.from_pretrained(base_model_name_or_path,device_map='auto',torch_dtype=torch.float16,load_in_8bit=True)

model = PeftModel.from_pretrained(model, finetune_model_path, device_map={"": 0})
model = model.eval()
input_ids = tokenizer(['<s>Human: 介绍一下北京\n</s><s>Assistant: '], return_tensors="pt",add_special_tokens=False).input_ids.to('cuda')
generate_input = {
    "input_ids":input_ids,
    "max_new_tokens":512,
    "do_sample":True,
    "top_k":50,
    "top_p":0.95,
    "temperature":0.3,
    "repetition_penalty":1.3,
    "eos_token_id":tokenizer.eos_token_id,
    "bos_token_id":tokenizer.bos_token_id,
    "pad_token_id":tokenizer.pad_token_id
}
generate_ids = model.generate(**generate_input)
text = tokenizer.decode(generate_ids[0])
print(text)

4.模型量化和加载方式

  模型量化和LoRA微调具体实现代码train/sft/finetune_clm_lora.py参考文献[3]。修改ModelArguments类中的load_in_bits: Optional[int] = field(default=4)。本质上就是先对模型做量化,然后再LoRA微调。核心代码如下所示:

# 步骤1:导入peft库中Lora相关模块
from peft import (
    LoraConfig,
    PeftModel,
    get_peft_model,
    get_peft_model_state_dict,
    prepare_model_for_int8_training,
    prepare_model_for_kbit_training,
    set_peft_model_state_dict,
)

# 步骤2:导入transformers库中量化相关模块
from transformers import (
    BitsAndBytesConfig,
)

# 步骤3:lora配置
lora_config = LoraConfig(  # lora配置
        r = model_args.lora_r,  # r表示秩
        lora_alpha = model_args.lora_alpha,  # alpha表示缩放因子
        # target_modules = ["query_key_value"], # 目标模块
        # target_modules =  ['q_proj', 'k_proj', 'v_proj', 'o_proj'], # 目标模块
        target_modules = model_args.target_modules,  # 目标模块
        fan_in_fan_out = False,  # 是否使用fan_in_fan_out
        lora_dropout = 0.05,  # lora_dropout
        inference_mode = False,  # 是否使用推理模式
        bias = "none",  # 偏置
        task_type = "CAUSAL_LM",  # 任务类型
    )

# 步骤4:bnb配置
bnb_config = BitsAndBytesConfig(  # bnb配置
        load_in_4bit=True,  # 是否使用4bit
        bnb_4bit_use_double_quant=True,  # 是否使用双量化
        bnb_4bit_quant_type="nf4",  # 量化类型
        bnb_4bit_compute_dtype=torch.bfloat16  # 计算类型
    )

# 步骤5:加载model
model = AutoModelForCausalLM.from_pretrained( # 从预训练模型中加载模型
    model_args.model_name_or_path, # 模型名或路径
    from_tf = bool(".ckpt" in model_args.model_name_or_path), # 是否从tensorflow加载
    config = config, # 配置
    cache_dir = model_args.cache_dir, # 缓存目录
    revision = model_args.model_revision, # 模型版本
    use_auth_token = True if model_args.use_auth_token else None, # 是否使用token
    torch_dtype = torch_dtype, # torch数据类型
    load_in_8bit = True if model_args.load_in_bits == 8 else False, # 是否使用8bit
    quantization_config = bnb_config if model_args.load_in_bits == 4 else None, # 量化配置
    device_map = {"": int(os.environ.get("LOCAL_RANK") or 0)} # 设备映射
)

# 步骤6:准备模型进行kbit训练
model = prepare_model_for_kbit_training(model) 

# 步骤7:获取peft模型
model = get_peft_model(model, lora_config)

# 步骤8:初始化Trainer
trainer = Trainer( # 训练器
    model = model, # 模型
    args = training_args, # 训练参数
    train_dataset = train_dataset if training_args.do_train else None, # 训练数据集
    eval_dataset = eval_dataset if training_args.do_eval else None, # 评估数据集
    tokenizer = tokenizer, # tokenizer
    # 数据收集器将默认为DataCollatorWithPadding,因此我们将其更改
    data_collator = transformers.DataCollatorForSeq2Seq( # 数据收集器
        tokenizer, pad_to_multiple_of=8, return_tensors="pt", padding=True # tokenizer,填充到8的倍数,返回张量,填充
    ),
    compute_metrics=compute_metrics if training_args.do_eval and not is_torch_tpu_available() else None, # 计算指标
    preprocess_logits_for_metrics=preprocess_logits_for_metrics if training_args.do_eval and not is_torch_tpu_available() else None, # 为指标预处理logits
    callbacks=([SavePeftModelCallback] if isinstance(model, PeftModel) else None), # 回调
)

  虽然LoRA微调和模型量化代码走通了,但是里面涉及到很多细节知识点需要深挖,比如LoRA具体代码实现[4][5][6],peft库支持微调方法(LoRA|Prefix Tuning|P-Tuning v1|P-Tuning v2|Prompt Tuning|AdaLoRA|LLaMA-Adapter|IA3)和模型(Causal Language Modeling|Conditional Generation|Sequence Classification|Token Classification|Text-to-Image Generation|Image Classification|Image to text (Multi-modal models)|Semantic Segmentation)的具体代码实现[4][5],模型量化(混合精度训练、4bit、8bit、fp16、fp32、bf16、AutoGPTQ库和bitsandbytes库)等。不管怎样先实践起来,更高一层的实践才能够理解低一层的理论。

参考文献:

[1]llama2 hf:https://huggingface.co/blog/llama2

[2]全参数微调时,报没有target_modules变量:https://github.com/FlagAlpha/Llama2-Chinese/issues/169

[3]finetune_clm_lora.py:https://github.com/ai408/nlp-engineering/blob/main/20230916_Llama2-Chinese/train/sft/finetune_clm_lora.py

[4]peft github:https://github.com/huggingface/peft

[5]peft hf:https://huggingface.co/docs/peft

[6]LoRA论文:https://arxiv.org/pdf/2106.09685.pdf

Llama2-Chinese项目:3.2-LoRA微调和模型量化的更多相关文章

  1. osg项目经验1<MFC+OSG中模型点选效果>

    点选主要是重载osg的GUIEventHandler, class CPickHandler : public osgGA::GUIEventHandler{ //自定义回调函数名:CPickHand ...

  2. [iOS微博项目 - 4.1] - cell的frame模型

    github: https://github.com/hellovoidworld/HVWWeibo A.cell的frame模型设计 1.需求 每个cell都有一个frame实例引用 frame模型 ...

  3. 使用 LoRA 进行 Stable Diffusion 的高效参数微调

    LoRA: Low-Rank Adaptation of Large Language Models 是微软研究员引入的一项新技术,主要用于处理大模型微调的问题.目前超过数十亿以上参数的具有强能力的大 ...

  4. 解密Prompt系列6. lora指令微调扣细节-请冷静,1个小时真不够~

    上一章介绍了如何基于APE+SELF自动化构建指令微调样本.这一章咱就把微调跑起来,主要介绍以Lora为首的低参数微调原理,环境配置,微调代码,以及大模型训练中显存和耗时优化的相关技术细节 标题这样写 ...

  5. 使用BERT预训练模型+微调进行文本分类

    本文记录使用BERT预训练模型,修改最顶层softmax层,微调几个epoch,进行文本分类任务. BERT源码 首先BERT源码来自谷歌官方tensorflow版:https://github.co ...

  6. 花 1 小时,开源设计 LoRa 继电器开关

    提示1:锐米所有 LoRa 产品严格遵循国标标准的 LoRaWAN 协议. 提示2:您可以免费复制,修改和商用本项目,请注明锐米原创. 提示3:如果您有其他 LoRa 需求或建议,欢迎联系锐米 sup ...

  7. 3.Scikit-Learn实现完整的机器学习项目

    1       完整的机器学习项目 完成项目的步骤: (1)    项目概述 (2)    获取数据 (3)    发现并可视化数据,发现规律. (4)    为机器学习算法准备数据. (5)    ...

  8. 一个完整的机器学习项目在Python中演练(四)

    大家往往会选择一本数据科学相关书籍或者完成一门在线课程来学习和掌握机器学习.但是,实际情况往往d是,学完之后反而并不清楚这些技术怎样才能被用在实际的项目流程中.就像你的脑海中已经有了一块块" ...

  9. 一个完整的机器学习项目在Python中演练(三)

    大家往往会选择一本数据科学相关书籍或者完成一门在线课程来学习和掌握机器学习.但是,实际情况往往是,学完之后反而并不清楚这些技术怎样才能被用在实际的项目流程中.就像你的脑海中已经有了一块块"拼 ...

  10. 一个完整的机器学习项目在Python中的演练(二)

    大家往往会选择一本数据科学相关书籍或者完成一门在线课程来学习和掌握机器学习.但是,实际情况往往是,学完之后反而并不清楚这些技术怎样才能被用在实际的项目流程中.就像你的脑海中已经有了一块块"拼 ...

随机推荐

  1. x.ai还是OpenAI?埃隆·马斯克的AI帝国【1】

    大家可能知道,在上个月底埃隆·马斯克与1000多名来自各个行业的专家一起联名签署了"Future of Life Institute"发起的一个请愿书,呼吁全社会暂停至少6个月的高 ...

  2. Python正则表达式完全指南

    本篇文章将深入探讨python的一项强大工具:正则表达式.正则表达式是一个强大的文本处理工具,可以用来匹配,搜索,替换和解析文本.我们将逐步展示如何在Python中使用正则表达式,包括其基本语法,常见 ...

  3. 【HarmonyOS】HarmonyOS应用APP与HAP包签名信息查看方法

    ​HarmonyOS可以通过DevEco Studio 构建对应的APP包或者是HAP包,对于签名我们有两种方式: DevEco Studio提供了根据开发者信息生成自动调试签名的能力方便各位开发者进 ...

  4. git reset --hard 撤回后commit的代码消失了的解决办法

    楼主在今天的工作中使用了这个命令 git reset --hard 撤回后commit的代码消失了,因为有commit,所以暂时得到了拯救,太不容易了,差点以为自己写的代码没了. 网上到处找帖子,看看 ...

  5. 温故知新,signalR、RSA加密、ConcurrentQueue队列

    这是一个用户密码异步加解密的Demo,包含了RSA非对称加密,ConcurrentQueue线程安全队列使用,signalR实时推送加解密信息等内容. 说在前面的话 距离上次更新已然快过去一年了,这中 ...

  6. Python运维开发之路《函数》

    函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也可以自己创建函 ...

  7. Taurus .Net Core 微服务开源框架:Admin 插件【4-2】 - 配置管理-Mvc【含请求日志打印】

    前言: 继上篇:Taurus .Net Core 微服务开源框架:Admin 插件[4-1] - 配置管理-Kestrel[含https启用] 本篇继续介绍下一个内容: 1.系统配置节点:Mvc 配置 ...

  8. Kubernetes(k8s)控制器(五):有状态应用StatefulSet

    目录 一.系统环境 二.前言 三.StatefulSet简介 四.有状态应用和无状态应用区别 五.StatefulSet 5.1 创建StatefulSet 5.2 scale扩展副本数 5.3 创建 ...

  9. 【2020GET】即构科技蒋宁波:教育行业客户需求的核心是什么?

    11月24日,由即构科技主办的2020GET大会教育科技分论坛在北京成功召开,来自叮咚课堂.小冰.360OS.蕃茄田艺术.即构科技的6位资深教育/科技大咖,在论坛上进行深度分享. 以下为即构科技联合创 ...

  10. 关于 Task 简单梳理

    〇.前言 Task 是微软在 .Net 4.0 时代推出来的,也是微软极力推荐的一种多线程的处理方式. 在 Task 之前有一个高效多线程操作累 ThreadPool,虽然线程池相对于 Thread, ...