大模型系列之LLaMA Factory微调学习
2025-1-24 10:16:0 Author: mp.weixin.qq.com(查看原文) 阅读量:0 收藏

关于LLaMA Factory

在人工智能技术迅速发展的今天,如何高效地微调和部署大型语言模型(LLM)成为了研究和应用的热点。Llama-Factory 作为一个开源的微调框架,正是在这一背景下应运而生。它旨在为开发者提供一个简便、高效的工具,以便在现有的预训练模型基础上,快速适应特定任务需求,提升模型表现。

Llama-Factory 支持多种流行的语言模型,如 LLaMA、BLOOM、Mistral、Baichuan 等,涵盖了广泛的应用场景。从学术研究到企业应用,Llama-Factory 都展示了其强大的适应能力和灵活性。此外,Llama-Factory 配备了用户友好的 LlamaBoard Web 界面,降低了使用门槛,使得即便是没有深厚编程背景的用户,也能轻松进行模型微调和推理操作。

Llama-Factory 的出现,不仅为开发者节省了大量的时间和资源,还推动了 AI 技术的普及和应用。通过它,更多的人能够参与到 AI 模型的定制和优化中,推动整个行业的创新与发展。

本文将对Llama-Factory框架进行学习,并将微调后的模型通过Ollama部署。

环境搭建

训练大模型需要GPU,出于学习的目的,没有选择阿里云PAI平台,而是直接创建ECS,手动复现整个过程。

计划训练7B的模型,经过对比选择了如下配置的机器:

CPU:8
GPU:1
GPU类型:NVIDIA A10
GPU显存:24 GB
Memory:30 GB
带宽:16.00 Gbps
系统盘大小:500 GB

系统选择Ubuntu,并且帮我们默认安装好GPU驱动。

需要等一会,GPU驱动安装好后才能连接ECS

输入nvidia-smi就可以看到我们的显卡了

参考官方安装步骤

https://github.com/hiyouga/LLaMA-Factory/blob/main/README_zh.md

git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
pip install -e ".[torch,metrics]"

安装完毕后输入如下命令,打开可视化界面

llamafactory-cli webui

微调需要数据,在这里我找了一份阿里云教程中公开的数据集

https://help.aliyun.com/zh/pai/use-cases/fine-tune-a-llama-3-model-with-llama-factory

cd LLaMA-Factory
wget https://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/release/tutorials/llama_factory/data.zip
mv data rawdata && unzip data.zip -d data

内容主要是一些角色扮演,多轮对话

[
{
    "conversations": [
      {
        "from": "human",
        "value": "模仿西游记的孙悟空的语气和我聊天。如果你把水冻住,你会得到什么?"
      },
      {
        "from": "gpt",
        "value": "哈哈,这问题还难倒俺老孙吗?把水冻住就是变成冰啦!不过若是在取经的路上用得着这冰来做啥,比如过火焰山时,那可就是解渴消暑的好东西了!你觉得呢?还有没有更有趣的问题让俺老孙来解答?"
      },
      {
        "from": "human",
        "value": "那怎么做才可以把水冻住"
      },
      {
        "from": "gpt",
        "value": "哈哈,对俺老孙来说,这可是轻而易举的事情!只需将水放在凉快的地方,或者使用法力,一吹仙气,水便能迅速结冰啦!当然,在你的世界里,把水放进冰箱的冷冻室里等一阵子,水就会变成冰块了。要是嫌慢,可以尝试用点干冰或液氮什么的,不过得小心使用哈!毕竟不像俺老孙有抗寒法力。"
      }
    ]
}
]

微调参数配置

有可视化界面还是很方便的,通过下拉框直接可以选择基模型,并且自动拉取下载。

选择我们的微调数据,预览数据集,可以看到正常识别

微调会涉及到许多参数设置,LLaMA Factory里已经内置了一些推荐的默认值。

这里涉及的参数很多,挑了一些比较重要的参数进行介绍。

微调方法

内置的微调方法有以下三种:

  • • full:全参微调,将整个模型都进行微调,对显存要求巨大。
  • • freeze:冻结微调,将模型的大部分参数冻结,只对部分参数进行微调,可以降低对显存的要求。
  • • lora:将模型的部分参数冻结,只对部分参数进行微调,但只在特定的层上进行微调,极大节约显存。

默认情况下为lora,因为使用lora轻量化微调方法能极大程度地节约显存。

学习率

学习率是最重要的超参数之一,它决定了在每次参数更新时参数改变的幅度。一个太大的学习率可能会导致模型训练不稳定,而太小的学习率会导致训练过程缓慢。微调时,通常使用比预训练阶段更小的学习率,因为我们希望模型参数的改变更加细微,以免破坏已学到的有用信息。

关于学习率看到了一个比较形象的解释:学习率有点像教一个小孩学习新知识,如果你一次性给他太多的信息,他可能会感觉到困惑,无法吸收。但是如果给的信息太少,学习进度又会非常慢。

学习率到底设置多少算是合适呢?看了一圈文章下来总结就是:得试。

建议从保守的小值开始,然后根据训练过程的反馈进行调整。同时使用学习率调度和自适应优化器可以帮助在训练过程中自动调整学习率,提高模型性能。

常见的学习率参数包括但不限于:

  • • 1e-1(0.1):相对较大的学习率,用于初期快速探索。
  • • 1e-2(0.01):中等大小的学习率,常用于许多标准模型的初始学习率。
  • • 1e-3(0.001):较小的学习率,适用于接近优化目标时的细致调整。
  • • 1e-4(0.0001):更小的学习率,用于当模型接近收敛时的微调。
  • • 5e-5(0.00005):非常小的学习率,常见于预训练模型的微调阶段,例如在自然语言处理中微调BERT模型。

下面是大模型给的一些建议:

  • • 快速探索:在模型训练初期或者当你不确定最佳参数时,可以使用较大的学习率(例如0.1或0.01),快速找到一个合理的解。
  • • 细致调整:当你发现模型的性能开始稳定,但还需要进一步优化时,可以减小学习率(例如0.001或0.0001),帮助模型更精确地找到最优解。
  • • 微调预训练模型:当使用已经预训练好的模型(如在特定任务上微调BERT)时,通常使用非常小的学习率(例如5e-5或更小),这是因为预训练模型已经非常接近优化目标,我们只需要做一些轻微的调整。

截断长度

Cutoff Length是训练句子截断长度,句子越长,显存占用越多,如果显存不够可以考虑降低到512甚至256。可以根据微调目标需要的长度进行设置。微调后,模型处理长度大于Cutoff Length的句子的能力会下降。

计算类型

大模型的计算精度是指在训练和推理过程中,模型参数和计算操作所使用的数值表示方式的精确程度。

这里介绍一下背景知识:float 和 double 类型的数据在内存中以二进制方式存储,由三部分组成:

  • • 符号位 S(Sign): 0 代表正数,1 代表负数
  • • 指数位 E(Exponent): 用于存储科学计数法中的指数部分,决定了数据的范围
  • • 尾数位 M(Mantissa): 用于存储尾数(小数)部分,决定了数据的精度

下面是 FP16 和 FP32 (float) 的存储示例图:

最早的 GPU 默认使用 FP32 类型进行运算,但随着模型越来越大,FP32 类型占内存/显存资源大且运算速度慢的问题逐渐暴露了出来。为了降低模型的大小使得在固定显存的 GPU 上可以运行更大(参数量更多)的模型,且提升模型的训练和推理速度,各种低精度的数据类型被提出。

主要有以下几种常见的精度:

  1. 1. FP32(单精度浮点):
    • • 使用32位来表示一个浮点数
    • • 精度较高,但计算和存储开销大
  2. 2. FP16(半精度浮点):
    • • 使用16位表示一个浮点数
    • • 相比FP32可以减少内存占用和提高计算速度
    • • 但可能会带来一定的精度损失
  3. 3. BF16(Brain Floating Point):
    • • 介于FP32和FP16之间的16位浮点格式
    • • 保留了FP32的指数位,但减少了尾数位
    • • 在保持较好精度的同时提高计算效率
  4. 4. INT8(8位整数量化):
    • • 将浮点数映射到256个整数值
    • • 大幅减少模型大小和计算量
    • • 但精度损失较大,需要特殊的量化训练
  5. 5. 混合精度:
    • • 在训练或推理过程中混合使用不同精度
    • • 如FP16用于大部分计算,FP32用于关键操作
    • • 平衡精度和效率

较低的精度可以提高计算速度、减少内存占用,但可能会影响模型的准确性。因此在实际应用中,需要根据具体需求和硬件条件来选择合适的精度。

训练轮数

训练轮数,也称为epochs,是模型训练过程中的一个重要参数。它表示模型在训练集上训练的完整次数。例如,如果我们有一个训练集,并且我们的模型需要学习这个训练集的所有数据,那么一个epoch就是指模型对这个训练集进行一次完整的遍历。

似乎没有什么确定的好的办法,只能多调整几次对比看看效果。以下是大模型给出的建议:

  1. 1. 通常从较小的轮数开始,比如3-5轮,观察模型性能。
  2. 2. 根据验证集上的性能来调整。如果性能还在提升,可以适当增加轮数;如果开始过拟合,则应减少轮数。
  3. 3. 对于大规模预训练模型,往往只需要少量轮数就能达到不错的效果,通常不超过10轮。
  4. 4. 使用early stopping策略,在验证集性能不再提升时自动停止训练。
  5. 5. 对于不同规模的数据集,合适的轮数也有区别:
    • • 小数据集(<1万样本):可能需要10-20轮
    • • 中等数据集(1-10万样本):5-10轮左右
    • • 大数据集(>10万样本):3-5轮可能就足够
  6. 6. 配合学习率衰减策略使用,可以在较少轮数内达到较好效果。
  7. 7. 不同任务类型可能需要的轮数也不同,需要具体任务具体分析。
  8. 8. 可以尝试使用较大learning rate配合较少的epoch数。
  9. 9. 监控训练过程中的loss变化,作为调整轮数的参考。

总之需要通过实验来确定最佳轮数,有点类似玄学或者魔法,参数具体设置多少充满着不确定性,没有固定的标准。

梯度累积

梯度累积(Gradient Accumulation)的基本思想是将一次性的整批参数更新的梯度计算变为以一小步一小步的方式进行。具体而言该方法以小批次的方式进行模型前向传播和反向传播,过程中迭代计算多个小批次梯度并累加,当累积到足够多的梯度时,执行模型的优化步骤更新参数。这也是一种典型的时间换空间的做法,即我们可以实现在有限的GPU内存上更新大量参数,不过额外添加的小批次前向传播和后向传播会使得训练速度变慢一些。

例如,若目标批量大小是1,024,但设备每次只能处理256个样本,那么可以通过累积四个步骤中每个步骤的256个样本的梯度,来模拟出一个包含1,024个样本的批量更新。

这种方法在有限的内存资源下,平衡了对大批量的需求,有助于实现更稳定的梯度估计以及可能达到的更快收敛速度。

LoRA的秩

LoRA(Low-Rank Approximation)是一种用于大模型微调的方法,它通过降低模型参数矩阵的秩来减少模型的计算和存储成本。在微调大模型时,往往需要大量的计算资源和存储空间,而LoRA可以通过降低模型参数矩阵的秩来大幅度减少这些需求。

具体来说,LoRA使用矩阵分解方法,将模型参数矩阵分解为两个较低秩的矩阵的乘积。这样做的好处是可以用较低秩的矩阵近似代替原始的参数矩阵,从而降低了模型的复杂度和存储需求。

LoRA的秩可以根据模型的需求进行设置。一般来说,秩越低,模型的复杂度越低,但性能可能会受到一定的影响。所以在微调大模型时,需要根据具体情况来选择合适的秩大小,以平衡模型的性能和资源的使用。

建议根据硬件条件进行选择,一般可选16或32,模型微调效果较佳。

LoRA的缩放系数

缩放系数是用来表示模型中每个层的相对重要性的参数。在LoRA中,每个层都有一个缩放系数,用于调整该层对总体损失函数的贡献。较高的缩放系数表示该层的权重更大,较低的缩放系数表示该层的权重较小。

缩放系数的选取可以根据问题的特点和需求进行调整。通常情况下,较低层的缩放系数可以设置为较小的值,以保留更多的原始特征信息;而较高层的缩放系数可以设置为较大的值,以强调更高级别的抽象特征。

LoRA+学习率比例

点击 LoRA 参数设置,设置LoRA+学习率比例为16,LoRA+被证明是比LoRA学习效果更好的算法。

LoRA作用模块

在LoRA作用模块中填写 all,即将LoRA层挂载到模型的所有线性层上,提高拟合效果。

开始训练

一开始遇到两个问题,第一个是最开始选择的是llama3.1,但是llama是需要登录的。由于我们只是处于学习目的,对模型没有要求,所以后面换成了不需要登录就可以下载的Qwen2。

修改好后就可以正常拉取了
第二个问题是训练的时候报了OOM,显存不够了
我们可以调整一下参数来减少一些显存的使用。这里优化了一下截断长度的设置,从2048->1024后不再报错。

最终的参数配置如下

点击开始,炼丹炼起来了

看一下显卡资源情况,23G的显存用了20G

控制台上可以看到进度条

效果评估

大概等了几十分钟,提示我们已经训练完毕。

切换到Evaluate&Predict标签,选择我们的eval数据集,开始评估

评估的结果如下

这个结果是好还是不好呢?问问大模型吧

这些评估结果提供了模型性能的多个方面,让我们逐一分析:
1. BLEU-4 分数:12.2547
  ○ BLEU(Bilingual Evaluation Understudy)是机器翻译质量的评估指标。
  ○ BLEU-4 考虑了最多4-gram的匹配。
  ○ 分数范围通常是0-100,你的分数为12.25,相对较低。
  ○ 这表明生成的文本与参考文本的匹配度不是很高,可能需要进一步改进。
2. ROUGE 分数:
  ○ ROUGE(Recall-Oriented Understudy for Gisting Evaluation)用于评估自动摘要的质量。
  ○ ROUGE-1: 32.5636(单个词的匹配度)
  ○ ROUGE-2: 12.1632(两个词的连续匹配度)
  ○ ROUGE-L: 20.9140(最长公共子序列)
  ○ 这些分数表明模型在生成单词层面上表现较好,但在捕捉更长的短语结构上还有改进空间。
3. 性能指标:
  ○ 模型准备时间:0.0035秒(非常快)
  ○ 预测运行时间:291.2128秒(约4.85分钟)
  ○ 每秒处理的样本数:0.103
  ○ 每秒的步骤数:0.052
4. 这些指标显示模型加载很快,但推理速度相对较慢。可能是因为模型较大或硬件限制。

切换到Chat标签,我们可以加载微调后的模型进行对话,查看是否符合预期

扮演诸葛亮和我对话,我的笔记本电脑丢了怎么办?

扮演孙悟空和我对话,我的笔记本电脑丢了怎么办?

我们可以用原始的基模型对比一下效果:

导出模型

训练完成后,我们可以导出模型,从而发布到HF上或者用来本地部署。

文件目录结构如下,整体大概15G

  1. 1. added_tokens.json:包含添加到词汇表中的自定义标记。
  2. 2. merges.txt:用于字节对编码 (BPE) 的合并规则。
  3. 3. model-00001-of-00004.safetensors 到 model-00004-of-00004.safetensors: 这些是模型权重的分片文件,使用 safetensors 格式存储。
  4. 4. special_tokens_map.json:特殊标记(如 [PAD], [CLS], [SEP] 等)的映射。
  5. 5. vocab.json:模型的词汇表。
  6. 6. config.json:模型的配置文件,包含架构和超参数信息。
  7. 7. generation_config.json:用于文本生成任务的配置文件。
  8. 8. model.safetensors.index.json:safetensors 文件的索引。
  9. 9. tokenizer_config.json:分词器的配置文件。
  10. 10. tokenizer.json:包含分词器的完整信息,用于将文本转换为标记。

Ollama部署模型

安装

Ollama 是一个开源的大型语言模型服务工具,旨在帮助用户快速在本地运行大模型。通过简单的安装指令,用户可以通过一条命令轻松启动和运行开源的大型语言模型。 它提供了一个简洁易用的命令行界面和服务器,专为构建大型语言模型应用而设计。用户可以轻松下载、运行和管理各种开源 LLM。

安装命令

curl -fsSL https://ollama.com/install.sh | sh
git clone --depth=1 https://github.com/ggerganov/llama.cpp
pip3 install -r requirements.txt

格式对比

ollama支持多种大模型格式,其中主要的是Safetensors和GGUF:

Safetensors是Hugging Face开发的文件格式,专为存储机器学习模型权重而设计。它注重安全性、快速加载和内存效率,支持内存映射和部分加载,适用于大型模型。Safetensors在Hugging Face生态系统中广泛应用,特别适合需要快速加载和跨平台兼容的场景。我们导出的默认就是Safetensors格式。

GGUF是GGML格式的改进版,主要用于llama.cpp项目,专为高效运行大型语言模型而创建。它支持多种量化方法,优化了CPU和GPU上的推理性能,并能存储额外的模型信息。GGUF特别适合在资源受限环境中运行大型语言模型,如边缘计算和移动设备,其设计重点是推理效率和量化支持。

我们可以通过https://github.com/ggerganov/llama.cpp这个项目,将Safetensors的模型转为GGUF格式

python3 convert_hf_to_gguf.py /root/Qwen2.5-7B-SFT --outfile /root/Qwen2.5-7B-SFT.gguf
FROM /root/Qwen2.5-7B-SFT.gguf # 加载gguf格式的模型

FROM /root/Qwen2.5-7B-SFT/ # 加载Safetensors格式的模型目录

经过对比测试,gguf格式的模型加载速度会快很多。

人工智障问题

前面都比较顺利,但是在部署微调后的模型遇到了一个很头疼的问题:我怎么训练了一个人工智障出来?

经过查阅文档得知,Ollama加载本地模型需要创建一个Modelfile文件,内容如下

FROM /root/Qwen2.5-7B-SFT.gguf

然后加载

ollama create my-qwen -f /root/Modelfile

运行模型

ollama run my-qwen

接下来诡异的事情发生了:我输入了一个 你好,大模型告诉我他有糖尿病10年了

重新启动了一次,回答的也是乱七八糟的

又研究了一下文档,发现如果加载自己微调的模型,是需要在modelfile里配参数的

https://github.com/ollama/ollama/blob/main/docs/modelfile.md#format

按照文档修改如下

FROM /root/Qwen2.5-7B-SFT.gguf
PARAMETER temperature 0.7
PARAMETER stop "<|im_start|>"
PARAMETER stop "<|im_end|>"
TEMPLATE """
<|im_start|>system
{{ .System }}<|im_end|>
<|im_start|>user
{{ .Prompt }}<|im_end|>
<|im_start|>assistant
"""
SYSTEM """You are a helpful assistant."""

开始看起来还不错

但是多问几个问题就会发现:每次回答的最后都会多一些多余的内容。

又换了好几个别人blog上给出的模板,还是会有同样的问题。

这里有3个怀疑的点:

  1. 1. 模板问题:因为之前添加了官方文档里的模板之后,回答离谱的程度有所收敛。所以怀疑是不是模板依然没有配置正确?但是我是从官方文档上复制的呀?
  2. 2. 模型问题:还有一种可能是我的微调数据集质量比较低,导致模型学到了奇奇怪怪的东西。
  3. 3. 量化问题:HF格式转为GGUF的时候有精度丢失。不过奇怪的是我在转换的时候也没有加修改量化的参数啊?

为了排除模型的问题,我做了个对比实验,在LLamaFactory中加载微调后的模型反复问,都没有出现回答的最后出现多余字符的问题。

为了排除问题3,继续控制变量进行对比,不使用gguf格式,直接使用原来的huggingface的Safetensors格式,并且去掉模板

FROM /root/Qwen2.5-7B-SFT/
SYSTEM """You are a helpful assistant."""

可以看到回答的内容又开始离谱了起来

所以基本可以确定是模板配置的问题,但我是按照官方文档配置的啊?

经过一番搜索,后来又在另外一个文档里找到了一份配置:

https://ollama.com/library/qwen2/blobs/62fbfd9ed093

调整之后运行,经过反复测试再也没有出现过回答最后出现多余内容的情况,终于不再是人工智障了

那么问题出在哪儿呢?

肉眼diff了一下发现:官方文档给的模板,最后怎么少了一个im_end标签??

  1. 1. 按照常识,im_start跟im_end这种标记符应该是成对出现的才对。
  2. 2. 没有成对出现的时候,模型出现了结尾输出多余信息的情况。

是Ollama的文档错了吗?

但是奇怪的是后来我又查询了微软官方给出的ChatML样例,同样是没有结尾标记的: 

https://learn.microsoft.com/zh-cn/azure/ai-services/openai/how-to/chat-markup-language

而在huggingface对于模板的描述里,提到了另外一个参数:add_generation_prompt。似乎这个参数可以决定是否要添加最后的结束标记。 https://huggingface.co/docs/transformers/zh/chat_templating

所以加不加最后的结束标记是跟具体的模型有关的吗?文档里也没有明确说明,这点确实让我非常疑惑,如果有知道原因的小伙伴麻烦告知我一下,谢谢。

总之,在我们的场景下,最后可以正常运行的的配置模板参数如下:

FROM /root/Qwen2.5-7B-SFT.gguf
PARAMETER temperature 0.7
PARAMETER top_p 0.7
PARAMETER stop "<|im_start|>"
PARAMETER stop "<|im_end|>"
TEMPLATE """{{ if .System }}<|im_start|>system
{{ .System }}<|im_end|>
{{ end }}{{ if .Prompt }}<|im_start|>user
{{ .Prompt }}<|im_end|>
{{ end }}<|im_start|>assistant
{{ .Response }}<|im_end|>
"""
SYSTEM """You are a helpful assistant."""

查看一下大概占用了14G显存,推理所需的显存比微调还是少一些。

小插曲

用llama factory加载sft后的模型进行对比实验,发现token吐得特别慢。

一看诡异的事情发生了:内存跑满了,GPU没跑起来。可是前一天微调训练的时候还是能调用GPU的。

发现我的torch莫名其妙变成了cpu版本

重新安装torch
pip3 install torch torchvision torchaudio

此时cuda可以正常识别了

总结

本文介绍了使用LLaMA Factory进行微调的步骤,包括环境搭建、数据准备、参数配置、训练和效果评估等,最终成功微调模型并使用Ollama部署,提升了模型表现,达到了预期的效果。

有一点感受是跟之前接触的安全实验不太一样:大多数的安全实验都是我打了这个Payload,就一定会出现确定的结果,不管是弹计算器还是反弹Shell,一切都是确定的。而大模型的训练往往充满了玄学成分,可能需要多实验几次才知道什么是最优参数。

参考链接

https://opencsg.com/docs/llama-factory-guide/quick_start

https://help.aliyun.com/zh/pai/use-cases/fine-tune-a-llama-3-model-with-llama-factory

https://blog.csdn.net/H66778899/article/details/140554007

https://blog.csdn.net/m0_74748557/article/details/142901519

https://community.modelscope.cn/6715ed34cd8b2677c3d316c5.html

https://www.53ai.com/news/OpenSourceLLM/2024072585037.html

https://blog.csdn.net/qq_43799400/article/details/134182459


文章来源: https://mp.weixin.qq.com/s?__biz=Mzg2MTc1NDAxMA==&mid=2247484342&idx=1&sn=58be391a85f1cab4cdd6b7b0b41b1300&chksm=ce130443f9648d55205cc7a98a9fe40d7f0727f8daa16072fa35b7a71c3bd4150fe9e98a5e97&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh