LLM系列 | 20 : Llama2 实战(下篇)-中文语料微调(附完整代码)

简介

紧接前文:

  • 万字长文细说ChatGPT的前世今生
  • Llama 2实战(上篇):本地部署(附代码)

上篇主要介绍Llama2的基本情况和基于官方模型实测Llama2在中英上的效果,包括单轮和多轮对话。今天这篇小作文作为Llama2的下篇,主要介绍如何用中文语料对Llama 2的基座模型(7B版)进行微调并实测微调后模型的效果。本文实验完整代码请找小编索取。后续将实战Code Llama感兴趣的小伙伴可以关注下!

数据准备

基座模型下载可以参考Llama 2实战(上篇):本地部署(附代码)中的模型下载部分。官方Repo: llama-recipes里面提到支持的默认数据格式有三种:grammar_dataset, alpaca_datasetandsamsum_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.jsonadapter_model.bin文件,这就是微调后的参数结果。

模型inference

使用如下脚本加载微调后的模型进行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

你可能感兴趣的:(LLM,人工智能,NLP,人工智能,LLM,llama,实战)