百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程文章 > 正文

【从零开始】11. LLaMA-Factory 微调 Qwen 模型(番外篇)

qiyuwang 2025-04-07 18:43 9 浏览 0 评论

书接上回,在完成了 RAGChecker 测试后,离 RAG 应用真正发布还差最后一步 - 基础信息指令微调。考虑到模型还是需要具备一定程度的“自我认知”,因此需要将公司信息“嵌入”到模型里面的。为此,我选择了 LLaMA-Factory(以下简称“lf”)去完成这个事儿。

之所以选 lf 还是因为它简单,好使...

1. LLaMA-Factory 部署

本次 lf 微调我们将采用源码方式部署。

首先从 github 将代码 checkout 下来,如下图:

(base) pai@pai:~/llm/nlp$ git clone https://github.com/hiyouga/LLaMA-Factory.git

为什么要使用源码方式部署?后面会讲解原因。

2. 创建虚拟环境并安装依赖

(base) pai@pai:~/llm/nlp$ cd LLaMA-Factory
(base) pai@pai:~/llm/nlp/LLaMA-Factory$ conda create -n lf  python=3.11
Channels:
 - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
 - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r
 - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2
 - defaults
Platform: linux-64
Collecting package metadata (repodata.json): done
Solving environment: done
## Package Plan ##

  environment location: /home/pai/anaconda3/envs/lf

  added / updated specs:
    - python=3.11
  ...

使用 conda 创建运行环境后切换到源码目录通过pip install -r requirements.txt命令安装依赖,如下图:

(base) pai@pai:~/llm/nlp/LLaMA-Factory$ conda activate lf
(lf) pai@pai:~/llm/nlp/LLaMA-Factory$ pip install -r requirements.txt 
...

3. 组件安装

根据官网的提示,if 是可以选择组件进行安装的,基础版本可以只选 torch 和 metrics 安装即可。但是装都装了,不差那么点事儿,索性全部能装的都装上吧。如下图:

(lf) pai@pai:~/llm/nlp/LLaMA-Factory$ pip install -e ".[torch,metrics,deepspeed,liger-kernel,bitsandbytes,hqq,gptq,awq,aqlm,vllm,galore,badam,adam-mini,qwen,modelscope,quality]"
...

安装过程中可能会出现一堆像“无法连接”、“依赖缺失”等情况,这时只需离线安装即可(之前文章已经提到过如何离线安装,这里就不再详述了)。

4. 启动 webui 页面

完成组件安装后就可以尝试启动 webui 界面了,一般来说第一次启动会伴随着报错,如下图:

(lf) pai@pai:~/llm/nlp/LLaMA-Factory$ CUDA_VISIBLE_DEVICES=0 python src/webui.py
[2024-11-01 04:01:01,361] [INFO] [real_accelerator.py:203:get_accelerator] Setting ds_accelerator to cuda (auto detect)
df: /home/pai/.triton/autotune: No such file or directory
 [WARNING]  async_io requires the dev libaio .so object and headers but these were not found.
 [WARNING]  async_io: please install the libaio-dev package with apt
 [WARNING]  If libaio is already installed (perhaps from source), try setting the CFLAGS and LDFLAGS environment variables to where it can be found.
 [WARNING]  Please specify the CUTLASS repo directory as environment variable $CUTLASS_PATH
 [WARNING]  sparse_attn requires a torch version >= 1.5 and < 2.0 but detected 2.4
 [WARNING]  using untested triton version (3.0.0), only 1.0.0 is known to be compatible
/home/pai/anaconda3/envs/lf/lib/python3.11/site-packages/deepspeed/runtime/zero/linear.py:47: FutureWarning: `torch.cuda.amp.custom_fwd(args...)` is deprecated. Please use `torch.amp.custom_fwd(args..., device_type='cuda')` instead.
  @autocast_custom_fwd
/home/pai/anaconda3/envs/lf/lib/python3.11/site-packages/deepspeed/runtime/zero/linear.py:66: FutureWarning: `torch.cuda.amp.custom_bwd(args...)` is deprecated. Please use `torch.amp.custom_bwd(args..., device_type='cuda')` instead.
  @autocast_custom_bwd
/home/pai/anaconda3/envs/lf/lib/python3.11/site-packages/vllm/connections.py:8: RuntimeWarning: Failed to read commit hash:
No module named 'vllm._version'
  from vllm.version import __version__ as VLLM_VERSION
Running on local URL:  http://0.0.0.0:7860

To create a public link, set `share=True` in `launch()`.

以上报错是因为找不到“vllm._version”模块导致的,这时需要更新一下 vllm 依赖,如下图:

(lf) pai@pai:~/llm/nlp/LLaMA-Factory$ pip install --upgrade vllm
Looking in indexes: https://mirrors.aliyun.com/pypi/simple
Requirement already satisfied: vllm in /home/pai/anaconda3/envs/lf/lib/python3.11/site-packages (0.6.3)
Collecting vllm
  Downloading https://mirrors.aliyun.com/pypi/packages/4a/4c/ee65ba33467a4c0de350ce29fbae39b9d0e7fcd887cc756fa993654d1228/vllm-0.6.3.post1-cp38-abi3-manylinux1_x86_64.whl (194.8 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 194.8/194.8 MB 1.4 MB/s eta 0:00:00
...

一般将依赖更新到最新版后就能解决问题。再尝试启动 webui。

喔!抛出了另一个异常信息,如下图:

(lf) pai@pai:~/llm/nlp/LLaMA-Factory$ llamafactory-cli webui
[2024-11-01 05:46:26,356] [INFO] [real_accelerator.py:203:get_accelerator] Setting ds_accelerator to cuda (auto detect)
 [WARNING]  async_io requires the dev libaio .so object and headers but these were not found.
 [WARNING]  async_io: please install the libaio-dev package with apt
 [WARNING]  If libaio is already installed (perhaps from source), try setting the CFLAGS and LDFLAGS environment variables to where it can be found.
 [WARNING]  Please specify the CUTLASS repo directory as environment variable $CUTLASS_PATH
 [WARNING]  sparse_attn requires a torch version >= 1.5 and < 2.0 but detected 2.4
 [WARNING]  using untested triton version (3.0.0), only 1.0.0 is known to be compatible
/home/pai/anaconda3/envs/lf/lib/python3.11/site-packages/deepspeed/runtime/zero/linear.py:47: FutureWarning: `torch.cuda.amp.custom_fwd(args...)` is deprecated. Please use `torch.amp.custom_fwd(args..., device_type='cuda')` instead.
  @autocast_custom_fwd
/home/pai/anaconda3/envs/lf/lib/python3.11/site-packages/deepspeed/runtime/zero/linear.py:66: FutureWarning: `torch.cuda.amp.custom_bwd(args...)` is deprecated. Please use `torch.amp.custom_bwd(args..., device_type='cuda')` instead.
  @autocast_custom_bwd
Running on local URL:  http://0.0.0.0:7860

To create a public link, set `share=True` in `launch()`.

这次看到的是 deepspeed 警告,那么同理更新一下 deepspeed 吧。

(lf) pai@pai:~/llm/nlp/LLaMA-Factory$ pip install --upgrade deepspeed
...

更新后再启动就看不到其他异常信息抛出了。如下图:

(lf) pai@pai:~/llm/nlp/LLaMA-Factory$ llamafactory-cli webui
[2024-11-01 06:46:43,215] [INFO] [real_accelerator.py:219:get_accelerator] Setting ds_accelerator to cuda (auto detect)
Running on local URL:  http://0.0.0.0:7860

To create a public link, set `share=True` in `launch()`.

5. 上传自定义数据

验证完 lf 正常启动后,在正式指令微调前,我们还需要上传自己的数据。

首先,我们需要创建自己的数据集并保存成 json 文件。我这里创建的是 alpaca 格式的数据集,具体格式如下:

[
  {
    "instruction": "<<这里是问题>>",
    "input": "",
    "output": "<<这里是答案>>"
  },
  ...
]

在完成了数据集的整理之后将数据集上传到项目的 data 文件夹中,如下图:

接着修改 data 文件夹下的 dataset_info.json 文件,如下图:

{
    "enterprise":{
      "file_name": "enterprise_tuning.json",
      "columns": {
        "prompt": "instruction",
        "query": "input",
        "response": "output",
        "history": "history"
      }
  },
  ...
}

这里的配置主要是对 columns 进行映射并将数据集作为系统级别命名为“enterprise”。

6. 指令微调

微调数据集准备完毕,接下来需要做一下微调配置。首先,在examples/train_lora/路径下创建一个 Lora 微调配置文件qwen2_5_lora_sft.yaml,如下图:

### model
model_name_or_path: /home/pai/.cache/huggingface/hub/models--Qwen--Qwen2.5-7B-Instruct/snapshots/bb46c15ee4bb56c5b63245ef50fd7637234d6f75

### method
stage: sft
do_train: true
finetuning_type: lora
lora_target: all

### dataset
dataset: identity,enterprise
template: qwen
cutoff_len: 2048
max_samples: 4000
overwrite_cache: true
preprocessing_num_workers: 16

### output
output_dir: /home/pai/llm/nlp/LLaMA-Factory/saves/Qwen2.5-7B/lora
logging_steps: 10
save_steps: 500
plot_loss: true
overwrite_output_dir: true

### train
per_device_train_batch_size: 8
gradient_accumulation_steps: 2
learning_rate: 1.0e-4
num_train_epochs: 3.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
ddp_timeout: 180000000

### eval
val_size: 0.1
per_device_eval_batch_size: 1
eval_strategy: steps
eval_steps: 500

这里感谢 CSDN@路人与大师 提供的配置信息(文章里面详细地解释了每个参数的意思,受益良多,感谢分享),原文地址:llama factory lora 微调 qwen2.5 7B Instruct模型_qwen2.5 lora微调-CSDN博客

第一次的微调配置就以上面文章中提供的配置信息进行了部分调整而来的。接着执行

(lf) pai@pai:~/llm/nlp/LLaMA-Factory$ llamafactory-cli train examples/train_lora/qwen2_5_lora_sft.yaml

开始执行。

7. 微调报错处理

在开始执行不久,也就是在读取数据集的时候就抛出了以下错误,如下图:

Converting format of dataset (num_proc=16):   0%|                                                                                                                                                                                             | 0/3601 [00:00<?, ? examples/s]
multiprocess.pool.RemoteTraceback: 
"""
Traceback (most recent call last):
  File "/home/pai/anaconda3/envs/lf/lib/python3.11/site-packages/multiprocess/pool.py", line 125, in worker
    result = (True, func(*args, **kwds))
                    ^^^^^^^^^^^^^^^^^^^
  File "/home/pai/anaconda3/envs/lf/lib/python3.11/site-packages/datasets/utils/py_utils.py", line 678, in _write_generator_to_queue
    for i, result in enumerate(func(**kwargs)):
  File "/home/pai/anaconda3/envs/lf/lib/python3.11/site-packages/datasets/arrow_dataset.py", line 3528, in _map_single
    example = apply_function_on_filtered_inputs(example, i, offset=offset)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pai/anaconda3/envs/lf/lib/python3.11/site-packages/datasets/arrow_dataset.py", line 3427, in apply_function_on_filtered_inputs
    processed_inputs = function(*fn_args, *additional_args, **fn_kwargs)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pai/llm/nlp/LLaMA-Factory/src/llamafactory/data/aligner.py", line 84, in convert_alpaca
    if dataset_attr.history and isinstance(example[dataset_attr.history], list):
                                           ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pai/anaconda3/envs/lf/lib/python3.11/site-packages/datasets/formatting/formatting.py", line 277, in __getitem__
    value = self.data[key]
            ~~~~~~~~~^^^^^
KeyError: 'history'

经验证,这个错误除了源码部署时出现外,在 docker 环境下也会出现(不知道是不是配置有误,按理说 docker 环境下不会出现这种情况)。在网上找了一圈,最终在 github 中找到的解决方案:
https://github.com/hiyouga/LLaMA-Factory/issues/2490

这里需要对源码进行修改,如下图:

BTW,在修改代码后需要删除项目中对应的 pyc 文件,再之后直接通过python src/train.py来启动。如下图:

(lf) pai@pai:~/llm/nlp/LLaMA-Factory$ CUDA_VISIBLE_DEVICES=0 python src/train.py examples/train_lora/qwen2_5_lora_sft.yaml

在开始微调后你或许还会遇到AttributeError: 'AdamW' object has no attribute 'train'报错。这时可以尝试将 transformers 进行降级处理。

AttributeError: ‘AdamW’ object has no attribute ‘train’ · Issue #33620 · huggingface/transformers

按照大神的说法将 transformers 降级到 4.44.2 版本即可,亲测有效。

8. 第一次微调

好了,现在让我们进行第一次微调吧,生成结果如下:

***** train metrics *****
  epoch                    =        3.0
  total_flos               = 60516616GF
  train_loss               =     0.4413
  train_runtime            = 0:17:44.80
  train_samples_per_second =      9.506
  train_steps_per_second   =      0.594
...
***** eval metrics *****
  epoch                   =        3.0
  eval_loss               =      0.368
  eval_runtime            = 0:00:18.79
  eval_samples_per_second =     19.955
  eval_steps_per_second   =     19.955

嗯...这样的结果只能说是中规中矩,毕竟验证损失比训练损失小,说明模型没有过拟合,但还是有继续优化的空间。

9. 第二次微调

鉴于第一次微调的结果,我调整了一下配置文件的参数:

### output
save_steps: 200

### train
gradient_accumulation_steps: 4
learning_rate: 3.0e-5
num_train_epochs: 4.0
warmup_ratio: 0.03

### eval
eval_steps: 200
  1. learning_rate = 1e-4 可能偏大,先尝试降到 3.0e-5 试试;
  2. 使用更温和的 warmup_ratio = 0.03;
  3. 第一次 num_train_epochs = 3.0,增加到 4 轮;
  4. 同时调整 eval_steps 和 save_steps 为 200 以更密切监控训练过程;
  5. 尝试将 gradient_accumulation_steps 调整为 2;

最终得到这样的结果:

***** train metrics *****
  epoch                    =      3.981
  total_flos               = 80078311GF
  train_loss               =       0.68
  train_runtime            = 0:23:35.31
  train_samples_per_second =      9.536
  train_steps_per_second   =      0.297
...
***** eval metrics *****
  epoch                   =      3.981
  eval_loss               =     0.4965
  eval_runtime            = 0:00:19.50
  eval_samples_per_second =     19.227
  eval_steps_per_second   =     19.227

额...第二次的损失值反而上升了,这可能表明模型产生了一定程度的过拟合或学习率设置不够合适。

10. 第三次微调

根据第二次微调的结果,我又做了以下的调整:

### train
gradient_accumulation_steps: 2
learning_rate: 7.0e-5
num_train_epochs: 2.0
warmup_ratio: 0.1
weight_decay: 0.1

### lora
lora_rank: 8
lora_alpha: 32
lora_dropout: 0.1
  1. 增加了 weight_decay 参数来增加正则化;
  2. 取消了 ddp_timeout 参数;
  3. gradient_accumulation_steps 改回 2 ,因为 4 可能过大;
  4. 将 warmup_ratio 调回 0.1;
  5. 尝试减少训练轮次到 2 轮,因为当前可能训练过度;
  6. 第二次微调的学习率可能过小,导致模型收敛较慢,这里调整到 7.0e-5;

最终得到这样的结果:

***** train metrics *****
  epoch                    =        2.0
  total_flos               = 40136469GF
  train_loss               =     0.5282
  train_runtime            = 0:12:54.95
  train_samples_per_second =      8.708
  train_steps_per_second   =      0.545
...
***** eval metrics *****
  epoch                   =        2.0
  eval_loss               =     0.3871
  eval_runtime            = 0:00:18.87
  eval_samples_per_second =     19.872
  eval_steps_per_second   =     19.872

Yo~,从结果看来这样的配置调整是有效的,相较于第二次微调有了明显的改善。并且 train_loss 和 eval_loss 的差距合理,没有明显过拟合。

11. 第四次微调

好,现在明确了方向。接下来就向着这个方向走就可以了。

### dataset
max_samples: 5000

### output
save_steps: 100

### train
learning_rate: 8.0e-5
num_train_epochs: 2.5
warmup_ratio: 0.15

### lora
lora_rank: 16
lora_alpha: 64

### eval
val_size: 0.15
eval_steps: 100
  1. 继续提高学习率到 8.0e-5;
  2. 适当地增加训练轮次;
  3. 增加预热比例到 0.15;
  4. 增加 LoRA 秩到 16,alpha 值到 64;
  5. eval_steps 和 save_steps 降低到 100;

最终得到这样的结果:

***** train metrics *****
  epoch                    =     2.4962
  total_flos               = 47735668GF
  train_loss               =     0.4542
  train_runtime            = 0:16:31.14
  train_samples_per_second =      8.036
  train_steps_per_second   =      0.502
...
***** eval metrics *****
  epoch                   =     2.4962
  eval_loss               =     0.3504
  eval_runtime            = 0:00:28.31
  eval_samples_per_second =     19.884
  eval_steps_per_second   =     19.884

eval_loss 持续下降到了0.3504,这是目前最好的结果。train_loss 和 eval_loss 的差距合理(约0.1),说明没有过拟合。

12. 结论

从第四次之后其实已经找到了微调的方向,后面又陆续开展了其他参数的微调工作,这一共做了十一次。分析结果如下:

第一次:train_loss=0.4413, eval_loss=0.368   (差距0.0733)
第二次:train_loss=0.68,   eval_loss=0.4965  (差距0.1835)  最差
第三次:train_loss=0.5282, eval_loss=0.3871  (差距0.1411)
第四次:train_loss=0.4542, eval_loss=0.3504  (差距0.1038)
第五次:train_loss=0.4184, eval_loss=0.3423  (差距0.0761)  eval_loss最佳
第六次:train_loss=0.4413, eval_loss=0.3624  (差距0.0789)
第七次:train_loss=0.3834, eval_loss=0.3602  (差距0.0232)  train_loss最佳
第八次:train_loss=0.4377, eval_loss=0.356   (差距0.0817)
第九次:train_loss=0.4069, eval_loss=0.357   (差距0.0499)
第十次:train_loss=0.4219, eval_loss=0.3553  (差距0.0666)
第十一次:train_loss=0.4393, eval_loss=0.3582 (差距0.0811)

根据观察 loss 的情况可知,train_loss 基本保持在 0.38-0.44 之间,而 eval_loss 则保持在 0.35-0.36 之间,并且最近 5 次的结果都相当稳定。其中,最佳的 train_loss 发生在第七次为 0.3834,而最佳的 eval_loss 发生在第五次为 0.3423,两者之间最小差距发生在第七次为 0.0232。

从收敛趋势来看,从第七次以后,性能提升已经相当小了,最近几次的调整没有带来明显改善且 loss 值在一个相对稳定的区间内波动。

因此我感觉已经没有继续优化的必要了,就拿回第七次的配置就可以了。

哦,在完成微调之后其实并没有结束微调工作,还有更大一部分内容在于对模型输出的验证,这个各位可以根据自己实际的业务需要进行人工校验。

至此,LLaMA-Factory 微调正式结束。

(未完待续...)

相关推荐

# 安装打开 ubuntu-22.04.3-LTS 报错 解决方案

#安装打开ubuntu-22.04.3-LTS报错解决方案WslRegisterDistributionfailedwitherror:0x800701bcError:0x80070...

利用阿里云镜像在ubuntu上安装Docker

简介:...

如何将Ubuntu Kylin(优麒麟)19.10系统升级到20.04版本

UbuntuKylin系统使用一段时间后,有新的版本发布,如何将现有的UbuntuKylin系统升级到最新版本?可以通过下面的方法进行升级。1.先查看相关的UbuntuKylin系统版本情况。使...

Ubuntu 16.10内部代号确认为Yakkety Yak

在正式宣布Ubuntu16.04LTS(XenialXerus)的当天,Canonical创始人MarkShuttleworth还非常开心的在个人微博上宣布Ubuntu下个版本16.10的内...

如何在win11的wsl上装ubuntu(怎么在windows上安装ubuntu)

在Windows11的WSL(WindowsSubsystemforLinux)上安装Ubuntu非常简单。以下是详细的步骤:---...

Win11学院:如何在Windows 11上使用WSL安装Ubuntu

IT之家2月18日消息,科技媒体pureinfotech昨日(2月17日)发布博文,介绍了3中简便的方法,让你轻松在Windows11系统中,使用WindowsSubs...

如何查看Linux的IP地址(如何查看Linux的ip地址)

本头条号每天坚持更新原创干货技术文章,欢迎关注本头条号"Linux学习教程",公众号名称“Linux入门学习教程"。...

怎么看电脑系统?(怎么看电脑系统配置)

要查看电脑的操作系统信息,可以按照以下步骤操作,根据不同的操作系统选择对应的方法:一、Windows系统通过系统属性查看右键点击桌面上的“此电脑”(或“我的电脑”)图标,选择“属性”。在打开的...

如何查询 Linux 内核版本?这些命令一定要会!

Linux内核是操作系统的核心,负责管理硬件资源、调度进程、处理系统调用等关键任务。不同的内核版本可能支持不同的硬件特性、提供新的功能,或者修复了已知的安全漏洞。以下是查询内核版本的几个常见场景:...

深度剖析:Linux下查看系统版本与CPU架构

在Linux系统管理、维护以及软件部署的过程中,精准掌握系统版本和CPU架构是极为关键的基础操作。这些信息不仅有助于我们深入了解系统特性、判断软件兼容性,还能为后续的软件安装、性能优化提供重要依据。接...

504 错误代码解析与应对策略(504错误咋解决)

在互联网的使用过程中,用户偶尔会遭遇各种错误提示,其中504错误代码是较为常见的一种。504错误并非意味着网站被屏蔽,它实际上是指服务器在规定时间内未能从上游服务器获取响应,专业术语称为“Ga...

猎聘APP和官网崩了?回应:正对部分职位整改,临时域名可登录

10月12日,有网友反映猎聘网无法打开,猎聘APP无法登录。截至10月14日,仍有网友不断向猎聘官方微博下反映该情况,而猎聘官方微博未发布相关情况说明,只是在微博内对反映该情况的用户进行回复,“抱歉,...

域名解析的原理是什么?域名解析的流程是怎样的?

域名解析是网站正常运行的关键因素,因此网站管理者了解域名解析的原理和流程对于做好域名管理、解决常见解析问题,保障网站的正常运转十分必要。那么域名解析的原理是什么?域名解析的流程是怎样的?接下来,中科三...

Linux无法解析域名的解决办法(linux 不能解析域名)

如果由于误操作,删除了系统原有的dhcp相关设置就无法正常解析域名。  此时,需要手动修改配置文件:  /etc/resolv.conf  将域名解析服务器手动添加到配置文件中  该文件是DNS域名解...

域名劫持是什么?(域名劫持是什么)

域名劫持是互联网攻击的一种方式,通过攻击域名解析服务器(DNS),或伪造域名解析服务器(DNS)的方法,把目标网站域名解析到错误的地址从而实现用户无法访问目标网站的目的。说的直白些,域名劫持,就是把互...

取消回复欢迎 发表评论: