• Llama2-Chinese项目:3.2-LoRA微调和模型量化


      提供LoRA微调和全量参数微调代码,训练数据为data/train_sft.csv验证数据data/dev_sft.csv数据格式"Human: "+问题+"\nAssistant: "+答案。本文主要介绍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 \                    # 恢复检查点
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    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), # 回调
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    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(['Human: 介绍一下北京\nAssistant: '], 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    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), # 回调
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

      虽然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

  • 相关阅读:
    tomcat Filter内存马
    Iphone文件传到电脑用什么软件,看这里
    Java的JDBC编程
    力扣------两数之和
    【Git】idea提交项目到Gitee
    Docker快速部署Apache Guacamole
    边缘计算:下一个技术前沿
    进阶vue面试题总结
    nginx参数调优能提升多少性能
    在 .NET 应用程序中运行 JavaScript
  • 原文地址:https://blog.csdn.net/shengshengwang/article/details/133471730