prompt_style ="""Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
### Instruction:
{}
### Input:
{}
### Response:
{}"""
inputs = tokenizer([
prompt_style.format("You are a professional machine learning engineer","How would you deal with NaN validation loss?","",)],
return_tensors="pt",).to("cuda")
text_streamer = TextStreamer(tokenizer)
_ = model.generate(**inputs, streamer=text_streamer, max_new_tokens=128)技术支持
prompt_style ="""Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
### Instruction:
You are a math genius who can solve any level of algebraic problems. Please answer the following math question.
### Input:
{}
### Response:
{}"""技术支持
used_memory =round(torch.cuda.max_memory_reserved()/1024/1024/1024,3)
used_memory_for_lora =round(used_memory - start_gpu_memory,3)
used_percentage =round(used_memory /max_memory*100,3)
lora_percentage =round(used_memory_for_lora/max_memory*100,3)print(f"{trainer_stats.metrics['train_runtime']} seconds used for training.")print(f"{round(trainer_stats.metrics['train_runtime']/60,2)} minutes used for training.")print(f"Peak reserved memory = {used_memory} GB.")print(f"Peak reserved memory for training = {used_memory_for_lora} GB.")print(f"Peak reserved memory % of max memory = {used_percentage} %.")print(f"Peak reserved memory for training % of max memory = {lora_percentage} %.")技术支持
1207.5478 seconds used for training.20.13 minutes used for training.
Peak reserved memory =9.73 GB.
Peak reserved memory for training =3.746 GB.
Peak reserved memory % of max memory =61.241%.
Peak reserved memory for training % of max memory =23.578%.技术支持
from IPython.display import display, Markdown
FastLanguageModel.for_inference(model)
inputs = tokenizer([
prompt_style.format("If the system of equations \begin{align*} 3x+y&=a,\\ 2x+5y&=2a, \end{align*} has a solution $(x,y)$ when $x=2$, compute $a$.","",)],
return_tensors="pt",).to("cuda")
outputs = model.generate(
input_ids=inputs.input_ids,
attention_mask=inputs.attention_mask,
max_new_tokens=250,
use_cache=True,)
response = tokenizer.batch_decode(outputs)
Markdown(response[0].split("\n\n### Response:")[1])技术支持
可以看到,生成的响应与数据集一致,并给出了代数题的解答。
7. 保存模型与分词器
现在将我们的微调模型保存并推送到 Hugging Face Hub,便于分享或部署。
new_model_online ="kingabzpro/Llama-3.1-8B-MATH"
new_model_local ="Llama-3.1-8B-MATH"
model.save_pretrained(new_model_local)# Local saving
tokenizer.save_pretrained(new_model_local)# Local saving技术支持
下面的代码将在 Hugging Face 上创建一个新仓库,然后连同元数据一起推送 LoRA 与分词器。
from huggingface_hub import login
from kaggle_secrets import UserSecretsClient
user_secrets = UserSecretsClient()
hf_token = user_secrets.get_secret("HUGGINGFACE_TOKEN")
login(hf_token)技术支持
通过提供 Hugging Face 模型仓库链接加载 LoRA 与分词器。
启用快速模型推理。
from unsloth import FastLanguageModel
new_model_name ="kingabzpro/Llama-3.1-8B-MATH"
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = new_model_name,# YOUR MODEL YOU USED FOR TRAINING
max_seq_length =2048,
dtype =None,
load_in_4bit =True,)
FastLanguageModel.for_inference(model);技术支持
复制并粘贴上一节中的提示模板。
prompt_style ="""Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
### Instruction:
You are a math genius who can solve any level of algebraic problem. Please answer the following math question.
### Input:
{}
### Response:
{}"""技术支持
使用数据集中的示例数学题测试模型,确保其仍然工作正常。
from IPython.display import display, Markdown
inputs = tokenizer([
prompt_style.format("Solve the equation $|y-6| + 2y = 9$ for $y$.",# input"",# output - leave this blank for generation!)],
return_tensors="pt",).to("cuda")
outputs = model.generate(
input_ids=inputs.input_ids,
attention_mask=inputs.attention_mask,
max_new_tokens=250,
pad_token_id=tokenizer.eos_token_id,)
response = tokenizer.batch_decode(outputs)
Markdown(response[0].split("\n\n### Response:")[1])技术支持
模型运行良好,可以与基础模型合并。
创建临时目录并更改目录指针。为何要这样做?Kaggle 的 working 目录仅提供 20 GB,不足以合并并推送完整模型。在根目录创建另一个文件夹可使用 60 GB 临时存储。
在微调模型时,您是否遇到过内存问题,或觉得整个过程耗时难以忍受?如果您正在寻找比 Transformers 库更高效、更快速的方式来微调大语言模型(LLM),那您来对地方了!
在本教程中,我们将演示如何仅用 9GB 显存微调 Llama 3.1 3B 模型,速度比传统 Transformers 方法快 2 倍。我们还将介绍如何进行快速推理、将模型转换为 vLLM 兼容和 GGUF 文件格式,并仅用几行代码将已保存的模型无缝推送到 Hugging Face Hub。
如果您对这些概念还不熟悉,建议先学习大型语言模型(LLM)概念精通课程,打好基础再开始微调。
图片由作者提供
什么是 Unsloth?
Unsloth AI 是一个用于快速微调与调用大语言模型的 Python 框架。它提供简单的 API,且性能较 Transformers 快 2 倍。
核心特性
使用 Unsloth 访问 LLM
使用 Unsloth 访问 Llama-3.1 模型非常简单。我们不会加载 16-bit 模型,而是加载 Hugging Face 上的 4-bit 版本,以节省 GPU 内存并加速推理。
FastLanguageModel函数加载模型和分词器。我们收到了关于如何处理 NaN 验证损失的积极回应。
如果您在 Kaggle 上运行上述代码遇到问题,这里有示例 Notebook:使用 Unsloth 访问 LLM。
通过学习 用 Python 入门 LLM课程,您将了解 Transformer 架构的基础,包括如何构建、微调与评估。它是您迈入 Python 进行 LLM 微调世界的入门之路。
使用 Unsloth 微调 Llama 3.1 模型
在本指南中,我们将使用 Unsloth 在lighteval/MATH 数据集上微调 Llama 3.1。该数据集包含以 Markdown 格式给出的代数题及其解答,整体分为五个难度级别。
为更好理解本指南,建议您阅读大型语言模型微调入门指南,了解微调 LLM 各步骤背后的原理。先用几分钟浏览该指南,有助于理解本文过程。
1. 环境配置
我们使用 Kaggle Notebook 作为云端 IDE,在处理模型或数据前需要先完成 Notebook 配置。
2. 加载模型与分词器
与推理部分相同,我们将加载 Llama 3.1 的 4-bit 量化版本,最大序列长度为 2048,数据类型为 None。
3. 加载与处理数据集
随后我们将加载并处理数据集。为此,需先创建一个提示模板,帮助我们获得所需结果。
提示模板包含系统提示、指令,以及输入与响应的占位符。
接下来创建函数,使用提示模板将数学题与解答作为输入,转换为规范文本。请务必添加 EOS_TOKEN 以避免重复生成。
从数据集中加载 500 个样本,对数据集应用
formatting_prompts_func函数,并查看 text 列的首个样本。文本包含系统提示、指令、代数题目以及对应解答。
4. 模型设置
我们使用基础模型中的线性模块为模型添加 LoRA(低秩适配器)。
使用模型、分词器、最大序列长度和训练参数来设置 trainer。这与使用 Transformers 与 TRL 库设置 trainer 十分类似。
5. 模型训练
模型训练耗时将近 19 分钟,表现相当不错。训练损失在每一步都有显著降低。
您可以前往 Weights & Biases 仪表盘查看详细的模型性能与硬件指标。
您也可以使用
torch与trainer_stats生成自己的报告。模型在 20 分钟内完成训练,训练峰值预留内存约为 3.7 GB。与传统方法(先加载全量模型再量化并微调)相比,这一数值相当低;传统方法至少需要 15 GB GPU 内存。
6. 微调后测试模型
要在微调后测试模型,我们需要激活快速推理。接着,从数据集中选取一个代数题填入提示模板并转为 token,然后使用模型生成响应,并对输出进行解码以显示文本。
生成文本为 Markdown 格式,我们已将其转换为能正确显示数学公式的文本。
可以看到,生成的响应与数据集一致,并给出了代数题的解答。
7. 保存模型与分词器
现在将我们的微调模型保存并推送到 Hugging Face Hub,便于分享或部署。
下面的代码将在 Hugging Face 上创建一个新仓库,然后连同元数据一起推送 LoRA 与分词器。
来源:kingabzpro/Llama-3.1-8B-MATH
如果您在使用 Unsloth 微调 LLM 时遇到问题,请参考使用 Unsloth 微调 LLM Kaggle Notebook。它包含可稳定运行的代码,便于复现实验结果。
您 AI 学习旅程的下一步是使用 LLM 开发高效的 AI 应用。更多信息请参阅使用 LangChain 构建 LLM 应用教程。
合并与导出模型
将 LoRA 与基础模型合并需要额外的显存和本地存储。为避免超限,我们将创建一个新的 Kaggle Notebook,并将加速器设置为 P100 GPU。同时启用 Hugging Face API 以访问 API 密钥。
模型运行良好,可以与基础模型合并。
working目录仅提供 20 GB,不足以合并并推送完整模型。在根目录创建另一个文件夹可使用 60 GB 临时存储。完整的 16-bit 模型、适配器与量化后的 GGUF 文件均已在您的模型仓库中可用。要在本地使用微调模型,只需下载量化的 GGUF 文件,即可配合 Jan、Misty、GPT4ALL 或 Ollama 使用。
请参考微调 Llama 3.2 并在本地使用:分步指南教程,学习如何在客服数据集上微调 Llama 3.2、将模型合并导出到 Hugging Face Hub,并将微调模型转换为 GGUF 格式,以便在本地使用 Jan 应用。
来源:kingabzpro/Llama-3.1-8B-MATH
如果您在加载或将 LoRA 与基础模型合并时遇到问题,请查看使用 Unsloth 合并 LoRA 适配器 Kaggle Notebook 以获取进一步帮助。
同时也推荐阅读博客12 个适合各水平的 LLM 项目,其中列出了适合初学者、中级学习者、毕业生与专家的 LLM 项目。
结语
在本教程中,我们学习了如何以较低的计算资源高效微调 Llama 3.1 3B 模型,并获得比传统方法快 2 倍的速度。
我们还学习了如何进行快速模型推理、将 LoRA 与基础模型合并,并推送至 Hugging Face Hub。
此外,我们将模型转换为 llama.cpp 格式并进行量化,使微调模型能在笔记本电脑上通过 Jan 或 GPT4ALL 等应用轻松本地使用。
为进一步提升模型性能,我们强烈建议在完整数据集上进行微调、优化超参数,并打磨提示模板。在微调之后,您仍可以通过函数调用与 RAG 流水线等方式显著提升 AI 应用的表现。
要了解哪种方案更适合您的具体场景,请参阅RAG vs 微调:含实用示例的全面教程指南。文中包含示例代码,您可在自己的数据集上尝试并比较结果。