大模型微调的数据集构建
现在我们要做的就是把这样大段的文本形式的数据,转换成alpaca的形式。在以往我们只能通过人工的方式完成,而现在可以借助大模型的能力,大致思路就是让大模型根据文本,总结出对话、问答内容,这点可以通过Prompt工程实现。我们在进行大模型的微调时,往往想使用自己的内部数据,用于微调的数据通常是成千上万的问答对,如果手动搜集,需要花费大量的时间。现在就使用大模型来构造自己的数据集,并使用我们构造的数据
·
我们在进行大模型的微调时,往往想使用自己的内部数据,用于微调的数据通常是成千上万的问答对,如果手动搜集,需要花费大量的时间。现在就使用大模型来构造自己的数据集。
1.构造数据集的起因
数据集通常采用问答对形式,比如alpaca数据集的形式如下:
{
"instruction": "保持健康的三个提示。",
"input": "",
"output": "以下是保持健康的三个提示:\n\n1. 保持身体活动。每天做适当的身体运动,如散步、跑步或游泳,能促进心血管健康,增强肌肉力量,并有助于减少体重。\n\n2. 均衡饮食。每天食用新鲜的蔬菜、水果、全谷物和脂肪含量低的蛋白质食物,避免高糖、高脂肪和加工食品,以保持健康的饮食习惯。\n\n3. 睡眠充足。睡眠对人体健康至关重要,成年人每天应保证 7-8 小时的睡眠。良好的睡眠有助于减轻压力,促进身体恢复,并提高注意力和记忆力。"
}
但是实际上我们拿到的数据通常是一大段文本的形式,比如:
小时候,那时我还只有6岁,看到一本描写原始森林壮观景象的书,名叫真实的故事。书里有一幅很精彩的插画,画的是一条大蟒蛇正在吞食一只动物,下面就是那幅插画的复制品。
这本书上说:“大蟒蛇把它们的猎物不加咀嚼地整个吞下去,之后,就再也不动了,然后通过长达六个月的睡眠来消化掉这些食物。”
...
现在我们要做的就是把这样大段的文本形式的数据,转换成alpaca的形式。在以往我们只能通过人工的方式完成,而现在可以借助大模型的能力,大致思路就是让大模型根据文本,总结出对话、问答内容,这点可以通过Prompt工程实现。
2.Prompt设计
设计Prompt中,我们需要强调根据上下文内容,让大模型提取对话、问答等内容,比如:
from langchain.prompts import PromptTemplate
# 2. 创建提示模板:定义如何向模型提出请求以生成问答对
prompt_template = """请仔细阅读以下文本内容,并执行以下任务:
1. 生成一个简明的问题
2. 给出对应的准确答案
3. 保持问答对独立完整
文本内容:{chunk}
请严格使用以下格式输出:
问题:[生成的问题]
答案:[对应的答案]
"""
prompt = PromptTemplate.from_template(prompt_template) # 创建提示模板对象
3.llm调用
llm的调用根据自己的实际情况,我这里因为是需要离线部署,所以使用的本地部署的Qwen模型。结合langchain的ollama模块,调用Qwen模型。
from langchain_ollama.chat_models import ChatOllama
llm = ChatOllama(model='Qwen2.5:0.5b',
base_url=r'http://176.88.90.1:11434') # 使用特定的语言模型,
4.构建chain
构建的chain分为两个部分,包含prompt和llm
chain = prompt | llm
5.根据大模型输出的问答对,构架所需要的格式,给出完整代码。
from langchain.text_splitter import CharacterTextSplitter
from langchain.prompts import PromptTemplate
# 假设这是正确的导入路径,请根据实际情况调整
from langchain_ollama.chat_models import ChatOllama
def generate_qa_pairs(text, llm , max_pairs=30):
"""
从给定的文本中生成问题-答案对。
:param text: 要分析的文本内容
:param llm: 需要使用的大模型,我是使用的ollama调用的本地的通义千问。
:param max_pairs: 最大生成的问题-答案对数量
:return: 包含问题-答案对的列表,每个元素是一个字典
"""
# 1. 文本分割:将长文本分割成多个小块以便更好地处理
text_splitter = CharacterTextSplitter(
chunk_size=1000, # 每个小块的最大字符数
chunk_overlap=200 # 相邻小块之间的重叠字符数
)
chunks = text_splitter.split_text(text) # 分割文本为多个小块
# 2. 创建提示模板:定义如何向模型提出请求以生成问答对
prompt_template = """请仔细阅读以下文本内容,并执行以下任务:
1. 生成一个简明的问题
2. 给出对应的准确答案
3. 保持问答对独立完整
文本内容:{chunk}
请严格使用以下格式输出:
问题:[生成的问题]
答案:[对应的答案]
"""
prompt = PromptTemplate.from_template(prompt_template) # 创建提示模板对象
# 3. 处理每个文本块:遍历所有文本块并生成问答对
qa_pairs = [] # 存储最终的问答对
seen = set() # 用于去重,确保问题唯一性
for chunk in chunks:
# 构建链式调用:提示模板 -> 模型
chain = prompt | llm
result = chain.invoke(chunk) # 对每个文本块调用链式操作获取结果
# 4. 解析结果:从模型返回的结果中提取问答对
pairs = parse_response(result.content)
# 5. 去重和限制数量:去除重复的问题,并限制总问答对数量不超过max_pairs
for q, a in pairs:
if q not in seen and len(qa_pairs) < max_pairs:
seen.add(q)
qa_pairs.append({"question": q, "answer": a})
if len(qa_pairs) >= max_pairs:
break
return qa_pairs[:max_pairs] # 返回最终的问答对列表
def parse_response(text):
"""
解析模型返回的文本内容,提取出问题-答案对。
:param text: 模型返回的原始文本内容
:return: 包含问题-答案对的列表
"""
pairs = [] # 存储解析后的问答对
current_q = None # 当前正在处理的问题
# 遍历每一行文本
for line in text.split('\n'):
line = line.strip() # 去除两端空白字符
if line.startswith('问题:'): # 如果是问题行
current_q = line[4:].strip() # 提取问题内容
elif line.startswith('答案:') and current_q: # 如果是答案行且有当前问题
pairs.append((current_q, line[4:].strip())) # 添加问答对到列表
current_q = None # 清空当前问题
return pairs # 返回解析后的问答对列表
# 使用示例
text = '''很多成功的敏捷实践者初识这条原则就遇到了很多困难。欣然面对变化说起来简单,但是
在项目热火朝天地进行的时候,如果团队要处理需要大量工作的变化,开发人员可能会有
情绪,特别是在老板不顾及工作量,要求不改变截止时间的情况下。这个坎可能很难过,
特别是在团队因为项目延期而遭到抱怨的情况下。但是跨过了这个坎,收获也很大,因为
欣然面对需求变化是敏捷工具箱中最有力的工具之一。
为什么项目中的变化会激起众怒?理解这一点就是理解本条原则的关键。想一下,做项
目的时候发现正在开发的东西需求改变了,你会有怎样的感受?在知道需求变更之前,
你以为项目进展得很好。你可能已经做了很多决策:如何规划产品结构,要开发什么产
品,向客户承诺交付什么。结果现在项目外的某个人突然告诉你这个计划中有些错误,
是你出错了。
“你出错了”,接受这样的指责是很难的,如果这样说的人还享受着你的服务,那就更是如
此了。大部分软件工程师都是在技术自豪感的驱动下工作的:我们交付的产品我们能负
责,而且能满足用户的需求。而项目中发生的变化则对这种自豪感产生了威胁,因为变化
是在质疑你采用的方法,质疑你的设想'''
# 3. 初始化模型:创建语言模型实例
llm = ChatOllama(model='Qwen2.5:0.5b') # 使用特定的语言模型
result = generate_qa_pairs(text,llm=llm) # 调用函数生成问答对
# 输出结果
print(result)
更多推荐
所有评论(0)