在人工智能领域,大型语言模型(LLM)正迅速发展,为自然语言处理带来了前所未有的能力。然而,要充分利用这些模型,我们需要掌握微调、量化和推理等关键技术。本文以Qwen1.5模型为例,详细介绍如何使用llama-factory进行LoRA微调,并利用AutoGPTQ和AutoAWQ进行模型量化,最后通过Transformers和VLLM框架进行推理。同时,本文还将深入探讨Qwen1.5的使用规范,以及在实际操作中可能遇到的问题和解决方案。
一、使用llama-factory进行LoRA微调
LoRA(Low-Rank Adaptation)是一种高效的微调方法,它通过引入低秩矩阵来调整预训练模型的参数,从而在特定任务上获得更好的性能,同时显著减少了计算资源的需求。llama-factory是一个强大的工具,可以简化LoRA微调的流程。
1.1 数据准备
首先,我们需要准备符合llama-factory格式的数据集。以下是一个示例:
- train_clean.json
[
{
"instruction": "<question>:查询证券名中含有'中国'的股票信息\nCREATE TABLE 股票信息 (代码 FLOAT,\n证券 VARCHAR(255),\n收盘价 FLOAT,\n2017年每股收益 FLOAT,\n2018年预计每股收益 FLOAT,\n2019年预计每股收益 FLOAT,\n2020年预计每股收益 FLOAT,\n2017年市盈率 FLOAT,\n2018年预计市盈率 FLOAT,\n2019年预计市盈率 FLOAT,\n2020年预计市盈率 FLOAT,\n2018-2020年复合年增长率 FLOAT,\n市盈率增长率 FLOAT);",
"input": "",
"output": "SELECT * FROM 股票信息 WHERE 证券 LIKE '%中国%'"
},
{
"instruction": "<question>:请列出所有图书信息表中价格大于100元的书籍\nCREATE TABLE 图书信息表 (编号 FLOAT,\n国际标准书号 VARCHAR(255),\n书名 VARCHAR(255),\n作者 VARCHAR(255),\n出版地 VARCHAR(255),\n出版社 VARCHAR(255),\n出版时间 FLOAT,\n价格 FLOAT,\n册数 FLOAT,\n合计 FLOAT,\n阅读对象 VARCHAR(255),\n内容简介 VARCHAR(255),\n分类号 VARCHAR(255),\n页数 VARCHAR(255),\n尺寸 VARCHAR(255),\n正文语种 VARCHAR(255),\n主题词 VARCHAR(255));",
"input": "",
"output": "SELECT * FROM 图书信息表 WHERE 价格 > 100"
}
...
]
- dataset_info.json
{
"train_clean": {
"file_name": "train_clean.json",
"columns": {
"prompt": "instruction",
"query": "input",
"response": "output"
}
将数据文件放置在llama-factory根目录下的data文件夹中,并在dataset_info.json
中配置LoRA数据集。
1.2 定制LoRA训练参数
创建一个YAML文件,例如lyb_qwen_lora_sft.yaml
,用于配置LoRA训练的参数。以下是一个示例:
#model_name_or_path: E:\PyCharm\PreTrainModel\qwen_7b_chat
model_name_or_path: /media/xk/D6B8A862B8A8433B/data/qwen1_5-1_8b
stage: sft
do_train: true
finetuning_type: lora
lora_target: all
dataset: train_clean
dataset_dir: ../data
template: qwen
cutoff_len: 1024
#max_samples: 1000
overwrite_cache: true
preprocessing_num_workers: 2
output_dir: /media/xk/D6B8A862B8A8433B/data/save_lora/qwen1_5-1_8b_lora
logging_steps: 10
save_steps: 100
plot_loss: true
overwrite_output_dir: true
per_device_train_batch_size: 2
gradient_accumulation_steps: 2
learning_rate: 0.00001
num_train_epochs: 2.0
lr_scheduler_type: cosine
warmup_steps: 0.1
fp16: true
val_size: 0.1
per_device_eval_batch_size: 2
evaluation_strategy: steps
eval_steps: 100
这个YAML文件定义了模型路径、微调类型、数据集、训练参数和评估参数等。
1.3 启动LoRA微调
使用以下代码启动LoRA微调:
from llmtuner.train.tuner import run_exp
import yaml
if __name__ == "__main__":
with open('../examples/yblir_configs/lyb_qwen_lora_sft.yaml', 'r', encoding='utf-8') as f:
param = yaml.safe_load(f)
run_exp(param)
在运行微调之前,务必检查Prompt的一致性。确保LoRA时的Prompt、训练时的Prompt以及量化时的Prompt保持一致,以避免精度损失。可以通过以下代码验证:
import torch
from transformers import AutoTokenizer
message = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "<question>:查询所有考试信息. CREATE TABLE 考试信息表 (日期 FLOAT,考试时间 VARCHAR(255),学院 VARCHAR(255),课程 VARCHAR(255),考试班级 FLOAT,班级人数 FLOAT,考试地点 VARCHAR(255));"},
{"role": "assistant", "content": "SELECT * FROM 考试信息表"}
]
raw_model_path='/media/xk/D6B8A862B8A8433B/data/qwen1_5-1_8b'
max_len=8192
tokenizer = AutoTokenizer.from_pretrained(raw_model_path)
text = tokenizer.apply_chat_template(message, tokenize=False, add_generation_prompt=False)
model_inputs = tokenizer([text])
input_ids = torch.tensor(model_inputs.input_ids[:max_len], dtype=torch.int)
print('input_ids=',input_ids)
#print('attention_mask=',input_ids.ne(tokenizer.pad_token_id))
确保llama-factory处理的input_ids
与tokenizer.apply_chat_template
处理的结果一致。如果不一致,需要修改llama-factory模板,例如在src/llmtuner/data/template.py
中添加token_ids += [198]
,以保持严格一致。
1.4 模型合并与导出
微调完成后,将LoRA适配器合并到原始模型中,并导出合并后的模型。
model_name_or_path: /media/xk/D6B8A862B8A8433B/data/qwen1_5-1_8b
adapter_name_or_path: /media/xk/D6B8A862B8A8433B/data/save_lora/qwen1_5-1_8b_lora/checkpoint-800
template: qwen
finetuning_type: lora
export_dir: /media/xk/D6B8A862B8A8433B/data/qwen1_5-1_8b_merge_800
export_size: 2
export_device: cpu
export_legacy_format: true
二、模型量化
模型量化是一种降低模型大小和加速推理的方法。通过将模型的权重从浮点数转换为整数,可以显著减少内存占用和计算复杂度。本文介绍如何使用AutoGPTQ和AutoAWQ进行模型量化。
2.1 AutoGPTQ量化
AutoGPTQ是一种基于GPTQ算法的量化工具,它可以将模型量化为4位或8位整数。
import logging
import json
import torch
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
from transformers import AutoTokenizer
def qwen_preprocess(lora_data_, tokenizer_, max_len_):
messages = []
for item in lora_data_:
temp = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": item['instruction']},
{"role": "assistant", "content": item['output']}
]
messages.append(temp)
data = []
for msg in messages:
text = tokenizer_.apply_chat_template(msg, tokenize=False, add_generation_prompt=False)
model_inputs = tokenizer_([text])
input_ids = torch.tensor(model_inputs.input_ids[:max_len_], dtype=torch.int)
data.append(dict(input_ids=input_ids, attention_mask=input_ids.ne(tokenizer_.pad_token_id)))
return data
if __name__ == '__main__':
model_dir_path = "/media/xk/D6B8A862B8A8433B/data/qwen1_5-1_8b_merge_800"
quantized_path = "/media/xk/D6B8A862B8A8433B/data/qwen1_5-1_8b_merge_800_int4_gptq"
quantize_dataset_path = '/media/xk/D6B8A862B8A8433B/GitHub/llama-factory/data/train_clean.json'
with open(quantize_dataset_path, 'r', encoding='utf-8') as f:
lora_data = json.load(f)
max_len = 8192
quantize_config = BaseQuantizeConfig(
bits=4,
group_size=128,
damp_percent=0.01,
desc_act=False,
static_groups=False,
sym=True,
true_sequential=True,
model_name_or_path=None,
model_file_base_name="model"
)
tokenizer = AutoTokenizer.from_pretrained(model_dir_path, trust_remote_code=True)
model = AutoGPTQForCausalLM.from_pretrained(
model_dir_path,
quantize_config,
device_map="auto",
trust_remote_code=True
)
data = qwen_preprocess(lora_data, tokenizer, max_len)
model.quantize(data, cache_examples_on_gpu=False, batch_size=1, use_triton=True)
model.save_quantized(quantized_path, use_safetensors=True)
tokenizer.save_pretrained(quantized_path)
在运行量化之前,同样需要验证Prompt的一致性。确保量化时使用的Prompt与LoRA微调时使用的Prompt一致。可以通过运行上述代码中的注释部分来验证。
2.2 AutoAWQ量化
AutoAWQ是另一种量化工具,它使用AWQ(Activation-Aware Weight Quantization)算法来量化模型。与GPTQ不同,AWQ对校准数据集的精度要求较低。
import json
import torch
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
def qwen_preprocess(lora_data_, tokenizer_, max_len_):
messages = []
for item in lora_data_:
temp = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": item['instruction']},
{"role": "assistant", "content": item['output']}
]
messages.append(temp)
data = []
for msg in messages:
text = tokenizer_.apply_chat_template(msg, tokenize=False, add_generation_prompt=False)
data.append(text)
return data
if __name__ == '__main__':
model_dir_path = "/media/xk/D6B8A862B8A8433B/data/qwen1_5-1_8b_merge_800"
quantized_path = "/media/xk/D6B8A862B8A8433B/data/qwen1_5-1_8b_merge_800_int4_awq"
quantize_dataset_path = '/media/xk/D6B8A862B8A8433B/GitHub/llama-factory/data/train_clean.json'
with open(quantize_dataset_path, 'r', encoding='utf-8') as f:
lora_data = json.load(f)
max_len = 2048
quant_config = {"zero_point": True, "q_group_size": 128, "w_bit": 4, "version": "GEMM"}
tokenizer = AutoTokenizer.from_pretrained(model_dir_path)
model = AutoAWQForCausalLM.from_pretrained(model_dir_path, device_map="auto", safetensors=True)
data = qwen_preprocess(lora_data, tokenizer, max_len)
model.quantize(tokenizer, quant_config=quant_config, calib_data=data)
model.save_quantized(quantized_path, safetensors=True)
tokenizer.save_pretrained(quantized_path)
三、模型推理
模型推理是指使用训练好的模型来生成文本或执行其他任务。本文介绍如何使用Transformers和VLLM框架进行模型推理。
3.1 Transformers直接推理
Transformers是一个流行的深度学习库,提供了许多预训练模型和工具。可以使用Transformers直接加载和使用量化后的模型。
import json
import sys
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
device = "cuda"
def qwen_preprocess(tokenizer_, msg):
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": msg}
]
text_ = tokenizer_.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
model_inputs_ = tokenizer_([text_], return_tensors="pt").to(device)
input_ids = tokenizer_.encode(text_, return_tensors='pt')
attention_mask_ = torch.ones(input_ids.shape, dtype=torch.long, device=device)
return model_inputs_, attention_mask_
if __name__ == '__main__':
model_path = '/media/xk/D6B8A862B8A8433B/data/qwen1_5-1_8b_merge_800'
data_path = '/media/xk/D6B8A862B8A8433B/GitHub/llama-factory/data/train_clean_eval.json'
with open(data_path, 'r', encoding='utf-8') as f:
data = json.load(f)
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(
pretrained_model_name_or_path=model_path,
torch_dtype="auto",
device_map="auto",
)
for i, item in enumerate(data):
print(f'{i} ------------------------------------------')
model_inputs, attention_mask = qwen_preprocess(tokenizer, item['instruction'])
generated_ids = model.generate(
model_inputs.input_ids,
max_new_tokens=512,
attention_mask=attention_mask,
pad_token_id=tokenizer.eos_token_id
)
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)
3.2 VLLM推理
VLLM是一个快速且易于使用的LLM推理库。它可以显著提高推理速度,尤其是在处理多批次请求时。
import json
import sys
import torch
from transformers import AutoTokenizer
from vllm import LLM, SamplingParams
device = "cuda"
def qwen_preprocess(tokenizer_, msg):
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": msg}
]
text_ = tokenizer_.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
return text_
if __name__ == '__main__':
model_path = '/media/xk/D6B8A862B8A8433B/data/qwen1_5-1_8b_merge_800'
data_path = '/media/xk/D6B8A862B8A8433B/GitHub/llama-factory/data/train_clean_eval.json'
with open(data_path, 'r', encoding='utf-8') as f:
data = json.load(f)
sampling_params = SamplingParams(temperature=0.7, top_p=0.8, repetition_penalty=1.05, max_tokens=512)
llm = LLM(model_path)
tokenizer = AutoTokenizer.from_pretrained(model_path)
for i, item in enumerate(data):
print(f'{i} ------------------------------------------')
text = qwen_preprocess(tokenizer, item['instruction'])
outputs = llm.generate([text], sampling_params)
for output in outputs:
generated_text = output.outputs[0].text
print(generated_text)
通过对比Transformers和VLLM的推理耗时,可以发现VLLM在单批次推理中也有显著的加速效果。对于多批次推理,VLLM的并行优化可以带来更大的性能提升。
四、总结
本文详细介绍了如何使用llama-factory进行Qwen1.5模型的LoRA微调,以及如何使用AutoGPTQ和AutoAWQ进行模型量化,并最终使用Transformers和VLLM框架进行推理。通过本文的学习,读者可以掌握Qwen1.5模型微调、量化和推理的全流程,并了解在实际操作中可能遇到的问题和解决方案。
Qwen1.5的使用方法与Qwen相比发生了很大的变化,代码已经合并到Transformers库中,使用风格变得统一。这种标准化是未来大型模型发展的一个趋势。过去一年,人工智能领域的技术发展迅速,我们应该拥抱变化,不断学习新的技术,才能在这个领域保持竞争力。