开源 AI 食谱文档
使用 Meta 合成数据工具包构建文档聊天机器人
并获得增强的文档体验
开始使用
使用 Meta 合成数据工具包构建文档聊天机器人
本笔记本演示了一种构建特定领域问答(Q&A)聊天机器人的实用方法。我们将专注于创建一个能够回答有关特定文档问题的聊天机器人——在本例中,是关于 LangChain 的聊天模型(Chat Models)文档。
目标: 微调一个小型高效的语言模型(LLM),使其能够理解并回答有关 LangChain 聊天模型文档的问题。
方法
- 数据获取: 从目标 LangChain 文档页面获取文本内容。
- 合成数据生成: 使用 Meta 的
synthetic-data-kit
从该文档自动生成问答对。 - 高效微调: 利用 Unsloth 和
Hugging Face 的 TRL SFTTrainer
,在生成的合成数据上高效微调 Llama-3.2-3B 模型。 - 评估: 用关于文档的特定问题测试微调后的模型。
这种方法使我们能够将 LLM 调整到特定领域,而无需大量手动整理的数据集。
使用的硬件
本笔记本在 Google Colab(免费版)上使用 NVIDIA T4 GPU 运行
1. 设置与安装
首先,我们需要安装必要的库。我们将使用 unsloth
来高效地处理和训练模型,使用 synthetic-data-kit
来生成我们的训练数据。
%%capture
# In Colab, we skip dependency installation to avoid conflicts with preinstalled packages.
# On local machines, we include dependencies for completeness.
import os
if "COLAB_" not in "".join(os.environ.keys()):
!pip install unsloth vllm==0.8.2
else:
!pip install --no-deps unsloth vllm==0.8.2
# Get https://github.com/meta-llama/synthetic-data-kit
!pip install synthetic-data-kit
%%capture
import os
if "COLAB_" in "".join(os.environ.keys()):
import sys, re, requests; modules = list(sys.modules.keys())
for x in modules: sys.modules.pop(x) if "PIL" in x or "google" in x else None
!pip install --no-deps bitsandbytes accelerate xformers==0.0.29.post3 peft "trl==0.15.2" triton cut_cross_entropy unsloth_zoo
!pip install sentencepiece protobuf datasets huggingface_hub[hf_xet] hf_transfer
# vLLM requirements - vLLM breaks Colab due to reinstalling numpy
f = requests.get("https://raw.githubusercontent.com/vllm-project/vllm/refs/heads/main/requirements/common.txt").content
with open("vllm_requirements.txt", "wb") as file:
file.write(re.sub(rb"(transformers|numpy|xformers|importlib_metadata)[^\n]{0,}\n", b"", f))
!pip install -r vllm_requirements.txt
2. 合成数据生成
我们将使用 Unsloth 的 SyntheticDataKit
(它封装了 Meta 的 synthetic-data-kit
)从我们选择的文档中创建问答对。
>>> from unsloth.dataprep import SyntheticDataKit
>>> generator = SyntheticDataKit.from_pretrained(
... model_name="unsloth/Llama-3.2-3B-Instruct",
... max_seq_length=2048,
... )
🦥 Unsloth: Will patch your computer to enable 2x faster free finetuning. 🦥 Unsloth Zoo will now patch everything to make training faster! INFO 05-05 15:14:48 [__init__.py:239] Automatically detected platform cuda.
generator.prepare_qa_generation(
output_folder="data", # Output location of synthetic data
temperature=0.7, # Higher temp makes more diverse data
top_p=0.95,
overlap=64, # Overlap portion during chunking
max_generation_tokens=512, # Can increase for longer QA pairs
)
>>> !synthetic-data-kit system-check
[?25l[32m VLLM server is running at [0m[4;94mhttps://:8000/v1[0m [32m⠋[0m[32m Checking VLLM server at https://:8000/v1...[0m [2KAvailable models: [1m{[0m[32m'object'[0m: [32m'list'[0m, [32m'data'[0m: [1m[[0m[1m{[0m[32m'id'[0m: [32m'unsloth/Llama-3.2-3B-Instruct'[0m, [32m'object'[0m: [32m'model'[0m, [32m'created'[0m: [1;36m1746459182[0m, [32m'owned_by'[0m: [32m'vllm'[0m, [32m'root'[0m: [32m'unsloth/Llama-3.2-3B-Instruct'[0m, [32m'parent'[0m: [3;35mNone[0m, [32m'max_model_len'[0m: [1;36m2048[0m, [32m'permission'[0m: [1m[[0m[1m{[0m[32m'id'[0m: [32m'modelperm-5296f16bbd3c425a82af4d2f84f0cbfe'[0m, [32m'object'[0m: [32m'model_permission'[0m, [32m'created'[0m: [1;36m1746459182[0m, [32m'allow_create_engine'[0m: [3;91mFalse[0m, [32m'allow_sampling'[0m: [3;92mTrue[0m, [32m'allow_logprobs'[0m: [3;92mTrue[0m, [32m'allow_search_indices'[0m: [3;91mFalse[0m, [32m'allow_view'[0m: [3;92mTrue[0m, [32m'allow_fine_tuning'[0m: [3;91mFalse[0m, [32m'organization'[0m: [32m'*'[0m, [32m'group'[0m: [3;35mNone[0m, [32m'is_blocking'[0m: [3;91mFalse[0m[1m}[0m[1m][0m[1m}[0m[1m][0m[1m}[0m [32m⠋[0m Checking VLLM server at https://:8000/v1... [2K[32m⠋[0m Checking VLLM server at https://:8000/v1... [?25h [1A[2K
2.1. 获取并导入文档
在本例中,我们将使用关于聊天模型的 LangChain 文档页面。
要获取文本
- 转到 MDX 文件的原始版本(例如,在 GitHub 上点击“Raw”)。
- 复制全部文本内容。
- 将其在本地保存为
.txt
文件。在本笔记本中,我们假设您已将其保存为/content/langchain-ai-langchain.txt
。您可以使用像gitingest
这样的工具或手动复制粘贴。
注意: 如果您在 Colab 中运行此笔记本,请确保将文本文件上传到您的 Colab 环境中的 /content/langchain-ai-langchain.txt
。
>>> # Make sure synthetic_data_kit_config.yaml points to the 'data_docs' folder
>>> !synthetic-data-kit -c synthetic_data_kit_config.yaml ingest /content/langchain-ai-langchain.txt
[?25l[32m⠋[0m Processing /content/langchain-ai-langchain.txt... [?25h [1A[2K[32m Text successfully extracted to [0m[1;32mdata/output/langchain-ai-langchain.txt[0m
2.2. 数据分块并生成问答对
导入的文档将被分割成更小的块,然后为每个块生成问答对。
>>> filenames = generator.chunk_data("data/output/langchain-ai-langchain.txt")
>>> print(f"Created {len(filenames)} chunks.")
Created 3 chunks.
import time
# Process 2 chunks for now -> can increase but slower!
for filename in filenames[:2]:
!synthetic-data-kit \
-c synthetic_data_kit_config.yaml \
create {filename} \
--num-pairs 25 \
--type "qa"
time.sleep(2) # Sleep some time to leave some room for processing
2.3. 格式化并保存问答对
生成的问答对随后被转换为适合微调的格式。
>>> qa_pairs_filenames = [
... f"data/generated/langchain-ai-langchain_{i}_qa_pairs.json"
... for i in range(len(filenames[:2]))
... ]
>>> for filename in qa_pairs_filenames:
... !synthetic-data-kit \
... -c synthetic_data_kit_config.yaml \
... save-as {filename} -f ft
[?25l[32m⠋[0m Converting data/generated/langchain-ai-langchain_0_qa_pairs.json to ft format with json storage... [?25h [1A[2K[1A[2K[32m Converted to ft format and saved to [0m [1;32mdata/final/langchain-ai-langchain_0_qa_pairs_ft.json[0m [?25l[32m⠋[0m Converting data/generated/langchain-ai-langchain_1_qa_pairs.json to ft format with json storage... [1A[2K[1A[2K[32m Converted to ft format and saved to [0m [1;32mdata/final/langchain-ai-langchain_1_qa_pairs_ft.json[0m
>>> generator.cleanup()
Attempting to terminate the VLLM server gracefully... Server did not terminate gracefully after 10 seconds. Forcing kill... Server killed forcefully.
2.4. 加载格式化后的数据集
现在,让我们加载生成并格式化后的数据。
from datasets import Dataset
import pandas as pd
final_filenames = [f"data/final/langchain-ai-langchain_{i}_qa_pairs_ft.json" for i in range(len(filenames[:2]))]
conversations = pd.concat([pd.read_json(name) for name in final_filenames]).reset_index(drop=True)
dataset = Dataset.from_pandas(conversations)
dataset[0]
dataset[-1]
内存管理说明(对资源受限环境至关重要)
如果在接下来的步骤中加载 Llama 模型进行微调时遇到 CUDA 内存不足(OOM)错误(即使在执行 `generator.cleanup()` 之后),这意味著 GPU 内存没有完全释放。这在像 Google Colab 免费版这样的环境中很常见。
解决方案策略
- 归档生成的数据: 在 `generator.cleanup()` 单元格之后,将整个 `/content/data` 文件夹压缩并下载到本地。
- 重启 Colab 运行时: 前往“运行时”->“重新启动运行时...”。这将完全清除 GPU 内存。
- 重新运行安装和导入: 再次执行初始的安装单元格和必要的导入单元格。
- 恢复数据: 上传压缩的数据文件夹并解压数据。
- 从恢复的文件中加载数据集: 使用脚本从解压后的 `/content/data/final/` 目录加载数据。
- 继续进行模型加载和微调。
下面的单元格包含压缩命令。如果您重新启动,您需要手动运行“可选:重启并重新加载数据”部分中的解压和数据加载代码。
# !zip -r data.zip /content/
# !unzip data.zip
# import os
# import pandas as pd
# from datasets import Dataset
# # Path to your folder containing JSON files
# folder_path = 'content/data/final/'
# # List all .json files in the folder
# final_filenames = [os.path.join(folder_path, f) for f in os.listdir(folder_path) if f.endswith('.json')]
# # Read and combine the JSON files
# conversations = pd.concat([
# pd.read_json(name) for name in final_filenames
# ]).reset_index(drop=True)
# # Convert to Hugging Face Dataset
# dataset = Dataset.from_pandas(conversations)
3. 使用 Unsloth 微调 LLM
现在,我们将使用 Unsloth 加载基础模型进行 4 位量化,然后在我们合成生成的数据集上对其进行微调。
3.1. 加载基础模型和分词器
我们将使用 4 位精度的 Llama-3.2-3B-Instruct
。Unsloth 使这一过程非常节省内存。
>>> from unsloth import FastLanguageModel
>>> import torch
>>> model, tokenizer = FastLanguageModel.from_pretrained(
... model_name="unsloth/Llama-3.2-3B-Instruct",
... max_seq_length=1024, # Choose any for long context!
... load_in_4bit=True, # 4 bit quantization to reduce memory
... load_in_8bit=False, # [NEW!] A bit more accurate, uses 2x memory
... full_finetuning=False, # [NEW!] We have full finetuning now!
... )
🦥 Unsloth: Will patch your computer to enable 2x faster free finetuning. 🦥 Unsloth Zoo will now patch everything to make training faster! INFO 05-05 15:54:31 [__init__.py:239] Automatically detected platform cuda. ==((====))== Unsloth 2025.4.7: Fast Llama patching. Transformers: 4.51.3. vLLM: 0.8.2. \\ /| Tesla T4. Num GPUs = 1. Max memory: 14.741 GB. Platform: Linux. O^O/ \_/ \ Torch: 2.6.0+cu124. CUDA: 7.5. CUDA Toolkit: 12.4. Triton: 3.2.0 \ / Bfloat16 = FALSE. FA [Xformers = 0.0.29.post3. FA2 = False] "-____-" Free license: http://github.com/unslothai/unsloth Unsloth: Fast downloading is enabled - ignore downloading bars which are red colored!
3.2. 添加 LoRA 适配器
我们使用 LoRA(低秩适配)进行参数高效微调。
model = FastLanguageModel.get_peft_model(
model,
r=16, # Choose any number > 0 ! Suggested 8, 16, 32, 64, 128
target_modules=[
"q_proj",
"k_proj",
"v_proj",
"o_proj",
"gate_proj",
"up_proj",
"down_proj",
],
lora_alpha=16,
lora_dropout=0, # Supports any, but = 0 is optimized
bias="none", # Supports any, but = "none" is optimized
# [NEW] "unsloth" uses 30% less VRAM, fits 2x larger batch sizes!
use_gradient_checkpointing="unsloth", # True or "unsloth" for very long context
random_state=3407,
use_rslora=False, # We support rank stabilized LoRA
loftq_config=None, # And LoftQ
)
3.3. 为聊天格式准备数据
我们需要将我们的数据集格式化为 Llama-3.2 模型期望的聊天模板。
<|begin_of_text|><|start_header_id|>system<|end_header_id|>
Cutting Knowledge Date: December 2023
Today Date: 01 May 2025
You are a helpful assistant.<|eot_id|><|start_header_id|>user<|end_header_id|>
What is 1+1?<|eot_id|><|start_header_id|>assistant<|end_header_id|>
2<|eot_id|>
def formatting_prompts_func(examples):
convos = examples["messages"]
texts = [tokenizer.apply_chat_template(convo, tokenize=False, add_generation_prompt=False) for convo in convos]
return {
"text": texts,
}
pass
# Get our previous dataset and format it:
dataset = dataset.map(
formatting_prompts_func,
batched=True,
)
dataset[0]
3.4. 训练模型
我们将使用 Hugging Face TRL 的 `SFTTrainer` 来微调模型,该类专门为监督式微调(SFT)设计。我们使用 SFTConfig 配置训练参数,指定数据集、模型、训练步骤和优化设置。这种设置使我们能够在有限的硬件环境下,利用梯度累积和像 adamw_8bit 这样的混合精度优化器来高效地微调模型。
from trl import SFTTrainer, SFTConfig
trainer = SFTTrainer(
model=model,
processing_class=tokenizer,
train_dataset=dataset,
eval_dataset=None, # Can set up evaluation!
args=SFTConfig(
dataset_text_field="text",
per_device_train_batch_size=2,
gradient_accumulation_steps=4, # Use GA to mimic batch size!
warmup_steps=5,
max_steps=60,
learning_rate=2e-4,
logging_steps=1,
optim="adamw_8bit",
weight_decay=0.01,
lr_scheduler_type="linear",
seed=3407,
report_to="none", # Use this for WandB etc
),
)
trainer_stats = trainer.train()
4. 推理与测试
让我们用一些与 LangChain 聊天模型文档相关的问题来测试我们微调后的模型。
>>> messages = [
... {"role": "user", "content": "What is the standard interface for binding tools to models?"},
... ]
>>> inputs = tokenizer.apply_chat_template(
... messages,
... tokenize=True,
... add_generation_prompt=True, # Must add for generation
... return_tensors="pt",
... ).to("cuda")
>>> from transformers import TextStreamer
>>> text_streamer = TextStreamer(tokenizer, skip_prompt=True)
>>> _ = model.generate(input_ids=inputs, streamer=text_streamer, max_new_tokens=256, temperature=0.1)
Standard [tool calling API](/docs/concepts/tool_calling): standard interface for binding tools to models.<|eot_id|>
5. 结论
我们已成功
- 获取了 LangChain 聊天模型 的文档文本。
- 使用
synthetic-data-kit
生成了合成的问答对。 - 使用 Unsloth 和 Hugging Face 的 TRL SFTTrainer 高效地微调了 Llama-3.2-3B 模型。
- 测试了模型回答特定于文档问题的能力。
本笔记本为创建针对各种文档或特定领域文本的专业聊天机器人提供了模板。合成数据生成和高效微调技术的使用使得这种方法即使在资源有限的情况下也易于实现。
进一步的改进可以包括
- 使用更大一部分的文档或多个相关页面。
- 更精细地筛选合成的问答对。
- 尝试不同的基础模型或进行超参数调优。
- 实施更强大的评估框架(例如,与一个保留的测试问题集进行比较,或使用 ROUGE、BLEU 等指标,如果适用,或使用 LLM-as-a-judge)。