基于openMind的MiniCPM PyTorch模型微调最佳实践

1 引言

 

2024年2月1日,面壁智能与清华大学自然语言处理实验室共同开源MiniCPM系列端侧大模型,主体语言模型MiniCPM-2B仅有24亿(2.4B)的非词嵌入参数量,总计2.7B参数量。

经过SFT后,MiniCPM-2B在公开综合性评测集上与Mistral-7B表现相近(中文、数学和代码能力更优),整体性能超越Llama2-13B、MPT-30B、Falcon-40B等模型。

经过DPO后,MiniCPM-2B在当前最接近用户体感的评测集MTBench上也超越了Llama2-70B-Chat、Vicuna-33B、Mistral-7B-Instruct-v0.1、Zephyr-7B-alpha等众多代表性开源大模型。

 

2 环境准备

2.1 安装Ascend CANN Toolkit和Kernels

安装方法请参考安装教程或使用以下命令。

# 请替换URL为CANN版本和设备型号对应的URL
# 安装CANN Toolkit
wget https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/Milan-ASL/Milan-ASL%20V100R001C17SPC701/Ascend-cann-toolkit_8.0.RC1.alpha001_linux-"$(uname -i)".run
bash Ascend-cann-toolkit_8.0.RC1.alpha001_linux-"$(uname -i)".run --install

# 安装CANN Kernels
wget https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/Milan-ASL/Milan-ASL%20V100R001C17SPC701/Ascend-cann-kernels-910b_8.0.RC1.alpha001_linux.run
bash Ascend-cann-kernels-910b_8.0.RC1.alpha001_linux.run --install

# 设置环境变量
source /usr/local/Ascend/ascend-toolkit/set_env.sh

2.2 安装openMind Hub Client以及openMind Library

  • 安装openMind Hub Client
pip install openmind_hub
  • 安装openMind Library,并安装PyTorch框架及其依赖。
pip install openmind[pt]

更详细的安装信息请参考魔乐社区官方的环境安装章节。

2.3 安装LLaMa Factory

git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
pip install -e ".[torch-npu,metrics]"

3 模型链接和下载

 

MiniCPM-2B模型系列由社区开发者在魔乐社区贡献,包括:

 

通过Git从魔乐社区下载模型的repo,以MiniCPM-2B-sft-bf16为例:

# 首先保证已安装 git-lfs(https://git-lfs.com)
git lfs install
git clone https://modelers.cn/AI-Research/MiniCPM-2B-sft-bf16.git

 

4 模型推理

 

用户可以使用openMind Library或者LLaMa Factory进行模型推理,以MiniCPM-2B-sft-bf16为例,具体如下:

 

  • 使用openMind Library进行模型推理

新建推理脚本inference_minicpm_2b_sft_bf16.py,推理脚本内容为:

from openmind import AutoModelForCausalLM, AutoTokenizer
import torch
torch.manual_seed(0)

# 若模型已下载,可替换成模型本地路径
path = 'AI-Research/MiniCPM-2B-sft-bf16'
tokenizer = AutoTokenizer.from_pretrained(path)
model = AutoModelForCausalLM.from_pretrained(path, torch_dtype=torch.bfloat16, device_map='npu', trust_remote_code=True)

responds, history = model.chat(tokenizer, "山东省最高的山是哪座山, 它比黄山高还是矮?差距多少?", temperature=0.8, top_p=0.8)
print(responds)

 

执行推理脚本:

python inference_minicpm_2b_sft_bf16.py

推理结果如下:

 

  • 使用LLaMa Factory与模型交互

在LLaMa Factory路径下新建examples/inference/minicpm_2b_sft_bf16.yaml推理配置文件,文件内容为:

model_name_or_path: xxx 
# 当前仅支持本地加载,填写MiniCPM-2B-sft-bf16本地权重路径
template: cpm

 

使用以下命令与模型进行交互:

llamafactory-cli chat examples/inference/minicpm_2b_sft_bf16.yaml

 

交互结果如下:

 

5 模型微调

我们使用单张昇腾NPU,基于LLaMa Factory框架,采用blossom-math-v2数据集对MiniCPM-2B-sft-bf16进行全参微调,让模型能够根据用户输入的数学问题计算得到正常答案。

 

5.1 数据集

 

blossom-math-v2是基于Math32和GSM8K衍生而来的中英文数学对话数据集,适用于数学问题微调。该数据集大小为10K,每条数据代表一个完整的题目及答案,包含id、input、output、answer和dataset五个字段,具体如下:

  • id:字符串,代表原始数据集中的题目id,与dataset字段结合可确定唯一题目。
  • input:字符串,代表问题。
  • output:字符串,代表gpt-3.5-turbo-0613生成的答案。
  • answer:字符串,代表正确答案。
  • dataset:字符串,代表原始数据集。

 

以下是示例:

{
 "id": "5",
 "input": "小刚的体重是28.4千克,小强的体重是小刚的1.4倍,小强的体重=多少千克?",
 "output": "小强的体重=小刚的体重 × 1.4 = 28.4千克 × 1.4 = 39.76千克",
 "answer": "39.76",
 "dataset": "Math23K"
},
  • 下载 blossom-math-v2 数据集

感谢社区开发者在魔乐社区贡献的blossom-math-v2数据集,使用Git将数据集下载至本地。

git lfs install
git clone https://modelers.cn/AI-Research/blossom-math-v2.git

 

  • 数据预处理

下载完成后,我们进行数据预处理:

  1. 将blossom-math-v2-10k.jsonl文件数据处理成alpaca数据格式,把answer字段拼接到output字段之后,使得模型在微调结束后能够在回答结尾输出正确答案。
  2. 将blossom-math-v2-10k.jsonl拆分为训练集和验证集,其中,训练集大小为9K,验证集大小为1K。

 

创建preprocess_blossom_math_v2.py脚本,脚本内容具体如下:

import json
import argparse
import os
import stat
import random

DEFAULT_FLAGS = os.O_WRONLY | os.O_CREAT
DEFAULT_MODES = stat.S_IWUSR | stat.S_IRUSR

def parse_args():
 parse = argparse.ArgumentParser()
 parse.add_argument("--data_path", type=str)
 parse.add_argument("--save_path", type=str)
 args = parse.parse_args()
 return args

def read_data(data_path):
 data = []
 with open(data_path, "r", encoding="utf-8") as f:
 lines = f.readlines()
 for line in lines:
 data.append(json.loads(line))
 return data

def train_dev_split(data, dev_ratio=0.1, shuffle=True, seed=42):
 if shuffle:
 random.seed(seed)
 random.shuffle(data)
        
 dev_num = int(len(data) * dev_ratio)
 train_data = data[dev_num:]
 dev_data = data[:dev_num]
 return train_data, dev_data

def convert_to_alpaca_format(data):
 results = []
 for sample in data:
 example = {}
 example["instruction"] = sample["input"]
 example["output"] = sample["output"] + "\nAnswer:" + sample["answer"]
 results.append(example)
 return results

def save_data(data, save_path, save_file_name):
 with os.fdopen(os.open(os.path.join(save_path, save_file_name), DEFAULT_FLAGS, DEFAULT_MODES), "w", encoding="utf-8") as f:
 json.dump(data, f, indent=4, ensure_ascii=False)

if __name__ == "__main__":
 args = parse_args()
 data = read_data(args.data_path)
 data = convert_to_alpaca_format(data)
 train_data, dev_data = train_dev_split(data)
 save_data(train_data, args.save_path, "blossom-math-v2-train.json")
 save_data(dev_data, args.save_path, "blossom-math-v2-dev.json")

通过以下命令执行脚本,将数据预处理的结果在当前路径下分别存为blossom-math-v2-train.json和blossom-math-v2-dev.json。

# xxx为blossom-math-v2-10k.jsonl文件路径
python preprocess_blossom_math_v2.py --data_path xxx --save_path ./

修改LLaMa Factory下的`data/dataset_info.json`文件,添加数据集描述:

"blossom_math_v2_train": {
 "file_name": "xxx", // 填写预处理完成的blossom-math-v2-train.json文件路径
 "columns": {
 "prompt": "instruction",
 "response": "output"
 }
},
"blossom_math_v2_dev": {
 "file_name": "xxx", // 填写预处理完成的blossom-math-v2-dev.json文件路径
 "columns": {
 "prompt": "instruction",
 "response": "output"
 }
},

以上为整个数据预处理流程,在配置文件中使用dataset: blossom_math_v2_train, blossom_math_v2_dev配置即可在微调中使用blossom-math-v2数据集。

 

 

5.2 微调

在LLaMa Factory路径下新建examples/train_full/minicpm_2b_full_sft.yaml微调配置文件,微调配置文件如下:

### model
model_name_or_path: xxx # 当前仅支持本地加载,填写MiniCPM-2B-sft-bf16本地权重路径

### method
stage: sft
do_train: true
finetuning_type: full

### dataset
dataset: blossom_math_v2_train
template: cpm
cutoff_len: 256
overwrite_cache: true
preprocessing_num_workers: 16

### output
output_dir: saves/minicpm_2b_sft_bf16/full/sft
logging_steps: 10
save_strategy: epoch
plot_loss: true
overwrite_output_dir: true

### train
per_device_train_batch_size: 16
gradient_accumulation_steps: 1
learning_rate: 3.0e-5
num_train_epochs: 3.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
ddp_timeout: 180000000

通过下面的命令启动微调:

export ASCEND_RT_VISIBLE_DEVICES=0
llamafactory-cli train examples/train_full/minicpm_2b_full_sft.yaml

 

5.3 微调可视化

 

5.4 微调结果

 

5.4.1 评估

训练结束后,通过LLaMa Factory使用微调完成的权重在blossom-math-v2-dev.json数据集上进行推理。在LLaMa Factory路径下新建examples/train_full/minicpm_2b_predict.yaml推理配置文件,配置文件内容如下:

### model
model_name_or_path: saves/minicpm_2b_sft_bf16/full/sft/checkpoint-1689/

### method
stage: sft
do_predict: true
finetuning_type: full

### dataset
eval_dataset: blossom_math_v2_dev
template: cpm
cutoff_len: 256
overwrite_cache: true
preprocessing_num_workers: 16

### output
output_dir: saves/minicpm_2b_sft_bf16/full/predict
overwrite_output_dir: true

### eval
per_device_eval_batch_size: 64
predict_with_generate: true

通过下面的命令启动评估:

export ASCEND_RT_VISIBLE_DEVICES=0
llamafactory-cli train examples/train_full/minicpm_2b_predict.yaml

推理结果将会生成在saves/minicpm_2b_sft_bf16/full/predict路径下。其中,generated_predictions.jsonl将每条推理结果都保存为json,prompt、laebl和`predict`三个字段分别表示输入、真实标签、预测标签。

经过微调,模型会把计算结果在末尾输出。可以通过简单的正则表达式将其提取出来,并计算模型推理的正确率。新建脚本calc_acc.py,具体内容如下:

import argparse
import json
import re

def parse_args():
 parse = argparse.ArgumentParser()
 parse.add_argument("--data_path", type=str)
 args = parse.parse_args()
 return args

def read_data(data_path):
 data = []
 with open(data_path, "r", encoding="utf-8") as f:
 lines = f.readlines()
 for line in lines:
 data.append(json.loads(line))
 return data

def calc_acc(data):
 total_num = len(data)
 pattern = re.compile(r'[0-9]*$')
 correct_num = 0
    
 for i in range(total_num):
 label = pattern.search(data[i]["label"]).group()
 predict = pattern.search(data[i]["predict"]).group()
 if predict == label:
 correct_num += 1
 acc = round(correct_num / total_num, 2) * 100
 print(f"Acc: {acc}%")

if __name__ == "__main__":
 args = parse_args()
 data = read_data(args.data_path)
 calc_acc(data)

通过以下命令执行脚本:

# xxx为generated_predictions.jsonl文件路径
python calc_acc.py --data_path xxx

5.4.2 推理

修改LLaMa Factory路径下examples/inference/minicpm_2b_sft_bf16.yaml推理配置文件,配置文件内容改为:

model_name_or_path: saves/minicpm_2b_sft_bf16/full/sft/checkpoint-1689
template: cpm

通过下面的命令启动推理:

llamafactory-cli chat examples/inference/minicpm_2b_sft_bf16.yaml

训练前推理结果为:

  • 问题:当Paul看电影时,他在跑步机上跑步。他可以在12分钟内跑完一英里。他看两部电影,每部电影平均长度为1.5小时。他跑了多少英里?

训练后推理结果为:

  • 问题1:当Paul看电影时,他在跑步机上跑步。他可以在12分钟内跑完一英里。他看两部电影,每部电影平均长度为1.5小时。他跑了多少英里?

  • 问题2:一个平行四边形的面积是64平方厘米,底是16厘米,高=多少厘米.

 

6 结语

本次实践采用的是一款应用使能开发套件openMind,在调用数据集训练、推理、微调等方面挺方便顺畅的。朋友们可以试试,也欢迎分享你们的经验,一起交流。

openMind,一款应用使能开发套件,为各大模型社区提供支持,提供海量模型/数据托管能力、在线推理体验服务,同时具备模型训练、微调、评估、推理等全流程开发能力。开发者通过简单的API接口即可实现微调、推理等任务,极大缩短开发周期,助力AI技术的创新发展。目前,openMind已支持魔乐(modelers.cn)等AI生态社区,欢迎体验。

 

相关链接:

[1] openMind Library介绍:https://modelers.cn/docs/zh/openmind-library/overview.html

[2] openMind Hub Client介绍:https://modelers.cn/docs/zh/openmind-hub-client/overview.html

 

微软开源基于 Rust 的 OpenHCL 字节跳动商业化团队模型训练被“投毒”,内部人士称未影响豆包大模型 华为正式发布原生鸿蒙系统 OpenJDK 新提案:将 JDK 大小减少约 25% Node.js 23 正式发布,不再支持 32 位 Windows 系统 Linux 大规模移除疑似俄开发者,开源药丸? QUIC 在高速网络下不够快 RustDesk 远程桌面 Web 客户端 V2 预览 前端开发框架 Svelte 5 发布,历史上最重要的版本 开源日报 | 北大实习生攻击字节AI训练集群;Bitwarden进一步脱离开源;新一代MoE架构;给手机装Linux;英伟达真正的护城河是什么?
{{o.name}}
{{m.name}}

猜你喜欢

转载自my.oschina.net/u/8066678/blog/16274648