零基础定制大模型:从基础模型到个性化应用的完整指南
零基础定制大模型:从基础模型到个性化应用的完整指南
本文将带你从零开始,了解如何基于基础模型和自定义数据集来定制大模型。以《甄嬛传》为例,详细讲解大模型的基础知识、微调技术、数据准备、训练过程以及评估方法。
1.基础知识
什么是大模型?
大模型LLM(Large Language Model)是基于深度学习技术的大规模人工智能模型,拥有非常庞大的数据规模和训练数据量,可以在多种任务中表现出高度的通用性和卓越性能。例如,OpenAI的GPT-4模型拥有数千亿级别的参数。训练数据来源于互联网海量数据,涵盖多种语言、多种领域和不同的任务。
如何展现大模型通用性?
大模型并不是针对特定任务设计的,而是在训练后通过微调(Fine-tuning)或者提示学习(Prompt Learning)适应不同的任务,例如文本生成、翻译、代码编写、图像生成等。
如何定制大模型?用到的技术主要是什么?
定制大模型是指针对特定应用场景或需求,对已有的大规模预训练模型进行优化、微调或改造,以实现更高效、更贴合需求的性能。主要用到的技术包括:
- 微调 (Fine-Tuning)
- 知识蒸馏 (Knowledge Distillation)
- 多模态扩展
- 增强学习(Reinforcement Learning)
有什么参调方法呢?
- 全参数调整(Full Tuning)
- 参数高效微调Parameter Efficient Fine-tuning (PEFT)
- 适配器方法(Adapter Tuning)
什么是LoRA(Low-Rank Adaptation)?
LoRA是一种通过低秩矩阵更新来降低微调复杂度的技术。例如,使用Adam对GPT-3: 175B进行微调,与使用LoRA进行微调相比,可训练参数减少10,000倍,GPU内存需求减少3倍。
如何通过LoRA进行微调?
- 选择一个预训练的基础模型。
- 选择一个要进行微调的层,例如Transformer的attention层。
- 引入低秩矩阵对原来的权重矩阵W,进行低秩分解,让W’=AXB近似于W,X代表矩阵乘法。
- 冻结原始权重矩阵W,使用目标用例数据,仅更新低秩矩阵A和B。
- 评估调整后的模型性能,超参数调优,模型评估。
- 对模型进行部署和监控。
什么是量化低秩适应(QLoRA)(Quantized Low-Rank Adaptation)?
QLoRA将LoRA与量化相结合,优化内存使用和计算效率。添加少量可训练的参数,同时保持原始参数冻结。QLoRA通过将转换器模型量化到4位精度,并使用分页优化器处理内存峰值,来改进LoRA。
什么是前缀调优(Prefix Tuning)?
前缀调优是一种通过在模型输入前添加一系列可学习向量(前缀)来微调大型预训练模型的技术。修改输入表示,而不是模型的内部参数,使其成为一种轻量级和高效的微调技术。
什么是提示优化(Prompt Tuning)?
提示优化是一种使用提示来指导模型行为的技术。提示可以是静态的(固定文本),也可以是引入输入序列的动态的(可学习的嵌入)。修改模型的输入上下文以指导它的响应,而不更改模型的内部参数。前缀调优使用可学习的向量(不是实际的文本),而提示调优可以使用固定的文本提示或可学习的嵌入。
什么是过拟合?
过拟合是指模型在训练数据上表现很好,但在新数据上表现不佳的现象。通常是因为模型记忆了过多的不必要的细节(偶然的、不重要的噪声)。
JSON和JSONL介绍
JavaScript Object Notation 使用键值对来表示数据。通常包含一个对象(用{}表示),键一般是字符串(“age”),中间用冒号连接(:),值可以为数字(30)、字符串(“Zhang San”)、boolean值(false)、数组([“Chinese”,”Math”,”English”]),或者另一个对象。
JSON Lines可以处理大型数据集,一行一个json。
主流数据集格式
Alpaca
Alpaca数据集格式包括instruction、input、output、system、history组成,人类指令、人类输入、模型回答、提示词、历史消息记录表。
ShareGPT
ShareGPT数据集格式包括人类指令、模型回答、可选的系统提示词、工具列表。
模拟甄嬛对话的微调大模型程序
相关资料
- 项目背景参考:https://www.datawhale.cn/activity/110/21/78?rankingPage=1
- 详细步骤参考:https://www.datawhale.cn/activity/110/21/76?rankingPage=1
如何准备数据?
通过网络获得《甄嬛传》剧本数据,每一句的人物及对应的台词。使用正则表达式或者其他方法进行快速的提取,并抽取出关注的角色的对话。最后再将其整理成 json 格式的数据。
原始数据
第十一集
第203幕(续)
甄嬛:兔子急了也会咬人的。
眉庄:可是连苏培盛都棘手的事,陵容却能解决得这么干脆利落,实在是让我太意外了。
甄嬛:虽然意外,可陵容毕竟也是为了咱们。
眉庄:话是如此,可是我总还觉得这不像我日日面对的那个陵容。
(陵容在屏风外听得对话,默然离去。)
第204幕
(翊坤宫)
华妃:死绝了?
颂芝:死得透透的了,拉去乱葬岗的时候,有人亲眼瞧见。下手可真够狠的,脖子都勒断了半根,可吓人了。
华妃:那些没根儿的东西,做事倒是挺利落的。也难怪呀,她连皇上身边的人都敢得罪,活该她有今天。她没说漏了嘴吧?
颂芝:一句都没有。
华妃:算她识相。从前小看了那个病歪歪的甄嬛,以后走着瞧吧。(雷声)且闷了这么些天,也该下场大雨了。
提取数据
def extract_dialogues(file_path):
result = []
current_act_dialogues = []
with open(file_path, 'r', encoding='utf-8') as file:
lines = file.readlines()
for line in lines:
line = line.strip()
if line.startswith("("): # 过滤旁白行
continue
if line.startswith("第") and line.endswith("幕"): # 解析新的一幕
if current_act_dialogues:
result.append(current_act_dialogues)
current_act_dialogues = []
elif ":" in line: # 按:切分角色和对话内容
role, content = line.split(":", 1)
role = role.strip()
content = content.strip()
current_act_dialogues.append({"role": role, "content": content})
if current_act_dialogues: # 保存最后一幕
result.append(current_act_dialogues)
return result
file_path = '甄嬛传剧本11-20.txt'
dialogues = extract_dialogues(file_path)
print(dialogues)
处理结果
[
{"role": "甄嬛", "content": "兔子急了也会咬人的。"},
{"role": "眉庄", "content": "可是连苏培盛都棘手的事,陵容却能解决得这么干脆利落,实在是让我太意外了。"},
{"role": "甄嬛", "content": "虽然意外,可陵容毕竟也是为了咱们。"},
{"role": "眉庄", "content": "话是如此,可是我总还觉得这不像我日日面对的那个陵容。"}
],
[
{"role": "华妃", "content": "死绝了?"},
{"role": "颂芝", "content": "死得透透的了,拉去乱葬岗的时候,有人亲眼瞧见。下手可真够狠的,脖子都勒断了半根,可吓人了。"},
{"role": "华妃", "content": "那些没根儿的东西,做事倒是挺利落的。也难怪呀,她连皇上身边的人都敢得罪,活该她有今天。她没说漏了嘴吧?"},
{"role": "颂芝", "content": "一句都没有。"},
{"role": "华妃", "content": "算她识相。从前小看了那个病歪歪的甄嬛,以后走着瞧吧。(雷声)且闷了这么些天,也该下场大雨了。"}
]
正则提取
def extract_zhenhuan_dialogues(dialogues):
result = []
zhenhuan_dialogues = []
for act in dialogues:
for i, dialogue in enumerate(act):
if "甄嬛" in dialogue["role"] and i > 0:
zhenhuan_dialogues.append(act[i-1])
zhenhuan_dialogues.append(dialogue)
result.append(zhenhuan_dialogues)
zhenhuan_dialogues = []
return result
zhenhuan_dialogues = extract_zhenhuan_dialogues(dialogues)
print(zhenhuan_dialogues)
数据增强
利用两到三条数据作为 example 丢给LLM,让其生成风格类似的数据。也可以找一部分日常对话的数据集,使用 RAG 生成一些固定角色风格的对话数据。
如何微调?
定制一个自己的专属大模型最方便的步骤 ≈ 指定数据集 + 开源大模型 + 微调平台(如讯飞星辰Maas)
- 选择基础模型
- 选择微调方法
- 上传数据集
参数调整
- 深度学习
- 学习率:控制每次迭代更新模型参数的步幅。较小学习率更新慢但精确,较大学习率训练速度快但可能不稳定。
- 训练次数:建议根据数据规模设置合理的 epochs。
- 输入序列长度:模型输入序列的最大长度,短序列效率高,长序列适合上下文较多的任务。
- LORA(低秩自适应)
- LORA秩:决定模型更新参数的数量,影响计算量与性能表现。
- LORA随机丢弃:类似于正则化操作,避免过拟合,提升泛化能力。
- LORA作用模块:主要在预训练模型的自注意力机制和前馈网络中进行权重适配。
- LORA缩放因数:控制训练过程参数更新幅度,适配不同任务要求。
- 训练加速
- Galore特性:通过低秩特性减少参数存储和计算量,提高模型加速效果。
- BAdam特性:一种内存高效的优化算法,通过减少参数计算次数和内存占用,进一步提高训练效率。
如何评价生成结果?
- 人工评价:准确率、流畅度。
- 机器评价:BLEU分数、F1-score。
如何优化?
通过以下方式优化微调方式及性能
- 损失图:绘制训练损失和验证损失的曲线图。如果验证损失在训练损失持续下降时上升,可能表明模型过拟合。
- 平衡语料比例:根据原始语料和新添加语料的比例进行微调,防止模型过拟合。可以采用如80%原始语料和20%新语料的比例开始。
深入学习?
参考目录链接:https://www.datawhale.cn/activity/110/21/83?rankingPage=1
如何在自己的电脑进行训练?
参考链接:https://github.com/datawhalechina/self-llm/blob/master/examples/Chat-%E5%AC%9B%E5%AC%9B/readme.md
选择 LLaMA3_1-8B-Instruct 模型进行微调,首先还是要下载模型。其次,准备训练代码,在当前目录下放置train.py。微调教程链接:https://github.com/datawhalechina/self-llm/blob/master/models/LLaMA3/04-LLaMA3-8B-Instruct%20Lora%20%E5%BE%AE%E8%B0%83.md
- 指令集构建:微调数据集构建
- 数据格式化:需要将输入文本编码为 input_ids,将输出文本编码为 labels,编码之后的结果都是多维的向量。我们首先定义一个预处理函数,这个函数用于对每一个样本,编码其输入、输出文本并返回一个编码后的字典。
- 加载 tokenizer 和半精度模型
- 定义 LoraConfig
- 自定义 TrainingArguments 参数
- 使用 Trainer 训练
- 保存 lora 权重
- 加载 lora 权重推理
模型训练结果
训练结果体验截图显示,初始训练效果并不理想,但使用Spark Max进行十次迭代后,效果有了显著提升。
使用Spark Max模型进行训练后,效果如下: