大语言模型实战

上一节对Baichuan2-7B模型进行裸测时,虽然模型可以通过系统提示词理解模仿哪一种角色,但是输出结果依然比较生硬。对于“孙悟空”的角色,在进行对话阶段,口吻与人物本身差距较大。那么如何对本地模型进行定制优化,将角色语言特征、相关知识注入模型中来提高模型角色扮演的效果呢?本节通过构建一个基于Baichuan2-7B的角色扮演模型,让读者更加深入地了解任务原理、流程及如何在利用角色扮演的对话数据来进行大型语言模型的调优。

 

1. 项目介绍


本项目是基于Baichuan2-7B模型的角色扮演实战。利用Baichuan2-7B模型从角色扮演对话数据集中学习角色的身份、观点、经历、成就、社交关系、语言特征等知识内容,从而提高Baichuan2-7B模型在角色扮演场景的效果,同时让读者更加深度地了解大型语言模型在角色扮演场景中如何进行微调。代码见GitHub中RolePlayProj 项目,项目主要结构如下。

·data:存放数据的文件夹。

■rolebench-zh_role_specific_train.jsonl:角色扮演角色背景知识训练集。

■rolebench-zh_general_train.jsonl:角色扮演角色通用知识训练集。

■rolebench-zh_role_specific_test.jsonl:角色扮演角色背景知识测试集。

■rolebench-zh_general_test.jsonl:角色扮演角色通用知识测试集。

·baichuan:Baichuan2-7B模型相关文件夹。

■modeling_baichuan.py:Baichuan2-7B模型文件。

■tokenization_baichuan.py:Baichuan2-7B模型分词器文件。

■configuration_baichuan.py:Baichuan2-7B模型配置文件。

■generation_utils.py:Baichuan2-7B模型工具文件。

·data_helper.py:数据处理文件。

·train.py:模型训练文件。

·web_demo.py:模型推理Web页面文件。

·merge_params.py:模型参数合并文件。

·utils.py:模型工具文件,包括模型数据类、参数打印函数、模型保存函数、模型验证函数、线性层查找函数等。

本项目从数据预处理、模型微调、模型预测几个部分入手,手把手地带领大家一起完成一个大型语言模型角色扮演微调任务。

 

 

2. 数据预处理


RoleLLM论文:https://arxiv.org/abs/2310.00746。

本项目中的角色扮演数据来自RoleLLM论文 ,一种用于评估、引导和提升大型语言模型中角色扮演能力的数据集。该数据集共包含100个角色的角色对话数据,其中英文角色95个,中文角色5个。本项目从中文5个角色中提取3个角色(包括张飞、李白、孙悟空)内容进行大型语言模型的微调,并将数据集转换为标准大型语言模型微调格式,包含提示内容、输入内容和输出内容。

为了让大型语言模型更好地学习到角色相关的信息,该数据中不仅包含角色的背景知识对话数据,还包含通用知识的对话数据,不仅可以学习到角色的背景知识内容,也可以根据角色的特点回答通用领域的知识,使大型语言模型扮演的角色更加生动形象。原始数据如下:

 

数据处理代码见data_helper.py文件,具体流程如下。

步骤1:将角色背景知识对话数据和通用知识对话数据进行合并,仅保留“孙悟空”“张飞”“李白”三种角色的数据。

步骤2:加载角色描述数据,针对不同角色数据构建不同的提示词。

步骤3:遍历数据,当一条数据中存在多个回复内容时,最多取两个回复内容。

步骤4:将数据按照instruction、input和output形式进行保存,写入文件中。

 

 

 

设置原始数据路径和训练集、测试集保存路径,运行得到最终数据结果,具体如下。

 

单个样本示例如下。

 

对于模型微调,需要构建模型所需要的数据类,加载训练数据和测试数据,将文本数据转化成模型训练可用的索引ID数据,详细代码在utils.py文件中,数据构造过程具体如下。

步骤1:遍历文件中的每一个样本,利用json.loads进行数据加载。

步骤2:构造角色扮演模型所需的系统指令内容。

步骤3:构建用户输入内容,当用户输入内容长度超过最大长度时,进行向前截断,并生成对应的label。

步骤4:构建模型输出内容,当模型输出内容长度超过最大长度时,进行向前截断。

步骤5:将系统指令、用户输入、模型输出进行拼接,构建完整的模型训练所需数据。

步骤6:将每个样本进行保存,用于后续训练使用。

 

 

 

3. 模型微调


该项目的大型语言模型微调主要采用QLoRA方法,其详细原理参见2.3.7节。模型微调文件为train.py,主要包括模型训练参数设置函数和模型训练函数,主要涉及以下步骤。

步骤1:设置模型训练参数。如果没有输入参数,则使用默认参数。

步骤2:通过判断单卡训练还是多卡训练,设置并获取显卡信息,用于模型训练。并设置随机种子,方便模型复现。

步骤3:实例化BaichuanTokenizer分词器和BaichuanForCausalLM模型,并在模型初始化过程中采用INT4初始化。

步骤4:找到模型中所有的全连接层,并初始化LoRA模型。

步骤5:加载模型训练所需要的训练数据和测试数据,如果是多卡训练需要分布式加载数据。

步骤6:加载DeepSpeed配置文件,并通过训练配置参数修改optimizer、scheduler等配置。

步骤7:利用DeepSpeed对原始模型进行初始化。

步骤8:遍历训练数据集,进行模型训练。

步骤9:获取每个训练批次的损失值,并进行梯度回传,模型调优。

步骤10:当训练步数整除累积步数时,记录训练损失值;当步数达到模型保存步数时,用测试数据对模型进行验证,并计算困惑度指标及模型保存。

 

 

 

 

 

在模型在训练时,可以在文件中修改相关配置信息,也可以通过命令行运行train.py文件指定相关配置信息。其中,一般进行设置的配置信息如表6-3所示。

 

表6-3 模型训练配置信息

 

模型单卡训练命令如下。

 

在模型训练过程中,通过CUDA_VISIBLE_DEVICES参数控制具体哪块或哪几块显卡进行训练,如果不加该参数,表示使用运行机器上所有卡进行训练。模型训练过程所需的显存大小为13GB,训练参数占总参数的比例为0.8314%。运行状态如图6-7所示。

 

 

模型训练完成后,可以使用tensorboard查看训练损失下降情况,如图6-8所示。

 

 

4. 模型预测


为了保证模型在保存时,所存储变量尽可能小,以节约模型存储时间,在6.3.2节中模型存储时仅保存了训练的参数,即外挂的LoRA参数。因此在模型预测前,需要进行参数融合,即将外挂参数合并到原来的模型参数中,形成一个新的模型。这样在模型进行预测的过程中,不会增加额外的预测时间。参数融合代码见merge_params.py文件,具体步骤如下。

步骤1:设置模型融合参数。

步骤2:加载Baichuan2-7B原始模型参数。

步骤3:加载LoRA方法训练的增量参数。

步骤4:调用merge_and_unload函数,将外挂参数合并到原始参数中。

步骤5:将融合后的模型参数进行保存。

 

在进行模型测试时,可以参考6.2节中的推理代码,仅需将模型路径修改为上述合并LoRA参数后的路径即可。当然针对不同的角色,需要修改对应的系统提示内容,在此不进行过多介绍。

本小节利用Gradio工具搭建一个角色扮演对话应用,详细步骤如下。

步骤1:初始化配置信息,加载Baichuan2的模型和Tokenizer。

步骤2:加载角色描述信息。

步骤3:创建自定义的交互式Web应用和演示。

步骤4:创建一个chatbot机器人组件,并定义用户输入框、提交按钮、清空按钮、单选角色框、模型输出最大长度设置框以及模型解码Top-p值设置框。

步骤5:设置当单击“提交”按钮后,调用单条预测函数,并将输入框清空。

步骤6:设置当单击“清空”按钮后,重新初始化chatbot机器人组件。

步骤7:设置IP以及对应端口号,运行demo。

 

 

 

模型Web应用执行命令如下,运行后如图6-9所示。

 

 

通过Web端进行角色扮演推理测试,针对“孙悟空”角色进行对话,测试样例详细如图6-10所示。可以发现,经过微调后的Baichuan2-7B模型的语言风格与角色更加匹配,扮演效果更加优秀。

 

但由于训练数据的限制,上述训练的模型仅适用于单轮对话,并且对话的生成内容比较简短。如果需要支持多轮对话,只需构造对应角色的多轮对话数据即可,本章的整体微调、预测流程依然适配。