隐私保护指令对齐:基于 DP-SGD 与 DP 直方图重采样的 LLaMA 对齐流水线(dp_instructions 实战指南)
2026/9/20 17:40:41 网站建设 项目流程

隐私保护指令对齐:基于 DP-SGD 与 DP 直方图重采样的 LLaMA 对齐流水线(dp_instructions 实战指南)

【免费下载链接】google-researchGoogle Research项目地址: https://gitcode.com/gh_mirrors/go/google-research

导读

本文围绕仓库中 dp_instructions 目录,系统讲解如何复现论文《Privacy-preserving Instructions for Aligning Large Language Models》(arXiv:2402.13659) 的完整实验流水线:从 LMSYS-Chat-1M 数据预处理、LLaMA 7B/13B 的差分隐私(DP)微调与采样、基于 DP 直方图的合成指令重采样,到调用 OpenAI GPT-3.5-Turbo 标注并完成监督微调(SFT),最后给出基于 Huggingface TRL 的 RLHF 扩展方向。读完本文,你将掌握每一阶段的核心原理、可复现的命令行参数含义以及底层源码实现(如逐样本梯度裁剪、噪声乘子搜索、UnitedSignal 式重采样),可直接按图索骥跑通整条对齐流水线。

项目背景与五阶段流水线总览

该仓库的核心目标,是在不直接暴露真实用户指令的前提下,为开源 LLM(LLaMA 7B/13B)构建一套可用于对齐(Alignment)的高质量指令数据,并完成模型微调。整体实现分为五个阶段:

  1. DP 微调 LLaMA 7B/13B:在 Chatbot Arena 指令上做差分隐私微调(阶段 1);
  2. 从微调模型采样:生成初始合成指令(阶段 2);
  3. DP 直方图重采样:对初始合成指令用差分隐私直方图方法(UnitedSignal 思路)重采样(阶段 3);
  4. OpenAI API 标注:调用 GPT-3.5-Turbo 为真实/合成指令生成回答(阶段 4);
  5. 监督微调(SFT):用 (指令, 回答) 数据对 LLaMA 7B/13B 做有监督微调(阶段 5)。

此外,对于 PPO 风格的 RLHF,仓库明确采用 Huggingface TRL、resample_with_dp_histogram 与 query_gpt35_for_annotation 三个子目录,顶层 README.md 给出了整体导航。

环境安装与依赖解析

仓库顶层提供了 requirements.txt,安装命令为:

pip install -r requirements.txt

从依赖清单可以看出技术选型:

依赖包作用
torch>=2.2.0transformers>=4.38.2acceleratedatasetstokenizersHuggingface 生态,模型加载、训练与数据管理
peftLoRA 参数高效微调,冻结预训练权重、只更新 adapter 参数
bitsandbytes低比特量化(训练命令中--qbits 16用于降低显存占用)
prv_accountant基于 Poisson Subsampled Gaussian Mechanism(PRV)的差分隐私隐私预算核算
scikit-learnfaiss重采样阶段的 PCA 降维与 KMeans 聚类
nltk数据预处理阶段的分词(n-gram 去重)
openai调用 GPT-3.5-Turbo 进行指令标注
absl-pySentencePieceprotobufnumpy通用基础设施与分词支持

说明:prv_accountant并非默认随 transformers 安装,DP 相关实验前需单独确认安装成功。

第一步:预处理 LMSYS-Chat-1M 构建 Chatbot Arena 指令集

在开始微调之前,需要先申请 LMSYS-Chat-1M 数据集的访问权限,然后运行顶层脚本:

python data_preprocess.py

脚本会加载lmsys/lmsys-chat-1mtrainsplit,处理结果以 HuggingfaceDatasetDict形式保存到dp_finetuning/data/目录(具体为chatbot_arena_instructions_train180k)。从 data_preprocess.py 源码看,预处理共分四步(对应论文 Appendix E):

  1. 基础过滤:仅保留英文对话、剔除被 redact 的对话、剔除被 OpenAI moderation 工具标记(flagged)的对话,并且只取每段对话的第一轮(role 必须为user)作为指令。
  2. 序列级去重(sequence-level de-duplication):用字典对完全相同的指令字符串去重。
  3. n-gram 去重:对每条指令分词(nltk.word_tokenize)后提取大小为 10 的 n-gram 集合,一旦某 n-gram 已出现过即丢弃该指令,用于去除高度相似的指令。
  4. 异常重复过滤:将指令转为小写后,过滤掉“化学工业”类模板化重复(如write an instruction of ... words in chemical industrygive me an introduction over ... a chemical company等四类模式)。

最终得到约 18.88 万条指令,随后随机打乱并划分为 train(前 180,000)、val(第 180,000–185,000)、test(其余),保存为DatasetDict(三列均为instruction)。这正是后续所有 DP 实验使用的chatbot_arena_instructions_train180k数据集来源。

第二步:LLaMA 的差分隐私微调(dp_finetuning)

dp_finetuning/README.md 定义了四种实验类型:

  • Type A:DP 微调 LLaMA 7B/13B 以生成合成指令
  • Type B:从 Type A 微调好的模型采样生成合成指令
  • Type C:用带标注的 (指令, 回答) 对做监督微调(SFT)
  • Type D:从 Type C 模型采样生成回答

实现基于 transformers、datasets、accelerate 与 PEFT。为降低显存开销,采用LoRA 微调:冻结预训练权重,仅更新极少量 adapter 参数。

DP 核心实现(utils/dp_utils.py)

差分隐私训练的关键函数集中在 dp_finetuning/utils/dp_utils.py,其三大支柱是:

  1. 逐样本梯度计算:通过 PyTorch 的 forward/backward hook 挂到 LoRA 分支的lora_Alora_B线性层上(make_lora_model_dp)。linear_forward_hook缓存层输入,linear_backward_hook利用torch.einsum把批量激活与输出梯度做外积,得到形状为(n, ...)grad_sample(n 为 batch 内样本数),即每个样本各自的梯度,token 级梯度在同一序列内求和。
  2. 逐样本梯度裁剪与累积clip_grad_sample先逐样本计算 L2 范数(get_grad_norm),再按clip(裁剪阈值)对每个样本的梯度做缩放(范数超过阈值则归一化到阈值);clip_and_accumulate_perexample_grads负责把混合精度缩放还原(除以 scaler scale)后执行裁剪与跨微批(micro-batch)累积(accumulated_grad),实现标准 DP-SGD 的“裁剪-累积-加噪”流程。
  3. 聚合梯度加噪:累积完成后由训练主循环按 DP-SGD 方式加入高斯噪声(噪声乘子由get_noise_multiplier求出)。

噪声乘子搜索get_noise_multiplier(eps, delta, steps, sampling_prob)以初值init_sigma=10起步,调用search_for_sigma以精度 1 → 0.1 → 0.01 逐级逼近,找到使get_epsilon_prv计算出的上界 epsilon 不超过目标 eps 的最小噪声乘子;若无法找到则抛出ValueError。epsilon 的计算基于prv_accountantPoissonSubsampledGaussianMechanismPRVAccountanteps_error=0.1delta_error=delta/10)。

训练命令生成器(generate_train_command.py)

所有训练命令均由 dp_finetuning/generate_train_command.py 生成,其解析的核心参数及含义:

参数默认值说明
--dataset_namechatbot_arena_instructions_train180k数据集名;含labelled前缀时视为 (指令,回答) SFT 数据集
--model_nameyahma/llama-7b-hf基础模型,实验中常用yahma/llama-13b-hf
--job_sessdebug任务会话名,作为输出目录前缀
--perdevice_bs4每张 GPU 的 batch size
--gpus1GPU 数量,决定使用accelerate_config_nofsdp.cfg还是..._gpu{N}.cfg
--max_seq_len2048最大序列长度
--total_bs32总 batch size,梯度累积步数 =total_bs // (perdevice_bs * gpus)
--num_epochs3训练轮数
--lr3e-4学习率
--lr_schedulerconstant学习率调度器
--wd0.0权重衰减
--clip-1逐样本梯度裁剪范数,-1 表示不裁剪(非 DP)
--eps-1(eps, delta)-DP 的目标 epsilon,-1 表示不启用 DP
--delta5e-7目标 delta
--prompt_styleNonevicuna(指令-回答格式)或uncond_generation(无条件生成指令)
--no_eval_at_startFalse是否跳过第 0 epoch 的评估(DP 场景建议开启)

脚本内部根据steps = num_epochs / (total_bs / 180000)与采样概率total_bs / 180000计算噪声乘子并打印,例如Noise multiplier is {np} for ({eps,delta})-DP。最终生成的命令为accelerate launch --config_file ... train_clm.py ...并带--gradient_ckpt--num_warmup_steps 30--qbits 16等训练参数。会话名(sess)会编码模型、eps、delta、bs、maxseq、epoch、lr、clip、np、gpus 等超参数,便于管理多次实验。

Type A:DP 微调生成合成指令

非 DP 基线(生成器,13B,3 epoch):

python generate_train_command.py --dataset_name chatbot_arena_instructions_train180k --model_name yahma/llama-13b-hf --job_sess debug --eps -1 --perdevice_bs 4 --gpus 1 --max_seq_len 1024 --total_bs 32 --num_epochs 3 --prompt_style uncond_generation --no_eval_at_start

(2.86, 5e-7)-DP(13B,10 epoch,单张 A100 约需 5 天):

python generate_train_command.py --dataset_name chatbot_arena_instructions_train180k --model_name yahma/llama-13b-hf --job_sess debug --eps 2.86 --delta 5e-7 --perdevice_bs 4 --gpus 1 --max_seq_len 1024 --total_bs 4096 --num_epochs 10 --prompt_style uncond_generation --lr 1e-3 --clip 0.5 --no_eval_at_start

(5.94, 5e-7)-DP(13B):

python generate_train_command.py --dataset_name chatbot_arena_instructions_train180k --model_name yahma/llama-13b-hf --job_sess debug --eps 5.94 --delta 5e-7 --perdevice_bs 4 --gpus 1 --max_seq_len 1024 --total_bs 4096 --num_epochs 10 --prompt_style uncond_generation --lr 1e-3 --clip 0.5 --no_eval_at_start

注意:DP 场景下total_bs提升到 4096(配合 10 epoch),clip取 0.5,学习率lr取 1e-3。训练 checkpoint 统一保存到outputs/[job_sess]目录。从源码可见,使用chatbot_arena_instructions_train180k数据集时强制要求prompt_style == uncond_generation

Type B:从微调模型采样生成合成指令

通过 generate_inference_command.py 生成推理命令。其解析的生成参数包括--no_sample(贪心解码)、--top_k--top_p--temperature--repetition_penalty(1.0 表示无惩罚)、--enforce_min_new_tokens--main_process_port(accelerate 端口,默认 29500),以及--adapter_path(LoRA 权重路径)与--instruction_file。无条件生成合成指令的示例:

python generate_inference_command.py --instruction_file utils/syn_instruct.txt --model_name yahma/llama-13b-hf --adapter_path [path to the fine-tuned apater] --job_sess debug --max_seq_len 1024 --top_p 0.95 --prompt_style uncond_generation

输出保存到inference_outputs/[job_sess]。源码对无条件生成做了约束:instruction_file必须包含syn_instruct字样,否则断言失败。实际部署中建议并行启动多个单 GPU 推理进程以加速合成指令生成,每个进程产出一个文本文件,供下一步合并。

第三步:用 DP 直方图重采样合成指令(resample_with_dp_histogram)

resample_with_dp_histogram/README.md 描述了三个主组件,整体思路源于 UnitedSignal 的差分隐私直方图匹配法:

  1. get_instructions_from_generations.py:把并行多进程生成的多份文本文件合并成一个 .pkl(python list,元素为字符串指令);
  2. get_embeddings.py:为 .pkl 中的指令计算 Sentence-T5 嵌入;
  3. resample_with_embeddings.py:基于嵌入做聚类与 DP 直方图重采样,过滤合成指令。

合并并行输出为 .pkl(可选)

此步可选——只要 .pkl 内是字符串指令列表即可。若使用 dp_finetuning 并行采样产生的文本文件,示例命令:

python get_instructions_from_generations.py --generation_folder [path to the folder, by default is inference_outputs/[job_sess]] --output_file [path to the output .pkl file]

计算 Sentence-T5 嵌入

get_embeddings.py 会自动下载 SentenceTransformer 模型(默认sentence-transformers/sentence-t5-base,可通过--model_name换为 xxl 变体),按--batch_size(默认 16)分批编码--num_targets(默认 180000)条指令,并输出到embeddings/embeddings_{base|xxl}_{start}_{end}_{name}.npy

python get_embeddings.py --instruction_file [path to the .pkl file] --output_file [path to the output embedding .npy file] --num_targets 1000000

源码显示嵌入维度为 (N, 768),--start_index支持分片计算超大批次。

DP 直方图重采样

核心脚本 resample_with_embeddings.py 的命令:

python resample_with_embeddings.py --real_embeddings_path [path to a .npy file] --syn_embeddings_path [path to a .npy file] --syn_instructions_path [path to a .pkl file] --output_name [a string] --num_buckets 1000 --histogram_sigma 10

算法流程(结合源码):

  1. 降维:对 L2 归一化后的嵌入做 PCA,取累计解释方差 ≥ 0.9 的维度(get_pca_index);
  2. 聚类建桶:用faiss.Kmeansniter=500nredo=5)在合成嵌入子集(--num_samples,默认 180000)上聚成--num_buckets(示例为 1000)个桶,得到 kmeans 与 PCA 参数;
  3. 构建直方图:用同一聚类模型把真实指令与合成指令分别映射到桶,得到真实分布 p_bins 与合成分布 q_bins(build_histogram,按密度归一化);
  4. DP 化真实直方图privitize_p_binsp_bins * n加高斯噪声N(0, sigma)--histogram_sigma,默认 10)再除以 n、截断负值,得到 noisy_p_bins——这是隐私保护的关键一步;
  5. 按比值重采样:计算每个桶的ratio = noisy_p_bins / q_bins,按 ratio 降序(argsort(ratio)[::-1])逐桶抽取num_target_samples * noisy_p_bins[idx]个合成样本;若桶内样本不足会打印警告并全部取用(此时建议生成更多初始合成指令);
  6. 保存结果:输出max{max_ratio}_subsampled_instructions_n{count}_{output_name}_seed{seed}.pkl与对应的嵌入 .npy 文件。

--sampling_method仅支持uniformdistance(默认 uniform),--seed控制可复现性。重采样后的合成指令分布在分布上与真实指令对齐,同时由于直方图注入了高斯噪声,单个真实指令的信息不会直接泄露。

第四步:调用 OpenAI API 为指令生成回答(query_gpt35_for_annotation)

query_gpt35_for_annotation/README.md 说明:首先需要把 query_openai_labelling.py 中的 OpenAI key 替换为自己的 key。

脚本读取 .pkl 文件并选取指定数量的指令,通过20 个并行进程调用 OpenAI GPT-3.5-Turbo API 进行标注。为避免长时间同步等待,每次调用只标注 5000 条指令;脚本会自动保存结果,再次调用时自动跳过已标注指令。若想一次性标注 .pkl 中的全部指令,使用端到端脚本 batch_label_commands.py:

python batch_label_commands.py --instruction_file [path to .pkl] --output_file [path of the output .pkl] --samples_to_label [how many instructions to label in total?]

--samples_to_label指定总共要标注多少条指令。标注对象既可以是预处理得到的真实指令(data/chatbot_arena_instructions_train180k),也可以是前一步 DP 重采样后的合成指令。

第五步:用 (指令, 回答) 对做监督微调(Type C / Type D)

标注完成后进入监督微调阶段。需要先把指令提取为 python list(元素为字符串)并 dump 成 .pkl,标注结果按 HuggingfaceDatasetDict组织,包含 train/val/test 三个 split,每个 split 是一个含instructionanswer两列的Dataset数据集文件名必须以labelled开头——这正是 generate_train_command.py 判断 SFT 模式的依据(elif 'labelled' in dataset_name),此时强制使用--prompt_style vicuna

Type C:监督微调

用真实指令-回答对做 (5.94, 5e-7)-DP SFT(7B):

python generate_train_command.py --dataset_name labelled_real_arena180k --model_name yahma/llama-7b-hf --job_sess debug --eps 5.94 --delta 5e-7 --perdevice_bs 2 --gpus 1 --max_seq_len 2048 --total_bs 4096 --num_epochs 10 --prompt_style vicuna --lr 1e-3 --clip 1.0

用合成指令-回答对做非 DP SFT(7B),数据来自 DP 重采样与 GPT-3.5 标注流水线(如labelled_syn300k_13b_eps594_uncond_syn_clip05_13b_selected_sigma10_1000bins,名字即编码了上游超参数:13B 模型、eps=5.94、clip=0.5、sigma=10、1000 bins):

python generate_train_command.py --dataset_name labelled_syn300k_13b_eps594_uncond_syn_clip05_13b_selected_sigma10_1000bins --model_name yahma/llama-7b-hf --job_sess debug --eps -1 --perdevice_bs 4 --gpus 1 --max_seq_len 1024 --total_bs 32 --num_epochs 3 --prompt_style vicuna

Type D:为评测指令生成回答

用 Type C 微调好的 checkpoint 为 Chatbot Arena 或 AlpacaEval 的评测指令生成回答(vicuna 提示风格):

python generate_inference_command.py --instruction_file utils/alpaca_eval_instructions.txt --model_name yahma/llama-13b-hf --adapter_path [path to the fine-tuned apater] --job_sess debug --max_seq_len 1024 --top_p 0.95 --prompt_style vicuna

输出同样保存在inference_outputs/[job_sess],可作为下游评测(如 AlpacaEval)的模型回答输入。

进阶:RLHF(PPO)扩展

论文完整对齐流程还包括 RLHF 阶段。仓库明确表示 PPO 风格的 RLHF 直接复用 Huggingface TRL(trl)的现成实现,因此你可以在完成 Type C/D 的 SFT checkpoint 后,基于 TRL 的PPOTrainer搭建 reward 模型与 PPO 训练环,将本流水线产出的隐私保护对齐模型作为策略模型的初始化权重。该阶段不包含在本仓库代码内,但流水线的前五步已完整覆盖数据侧与监督侧的全部隐私保护环节。

复现要点与工程建议

  • 隐私参数闭环:eps、delta、total_bs、num_epochs 共同决定噪声乘子;generate_train_command.py会在启动前打印噪声乘子供核对,建议先跑小规模--job_sess debug验证命令生成与 DP 核算无误,再放大到完整配置。
  • DP 与内存:DP 需要逐样本梯度,因此配合 LoRA(只对lora_A/lora_B挂 hook)与--qbits 16量化显著降低显存;训练脚本还启用了--gradient_ckpt(梯度检查点)。
  • 超大规模嵌入get_embeddings.py支持--start_index分片与--num_targets控制,便于在单机内存约束下处理百万级指令。
  • 并行与容错:合成指令生成建议并行多进程,产出文本后由get_instructions_from_generations.py合并;GPT-3.5 标注按 5000 条/次分批、自动续标,天然支持断点续跑。
  • 结果产物:训练 checkpoint 在outputs/[job_sess],推理输出在inference_outputs/[job_sess],重采样结果以max{ratio}_subsampled_instructions_n{count}_{name}_seed{seed}.pkl命名,便于按超参数检索。

以上所有命令与参数均以当前仓库实际代码为准;如果你要复现论文中的具体数值结果(如 (2.86, 5e-7)-DP 下 13B 生成器的 5 天训练耗时),需要具备与论文一致的硬件(如单卡 A100)与数据集访问权限。

【免费下载链接】google-researchGoogle Research项目地址: https://gitcode.com/gh_mirrors/go/google-research

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询