DALL·E 3绘制的
在之前的两篇文章中,我们探讨了《在云 Kubernetes (K8s) 集群中托管自己的 Ollama 服务》(https://medium.com/@yuxiaojian/host-your-own-ollama-service-in-a-cloud-kubernetes-k8s-cluster-c818ca84a055) 和《使用 Nvidia GPU 在 Kubernetes 中运行自己的 Ollama》(https://medium.com/@yuxiaojian/run-your-own-ollama-in-kubernetes-with-nvidia-gpu-8974d0c1a9df)。到现在为止,你应该已经有一个自己托管的不错的 LLM 服务了。在本文中,我们将深入探讨使用 Unsloth 对 Ollama 模型进行微调的过程,以 Llama 3.1 为例。同样的方法也适用于其他模型。
为为什么要进行微调?微调是LLM中的一个过程,在这个过程中,预训练模型会在特定的数据集上进行进一步训练,以使模型适应特定任务或领域。这就像让一个掌握基本烹饪技巧的厨师专门烹制意大利菜。
微调(Fine-tuning)使您可以根据您的偏好调整大型语言模型(LLM)的响应风格或使其适应特定领域的指令(如医疗)。这不仅让模型可以利用现有的广泛知识,还可以使其在新领域中更专业。
微调 vs RAG(检索增强生成模型)RAG(检索增强生成)结合了大规模语言模型和信息检索系统的能力。它从外部数据库中检索出相关文档或数据,并利用这些信息来生成更准确且符合上下文的回答。就像一个学生在回答问题前查阅最相关的书籍和文章,从而给出一个充分知情的回答。
虽然RAG能够解决一些问题,但在RAG可用的情况下,即使微调LLM仍然很重要的原因还有很多。
- 增强特定任务或领域的性能。微调可以显著提高模型在特定任务或领域上的性能,即使这些任务或领域并不是基础模型特定训练的目标。
- 确保一致的风格或语气。微调使模型能够学习到与您的特定用例相匹配的一致风格或语气。
- 吸收隐含知识和推理模式。微调可以帮助模型吸收那些未在可检索文档中明确说明的隐含知识和推理模式。经过微调的模型能更好地理解上下文和细微差别。
- 安全地整合专有知识。微调可以让专有知识安全地直接融入模型,这比将其存储在外部知识库中用于检索式生成(RAG)更安全。
- 减少错误输出。微调可以通过在训练过程中暴露模型到噪声或稀疏数据来帮助模型更好地处理这种数据,从而减少错误输出。
不过,RAG和微调并不互相排斥,它们并不是互斥的。在很多情况下,这两种技术的结合往往能取得最佳效果。微调能增强模型的基础能力和对该领域的理解,而RAG则可以补充最新或高度特定的信息。
搭建环境微调环境有很多依赖项,从头安装可能会比较复杂。我使用容器镜像来维护一致的运行环境,我使用谷歌云上的带有NVIDIA T4 GPU的虚拟机。
1. 安装GPU驱动程序。关于使用包管理器安装驱动程序的信息,请参阅NVIDIA 驱动程序快速安装指南。如果您的操作系统是 Ubuntu
,您可以使用 ubuntu-drivers
自动检测并安装驱动程序。
apt install ubuntu-drivers-common # 安装ubuntu驱动管理工具
ubuntu-drivers devices # 查看系统支持的驱动设备
ubuntu-drivers autoinstall # 自动安装推荐的驱动
重启 # 重启系统
重启一下虚拟机并运行 nvidia-smi
,它应该显示出显卡的信息。
nvidia-smi
2024年8月21日 00:54:26
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.183.01 驱动版本: 535.183.01 CUDA 版本: 12.2 |
|-----------------------------------------+----------------------+----------------------+
| GPU 名称 Persistence-M | Bus-Id Disp.A | 易变未校正 ECC |
| 风扇 温度 性能 功耗:使用/上限 | 内存占用 | GPU 占用率 计算状态 |
| | | MIG 状态 |
|=========================================+======================+======================|
| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |
| N/A 69C P8 12W / 70W | 70MiB / 15360MiB | 0% 默认模式 |
| | | 不适用 |
+-----------------------------------------+----------------------+----------------------+
+---------------------------------------------------------------------------------------+
| 进程信息: |
| GPU GI CI PID 类型(进程) 进程名称 GPU 内存占用 |
| ID ID |
|=======================================================================================|
| 0 N/A N/A 1695 G /usr/lib/xorg/Xorg 59MiB |
| 0 N/A N/A 1884 G /usr/bin/gnome-shell 7MiB |
+---------------------------------------------------------------------------------------+
2. 安装 NVIDIA 容器工具套件
安装NVIDIA容器工具套件。
# 配置生产存储库:
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
# 更新软件包列表:
sudo apt-get update
# 安装NVIDIA容器工具套件:
sudo apt-get install -y nvidia-container-toolkit # (安装NVIDIA容器工具套件)
配置 Docker 运行时来使用它。
配置NVIDIA容器工具包,设置容器运行时为Docker
nvidia-ctk runtime configure --runtime=docker
重启Docker服务
systemctl restart docker
3. 创建容器映像文件
该图像基于nvidia/cuda
以支持GPU,配置了Python 3.11环境,并在requirements.txt文件中列出了所有Python依赖项。
图像从 nvidia/cuda
构建来实现 GPU 支持。它配置了一个 Python 3.11
环境。
# 从 NVIDIA CUDA 12.6.0 基础 Ubuntu 22.04 镜像开始
FROM nvidia/cuda:12.6.0-base-ubuntu22.04
# 设置环境变量
ENV DEBIAN_FRONTEND=noninteractive
# 通过单个 RUN 命令更新,安装必要的包并清理
RUN apt-get update && apt-get install -y --no-install-recommends \
software-properties-common \
curl \
build-essential \
git \
&& add-apt-repository ppa:deadsnakes/ppa \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
python3.11 \
python3.11-dev \
python3.11-distutils \
&& curl -sS https://bootstrap.pypa.io/get-pip.py | python3.11 \
&& update-alternatives --install /usr/bin/python python /usr/bin/python3.11 1 \
&& update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1 \
&& apt-get purge -y --auto-remove software-properties-common \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# 验证 Python 版本及 pip 版本
RUN python --version && pip --version
# 设置工作目录为 /app 并复制文件到该目录
WORKDIR /app
COPY . /app
RUN pip install --no-cache-dir -r requirements.txt
ENV NVIDIA_DRIVER_CAPABILITIES=compute,utility
ENV NVIDIA_VISIBLE_DEVICES=all
# 启动容器时运行的命令
CMD ["/bin/bash"]
requirements.txt
列出了所有所需的 Python 依赖项。
accelerate==0.33.0
bitsandbytes==0.43.3
peft==0.12.0
trl==0.8.6
unsloth==2024.8
xformers==0.0.26.post1
sentencepiece
新建一个文件夹,并将「Dockerfile」和「requirements.txt」放入文件夹中,然后构建图像。
docker build -t cuda-py311-tuner . (使用当前目录中的Dockerfile构建一个名为cuda-py311-tuner的镜像)
安装了Unsloth框架后,镜像文件会增长到7GB,微调过程也需要大量磁盘空间。我建议至少准备100GB的磁盘空间。
现在,运行这个容器。主机路径 /opt/fine-tuning
挂载到了容器的 /app
,作为它的工作目录。
docker run --rm --gpus=all -v /opt/fine-tuning:/app -it cuda-py311-tuner
这命令是用来运行一个Docker容器,使用所有可用的GPU,并将本地的 /opt/fine-tuning
目录挂载到容器中的 /app
目录,同时开启交互模式。
原来的调谐脚本来自Unsloth Llama 3.1 (8B)。数据集来自Hugging Face,您可以从这里获取数据集。你可以使用来自Hugging Face的数据集,或者从其他来源加载本地的数据集。
"""
原始文件位于 https://colab.research.google.com/drive/1NuloNJhx1hkQoM5LqNBAiLwUxQCkl7T0
"""
from unsloth import FastLanguageModel
import torch
max_seq_length = 2048 # 随便选!我们内部自适应 RoPE 扩展!
dtype = None # None 用于自动检测。Float16 用于 Tesla T4 和 V100,Bfloat16 用于 Ampere+
load_in_4bit = True # 使用 4bit 量化以减少内存使用。也可以设为 False。
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "unsloth/Meta-Llama-3.1-8B-Instruct-bnb-4bit",
max_seq_length = max_seq_length,
dtype = dtype,
load_in_4bit = load_in_4bit,
# token = "hf_...", # 如果使用如 meta-llama/Llama-2-7b-hf 之类的门控模型,需要提供 token
)
"""我们现在添加 LoRA adapters,这样我们只需要更新所有参数的 1 到 10%!"""
model = FastLanguageModel.get_peft_model(
model,
r = 16, # 随便选 > 0 的数!建议 8, 16, 32, 64, 128
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",],
lora_alpha = 16,
lora_dropout = 0, # 支持任何值,但 = 0 时性能更佳
bias = "none", # 支持任何值,但 = "none" 时性能更佳
# [NEW] "unsloth" 使用了 30% 更少的 VRAM,适合 2 倍更大的批次大小!
use_gradient_checkpointing = "unsloth", # True 或 "unsloth" 适用于非常长的上下文
random_state = 3407,
use_rslora = False, # 我们支持秩稳定的 LoRA
loftq_config = None, # 以及 LoftQ
)
# 准备数据
llama31_prompt="""system
{}user
{}assistant
{}"""
from datasets import load_dataset
dataset = load_dataset("yahma/alpaca-cleaned", split = "train")
def formatting_prompts_func(examples):
instructions = examples["instruction"]
inputs = examples["input"]
outputs = examples["output"]
texts = []
for instruction, input, output in zip(instructions, inputs, outputs):
text = llama31_prompt.format(instruction, input, output)
texts.append(text)
return { "text" : texts, }
pass
dataset = dataset.map(formatting_prompts_func, batched = True,)
from trl import SFTTrainer
from transformers import TrainingArguments
from unsloth import is_bfloat16_supported
trainer = SFTTrainer(
model = model,
tokenizer = tokenizer,
train_dataset = dataset,
dataset_text_field = "text",
max_seq_length = max_seq_length,
dataset_num_proc = 2,
packing = False, # 可以使短序列的训练速度加快 5 倍。
args = TrainingArguments(
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
warmup_steps = 5,
# num_train_epochs = 1, # 设置此选项以进行完整的一次训练过程。
max_steps = 60,
learning_rate = 2e-4,
fp16 = not is_bfloat16_supported(),
bf16 = is_bfloat16_supported(),
logging_steps = 1,
optim = "adamw_8bit",
weight_decay = 0.01,
lr_scheduler_type = "linear",
seed = 3407,
output_dir = "outputs",
),
)
# 开始微调
trainer_stats = trainer.train()
# 保存模型
"""<a name="Save"></a>
### 保存,加载微调后的模型
要将最终模型保存为 LoRA 适配器,可以使用 Huggingface 的 `push_to_hub` 在线保存,或者使用 `save_pretrained` 本地保存。
"""
model.save_pretrained("lora_model") # 本地保存
tokenizer.save_pretrained("lora_model")
# 保存为 8bit Q8_0 格式
if True: model.save_pretrained_gguf("model", tokenizer, quantization_method = [ "q8_0"])
此脚本展示了如何使用Unsloth库所提供的工具和Hugging Face的Transformers来精细调校大型语言模型。下面我来解释下这段代码做了什么:
-
导入和初始设置:
- 导入必要的库 (Unsloth
,torch
,datasets
,trl
,transformers
)。
- 设置模型加载的参数(最大序列长度和数据类型,量化)。 -
模型加载:
- 使用Unsloth的FastLanguageModel来加载预训练的Meta-Llama-3.1–8B-Instruct模型。
- 向模型中添加低秩适配器(LoRA)以实现高效的微调。 -
数据准备:
- 加载yahma/alpaca-cleaned
数据集,并使用定义的提示进行格式化。 -
训练设置:
- 配置监督微调训练器(SFTTrainer)的各种超参数。
- 启动微调过程。 - 模型保存:
- 将模型转换成GGUF格式
- 展示如何将微调后的LoRA适配器保存到本地
在容器内使用Python运行上述脚本,它将开始微调并在结束后保存一个GGUF文件./model/unsloth.Q8_0.gguf
。整个过程大约需要30分钟。
docker run --rm --gpus=all -v /opt/fine-tuning:/app -it cuda-py311-tuner
root@e536b44b6a67:/app# ls
unsloth-fine-tuning-llama31.py
root@e536b44b6a67:/app# python unsloth-fine-tuning-llama31.py
🦥 Unsloth: 帮您的电脑提速两倍的免费微调。
==((====))== Unsloth 2024.8: 快速Llama优化。Transformers = 4.44.1。
\\ /| GPU: Tesla T4。最大内存容量: 14.581 GB。平台 = Linux。
O^O/ \_/ \ Pytorch: 2.3.0+cu121。CUDA = 7.5。CUDA Toolkit = 12.1。
\ / Bfloat16 状态: FALSE。FA [Xformers = 0.0.26.post1。FA2 = False]
"-____-" 免费Apache许可证: http://github.com/unslothai/unsloth
...
信息:hf-to-gguf:设置模型量化版本
信息:gguf.gguf_writer:将以下文件写入:
信息:gguf.gguf_writer:model/unsloth.Q8_0.gguf: n_tensors = 292,total_size = 8.5G
写入进度: 100%|██████████| 8.53G/8.53G [03:05<00:00, 46.0Mbyte/s]
信息:hf-to-gguf:模型已成功导出为model/unsloth.Q8_0.gguf
Unsloth: 转换完成!输出位置: ./model/unsloth.Q8_0.gguf
...
模型存放在 lora_model
里。你可以加载它并转换成其他格式。
从unsloth导入FastLanguageModel
max_seq_length = 2048 # 选择任何长度!我们自动支持RoPE缩放!
dtype = None # None表示自动检测类型。Float16适用于Tesla T4和V100,Bfloat16适用于Ampere+架构。
load_in_4bit = True # 使用4位量化来减少内存使用,也可以设置为False。
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "lora_model", # 你在训练中使用的模型
max_seq_length = max_seq_length,
dtype = dtype,
load_in_4bit = load_in_4bit,
)
model.save_pretrained_gguf("model", tokenizer, quantization_method = ['f16', 'q4_k_m'])
LoRA(低功耗广域网)
注:此处解释“LoRA”为“低功耗广域网”,以帮助中文读者更好地理解。
LoRA 低秩适配器是一种在大规模语言模型(LLM)中使用的微调技术,通过在模型的各个层中注入低秩矩阵来实现微调。这种方法选择性地微调少量额外参数,同时保持大部分预训练参数不变。这种微调方式更新大约1%到10%的参数。
格式化数据集参阅这篇文章 准备您的数据集用于微调 Llama 3.1
将模型加载到Ollama奥勒马需要一个Modelfile来指定模型的提示格式。奥勒马使用模板将输入转化为模型可以理解的格式。想要详细了解,你可以参考准备您的数据集以微调Llama 3.1模型。
FROM ./unsloth.Q8_0.gguf
TEMPLATE """{{ if .System }}系统
{{ .System }}{{ end }}{{ if .Prompt }}用户
{{ .Prompt }}{{ end }}助手
{{ .Response }}"""
PARAMETER stop ""
PARAMETER stop ""
PARAMETER stop ""
PARAMETER stop "<|reserved_special_token"
把模型导入到Ollama
ollama create ollama31-instruct-8b-ft -f Modelfile
传输模型数据
使用现有层 sha256:40468b9fdf30bc295b675b879ddad31ca79e2ff47e49c7409e1cca129576d034
正在创建新层 sha256:95b5361453780fb5797ce5abfe9a330f5d33fdec13d2232ef1443ee0c3a86ecc
正在创建新层 sha256:9f5aee68966d4ca5d4bf3bac0ddbae092c53c9c50d7e2a5137c64a7afaecc8a8
创建新层 sha256:f5c31318abe48c20a1518ddd980cec67c4e4802c2b00e9b5bc316cb0423a750b
写入文件
成功了
你可以用 API 测试它
curl http://localhost:11434/api/chat -d '{ "model": "ollama31-instruct-8b-ft", "messages": [ { "role": "user", "content": "继续以下斐波那契数列:1, 1, 2, 3, 5, 8" } ] }'
如果你已经设置了云端 Kubernetes (K8s) 集群中的 Ollama 服务,你可以在 WebUI 中开始聊天会话。如需了解更多,请参阅自行在云端 Kubernetes (K8s) 集群中设置 Ollama 服务。
结论:仅仅今天,我就收到了一封邮件,OpenAI正在提供免费微调服务。
好消息!现在所有付费层级的开发者都可以对GPT-4o和GPT-4o mini进行微调功能,以帮助您在特定应用场景中降低成本并提高性能。
微调是提升大规模语言模型(LLM)的关键步骤,但过去这通常既昂贵又耗时。有了OpenAI的免费微调和Unsloth,微调现在几乎成了提升你的AI工具的必经之路。
共同学习,写下你的评论
评论加载中...
作者其他优质文章