微调 1B LLaMA 3.2:带代码的综合分步指南
通过微调 Llama 3.2 构建心理健康聊天机器人

让我们通过微调 Llama 3.2 来找到一些心灵的平静吧😊。
我们需要安装 unsloth 以实现 2 倍的快速训练和更小的模型大小。
!pip install unsloth
!pip uninstall unsloth -y && pip install --upgrade --no-cache-dir "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
我们将使用 Unsloth,因为它显著提高了微调大型语言模型 (LLM)(特别是 LLaMA 和 Mistral)的效率。通过 Unsloth,我们可以使用高级量化技术,例如 4 位和 16 位量化,以减少内存并加速训练和推理。这意味着我们甚至可以在资源有限的硬件上部署强大的模型,而不会影响性能。
此外,Unsloth 的广泛兼容性和自定义选项允许根据产品的特定需求进行量化过程。这种灵活性,加上其将 VRAM 使用量减少高达 60% 的能力,使 Unsloth 成为 AI 工具包中的重要工具。这不仅仅是关于优化模型,更是关于使尖端 AI 更易于实际应用且更高效。
对于微调,我使用了以下设置:
- Torch 2.1.1 with CUDA 12.1 用于高效计算。
- Unsloth 用于将大型语言模型 (LLM) 的训练速度提高 2 倍。
- H100 NVL GPU 用于处理密集的处理需求,但您可以使用功率较低的 GPU,例如 Kaggle GPU。
-
为什么选择 LLaMA 3.2?
它开源且易于访问,并提供灵活的自定义和微调以满足特定需求。由于 Meta 模型的权重是开源的,因此很容易在任何问题上进行微调,我们将使用 Hugging Face 的心理健康数据集对其进行微调
Python 库 📕 📗 📘 📙
数据处理与可视化
import os
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
plt.style.use('ggplot')
LLM 模型训练
import torch
from trl import SFTTrainer
from transformers import TrainingArguments, TextStreamer
from unsloth.chat_templates import get_chat_template
from unsloth import FastLanguageModel
from datasets import Dataset
from unsloth import is_bfloat16_supported
# Saving model
from transformers import AutoTokenizer, AutoModelForSequenceClassification
# Warnings
import warnings
warnings.filterwarnings("ignore")
%matplotlib inline
🦥 Unsloth:将修补您的计算机,以实现 2 倍更快的免费微调。
调用数据集
data = pd.read_json("hf://datasets/Amod/mental_health_counseling_conversations/combined_dataset.json", lines=True)
探索性数据分析 🔎 📊
让我们检查每个上下文中的单词长度
data['Context_length'] = data['Context'].apply(len)
plt.figure(figsize=(10, 3))
sns.histplot(data['Context_length'], bins=50, kde=True)
plt.title('Distribution of Context Lengths')
plt.xlabel('Length of Context')
plt.ylabel('Frequency')
plt.show()
filtered_data = data[data['Context_length'] <= 1500]
ln_Context = filtered_data['Context'].apply(len)
plt.figure(figsize=(10, 3))
sns.histplot(ln_Context, bins=50, kde=True)
plt.title('Distribution of Context Lengths')
plt.xlabel('Length of Context')
plt.ylabel('Frequency')
plt.show()
让我们现在检查每个响应中的单词长度
ln_Response = filtered_data['Response'].apply(len)
plt.figure(figsize=(10, 3))
sns.histplot(ln_Response, bins=50, kde=True, color='teal')
plt.title('Distribution of Response Lengths')
plt.xlabel('Length of Response')
plt.ylabel('Frequency')
plt.show()
filtered_data = filtered_data[ln_Response <= 4000]
ln_Response = filtered_data['Response'].apply(len)
plt.figure(figsize=(10, 3))
sns.histplot(ln_Response, bins=50, kde=True, color='teal')
plt.title('Distribution of Response Lengths')
plt.xlabel('Length of Response')
plt.ylabel('Frequency')
plt.show()
模型训练 🧪

让我们深入了解 Llama 3.2 模型并在我们的数据上对其进行训练
加载模型
您可以根据需要遵循的关键方面
- 最大序列长度
我们使用了 `max_seq_length` 5020,这是模型在单个输入序列中可以处理的最大 token 数量。这对于需要处理长文本的任务至关重要,确保模型在每次传递中都能捕获更多上下文。它可以根据需求使用。
- 加载 Llama 3.2 模型
使用 `FastLanguageModel.from_pretrained` 加载模型和 tokenizer,并指定预训练模型 `"unsloth/Llama-3.2-1B-bnb-4bitt"`。此模型针对 4 位精度进行了优化,可减少内存使用并提高训练速度,而不会显著影响性能。`load_in_4bit=True` 参数启用这种高效的 4 位量化,使其更适合在性能较低的硬件上进行微调。
- 应用 PEFT(参数高效微调)
然后,我们使用 `get_peft_model` 配置模型,该模型应用了 LoRA(低秩适应)技术。这种方法专注于仅微调模型的特定层或部分,而不是整个网络,从而大大减少了所需的计算资源。
诸如 `r=16` 和 `lora_alpha=16` 等参数调整这些适应的复杂性和缩放比例。`target_modules` 的使用指定了模型中应进行适应的层,其中包括参与注意力机制的关键组件,如 `q_proj`、`k_proj` 和 `v_proj`。
`use_rslora=True` 激活了秩稳定 LoRA,这提高了微调过程的稳定性。`use_gradient_checkpointing="unsloth"` 确保在训练期间通过选择性存储仅必要的计算来优化内存使用,进一步提高了模型的效率。
- 验证可训练参数
最后,我们使用 `model.print_trainable_parameters()` 打印出在微调期间将更新的参数数量,从而可以验证只有模型的预期部分正在训练。
这些技术的结合使微调过程不仅更高效,而且更易于访问,即使计算资源有限,您也可以部署此模型。
将最大 token 长度设置为 5020 对于低秩适应 (LoRA) 训练来说绰绰有余,但您可以根据数据和需求进行设置。
max_seq_length = 5020
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/Llama-3.2-1B-bnb-4bit",
max_seq_length=max_seq_length,
load_in_4bit=True,
dtype=None,
)
model = FastLanguageModel.get_peft_model(
model,
r=16,
lora_alpha=16,
lora_dropout=0,
target_modules=["q_proj", "k_proj", "v_proj", "up_proj", "down_proj", "o_proj", "gate_proj"],
use_rslora=True,
use_gradient_checkpointing="unsloth",
random_state = 32,
loftq_config = None,
)
print(model.print_trainable_parameters())
准备模型输入数据
需要记住的要点
- 数据提示结构
`data_prompt` 是一个格式化字符串模板,旨在指导模型分析提供的文本。它包含输入文本(上下文)和模型响应的占位符。此模板专门提示模型识别心理健康指标,从而更容易为心理健康相关任务微调模型。
- 序列结束 token
从 tokenizer 中检索 `EOS_TOKEN` 以表示每个文本序列的结束。此 token 对于模型识别提示何时结束至关重要,有助于在训练或推理期间保持数据结构。
- 格式化函数
`formatting_prompt` 用于获取一批示例并根据 `data_prompt` 对其进行格式化。它遍历输入和输出对,将它们插入模板并在末尾添加 EOS token。然后,该函数返回一个包含格式化文本的字典,可用于模型训练或评估。
- 函数输出
该函数输出一个字典,其中键为 `"text"`,值为格式化字符串列表。每个字符串都代表一个为模型完全准备好的提示,结合了上下文、响应和结构化提示模板。
data_prompt = """Analyze the provided text from a mental health perspective. Identify any indicators of emotional distress, coping mechanisms, or psychological well-being. Highlight any potential concerns or positive aspects related to mental health, and provide a brief explanation for each observation.
### Input:
{}
### Response:
{}"""
EOS_TOKEN = tokenizer.eos_token
def formatting_prompt(examples):
inputs = examples["Context"]
outputs = examples["Response"]
texts = []
for input_, output in zip(inputs, outputs):
text = data_prompt.format(input_, output) + EOS_TOKEN
texts.append(text)
return { "text" : texts, }
格式化训练数据
training_data = Dataset.from_pandas(filtered_data)
training_data = training_data.map(formatting_prompt, batched=True)
使用自定义参数和数据进行模型训练
使用 `sudo apt-get update` 刷新可用软件包列表,并使用 `sudo apt-get install build-essential` 安装基本工具。仅在出现任何错误时在 shell 中运行此命令。
#sudo apt-get update
#sudo apt-get install build-essential
训练设置以开始微调!
- 训练器初始化
我们将使用模型和 tokenizer 以及训练数据集初始化 `SFTTrainer`。`dataset_text_field` 参数指定数据集中包含用于训练的文本的字段,我们已在上面准备好该字段。训练器负责管理微调过程,包括数据处理和模型更新。
- 训练参数
`TrainingArguments` 类用于定义训练过程的关键超参数。这些包括
- `learning_rate=3e-4`:设置优化器的学习率。
- `per_device_train_batch_size=32`:定义每个设备的批处理大小,优化 GPU 使用率。
- `num_train_epochs=20`:指定训练 epoch 的数量。
- `fp16=not is_bfloat16_supported()` 和 `bf16=is_bfloat16_supported()`:启用混合精度训练以减少内存使用,具体取决于硬件支持。
- `optim="adamw_8bit"`:使用 8 位 AdamW 优化器,实现高效内存使用。
- `weight_decay=0.01`:应用权重衰减以防止过拟合。
- `output_dir="output"`:指定保存训练模型和日志的目录。
- 训练过程
最后,我们调用 `trainer.train()` 方法启动训练过程。它使用定义的参数微调模型,调整权重并从提供的数据集中学习。训练器还处理数据打包和梯度累积,优化训练管道以获得更好的性能。
有时 PyTorch 会保留内存而不释放。设置此环境变量有助于避免内存碎片。您可以在运行模型之前在环境或脚本中设置此变量
export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
如果 GPU 中有不再需要的变量,您可以使用 del 删除它们,然后调用
torch.cuda.empty_cache().
trainer=SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=training_data,
dataset_text_field="text",
max_seq_length=max_seq_length,
dataset_num_proc=2,
packing=True,
args=TrainingArguments(
learning_rate=3e-4,
lr_scheduler_type="linear",
per_device_train_batch_size=16,
gradient_accumulation_steps=8,
num_train_epochs=40,
fp16=not is_bfloat16_supported(),
bf16=is_bfloat16_supported(),
logging_steps=1,
optim="adamw_8bit",
weight_decay=0.01,
warmup_steps=10,
output_dir="output",
seed=0,
),
)
trainer.train()
推理
text="I'm going through some things with my feelings and myself. I barely sleep and I do nothing but think about how I'm worthless and how I shouldn't be here. I've never tried or contemplated suicide. I've always wanted to fix my issues, but I never get around to it. How can I change my feeling of being worthless to everyone?"
以下是一些需要注意的关键点
`model = FastLanguageModel.for_inference(model)` 将模型专门配置为进行推理,从而优化其生成响应的性能。
输入文本使用 `tokenizer` 进行标记化,它将文本转换为模型可以处理的格式。我们使用 `data_prompt` 来格式化输入文本,而响应占位符留空以从模型获取响应。`return_tensors = "pt"` 参数指定输出应为 PyTorch 张量,然后将其移动到 GPU(使用 `.to("cuda")`)以加快处理速度。
`model.generate` 方法根据标记化的输入生成响应。参数 `max_new_tokens = 5020` 和 `use_cache = True` 确保模型可以通过利用前几层的缓存计算来有效地生成长而连贯的响应。
model = FastLanguageModel.for_inference(model)
inputs = tokenizer(
[
data_prompt.format(
#instructions
text,
#answer
"",
)
], return_tensors = "pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens = 5020, use_cache = True)
answer=tokenizer.batch_decode(outputs)
answer = answer[0].split("### Response:")[-1]
print("Answer of the question is:", answer)
问题的答案是
很抱歉您感到如此不知所措。听起来您正在努力弄清楚自己发生了什么。我建议您去看一位专门治疗抑郁症患者的治疗师。抑郁症是人们普遍遇到的问题。解决抑郁症问题对于改善您的生活质量至关重要。抑郁症可能导致焦虑、绝望和对活动失去乐趣等其他问题。抑郁症也可能导致自杀念头。如果您有自杀念头,请拨打 911 或前往最近的医院急诊室。如果您没有自杀念头,但感到不知所措,请拨打 800-273-8255。这个号码是免费且保密的,您可以与他人谈论任何事情。您也可以访问 www.suicidepreventionlifeline.org 查找当地的自杀预防热线。<|end_of_text|>
os.environ["HF_TOKEN"] = "hugging face token key, you can create from your HF account."
model.push_to_hub("ImranzamanML/1B_finetuned_llama3.2", use_auth_token=os.getenv("HF_TOKEN"))
tokenizer.push_to_hub("ImranzamanML/1B_finetuned_llama3.2", use_auth_token=os.getenv("HF_TOKEN"))
README.md: 0%| | 0.00/583 [00:00<?, ?B/s] adapter_model.safetensors: 0%| | 0.00/45.1M [00:00<?, ?B/s] 已保存模型至 https://huggingface.co/ImranzamanML/1B_finetuned_llama3.2
model.save_pretrained("model/1B_finetuned_llama3.2")
tokenizer.save_pretrained("model/1B_finetuned_llama3.2")
('model/1B_finetuned_llama3.2/tokenizer_config.json', 'model/1B_finetuned_llama3.2/special_tokens_map.json', 'model/1B_finetuned_llama3.2/tokenizer.json')
model, tokenizer = FastLanguageModel.from_pretrained( model_name = "model/1B_finetuned_llama3.2", max_seq_length = 5020, dtype = None, load_in_4bit = True)
天哪,还在找东西吗?😄 别担心!您可以使用上面的提示格式和代码来获得心灵平静的响应 🧠✨