趋近智
将原始、可能非结构化的数据转换为适用于遵循指令的大语言模型进行监督微调(SFT)的高质量数据集,需要基本的准备步骤。细致的数据准备是微调成功的一个重要因素。
我们的目标是将一个源数据集(通常以多种格式存在)转换为一种统一的结构,清晰地区分指令、可选的上下文或输入,以及期望的模型输出。我们将专注于创建模型在SFT期间将学习完成的文本提示。
指令微调数据集通常以JSON或CSV等结构化格式存在。一个常见模式(受Alpaca等数据集启发)是字典列表,其中每个字典代表一个遵循指令的示例。我们假设我们的原始数据在一个JSON Lines (.jsonl) 文件中,其中每行都是一个JSON对象,如下所示:
{"instruction": "将摄氏温度转换为华氏温度。", "input": "25", "output": "77"}
{"instruction": "用简单的语言解释光合作用的原理。", "input": "", "output": "光合作用是植物利用阳光、水和二氧化碳转化为食物(糖)和氧气的过程。"}
{"instruction": "写一首关于雨天的短诗。", "input": "", "output": "灰蒙蒙的天空洒下轻柔的泪水,\n窗玻璃映照出阴沉,\n水坑倒影着云朵,\n自然叹息,湿润的芬芳。"}
{"instruction": "", "input": "将'hello'翻译成法语", "output": "Bonjour"}
{"instruction": "总结主要观点。", "input": "人工智能(AI)是机器表现出的智能,与人类和动物表现出的自然智能相对。主流的人工智能教材将该领域定义为对“智能体”的研究:任何感知其环境并采取行动以最大限度地提高其成功实现目标机会的设备。", "output": ""}
这些原始数据可能包含不一致之处:例如缺少指令、缺少输出,或者在隐式需要输入时输入为空。
对于SFT,我们通常将指令、输入(如果存在)和输出连接成一个单一的文本序列,通常使用特定的分隔符或模板。这种组合文本作为训练示例。模型被训练来在给定instruction和input作为上下文的情况下,预测output部分。
一种广泛使用的模板结构如下所示:
下面是一个描述任务的指令,并附有提供进一步上下文的输入。请编写一个适当完成请求的响应。
### 指令:
{instruction}
### 输入:
{input}
### 响应:
{output}
如果input字段为空,模板可能会被简化为:
下面是一个描述任务的指令。请编写一个适当完成请求的响应。
### 指令:
{instruction}
### 响应:
{output}
选择一致的格式对于模型学习遵循指令的模式很重要。###标记有助于清晰地划分提示的不同部分。介绍性句子为模型设置了上下文。
让我们使用Hugging Face的datasets库,这是NLP生态系统中处理数据集的标准工具。
加载数据:假设我们的原始数据位于raw_data.jsonl中:
from datasets import load_dataset
# 加载原始数据集
raw_dataset = load_dataset('json', data_files='raw_data.jsonl', split='train')
print(f"初始示例数量: {len(raw_dataset)}")
print("示例条目:")
print(raw_dataset[0])
清洗和过滤:我们需要删除不适合训练的示例。常见问题包括缺少指令或输出。
# 过滤掉指令或输出为空的示例
def filter_invalid_examples(example):
return example['instruction'] is not None and \
example['instruction'].strip() != "" and \
example['output'] is not None and \
example['output'].strip() != ""
cleaned_dataset = raw_dataset.filter(filter_invalid_examples)
print(f"清洗后的示例数量: {len(cleaned_dataset)}")
# 可选:根据长度进一步过滤(例如,删除过短/过长的示例)
# min_length = 10
# max_length = 1024 # 示例token长度限制
# cleaned_dataset = cleaned_dataset.filter(lambda x: min_length < len(x['instruction']) + len(x['output']) < max_length)
# print(f"长度过滤后的示例数量: {len(cleaned_dataset)}")
这一步非常重要。使用格式不佳的示例进行训练可能会显著降低模型性能或导致其学习到不良行为。
格式化数据:将选定的模板应用于每个示例。
def format_instruction(example):
if example.get('input') and example['input'].strip():
# 包含输入进行格式化
prompt = f"""下面是一个描述任务的指令,并附有提供进一步上下文的输入。请编写一个适当完成请求的响应。
### 指令:
{example['instruction']}
### 输入:
{example['input']}
### 响应:
{example['output']}"""
else:
# 不包含输入进行格式化
prompt = f"""下面是一个描述任务的指令。请编写一个适当完成请求的响应。
### 指令:
{example['instruction']}
### 响应:
{example['output']}"""
# 如果模型/训练框架要求,添加一个序列结束符
# prompt += "</s>" # 要求EOS token的模型的示例
return {"text": prompt}
formatted_dataset = cleaned_dataset.map(format_instruction, remove_columns=raw_dataset.column_names)
print("格式化后的示例条目:")
print(formatted_dataset[0]['text'])
生成的formatted_dataset包含一个名为text的单列,其中每个条目都是一个完整的提示,已准备好用于SFT。
可视化数据特征(可选):了解格式化后提示长度的分布,对于设置最大序列长度等训练参数很有用。
import pandas as pd
# 计算长度
lengths = [len(x['text']) for x in formatted_dataset]
df = pd.DataFrame({'length': lengths})
# 基本统计
print(df['length'].describe())
# 使用Plotly格式创建直方图
# (注意:对于大型数据集,请采样或使用适当的分箱)
# 此示例使用固定数据进行演示
chart_data = {"layout":{"title":{"text":"格式化提示长度分布"},"xaxis":{"title":{"text":"提示长度(字符)"}},"yaxis":{"title":{"text":"数量"}},"bargap":0.1},"data":[{"type":"histogram","x":[250, 300, 150, 450, 500, 600, 350, 280, 420, 550, 180, 220, 380, 480, 580, 320],"marker":{"color":"#228be6"}}]}
# 显示图表数据(在实际环境中,你会渲染此JSON)
print("图表JSON:")
准备好的数据集中格式化提示的字符长度分布。理解这一点有助于在训练期间设置合适的序列长度限制。
保存处理后的数据集:将最终数据集保存为适合你的训练框架的格式。Parquet通常是提高效率的好选择。
# 保存到磁盘
formatted_dataset.to_json("processed_instruction_dataset.jsonl", orient="records", lines=True)
# 或者以Arrow/Parquet格式保存以提高效率
# formatted_dataset.save_to_disk("processed_instruction_dataset_arrow")
print("已保存处理后的数据集。")
本实践练习展示了准备指令微调数据的主要工作流程。尽管特定的数据集和要求可能需要更复杂的清洗或格式化逻辑,但加载、清洗、转换和保存的基本步骤保持不变。一个准备充分的数据集是有效指令微调的支撑。
简洁的语法。内置调试功能。从第一天起就可投入生产。
为 ApX 背后的 AI 系统而构建
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造