悟了悟了数据制作教程
一、前言
高质量的数据集是训练和微调模型的关键,数据制作一般会借用一些大模型的API(如InterLM、DeepSeek、Qwen、OpenAI等),可以去对应官网查看或者用第三方的如硅基流动(注册会送一定量token,足够用来做数据)等,一般付费的效果更好。
本模型数据制作包含增量预训练数据和微调数据两部分,最终格式是适配了Xtuner训练格式的数据:
增量预训练数据是从网络上收集到的和《黑神话:悟空》、《西游记》、《悟空传》等相关的文本数据。
微调数据是用来给模型指令微调的问答对数据,制作难点在于知识的实时性和准确性,本模型借助茴香豆RAG功能,从收集的文本资料中提取准确的知识信息。
二、增量预训练数据
这一部分比较简单,不需要API,对收集到的原始文本数据切分后存为对应格式就行,可以参考 wulewule/data/generate_incremental_pretraining.py 这个脚本。
cd wulewule/data
python3 generate_incremental_pretraining.py --root-path ./ --save-path ./incremental_pretraining.jsonl
实现细节
文本切分
把原始数据切分成一个个小块给到模型训练(类似RAG中的chunking),使用了langchain中RecursiveCharacterTextSplitter方法对文本字符递归切分,额外增加了一些中文标点用来断句。
默认参数为:chunk-size(每个文本块的大小)为1024,chunk-overlap(相邻两个文本框重叠长度,防止丢失上下文信息)为50。
最终数据格式
xtuner 定义的增量预训练数据格式如下,"input"为空就行,文本块放在"output"里,想了解更多细节可以看xtuner官方增量预训练数据文档:
[
{
"conversation":[
{
"input": "", ## 空的,未使用
"output": "xxx" ## 切分后的文本
},
]
},
{
"conversation":[
{
"input": "",
"output": "xxx"
},
]
}
]
最终保存为jsonl格式,每行是一个字典,对应内容如下:
{"conversation": [{"input": "", "output": "《西游记白话文》\n\n\n 第1回 惊天地美猴王出世\n\n 这是一个神话故事,传说在很久很久以前,..."}]}
三、指令微调数据
普通大模型API只能基于训练时的知识库生成回答,很多知识都停留在过去,比如调用API问“黑神话悟空什么时候发布的”,大多数的都无法给出准确的答案(发布时间2024年08月20日),因为训练资料都是比这个日期之前的。
本模型先使用大模型API生成问题,然后借助茴香豆RAG功能,从收集的文本资料中提取对应问题准确的答案(如果本地资料未检索到,则联网检索答案)。
具体实现可以参考 wulewule/data/huixiangdou_rag_QA.py 这个脚本,使用前需要将data_utils.py中的"api_key"换成自己的,同时开启茴香豆server服务
实现细节
问题生成
借用大模型从【 "游戏概述", "章节与故事情节", "主要角色", "人物剧情梳理", "游戏世界观", "建筑与环境", "战斗系统", "游戏玩法", "艺术与音乐", "文化内涵", "市场影响", "彩蛋、网络梗" 】11个角度生成不同的问题:
aspects = [ "游戏概述", "章节与故事情节", "主要角色", "人物剧情梳理", "游戏世界观", "建筑与环境", "战斗系统", "游戏玩法", "艺术与音乐", "文化内涵", "市场影响", "彩蛋、网络梗" ]
question_number = 10
for ascpect in aspects:
messages=[
{"role":"system", "content": f"""你是一名提问助手,专注于围绕游戏《黑神话:悟空》的特定方面(例如主要角色、战斗机制或故事情节)提出问题,20个字以内。根据用户的指示,调整你的提问内容,引导玩家深入思考该方面的背景、动机及其在游戏中的作用和发展。不需要序号,最终返回为一个list,格式为['问题', '问题', ...]"""},
{"role": "user", "content": f"请你就《黑神话:悟空》的‘{ascpect}’方面提出{question_number}个不同的问题,帮助玩家了解游戏在该方面的信息。"},
]
text_res = response(messages, temperature=0.7)
messages中就是要给API的数据,基于上述prompt让它成为一个专门生成问题的助手,再从不同方面生成对应问题。
调用模型api
response函数就是调用API生成回答了,这边使用的是硅基流动平台(也可以用其他平台或者本地的模型),官网有很详细的文档,下面是一个使用requests的例子,需要将"" 换成你自己的:
import requests
url = "https://api.siliconflow.cn/v1/chat/completions"
payload = {
"model": "deepseek-ai/DeepSeek-V2.5",
"messages": [
{
"role": "user",
"content": "SiliconCloud推出分层速率方案与免费模型RPM提升10倍,对于整个大模型应用领域带来哪些改变?"
}
],
"stream": False,
"max_tokens": 512,
"stop": ["<string>"],
"temperature": 0.7,
"top_p": 0.7,
"top_k": 50,
"frequency_penalty": 0.5,
"n": 1,
"response_format": {"type": "json_object"}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.request("POST", url, json=payload, headers=headers)
print(response.text)
或者是用OpenAI的方式,api_key换成你自己的
from openai import OpenAI
client = OpenAI(
base_url='https://api.siliconflow.cn/v1',
api_key='your-api-key'
)
def response(messages, max_tokens=512, temperature=0.5):
res = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V2.5",
messages=messages,
max_tokens=max_tokens,
temperature=temperature,
).choices[0].message.content
return res
最终API返回的数据都是string类型,虽然prompt中说明了固定格式,但如果API模型效果没那么好,可能不会按这个格式输出,所以最好用正则处理一下结果,这里用的"deepseek-ai/DeepSeek-V2.5"好像没遇到这种情况。
开启茴香豆RAG服务
参考实战营-茴香豆本地标准版搭建的讲解,进行环境和模型配置即可,不再赘述。
用data目录下的文本数据创建自己的知识库后,开启服务端,监听 23333 端口,使用 chat_in_group
pipeline:
python3 -m huixiangdou.server --pipeline chat_in_group
# cURL 测试状态回调接口
curl -X POST http://127.0.0.1:23333/huixiangdou_stream -H "Content-Type: application/json" -d '{"text": "how to install mmpose","image": ""}'
# cURL 测试同步接口
curl -X POST http://127.0.0.1:23333/huixiangdou_inference -H "Content-Type: application/json" -d '{"text": "how to install mmpose","image": ""}'
如果需要开启网络检索,当模型本地检索不到结果时进行联网检索,需要注册Serper,参考开启网络搜索
调用RAG生成回答
将API得到的问题给到茴香豆RAG,然后得到最终答案即可,参考下方函数
def generate_answers(questions="黑神话悟空什么时候发布的", english=False):
"""
参考https://github.com/InternLM/HuixiangDou/blob/main/README_zh.md 先开启对应的服务
"""
# 定义请求的URL
url = 'http://127.0.0.1:23333/huixiangdou_inference'
# 定义你要发送的数据
data = {
"text": f"{questions}",
"image": ""
}
# 将数据转换成json格式
payload = json.dumps(data)
# 设置请求头
headers = {
'Content-Type': 'application/json'
}
# 发送POST请求
rag_response = requests.post(url, data=payload, headers=headers)
# 检查请求是否成功
if rag_response.status_code != 200:
logger.error(f"请求失败,状态码:{rag_response.status_code}")
res_txt = rag_response.text
res = json.loads(res_txt, strict=False)
answer = ""
for step in res["step"]:
if step["state"] == "success":
answer = step["response"]
logger.info(f"got response: {answer}")
if answer == "":
logger.error(f'no answer: {res_txt}')
return answer
最终数据格式
以下时xtuner使用的指令微调数据格式,如果是多轮对话,只需要再第一次对话中加入"system",最好这个system prompt和你最终使用时给模型的prompt保持一致
[{
"conversation":[
{
"system": "xxx",
"input": "xxx",
"output": "xxx"
}
]
},
{
"conversation":[
{
"system": "xxx",
"input": "xxx",
"output": "xxx"
},
{
"input": "xxx",
"output": "xxx"
}
]
}]
最终保存为jsonl格式,每行是一个字典,以下是一个例子
{"conversation": [{"system": "你是悟了悟了,...", "input": "游戏世界观的设定灵感来源是什么?", "output": "《黑神话:悟空》的游戏世界观深受《西游记》的影响,但在此基础上进行了大胆的创新和扩展。游戏构建了一个佛道衰败、妖魔横行的末法时代背景,其中许多角色和地点都源自《西游记》,如孙悟空、牛魔王、蜘蛛精等,这些角色在游戏世界中被赋予了新的故事和性格。同时,游戏还增加了新的神话元素和角色,如黄眉老祖、黑熊精等,这些新角色的加入丰富了游戏的世界观和剧情。因此,游戏世界观的设定灵感来源主要是《西游记》,但通过创新和扩展,构建了一个既熟悉又新奇的神话世界。"}, {"input": "在《黑神话:悟空》中,孙悟空的棍法技能在不同战斗场景中如何应用?请简要回答", "output": "在《黑神话:悟空》中,孙悟空的棍法技能在不同战斗场景中有着不同的应用方式,以下是简要回答:..."}]}