趋近智
理论提供依据,而实际应用则能巩固理解。在本节中,我们将运用之前讨论的原则,将原始数据集转换为适合指令微调的结构化格式。我们将完整地逐步展示一个可重复的工作流程:加载原始数据、清洗数据、将其整理成一致的提示格式,并最终为模型进行分词。
使用 databricks-dolly-15k 数据集的一个修改过的子集,该子集包含由人类生成的指令-响应对。
我们的第一步是加载数据并对其内容有一个初步了解。我们将使用 Hugging Face 的 datasets 库,它是LLM生态系统中简化数据加载和处理的有力工具。假设我们的数据存储在 JSON Lines (.jsonl) 文件中,其中每行是一个 JSON 对象。
from datasets import load_dataset
# 从本地文件或 Hugging Face Hub 加载数据集
# 在本例中,我们假设它在 Hub 上可用。
raw_dataset = load_dataset("databricks/databricks-dolly-15k", split="train")
# 让我们查看其结构和一些示例
print(raw_dataset)
print(raw_dataset[0])
输出将显示数据集的特征(列)和第一个示例的内容。通常,您会看到 instruction、context 和 response 等字段。
Dataset({
features: ['instruction', 'context', 'response', 'category'],
num_rows: 15011
})
{
'instruction': 'When did Virgin Australia start operating?',
'context': 'Virgin Australia, the trading name of Virgin Australia Airlines Pty Ltd, is an Australian-based airline. It is the largest airline by fleet size to use the Virgin brand. It commenced services on 31 August 2000 as Virgin Blue, with two aircraft on a single route.',
'response': 'Virgin Australia commenced services on 31 August 2000 as Virgin Blue, with two aircraft on a single route.',
'category': 'closed_qa'
}
我们的目标是将这些独立的字段转换为单一的、结构化的文本序列,供模型学习使用。
实际数据很少是完美的。它通常包含空字段、重复项或不相关的信息。少量高质量数据远比大量嘈杂数据有价值。
让我们进行一些基础清洗。一个常见问题是指令或响应缺失或过短而无用的条目。我们可以将其筛选掉。
# 筛选掉指令过短的示例
initial_size = len(raw_dataset)
filtered_dataset = raw_dataset.filter(lambda example: len(example['instruction']) > 3)
# 筛选掉响应过短的示例
filtered_dataset = filtered_dataset.filter(lambda example: len(example['response']) > 3)
print(f"Original size: {initial_size}")
print(f"Size after filtering: {len(filtered_dataset)}")
这是一个简单的筛选步骤。在生产环境中,您可能会添加更复杂的规则,例如删除包含特定关键词的示例,如果目标是英文文本则过滤掉非英文文本,或对相似指令进行去重。
模型通过识别模式来学习遵循指令。因此,一致的提示结构对于有效的微调非常必要。模型需要清晰地分辨指令、任何提供的输入以及预期的响应。
我们将采用一个简单而有效的模板来分离这些组成部分。
### Instruction:
{instruction}
### Input:
{context}
### Response:
{response}
如果示例没有 context,我们将完全省略 ### Input: 部分,以避免向模型输入空字段。让我们创建一个Python函数来格式化每个示例。
def format_prompt(example):
"""将单个示例格式化为标准化的提示字符串。"""
instruction = f"### Instruction:\n{example['instruction']}"
# 如果存在且非空,则使用 context
if example.get('context') and example['context'].strip():
context = f"### Input:\n{example['context']}"
else:
context = ""
response = f"### Response:\n{example['response']}"
# 连接各部分,过滤掉空字符串
full_prompt = "\n\n".join(filter(None, [instruction, context, response]))
return {"text": full_prompt}
此函数会创建一个名为 text 的新列,其中包含完整格式化的提示。我们可以使用高效的 map 方法将此转换应用于整个数据集。
# 将格式化函数应用于每个示例
structured_dataset = filtered_dataset.map(format_prompt)
# 让我们查看一个带上下文的示例和一个不带上下文的示例
print("--- Example with context ---")
print(structured_dataset[0]['text'])
print("\n--- Example without context ---")
# 找到一个不带上下文的示例并打印
for ex in structured_dataset:
if "### Input:" not in ex['text']:
print(ex['text'])
break
这个统一的文本字段正是模型在训练过程中将看到的内容。
数据准备的最后一步是分词:将格式化后的文本字符串转换为模型可以处理的整数ID序列。这一步还需要仔细处理特殊标记。
我们需要选择与我们打算微调的基础模型匹配的分词器。在本例中,我们使用 meta-llama/Llama-2-7b-hf 的分词器。此外,在每个提示的末尾添加一个序列结束 (eos) 标记也很有必要。这个标记向模型发出信号,表明响应已完成,从而教会它在推理时何时停止生成文本。
from transformers import AutoTokenizer
# 为特定模型加载分词器
model_id = "meta-llama/Llama-2-7b-hf"
# 注意:您可能需要通过 Hugging Face 认证才能访问此模型
# from huggingface_hub import login; login()
tokenizer = AutoTokenizer.from_pretrained(model_id)
# 如果尚未定义,则设置填充标记
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
def tokenize_function(examples):
# 将 EOS 标记附加到每个文本的末尾
text_with_eos = [s + tokenizer.eos_token for s in examples["text"]]
# 对文本进行分词
return tokenizer(
text_with_eos,
truncation=True, # 截断超出最大长度的序列
max_length=512, # 一个常用最大长度
padding=False, # 我们稍后将使用数据收集器处理填充
)
# 应用分词
tokenized_dataset = structured_dataset.map(
tokenize_function,
batched=True, # 批量处理示例以提高效率
remove_columns=structured_dataset.column_names # 移除旧的文本列
)
# 检查分词的输出
print(tokenized_dataset[0].keys())
print(tokenized_dataset[0]['input_ids'][:20]) # 打印前20个标记ID
输出现在包含 input_ids 和 attention_mask,这些是 Transformer 模型的标准输入。我们已成功将原始数据转换为可供模型使用的格式。
以下图表概述了我们的数据准备流程。
数据准备工作流程,从原始源文件到可用于训练的分词数据集。
完成所有这些工作后,将最终数据集保存到磁盘是明智之举。这样我们就可以在后续训练时快速加载它,无需重复这些预处理步骤。
# 将数据集保存到本地目录
tokenized_dataset.save_to_disk("./fine-tuning-dataset")
# 您可以随时使用以下方式重新加载:
# from datasets import load_from_disk
# reloaded_dataset = load_from_disk("./fine-tuning-dataset")
我们的数据集现已构建、清洗、整理、分词并保存完毕,我们已为下一阶段完全准备就绪:使用它来微调大型语言模型。
简洁的语法。内置调试功能。从第一天起就可投入生产。
为 ApX 背后的 AI 系统而构建
这部分内容有帮助吗?
databricks-dolly-15k 数据集、其创建方法及其在开源指令遵循大型语言模型中的作用。datasets 库的官方文档,涵盖数据加载、处理和管理,这对构建微调数据集至关重要。transformers 库中令牌化的指南,解释了其在为大型语言模型准备文本数据方面的实际用法。© 2026 ApX Machine Learning用心打造