在人工智能技术迅速发展的今天,如何高效地微调和部署大型语言模型(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里已经内置了一些推荐的默认值。
这里涉及的参数很多,挑了一些比较重要的参数进行介绍。
内置的微调方法有以下三种:
默认情况下为lora,因为使用lora轻量化微调方法能极大程度地节约显存。
学习率是最重要的超参数之一,它决定了在每次参数更新时参数改变的幅度。一个太大的学习率可能会导致模型训练不稳定,而太小的学习率会导致训练过程缓慢。微调时,通常使用比预训练阶段更小的学习率,因为我们希望模型参数的改变更加细微,以免破坏已学到的有用信息。
关于学习率看到了一个比较形象的解释:学习率有点像教一个小孩学习新知识,如果你一次性给他太多的信息,他可能会感觉到困惑,无法吸收。但是如果给的信息太少,学习进度又会非常慢。
学习率到底设置多少算是合适呢?看了一圈文章下来总结就是:得试。
建议从保守的小值开始,然后根据训练过程的反馈进行调整。同时使用学习率调度和自适应优化器可以帮助在训练过程中自动调整学习率,提高模型性能。
常见的学习率参数包括但不限于:
下面是大模型给的一些建议:
Cutoff Length是训练句子截断长度,句子越长,显存占用越多,如果显存不够可以考虑降低到512甚至256。可以根据微调目标需要的长度进行设置。微调后,模型处理长度大于Cutoff Length的句子的能力会下降。
大模型的计算精度是指在训练和推理过程中,模型参数和计算操作所使用的数值表示方式的精确程度。
这里介绍一下背景知识:float 和 double 类型的数据在内存中以二进制方式存储,由三部分组成:
下面是 FP16 和 FP32 (float) 的存储示例图:
最早的 GPU 默认使用 FP32 类型进行运算,但随着模型越来越大,FP32 类型占内存/显存资源大且运算速度慢的问题逐渐暴露了出来。为了降低模型的大小使得在固定显存的 GPU 上可以运行更大(参数量更多)的模型,且提升模型的训练和推理速度,各种低精度的数据类型被提出。
主要有以下几种常见的精度:
较低的精度可以提高计算速度、减少内存占用,但可能会影响模型的准确性。因此在实际应用中,需要根据具体需求和硬件条件来选择合适的精度。
训练轮数,也称为epochs,是模型训练过程中的一个重要参数。它表示模型在训练集上训练的完整次数。例如,如果我们有一个训练集,并且我们的模型需要学习这个训练集的所有数据,那么一个epoch就是指模型对这个训练集进行一次完整的遍历。
似乎没有什么确定的好的办法,只能多调整几次对比看看效果。以下是大模型给出的建议:
总之需要通过实验来确定最佳轮数,有点类似玄学或者魔法,参数具体设置多少充满着不确定性,没有固定的标准。
梯度累积(Gradient Accumulation)的基本思想是将一次性的整批参数更新的梯度计算变为以一小步一小步的方式进行。具体而言该方法以小批次的方式进行模型前向传播和反向传播,过程中迭代计算多个小批次梯度并累加,当累积到足够多的梯度时,执行模型的优化步骤更新参数。这也是一种典型的时间换空间的做法,即我们可以实现在有限的GPU内存上更新大量参数,不过额外添加的小批次前向传播和后向传播会使得训练速度变慢一些。
例如,若目标批量大小是1,024,但设备每次只能处理256个样本,那么可以通过累积四个步骤中每个步骤的256个样本的梯度,来模拟出一个包含1,024个样本的批量更新。
这种方法在有限的内存资源下,平衡了对大批量的需求,有助于实现更稳定的梯度估计以及可能达到的更快收敛速度。
LoRA(Low-Rank Approximation)是一种用于大模型微调的方法,它通过降低模型参数矩阵的秩来减少模型的计算和存储成本。在微调大模型时,往往需要大量的计算资源和存储空间,而LoRA可以通过降低模型参数矩阵的秩来大幅度减少这些需求。
具体来说,LoRA使用矩阵分解方法,将模型参数矩阵分解为两个较低秩的矩阵的乘积。这样做的好处是可以用较低秩的矩阵近似代替原始的参数矩阵,从而降低了模型的复杂度和存储需求。
LoRA的秩可以根据模型的需求进行设置。一般来说,秩越低,模型的复杂度越低,但性能可能会受到一定的影响。所以在微调大模型时,需要根据具体情况来选择合适的秩大小,以平衡模型的性能和资源的使用。
建议根据硬件条件进行选择,一般可选16或32,模型微调效果较佳。
缩放系数是用来表示模型中每个层的相对重要性的参数。在LoRA中,每个层都有一个缩放系数,用于调整该层对总体损失函数的贡献。较高的缩放系数表示该层的权重更大,较低的缩放系数表示该层的权重较小。
缩放系数的选取可以根据问题的特点和需求进行调整。通常情况下,较低层的缩放系数可以设置为较小的值,以保留更多的原始特征信息;而较高层的缩放系数可以设置为较大的值,以强调更高级别的抽象特征。
点击 LoRA 参数设置
,设置LoRA+学习率比例为16,LoRA+被证明是比LoRA学习效果更好的算法。
在LoRA作用模块中填写 all,即将LoRA层挂载到模型的所有线性层上,提高拟合效果。
一开始遇到两个问题,第一个是最开始选择的是llama3.1,但是llama是需要登录的。由于我们只是处于学习目的,对模型没有要求,所以后面换成了不需要登录就可以下载的Qwen2。
最终的参数配置如下
点击开始,炼丹炼起来了
看一下显卡资源情况,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
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个怀疑的点:
为了排除模型的问题,我做了个对比实验,在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标签??
是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版本
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