紧接前文:
上篇主要介绍Llama2的基本情况和基于官方模型实测Llama2在中英上的效果,包括单轮和多轮对话。今天这篇小作文作为Llama2的下篇,主要介绍如何用中文语料对Llama 2的基座模型(7B版)进行微调并实测微调后模型的效果。本文实验完整代码请找小编索取。后续将实战Code Llama感兴趣的小伙伴可以关注下!
基座模型下载可以参考Llama 2实战(上篇):本地部署(附代码)中的模型下载部分。官方Repo: llama-recipes里面提到支持的默认数据格式有三种:grammar_dataset
, alpaca_datasetand
和samsum_dataset
。为了节约时间,选择alpaca格式的中文SFT数据集,alpaca格式的数据示例如下:
[
{
"instruction": "根据给定的坐标确定最近的机场。",
"input": "40.728157, -73.794853",
"output": "距给定坐标最近的机场是纽约市的拉瓜迪亚机场 (LGA)。"
},
{
"instruction": "输出不同种类水果的列表",
"input": "",
"output": "1. 苹果\n2. 香蕉\n3. 橘子\n4. 芒果\n5. 草莓\n6. 葡萄\n7. 蓝莓\n8. 樱桃\n9. 猕猴桃\n10. 甜瓜\n11.菠萝\n12. 李子\n13.桃子"
},
{
"instruction": "找出字串中隐藏的信息",
"input": "业余咖啡",
"output": "隐藏的消息是“咖啡因爱好者”。"
}
]
本次实验具体选择的数据集是:GuanacoDataset/guanaco_non_chat-utf8.json。完成数据下载之后,将guanaco_non_chat-utf8.json
放到 llama-recipes/ft_datasets
目录下,并重命名为 alpaca_data.json
。训练时将训练集指定为 alpaca_dataset
即可,llama-recipes 将会自动找到这个文件。至此,SFT微调数据集准备完成。
微调训练脚本使用llama_finetuning.py
脚本,具体如下:
# 单机, 多卡
export CUDA_VISIBLE_DEVICES=1,2,3
torchrun --nnodes 1 --nproc_per_node 3 llama_finetuning.py \
--enable_fsdp \
--use_peft \
--peft_method lora \
--model_name /home/model_zoo/LLM/llama2/Llama-2-7b-hf/ \
--dataset alpaca_dataset \
--pure_bf16 \
--batch_size_training 50 \
--num_epochs 2 \
--output_dir /home/LLM/llama-recipes/PEFT/model
微调结束后,在/home/LLM/llama-recipes/PEFT/model
生成adapter_config.json
和adapter_model.bin
文件,这就是微调后的参数结果。
使用如下脚本加载微调后的模型进行inference:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023/09/03 10:15
# @Author : 卖热干面的小女孩
# @File : inference_multi_gpus_one_node.py
# @联系方式 : 《微-心-公-众-号 <小窗幽记机器学习>》
import pdb
import torch
from transformers import LlamaForCausalLM, LlamaTokenizer
from transformers import GenerationConfig
from peft import PeftModel, PeftConfig
peft_model_id = "./PEFT/model/"
temperature = 0.0
max_new_tokens = 256
config = PeftConfig.from_pretrained(peft_model_id)
model = LlamaForCausalLM.from_pretrained(config.base_model_name_or_path)
model = PeftModel.from_pretrained(model, peft_model_id)
tokenizer = LlamaTokenizer.from_pretrained(config.base_model_name_or_path)
device = "cuda:2"
model = model.to(device)
model.eval()
# test_prompt = "### Instruction: \n你谁? \n### Response:\n"
# test_prompt = "### Instruction: \n你叫做爱坤,擅长舞蹈、篮球、武术篮球、舞蹈篮球,喜欢Rap,能唱会跳,偶像是马保国?\n### Response:\n"
# test_prompt = "### Instruction: 根据以下信息,回答问题。爱坤的偶像是谁?直接给出答案即可,不要输出其他\n### Input: \n你叫做爱坤,擅长舞蹈、篮球、武术篮球、舞蹈篮球,喜欢Rap,能唱会跳,偶像是马保国?\n ### Response:\n"
# test_prompt = "### Instruction: 根据以下信息:<爱坤,擅长舞蹈、篮球、武术篮球、舞蹈篮球,喜欢Rap,能唱会跳,偶像是马保国?> 回答问题。爱坤的偶像是谁?直接给出答案即可。\n### Response:\n"
test_prompt = "### Instruction: 根据以下信息:<爱坤,擅长舞蹈、篮球、武术篮球、舞蹈篮球,喜欢Rap,能唱会跳,偶像是马保国?> 回答问题。爱坤崇拜谁?直接给出答案即可。\n### Response:\n"
inputs = tokenizer(test_prompt, return_tensors="pt")
model_input = tokenizer(test_prompt, return_tensors="pt").to(device)
input_ids = model_input["input_ids"].to(device)
generation_config = GenerationConfig(
temperature=temperature,
top_p=0.75,
top_k=40,
num_beams=4,
repetition_penalty=2.0
)
with torch.no_grad():
generation_output = model.generate(
input_ids=model_input['input_ids'],
generation_config=generation_config,
return_dict_in_generate=True,
output_scores=True,
max_new_tokens=max_new_tokens,
)
s = generation_output.sequences[0]
output = tokenizer.decode(s)
print("output=", output)
输出结果如下:
output= ### Instruction: 根据以下信息:<爱坤,擅长舞蹈、篮球、武术篮球、舞蹈篮球,喜欢Rap,能唱会跳,偶像是马保国?> 回答问题。爱坤崇拜谁?直接给出答案即可。
### Response:
爱坤崇拜马保国
可以看出,经过中文语料的简单微调,可以较好地支持中文问答。
上述方式以脚本方式加载模型并做inference。那么如果想要向外提供服务,可以使用alpaca-lora提供的脚本部署Web服务。这里为了减缓模型的重复回复,添加了 repeatition penalty
CUDA_VISIBLE_DEVICES=1 python3 generate.py --base_model /home/model_zoo/LLM/llama2/Llama-2-7b-hf/ --lora_weights /home/Repository/LLM/llama-recipes/PEFT/model