FunHPC乐算云:Fine-tuning Llama3.1-70B-Chinese-Chat model with Lora

Fine-tuning Llama3.1-70B-Chinese-Chat model with Lora

本文旨在探讨基于Llama factory使用LoRA(Low-Rank Adaptation)技术对Llama3.1-70B-Chinese-Chat模型进行微调的过程,重点介绍在单机多卡和多机多卡两种分布式训练环境下的实现方法。

  1. 环境准备

    1.1 平台环境

    微调Llama3.1-70B模型,本地环境跑不了。只能选择租用云上GPU。关于算力租赁平台,我选择的是FunHPC,理由如下:

    • 官网简单明了,进去就能租(无需排队),显卡类型多,总能找到适合的卡型。
    • 价格非常亲民,和其他平台相比,算是非常便宜了。
    • 提供code-server开发界面,开发方便快捷。

    磁盘空间扩容:

    • 600G

    显卡选择

    • 单机多卡训练选择的是8×A100(40G),即1台A100服务器。
    • 多机多卡训练选择的是2×8×100(40G),即2台A100服务器。

    租用镜像选择:

    • pytorch 2.2.0+cu121

    1.2 Llama factory 环境

    1.2.1 下载Llama factory
    git clone https://github.com/hiyouga/LLaMA-Factory.git
    
    1.2.2 安装 Llama factory环境
    cd LLaMA-Factory
    pip install -e ".[torch,metrics]"
    
  2. 数据集准备

    下面的python代码主要完成3个方面:

    1. 配置代理,方便访问huggingface,这里我画上了下,不方便透露了。
    2. 从huggingface上下载m-a-p/COIG-CQIA数据集,作为本次微调的数据集。
    3. 将下载后的数据集,转换成Alpaca格式。

    Alpaca格式如下:

    [
      {
        "instruction": "human instruction (required)",
        "input": "human input (optional)",
        "output": "model response (required)",
        "system": "system prompt (optional)",
        "history": [
          ["human instruction in the first round (optional)", "model response in the first round (optional)"],
          ["human instruction in the second round (optional)", "model response in the second round (optional)"]
        ]
      }
    ]
    
    import os
    import json
    from datasets import load_dataset, concatenate_datasets
    
    # 设置代理
    os.environ["http_proxy"] = "*****"
    os.environ["https_proxy"] = "*****"
    
    def prepare_data():
        data_names = ["chinese_traditional", "human_value", "wiki", "ruozhiba", "logi_qa", "wikihow", "zhihu", "douban", "xhs"]
        datasets = [load_dataset("m-a-p/COIG-CQIA", name=name, cache_dir="./data", split="train") for name in data_names]
        concatenated_dataset = concatenate_datasets(datasets)
        return concatenated_dataset
    
    def convert_to_alpaca_format(dataset):
        alpaca_data = []
        for entry in dataset:
            alpaca_entry = {
                "instruction": entry['instruction'],
                "input": entry['input'],
                "output": entry['output']
            }
            alpaca_data.append(alpaca_entry)
        return alpaca_data
    
    # 准备数据并转换为 Alpaca 格式
    datasets = prepare_data()
    alpaca_data = convert_to_alpaca_format(datasets)
    
    # 将结果保存为 JSON 文件,不转义非 ASCII 字符
    with open('alpaca_format_data.json', 'w', encoding='utf-8') as f:
        json.dump(alpaca_data, f, ensure_ascii=False, indent=4)
    
    print("转换完成,数据已保存为 'alpaca_format_data.json'")
    
    
  3. 模型准备

    3.1 模型下载

    pip install modelscope
    modelscope download --model XD_AI/Llama3.1-70B-Chinese-Chat --local_dir /data/coding/Llama3.1-70B-Chinese-Chat
    

    3.2 模型校验

    由于model比较大,会报显存错误。我将校验代码修改了一下,改成多卡调用。

    import torch
    from transformers import AutoModelForCausalLM, AutoTokenizer
    
    model_id = "/data/coding/Llama3.1-70B-Chinese-Chat"
    dtype = torch.bfloat16
    
    # 加载 tokenizer
    tokenizer = AutoTokenizer.from_pretrained(model_id)
    
    # 设置 pad_token_id
    pad_token_id = tokenizer.pad_token_id if tokenizer.pad_token_id is not None else tokenizer.eos_token_id
    
    # 使用 device_map 自动将模型切分到多 GPU
    model = AutoModelForCausalLM.from_pretrained(
        model_id,
        torch_dtype=dtype,
        device_map="auto"
    )
    
    chat = [{"role": "user", "content": "写一首关于机器学习的诗。"}]
    inputs = tokenizer.apply_chat_template(chat, tokenize=True, add_generation_prompt=True, return_tensors="pt")
    
    # 设置 attention_mask
    input_ids = inputs.input_ids.to("cuda:0")
    attention_mask = inputs.attention_mask.to("cuda:0") if "attention_mask" in inputs else None
    
    # 推理
    outputs = model.generate(
        input_ids,
        attention_mask=attention_mask,  # 添加 attention_mask
        max_new_tokens=8192,
        do_sample=True,
        temperature=0.6,
        top_p=0.9,
        pad_token_id=pad_token_id  # 设置 pad_token_id
    )
    
    response = outputs[0][input_ids.shape[-1]:]
    print(tokenizer.decode(response, skip_special_tokens=True))
    

    输出结果:

    机器学习的诗
    在数据的海洋中漂流,
    机器学习的帆船缓缓前行。
    算法的帆吸收着知识的风,
    在预测的天空下,探索未知的星辰。
    
    神经网络的缆绳,层层交织,
    像大脑的神经,智慧的图腾。
    激活函数的灯塔,指引方向,
    在复杂的迷宫中,寻找最短的路径。
    
    训练的锤炼,重复不息,
    优化的目标,精确无误。
    误差的波澜,逐渐平息,
    模型的力量,日益壮大。
    
    测试的考验,验证的时刻,
    真实世界的挑战,机遇与风险。
    泛化的梦想,跨越边界,
    机器学习的诗篇,永不止息。
    
    在这个数字的时代,
    机器学习的诗,讲述着未来。
    智能的火种,点燃希望,
    编织人类梦想的强大纽带。
    
  4. 模型训练

    在训练之前需要做的两件事儿:

    • 将alpaca_format_data.json文件放到Llama factory的data目录下。

    • 在dataset_info.json中添加:

      {

      “alpaca_format_data”: {

      “file_name”: “alpaca_format_data.json”

      },

    4.1 单机多卡训练

    我的llama3_lora_sft_ds31.yaml如下:

    ### model
    model_name_or_path: /data/coding/Llama3.1-70B-Chinese-Chat
    
    ### method
    stage: sft
    do_train: true
    finetuning_type: lora
    lora_target: all
    deepspeed: /data/coding/LLaMA-Factory/examples/deepspeed/ds_z3_offload_config.json
    
    ### dataset
    dataset: alpaca_format_data
    template: llama3
    cutoff_len: 1024
    max_samples: 30000
    overwrite_cache: true
    preprocessing_num_workers: 64
    
    ### output
    output_dir: /data/coding/LLaMA3-70B/lora/sft
    logging_steps: 10
    save_steps: 500
    plot_loss: true
    overwrite_output_dir: true
    
    ### train
    per_device_train_batch_size: 8
    gradient_accumulation_steps: 8
    learning_rate: 5.0e-4
    num_train_epochs: 5.0
    lr_scheduler_type: cosine
    warmup_ratio: 0.1
    bf16: true
    ddp_timeout: 180000000
    
    ### eval
    val_size: 0.1
    per_device_eval_batch_size: 8
    eval_strategy: steps
    eval_steps: 500
    load_best_model_at_end: true
    

    终端执行命令如下:

    FORCE_TORCHRUN=1 llamafactory-cli train /data/coding/LLaMA-Factory/examples/train_lora/llama3_lora_sft_ds31.yaml
    

    训练过程如下:

    [INFO|trainer.py:2134] 2024-08-16 09:14:59,763 >> ***** Running training *****
    [INFO|trainer.py:2135] 2024-08-16 09:14:59,763 >>   Num examples = 22,582
    [INFO|trainer.py:2136] 2024-08-16 09:14:59,763 >>   Num Epochs = 5
    [INFO|trainer.py:2137] 2024-08-16 09:14:59,763 >>   Instantaneous batch size per device = 8
    [INFO|trainer.py:2140] 2024-08-16 09:14:59,763 >>   Total train batch size (w. parallel, distributed & accumulation) = 512
    [INFO|trainer.py:2141] 2024-08-16 09:14:59,763 >>   Gradient Accumulation steps = 8
    [INFO|trainer.py:2142] 2024-08-16 09:14:59,763 >>   Total optimization steps = 220
    [INFO|trainer.py:2143] 2024-08-16 09:14:59,774 >>   Number of trainable parameters = 103,546,880
    
    {'loss': 2.021, 'grad_norm': 0.31445133686065674, 'learning_rate': 0.00022727272727272727, 'epoch': 0.23}                                                                               
    {'loss': 1.7366, 'grad_norm': 0.13742084801197052, 'learning_rate': 0.00045454545454545455, 'epoch': 0.45}                                                                                    
    {'loss': 1.6267, 'grad_norm': 0.06480135023593903, 'learning_rate': 0.0004979887032076989, 'epoch': 0.68}                                                                                         
    {'loss': 1.5846, 'grad_norm': 0.05686546117067337, 'learning_rate': 0.0004898732434036243, 'epoch': 0.91}                                                                                         
    {'loss': 1.5642, 'grad_norm': 0.05606243386864662, 'learning_rate': 0.0004757316345716554, 'epoch': 1.13}                                                                                         
    {'loss': 1.5164, 'grad_norm': 0.06566128134727478, 'learning_rate': 0.0004559191453574582, 'epoch': 1.36}                                                                                         
    {'loss': 1.5104, 'grad_norm': 0.06976603716611862, 'learning_rate': 0.0004309335095262675, 'epoch': 1.59}                                                                                         
    {'loss': 1.4946, 'grad_norm': 0.06345170736312866, 'learning_rate': 0.00040140242178441667, 'epoch': 1.81}                                                                                        
    {'loss': 1.4851, 'grad_norm': 0.07551503926515579, 'learning_rate': 0.0003680677686931707, 'epoch': 2.04}                                                                                         
    {'loss': 1.4064, 'grad_norm': 0.11773069202899933, 'learning_rate': 0.00033176699082935546, 'epoch': 2.27}                                                                                        
    {'loss': 1.411, 'grad_norm': 0.09556800872087479, 'learning_rate': 0.00029341204441673266, 'epoch': 2.49}                                                                                         
    {'loss': 1.4018, 'grad_norm': 0.09359253197908401, 'learning_rate': 0.000253966490958702, 'epoch': 2.72}                                                                                          
    {'loss': 1.3875, 'grad_norm': 0.1037076786160469, 'learning_rate': 0.00021442129043167875, 'epoch': 2.95}                                                                                         
    {'loss': 1.3314, 'grad_norm': 0.12813276052474976, 'learning_rate': 0.00017576990616793137, 'epoch': 3.17}                                                                                        
    {'loss': 1.3125, 'grad_norm': 0.1260678470134735, 'learning_rate': 0.00013898334684855646, 'epoch': 3.4}                                                                                          
    {'loss': 1.2788, 'grad_norm': 0.13497693836688995, 'learning_rate': 0.00010498577260720049, 'epoch': 3.63}                                                                                        
    {'loss': 1.2677, 'grad_norm': 0.12839122116565704, 'learning_rate': 7.463127807341966e-05, 'epoch': 3.85}                                                                                         
    {'loss': 1.2469, 'grad_norm': 0.175085186958313, 'learning_rate': 4.8682435617235344e-05, 'epoch': 4.08}                                                                                          
    {'loss': 1.2235, 'grad_norm': 0.1542418748140335, 'learning_rate': 2.7791137836269158e-05, 'epoch': 4.31}                                                                                         
    {'loss': 1.1948, 'grad_norm': 0.14159728586673737, 'learning_rate': 1.2482220564763668e-05, 'epoch': 4.53}                                                                                        
    {'loss': 1.2039, 'grad_norm': 0.13814429938793182, 'learning_rate': 3.1402778309014278e-06, 'epoch': 4.76}                                                
    {'loss': 1.2098, 'grad_norm': 0.13053908944129944, 'learning_rate': 0.0, 'epoch': 4.99} 
    
    {'train_runtime': 88370.9016, 'train_samples_per_second': 1.278, 'train_steps_per_second': 0.002, 'train_loss': 1.4279815023595637, 'epoch': 4.99}   
    
    [INFO|trainer.py:3821] 2024-08-17 09:50:24,508 >>   Num examples = 2510
    [INFO|trainer.py:3824] 2024-08-17 09:50:24,508 >>   Batch size = 8
    100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 40/40 [14:59<00:00, 22.49s/it]
    ***** eval metrics *****
      epoch                   =     4.9858
      eval_loss               =     1.5017
      eval_runtime            = 0:15:24.41
      eval_samples_per_second =      2.715
      eval_steps_per_second   =      0.043
    

    训练时间大致:15小时

    显卡占用如下:
    在这里插入图片描述

    训练loss如下:

    在这里插入图片描述

    4.2 多机多卡训练

    训练的yaml文件和单机多卡一致。

    终端执行命令如下:

    主节点:

    FORCE_TORCHRUN=1 NNODES=2 RANK=0 MASTER_ADDR=10.222.51.150 MASTER_PORT=29500 llamafactory-cli train /data/coding/LLaMA-Factory/examples/train_lora/llama3_lora_sft_ds31.yaml
    

    次节点:

    FORCE_TORCHRUN=1 NNODES=2 RANK=1 MASTER_ADDR=10.222.51.150 MASTER_PORT=29500 llamafactory-cli train /data/coding/LLaMA-Factory/examples/train_lora/llama3_lora_sft_ds31.yaml
    

    训练时间大致:155小时

    {'loss': 1.9696, 'grad_norm': 0.10808725655078888, 'learning_rate': 0.00045454545454545455, 'epoch': 0.45}                                                                                        
     13%|██████████████████▋                                            | 14/110 [22:44:00<155:12:34, 5820.36s/it
    

    由于这里训练时间较长,就不展示训练过程及训练loss。

  5. 模型效果展示

    CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 llamafactory-cli chat  --model_name_or_path /data/coding/Llama3.1-70B-Chinese-Chat  --adapter_name_or_path /data/coding/LLaMA3-70B/lora/sft --template llama3 --finetuning_type lora
    

    推理效果如下:

    User: 为什么昙花开花时间非常短?
    Assistant: 昙花原产于南美洲墨西哥、危地马拉、委内瑞拉等热带地区。昙花的花朵非常美丽,花朵颜色有红色、白色、紫色、粉红色、黄色等。它的花朵大多数在晚上开,第二天早晨就凋谢了,因此它又叫“一夜夫人”、“夜合花”等。昙花的花朵为什么时间这么短呢?原来昙花是喜暖的植物,白天温度高时,花瓣的伸展能力变弱,不能正常地舒展开来;而到了晚上温度低时,花瓣的伸展能力增强,花朵就开放了。到了第二天早晨,气温升高,花瓣的伸展能力又减弱,花朵便慢慢地合拢,最后就枯萎凋谢了。昙花的花朵时间短是由它的生理特性决定的。
    
    User: 为什么英国曾被称为“日不落帝国”?
    Assistant: 英国曾被称为“日不落帝国”,这是因为在18世纪和19世纪的殖民战争中,英国占领了世界各地的许多地方,从而建立了一个庞大的殖民帝国。当时英国在世界各地的殖民地非常多,无论什么时候,英国总有地方是白天。英国的这种特殊地位使它在当时被称为“日不落帝国”。但是,随着时间的推移,英国的殖民地逐渐独立,英国的殖民地数量也在不断减少,最终英国的殖民地几乎全部独立,英国的“日不落帝国”也随之消失了。
    
    User: 给我介绍一下算力显卡
    Assistant: 算力显卡是指那些专门用于计算的显卡。这些显卡通常具有非常高的性能和非常低的功耗,它们可以帮助用户进行各种复杂的计算任务,如数据挖掘、机器学习等。算力显卡的主要特点是它们具有非常高的浮点运算能力,这使得它们可以快速地处理大量的数据并产生高质量的结果。此外,算力显卡还具有非常低的功耗,这使得它们可以在长时间的运行中保持稳定的性能。
    

    6.总结

    ​ 本文通过单机多卡和多机多卡的情况下,在FunHPC乐算云,算力稳、快、趣、省好选择!完成基于Llama factory使用LoRA(Low-Rank Adaptation)技术对Llama3.1-70B-Chinese-Chat模型进行微调的过程。总体的推理效果不错。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值