微调入门:本文介绍如何基于Qwen2的大模型进行微调,涵盖环境安装、数据集准备、加载模型、配置训练可视化工具及完整代码实现的关键步骤。重点在于使用复旦中文新闻数据集进行训练,通过代码示例演示了从数据预处理、模型加载到训练过程的可视化与效果评估,旨在提供从零开始的微调实际操作指南。
环境安装
为了进行Qwen2的大模型微调,确保您的计算机已安装Python,并确保Python环境中包含以下必需的库:
- PyTorch
- CUDA
- swanlab
- modelscope
- transformers
- datasets
- peft
- accelerate
- pandas
可以通过以下命令一键安装上述库:
pip install swanlab modelscope transformers datasets peft pandas accelerate
此外,本案例测试于modelscope1.14.0、transformers4.41.2、datasets2.18.0、peft0.11.1、accelerate0.30.1、swanlab0.3.9版本。
准备数据集
本案例将使用复旦中文新闻数据集(zh_cls_fudan_news)进行训练。首先从魔搭社区下载train.jsonl
和test.jsonl
文件,然后按照以下步骤预处理数据集:
-
加载数据集:使用Python读取并解析
train.jsonl
和test.jsonl
文件。 -
转换格式:将原始数据集转换为适合大模型微调的格式。具体步骤包括:
- 为每个样本构建类似于:
{"instruction": "你是一个文本分类领域的专家,你会接收到一段文本和几个潜在的分类选项,请输出文本内容的正确类型", "input": "文本内容,类型选型:类别列表", "output": 真实类别}
- 将处理后的数据存储在新的
new_train.jsonl
和new_test.jsonl
文件中。
- 为每个样本构建类似于:
加载模型
使用modelscope下载Qwen2-1.5B-Instruct模型,并利用Transformers加载模型权重:
from modelscope import snapshot_download, AutoTokenizer
from transformers import AutoModelForCausalLM
model_dir = snapshot_download("qwen/Qwen2-1.5B-Instruct", cache_dir="./", revision="master")
tokenizer = AutoTokenizer.from_pretrained(f"{model_dir}/qwen/Qwen2-1___5B-Instruct", use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(f"{model_dir}/qwen/Qwen2-1___5B-Instruct", device_map="auto", torch_dtype=torch.bfloat16)
model.enable_input_require_grads()
配置训练可视化工具
使用SwanLab监控训练过程和效果评估,与Transformers集成以实现可视化和效果评估:
from swanlab.integration.huggingface import SwanLabCallback
swanlab_callback = SwanLabCallback(...) # 初始化SwanLabCallback
trainer = Trainer(
model=model,
args=TrainingArguments(...), # 参数自定义
callbacks=[swanlab_callback],
)
完整代码
训练过程的目录结构如下:
|--- train.py
|--- train.jsonl
|--- test.jsonl
在train.py
文件中,封装训练逻辑:
import json
import pandas as pd
from datasets import Dataset
from modelscope import snapshot_download, AutoTokenizer
from swanlab.integration.huggingface import SwanLabCallback
from peft import LoraConfig, TaskType, get_peft_model
from transformers import AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForSeq2Seq
import os
def dataset_jsonl_transfer(origin_path, new_path):
messages = []
with open(origin_path, "r") as file:
for line in file:
data = json.loads(line)
context = data["text"]
catagory = data["category"]
label = data["output"]
message = {
"instruction": "你是一个文本分类领域的专家,你会接收到一段文本和几个潜在的分类选项,请输出文本内容的正确类型",
"input": f"文本:{context},类型选型:{catagory}",
"output": label,
}
messages.append(message)
with open(new_path, "w", encoding="utf-8") as file:
for message in messages:
file.write(json.dumps(message, ensure_ascii=False) + "\n")
def process_func(example):
MAX_LENGTH = 384
instruction = tokenizer(
f"系统指令:你是一个文本分类领域的专家,你会接收到一段文本和几个潜在的分类选项,请输出文本内容的正确类型\n用户输入:{example['input']}\n助手输出:",
add_special_tokens=False,
)
response = tokenizer(f"{example['output']}", add_special_tokens=False)
input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id]
attention_mask = (
instruction["attention_mask"] + response["attention_mask"] + [1]
)
labels = [-100] * len(instruction["input_ids"]) + response["input_ids"] + [tokenizer.pad_token_id]
if len(input_ids) > MAX_LENGTH:
input_ids = input_ids[:MAX_LENGTH]
attention_mask = attention_mask[:MAX_LENGTH]
labels = labels[:MAX_LENGTH]
return {"input_ids": input_ids, "attention_mask": attention_mask, "labels": labels}
def predict(messages, model, tokenizer):
device = "cuda"
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
model_inputs = tokenizer([text], return_tensors="pt").to(device)
generated_ids = model.generate(
model_inputs.input_ids,
max_new_tokens=512
)
generated_ids = [
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
print(response)
return response
# 数据集处理和模型加载
model_dir = snapshot_download("qwen/Qwen2-1.5B-Instruct", cache_dir="./", revision="master")
tokenizer = AutoTokenizer.from_pretrained(f"{model_dir}/qwen/Qwen2-1___5B-Instruct/", use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(f"{model_dir}/qwen/Qwen2-1___5B-Instruct/", device_map="auto", torch_dtype=torch.bfloat16)
model.enable_input_require_grads()
train_dataset_path = "train.jsonl"
test_dataset_path = "test.jsonl"
train_jsonl_new_path = "new_train.jsonl"
test_jsonl_new_path = "new_test.jsonl"
if not os.path.exists(train_jsonl_new_path):
dataset_jsonl_transfer(train_dataset_path, train_jsonl_new_path)
if not os.path.exists(test_jsonl_new_path):
dataset_jsonl_transfer(test_dataset_path, test_jsonl_new_path)
# 训练逻辑
训练结果演示
训练结束后,可以通过SwanLab查看训练过程的可视化结果。例如,在2个epoch后,模型的损失(loss)值显著降低,表明训练有效。
预测训练后的模型性能,可以使用测试数据集进行评估。以下代码展示了如何使用测试数据集的前10条记录进行预测:
test_df = pd.read_json(test_jsonl_new_path, lines=True)[:10]
test_text_list = []
for index, row in test_df.iterrows():
instruction = row['instruction']
input_value = row['input']
messages = [
{"role": "system", "content": f"{instruction}"},
{"role": "user", "content": f"{input_value}"}
]
response = predict(messages, model, tokenizer)
messages.append({"role": "assistant", "content": f"{response}"})
result_text = f"{messages[0]}\n\n{messages[1]}\n\n{messages[2]}"
test_text_list.append(swanlab.Text(result_text, caption=response))
swanlab.log({"Prediction": test_text_list})
swanlab.finish()
至此,您已完成Qwen2指令微调的训练流程!
通过这篇文章,您不仅能够熟悉微调Qwen2大模型的整个流程,还能深入理解每个关键步骤背后的技术原理。希望您在实际操作中能够顺利复现这个过程,并进一步探索更多关于模型微调的实践技巧。记得收藏本文,作为您微调之旅的宝贵资源。
共同學習,寫下你的評論
評論加載中...
作者其他優質文章