messages =[{"role":"user","content":"What is the tallest building in the world?"}]
prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
outputs = pipe(prompt, max_new_tokens=120, do_sample=True)print(outputs[0]["generated_text"])技术支持
我们将使用 Weights & Biases API 跟踪模型表现。要访问该 API,我们需要 API 密钥。请在 Kaggle 中通过 Secrets 设置并启用 API 密钥,如下所示。
随后使用该 API 密钥初始化 Weights & Biases 项目。
import wandb
from kaggle_secrets import UserSecretsClient
user_secrets = UserSecretsClient()
wb_token = user_secrets.get_secret("wandb")
wandb.login(key=wb_token)
run = wandb.init(
project='Fine-tune llama-3.1-8b-it on Sentiment Analysis Dataset',
job_type="training",
anonymous="allow")技术支持
接着导入所有必要的 Python 包与函数。
import numpy as np
import pandas as pd
import os
from tqdm import tqdm
import bitsandbytes as bnb
import torch
import torch.nn as nn
import transformers
from datasets import Dataset
from peft import LoraConfig, PeftConfig
from trl import SFTTrainer
from trl import setup_chat_format
from transformers import(AutoModelForCausalLM,
AutoTokenizer,
BitsAndBytesConfig,
TrainingArguments,
pipeline,
logging)from sklearn.metrics import(accuracy_score,
classification_report,
confusion_matrix)from sklearn.model_selection import train_test_split技术支持
# Shuffle the DataFrame and select only 3000 rows
df = df.sample(frac=1, random_state=85).reset_index(drop=True).head(3000)# Split the DataFrame
train_size =0.8
eval_size =0.1# Calculate sizes
train_end =int(train_size *len(df))
eval_end = train_end +int(eval_size *len(df))# Split the data
X_train = df[:train_end]
X_eval = df[train_end:eval_end]
X_test = df[eval_end:]# Define the prompt generation functionsdefgenerate_prompt(data_point):returnf"""
Classify the text into Normal, Depression, Anxiety, Bipolar, and return the answer as the corresponding mental health disorder label.
text: {data_point["statement"]}
label: {data_point["status"]}""".strip()defgenerate_test_prompt(data_point):returnf"""
Classify the text into Normal, Depression, Anxiety, Bipolar, and return the answer as the corresponding mental health disorder label.
text: {data_point["statement"]}
label: """.strip()# Generate prompts for training and evaluation data
X_train.loc[:,'text']= X_train.apply(generate_prompt, axis=1)
X_eval.loc[:,'text']= X_eval.apply(generate_prompt, axis=1)# Generate test prompts and extract true labels
y_true = X_test.loc[:,'status']
X_test = pd.DataFrame(X_test.apply(generate_test_prompt, axis=1), columns=["text"])技术支持
output_dir="llama-3.1-fine-tuned-model"
peft_config = LoraConfig(
lora_alpha=16,
lora_dropout=0,
r=64,
bias="none",
task_type="CAUSAL_LM",
target_modules=modules,)
training_arguments = TrainingArguments(
output_dir=output_dir,# directory to save and repository id
num_train_epochs=1,# number of training epochs
per_device_train_batch_size=1,# batch size per device during training
gradient_accumulation_steps=8,# number of steps before performing a backward/update pass
gradient_checkpointing=True,# use gradient checkpointing to save memory
optim="paged_adamw_32bit",
logging_steps=1,
learning_rate=2e-4,# learning rate, based on QLoRA paper
weight_decay=0.001,
fp16=True,
bf16=False,
max_grad_norm=0.3,# max gradient norm based on QLoRA paper
max_steps=-1,
warmup_ratio=0.03,# warmup ratio based on QLoRA paper
group_by_length=False,
lr_scheduler_type="cosine",# use cosine learning rate scheduler
report_to="wandb",# report metrics to w&b
eval_strategy="steps",# save checkpoint every epoch
eval_steps =0.2)
trainer = SFTTrainer(
model=model,
args=training_arguments,
train_dataset=train_data,
eval_dataset=eval_data,
peft_config=peft_config,
dataset_text_field="text",
tokenizer=tokenizer,
max_seq_length=512,
packing=False,
dataset_kwargs={"add_special_tokens":False,"append_concat_token":False,})技术支持
100%|██████████|300/300[03:24<00:00,1.47it/s]
Accuracy:0.913
Accuracy for label Normal:0.972
Accuracy for label Depression:0.913
Accuracy for label Anxiety:0.667
Accuracy for label Bipolar:0.800
Classification Report:
precision recall f1-score support
Normal 0.920.970.95143
Depression 0.930.910.92115
Anxiety 0.750.670.7127
Bipolar 1.000.800.8915
accuracy 0.91300
macro avg 0.900.840.87300
weighted avg 0.910.910.91300
Confusion Matrix:[[139310][510550][63180][12012]]技术支持
现在我们可以保存 Kaggle Notebook,以保存结果和模型文件。点击右上角的“Save Version”按钮,选择版本类型“Quick Save”,并将输出保存类型设置为“Always save output when creating a Quick Save(创建 Quick Save 时始终保存输出)”。
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
from peft import PeftModel
import torch
# Reload tokenizer and model
tokenizer = AutoTokenizer.from_pretrained(base_model)
base_model_reload = AutoModelForCausalLM.from_pretrained(
base_model,
return_dict=True,
low_cpu_mem_usage=True,
torch_dtype=torch.float16,
device_map="auto",
trust_remote_code=True,)技术支持
接着将基础模型与微调适配器进行合并,如下所示。
# Merge adapter with base model
model = PeftModel.from_pretrained(base_model_reload, fine_tuned_model)
model = model.merge_and_unload()技术支持
在保存模型之前,先检查其是否正常工作。使用模型和分词器创建文本生成管线,并提供示例提示。
text ="I'm trapped in a storm of emotions that I can't control, and it feels like no one understands the chaos inside me"
prompt =f"""Classify the text into Normal, Depression, Anxiety, Bipolar, and return the answer as the corresponding mental health disorder label.
text: {text}
label: """.strip()
pipe = pipeline("text-generation",
model=model,
tokenizer=tokenizer,
torch_dtype=torch.float16,
device_map="auto",)
outputs = pipe(prompt, max_new_tokens=2, do_sample=True, temperature=0.1)print(outputs[0]["generated_text"].split("label: ")[-1].strip())技术支持
在开始之前先提醒一下:在医疗健康领域探索 AI 用例时,务必要谨慎,关注对患者可能造成的有害结果。下面的示例虽然全面,但纯粹用于展示如何在真实世界数据集上进行微调。如果您正在医疗或其他敏感领域推进 AI 用例,我们强烈建议阅读我们的AI 伦理指南。
在本教程中,我们将了解 Llama 3.1 系列模型,并在心理健康数据集上对 Llama-3.1-8b-It 模型进行情感分析方向的微调。我们的目标是自定义模型,使其能够基于文本预测患者的心理健康状态。我们还将把适配器与基础模型合并,并将完整模型保存到 Hugging Face Hub。
我们将学习 Llama 3.1 模型、如何在 Kaggle 上访问它们,以及如何使用 Transformers 库运行模型推理。我们还会在心理健康分类数据集上微调 Llama-3.1-8b-It 模型。最后,我们将把保存的适配器与基础模型合并,并将完整模型推送到 Hugging Face Hub。
如果您是新手,可以先阅读我们的文章大型语言模型微调入门指南,了解微调背后的理论。
图片由作者提供
Llama 3.1 简介
Llama 3.1 是 Meta AI 最新推出的多语言大型语言模型(LLM)系列,进一步推动了语言理解与生成的边界。该系列提供 8B、70B 和 405B 三种规模。
Llama 3.1 模型基于自回归语言模型架构,配备优化的 Transformer,可针对各类自然语言处理任务与数据集进行微调。其训练数据涵盖多样的公开在线数据,支持八种语言(英语、德语、法语、意大利语、葡萄牙语、印地语、西班牙语和泰语),并具备 128k 的上下文长度。
Llama 3.1 模型向所有人开放,采用自定义商业许可,下载权重所需的个人信息极少。
Llama 3.1 针对多语言对话进行了优化,并在通用对话、编程、数学、推理等多项基准上优于 Gemma 2、GPT-3.5 Turbo 和 GPT-4o。它是迄今速度最快、精度最高的开源模型之一。
来源:Llama 3.1 (meta.com)
您可以通过阅读什么是 Meta 的 Llama 3.1 405B?工作原理、用例与更多了解更多关于 Llama 3.1 的信息。
开始使用 Llama 3.1
本教程将使用 Kaggle Notebook 作为开发环境,因为它提供免费的 GPU 和 TPU。要在 Kaggle Notebook 中使用 Llama 3.1,请按以下步骤操作:
1. 在meta.com上填写表单,使用与您 Kaggle 账户相同的邮箱地址。
2. 访问 Kaggle 上的Meta | Llama 3.1模型仓库并点击“access the model”(获取访问)按钮。接受所有条款,几秒钟后您就能获得模型访问权限。
3. 在模型页面右上方点击“Code”按钮,使用 Llama 3.1 模型启动 Kaggle Notebook。
4. 选择框架、变体与版本,然后点击“Add Model”。
5. 在 Kaggle Notebook 中使用以下命令安装所需的 Python 包:
6. 使用 Transformers 库从本地目录加载模型和分词器。
7. 使用模型、分词器、torch 类型和设备映射创建文本生成管线。
8. 编写消息,并使用聊天模板将其转换为合适的提示。
9. 使用该提示运行管线,并打印生成的输出。
答案准确且详尽。
如果您在运行模型推理时遇到困难,请参阅 Kaggle Notebook: Llama 3.1 简单模型推理。
在心理健康障碍分类任务上微调 Llama 3.1
现在,我们需要加载数据集、进行处理,并对 Llama 3.1 模型进行微调。我们还将比较模型在微调前后的表现。
如果您刚接触 LLM,建议在进入本教程的微调部分之前,先学习掌握大型语言模型(LLM)概念课程。
1. 环境准备
首先,我们像上一节那样启动新的 Kaggle Notebook 并加载 Llama 3.1 模型。
然后按如下所示安装所需的 Python 包:
接下来,将情感分析(心理健康)数据添加到 Kaggle Notebook。为此,点击右上角的“Add Input”按钮,并将模型链接粘贴到搜索栏。然后点击加号(+)即可添加。
我们将使用 Weights & Biases API 跟踪模型表现。要访问该 API,我们需要 API 密钥。请在 Kaggle 中通过 Secrets 设置并启用 API 密钥,如下所示。
随后使用该 API 密钥初始化 Weights & Biases 项目。
接着导入所有必要的 Python 包与函数。
2. 加载并处理数据集
现在开始加载数据集、进行数据清洗,并移除三个含义不够明确的类别。
为简化处理,我们将删除“Suicidal(自杀倾向)”类别,因为 Llama 3.1 具备安全机制,会屏蔽某些触发性用词;“Stress(压力)”不被视为心理障碍;而“Personality Disorder(人格障碍)”与“Bipolar(双相障碍)”存在较多重叠。
因此,我们仅保留四个类别:“Normal(正常)”“Depression(抑郁)”“Anxiety(焦虑)”和“Bipolar(双相)”。
为节省训练时间,我们仅在 3000 条样本上进行微调。为此,我们将数据集打乱并选取 3000 行。
随后将数据集划分为训练集、验证集与测试集,用于训练与评估。
我们还将在训练集与验证集中使用
generate_prompt函数创建“text”列,该函数将“statement”和“status”两列组合起来。最后,我们将使用
generate_test_prompt函数在测试集中创建“text”列,并用“status”列生成y_true。我们将用其生成如下所示的模型评估报告。此时,我们想检查训练集中各类别的分布。
如下所示,“Normal”和“Depression”的分布几乎相当,其余标签为少数类。这意味着我们的数据集不平衡,模型在预测多数类时会优于少数类。
我们可以对数据集进行平衡处理,但这不是本教程的目标。
接下来,我们将把训练集和验证集转换为 Hugging Face Datasets。
然后展示“text”列中的第 4 个样本。
可以看到,“text”列包含系统提示、原始语句和作为标签的状态。
3. 加载模型与分词器
接下来,我们以 4 位量化方式加载 Llama-3.1-8b-instruct 模型,以节省 GPU 内存。
随后加载分词器并设置 pad token id。
4. 微调前的模型评估
这里我们创建
predict函数,使用文本生成管线从“text”列预测标签。运行该函数将基于测试集中的各样本返回心理障碍类别列表。随后我们创建
evaluate函数,基于预测标签与真实标签计算模型总体准确率及各类别准确率,生成分类报告,并打印混淆矩阵。运行后将得到详细的评估摘要。即便未进行微调,Llama 3.1 也有非常出色的表现。79% 的准确率已相当不错。让我们看看在数据集上微调后模型能提升到何种程度。
5. 构建模型
在构建模型时,我们先使用
bits and bytes库从模型中提取线性模块名称。然后基于目标模块、任务类型及其他参数配置 LoRA,并设置训练参数。这里的训练参数针对 Kaggle Notebook 进行了优化,如果在本地使用,您可能需要根据实际情况调整。
随后基于训练参数、模型、分词器、LoRA 配置与数据集创建训练器。
6. 模型训练
现在开始启动模型训练:
微调耗时约 1.5 小时,验证损失稳步下降。若想获得更佳表现,建议在完整数据集上训练至少五个 epoch。
接下来结束本次 Weights & Biases 运行。
您将能够查看运行摘要,包括模型性能所需的各项指标。
如需查看更详细的摘要,请前往您的 Weights & Biases 账户,在浏览器中打开该运行,您将看到交互式可视化。
然后我们在本地保存模型适配器和分词器。下一节将用它来把适配器与基础模型合并。
7. 微调后的模型测试
接下来是关键部分。我们的模型在微调后会更好还是更差?要验证,需要在测试集上运行“predict”函数,然后运行“evaluate”函数生成评估报告。
如您在下方结果所见,模型性能大幅提升,准确率从 79% 提高到 91.3%。F1 分数也表现良好。
现在我们可以保存 Kaggle Notebook,以保存结果和模型文件。点击右上角的“Save Version”按钮,选择版本类型“Quick Save”,并将输出保存类型设置为“Always save output when creating a Quick Save(创建 Quick Save 时始终保存输出)”。
如果您在微调模型时遇到问题,请参考 Kaggle笔记本获取更多帮助。
您也可以通过我们的指南本地微调 Llama 3 并在本地使用了解如何微调 Llama 3.0 模型。
合并并保存微调后的 Llama 3.1 模型
本节我们将把适配器与基础模型合并,并将完整版本保存到 Hugging Face Hub。
首先,启动一个启用 GPU 加速的新 Kaggle Notebook,并将已保存的 Notebook 添加为输入以访问模型文件。我们也可以像添加数据集一样,将其他 Kaggle Notebook 作为输入加入。
随后安装所需的 Python 包。
接着使用 API 密钥登录 Hugging Face Hub API,以便将模型文件推送到对应的模型仓库。
然后设置基础模型与微调模型的目录。
随后加载分词器与基础模型。
接着将基础模型与微调适配器进行合并,如下所示。
在保存模型之前,先检查其是否正常工作。使用模型和分词器创建文本生成管线,并提供示例提示。
我们的模型工作正常。
现在可以在本地保存模型和分词器。
然后将模型与分词器推送到 Hugging Face Hub。
这将会在 Hugging Face 上创建仓库,并推送所有模型与分词器文件。
您可以访问 Hugging Face 网站查看所有模型文件。
来源:kingabzpro/Llama-3.1-8B-Instruct-Mental-Health-Classification · Hugging Face
如果您在将适配器与基础模型合并时遇到问题,请参考该Kaggle 笔记本。
如果您觉得微调 LLM 较为困难,可以参考微调 OpenAI GPT-4:一步一步指南教程,学习如何用 OpenAI API 以极少代码在任意数据集上微调模型。
结语
微调不仅限于基于数据集的定制。我们可以在诸如机器翻译、聚类、分类、问答、嵌入等各类自然语言任务上微调大型语言模型。
在本教程中,我们学习了如何在心理健康分类数据集上微调 Llama 3.1。该模型可用于识别在日常生活中面临挑战的患者,甚至员工。
如果您想了解如何入门 LLM 并开始自行微调模型,建议学习开发大型语言模型技能路径。该系列课程将帮助您夯实 LLM 基础,迈入全新的 AI 驱动时代。