无分类器指导(CFG)在LLM 中的应用:通过负向提示词增强生成约束
大家好,今天我们来深入探讨无分类器指导(Classifier-Free Guidance,CFG)这一技术在大型语言模型(LLM)中的应用,重点聚焦于如何利用负向提示词(Negative Prompt)来增强生成内容的约束力,从而获得更符合期望、更高质量的输出。
1. 什么是无分类器指导(CFG)?
在深入研究负向提示词之前,我们需要先理解 CFG 的基本原理。CFG 是一种条件生成技术,最初应用于扩散模型(Diffusion Models),后来被成功引入到 LLM 领域。它的核心思想是在训练过程中,模型同时学习条件概率分布 p(x|y) 和无条件概率分布 p(x),其中 x 代表生成内容,y 代表条件(例如,提示词)。
在推理阶段,CFG 通过插值这两个概率分布来引导生成过程。具体来说,生成过程可以表示为:
x* = argmax_x [ w * log p(x|y) + (1 - w) * log p(x) ]
其中:
x*是最终生成的文本。w是指导强度(Guidance Scale),控制条件概率分布p(x|y)的影响程度。w越大,模型越倾向于生成符合条件y的文本;w越小,模型越倾向于生成无条件分布p(x)下的文本。p(x|y)是给定条件y下,生成x的条件概率。p(x)是生成x的无条件概率。
实际上,我们通常不需要直接计算概率分布,而是通过近似的方式实现 CFG。一种常见的实现方式是在模型的输出 logits 上进行插值。
2. 负向提示词(Negative Prompt):一种特殊的条件
负向提示词是一种特殊的条件 y,它描述了我们 不希望 生成的内容的特征。例如,如果我们希望生成一张高质量的照片,我们可以使用正向提示词 "high-quality photo",同时使用负向提示词 "blurry, low resolution"。
负向提示词在 CFG 中的作用是:通过降低模型生成不希望出现的特征的概率,从而增强对生成内容的约束力,提高生成质量。
3. CFG 与负向提示词的结合:工作原理
当 CFG 与负向提示词结合使用时,生成过程可以理解为:
- 计算正向提示词的 logits: 模型根据正向提示词计算生成文本的 logits。
- 计算负向提示词的 logits: 模型根据负向提示词计算生成文本的 logits。
- 插值 logits: 将正向提示词的 logits 和负向提示词的 logits 进行插值,得到最终的 logits。
- 生成文本: 使用最终的 logits 生成文本。
插值公式可以表示为:
logits_final = logits_positive + guidance_scale * (logits_positive - logits_negative)
其中:
logits_final是最终用于生成文本的 logits。logits_positive是正向提示词的 logits。logits_negative是负向提示词的 logits。guidance_scale是指导强度,控制负向提示词的影响程度。
这个公式的核心思想是:将正向提示词的 logits 向“远离”负向提示词的 logits 的方向移动,从而降低生成不希望出现的特征的概率。
4. 代码示例:使用 Hugging Face Transformers 实现 CFG 与负向提示词
下面是一个使用 Hugging Face Transformers 库实现 CFG 与负向提示词的示例代码,以文本生成任务为例:
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
# 模型和tokenizer
model_name = "EleutherAI/gpt-neo-1.3B" # 或者其他你喜欢的模型
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
if torch.cuda.is_available():
model = model.to("cuda")
def generate_text_with_cfg(prompt, negative_prompt, guidance_scale=5.0, max_length=100):
"""
使用CFG和负向提示词生成文本。
Args:
prompt: 正向提示词 (str)。
negative_prompt: 负向提示词 (str)。
guidance_scale: 指导强度 (float)。
max_length: 生成文本的最大长度 (int)。
Returns:
生成的文本 (str)。
"""
# 编码提示词
positive_input_ids = tokenizer.encode(prompt, return_tensors="pt")
negative_input_ids = tokenizer.encode(negative_prompt, return_tensors="pt")
if torch.cuda.is_available():
positive_input_ids = positive_input_ids.to("cuda")
negative_input_ids = negative_input_ids.to("cuda")
# 生成正向提示词的 logits
positive_output = model.generate(positive_input_ids,
max_length=max_length,
output_scores=True,
return_dict_in_generate=True)
positive_logits = positive_output.scores
# 生成负向提示词的 logits
negative_output = model.generate(negative_input_ids,
max_length=max_length,
output_scores=True,
return_dict_in_generate=True)
negative_logits = negative_output.scores
# 插值 logits (这里简化了,实际应用中需要更精细的处理)
final_logits = []
for i in range(len(positive_logits)):
final_logits.append(positive_logits[i] + guidance_scale * (positive_logits[i] - negative_logits[i]))
# 使用插值后的 logits 生成文本 (这里使用贪婪解码,可以替换为其他解码方法)
predicted_token_ids = [torch.argmax(logits, dim=-1) for logits in final_logits]
predicted_text = tokenizer.decode(torch.cat(predicted_token_ids).tolist(), skip_special_tokens=True)
return predicted_text
# 示例用法
prompt = "Write a story about a brave knight"
negative_prompt = "boring, cliche, predictable"
generated_text = generate_text_with_cfg(prompt, negative_prompt)
print(f"生成的文本: {generated_text}")
代码解释:
- 导入必要的库: 导入 Hugging Face Transformers 库中的
AutoModelForCausalLM、AutoTokenizer和 PyTorch。 - 加载模型和 tokenizer: 选择一个合适的 LLM 模型(例如
EleutherAI/gpt-neo-1.3B)并加载相应的 tokenizer 和模型。 generate_text_with_cfg函数:- 接收正向提示词、负向提示词、指导强度和最大长度作为输入。
- 使用 tokenizer 将正向提示词和负向提示词编码为 input IDs。
- 使用模型生成正向提示词和负向提示词的 logits。
- 关键步骤:插值 logits。 根据公式
logits_final = logits_positive + guidance_scale * (logits_positive - logits_negative)对 logits 进行插值。 注意: 这个示例中的插值是简化的,实际应用中需要更精细的处理,例如对齐 logits 的长度,处理 padding 等。 - 使用插值后的 logits 生成文本。 这里使用贪婪解码,也可以替换为其他解码方法,例如 beam search。
- 返回生成的文本。
- 示例用法:
- 定义正向提示词和负向提示词。
- 调用
generate_text_with_cfg函数生成文本。 - 打印生成的文本。
注意:
- 这段代码只是一个简单的示例,用于说明 CFG 和负向提示词的基本原理。 在实际应用中,需要根据具体任务和模型进行调整。
- logits 的插值需要更精细的处理,例如对齐 logits 的长度,处理 padding 等。
- 解码方法可以根据需要选择,例如 beam search 可以提高生成质量。
5. 负向提示词的设计技巧
负向提示词的设计对生成结果有重要影响。以下是一些设计负向提示词的技巧:
- 明确不希望出现的特征: 负向提示词应该明确描述不希望出现在生成内容中的特征。例如,如果希望生成清晰的图像,可以使用 "blurry, noisy, low resolution" 作为负向提示词。
- 避免过于宽泛的描述: 过于宽泛的负向提示词可能会导致模型过度约束,生成质量下降。例如,避免使用 "bad" 这样的词语,而是使用更具体的描述,例如 "poorly drawn, distorted, unnatural"。
- 使用与正向提示词相关的负向提示词: 负向提示词应该与正向提示词相关,以便更好地引导生成过程。例如,如果正向提示词是 "a portrait of a woman",可以使用 "ugly, deformed, disfigured" 作为负向提示词。
- 尝试不同的负向提示词组合: 通过尝试不同的负向提示词组合,可以找到最佳的生成效果。
- 使用加权负向提示词: 可以对不同的负向提示词进行加权,以控制它们的影响程度。 例如,可以使用 "ugly:0.8, deformed:0.5, disfigured:0.3" 来表示 "ugly" 的影响程度更高。
表格:负向提示词设计示例
| 正向提示词 | 负向提示词 | 预期效果 |
|---|---|---|
| A photo of a cat | blurry, low resolution, poorly lit | 生成清晰、高分辨率、光线充足的猫的照片 |
| A painting of a landscape | unrealistic, cartoonish, abstract | 生成更写实、自然的风景画 |
| A story about a knight | boring, cliche, predictable, unoriginal | 生成更引人入胜、新颖的骑士故事 |
| A song about love | sad, depressing, heartbreaking, melancholic | 生成更积极、充满希望的爱情歌曲 |
6. 指导强度(Guidance Scale)的选择
指导强度 guidance_scale 是 CFG 中一个重要的超参数,它控制着条件(包括负向提示词)对生成过程的影响程度。
guidance_scale越大: 模型越倾向于生成符合条件的内容,但也可能导致生成内容过于刻板、缺乏多样性。guidance_scale越小: 模型越倾向于生成无条件分布下的内容,但也可能导致生成内容偏离期望。
guidance_scale 的选择需要根据具体任务和模型进行调整。通常可以通过实验来找到最佳的 guidance_scale 值。
7. CFG 与负向提示词的局限性
虽然 CFG 和负向提示词可以有效地增强生成内容的约束力,但也存在一些局限性:
- 计算成本: CFG 需要计算正向提示词和负向提示词的 logits,增加了计算成本。
- 超参数调整:
guidance_scale的选择需要进行实验,增加了超参数调整的复杂性。 - 负向提示词的设计: 负向提示词的设计需要一定的技巧和经验。
- 可能导致生成内容过于刻板: 过高的
guidance_scale可能会导致生成内容过于刻板、缺乏多样性。 - 并非万能: 对于一些复杂的生成任务,CFG 和负向提示词可能无法完全满足要求。
8. 未来发展方向
CFG 和负向提示词是 LLM 领域中一个活跃的研究方向。未来发展方向包括:
- 更高效的 CFG 实现: 研究更高效的 CFG 实现方法,降低计算成本。
- 自动负向提示词生成: 开发自动生成负向提示词的方法,减少人工设计的工作量。
- 自适应指导强度: 研究自适应调整
guidance_scale的方法,提高生成质量。 - 与其他生成技术的结合: 将 CFG 与其他生成技术(例如,强化学习)相结合,进一步提高生成质量。
- 应用于更多领域: 将 CFG 和负向提示词应用于更多领域,例如文本摘要、机器翻译等。
9. 总结 CFG与负向提示词,增强了生成模型的控制能力
CFG 通过结合条件和无条件概率分布,引导 LLM 的生成过程。 负向提示词作为一种特殊的条件,允许我们指定不希望出现的特征,从而增强生成内容的约束力,提高生成质量。