提示词技巧
提示词很重要,因为它们描述了你希望扩散模型生成什么。最好的提示词是详细、具体且结构良好的,以帮助模型实现你的愿景。但是,创作一个很棒的提示词需要时间和精力,有时可能还不够,因为语言和文字可能不精确。这时你需要使用其他技巧来增强你的提示词,例如提示词增强和提示词加权,以获得你想要的结果。
本指南将向你展示如何使用这些提示词技巧以更少的精力生成高质量图像,以及如何调整提示词中某些关键词的权重。
提示词工程
这不是关于提示词工程的详尽指南,但它将帮助你了解良好提示词的必要部分。我们鼓励你继续尝试不同的提示词,并以新的方式将它们组合起来,看看什么效果最好。随着你编写更多提示词,你将对什么有效、什么无效产生直觉!
新的扩散模型在从基本提示词生成高质量图像方面做得非常好,但创建措辞良好的提示词以获得最佳结果仍然很重要。以下是一些编写良好提示词的技巧
- 图像的媒介是什么?是照片、绘画、3D 插图还是其他?
- 图像的主题是什么?是人物、动物、物体还是场景?
- 你想在图像中看到哪些细节?在这里,你可以发挥你的创造力,并尝试使用不同的词语来使你的图像栩栩如生。例如,照明如何?氛围和美学是什么样的?你正在寻找哪种艺术或插画风格?你使用的词语越具体和精确,模型就越能理解你想要生成什么。
使用 GPT2 增强提示词
提示词增强是一种无需花费太多精力构建提示词即可快速提高提示词质量的技术。它使用像 GPT2 这样的在 Stable Diffusion 文本提示词上预训练的模型,自动用其他重要的关键词来丰富提示词,从而生成高质量的图像。
该技术通过整理特定关键词列表并强制模型生成这些关键词来增强原始提示词。这样,你的提示词可以是“一只猫”,而 GPT2 可以将提示词增强为“土耳其屋顶上晒太阳的猫的电影剧照,高度详细,高预算好莱坞电影,宽银幕,忧郁,史诗,华丽,胶片颗粒质感锐利聚焦美丽详细复杂惊艳令人惊叹史诗”。
你还应该使用偏移噪声 LoRA 来改善明亮和黑暗图像中的对比度,并创造更好的整体照明。这个LoRA 可从stabilityai/stable-diffusion-xl-base-1.0 获取。
首先定义某些风格和关键词列表(你可以查看关键词 和风格 的更全面的列表,这些列表由 Fooocus 使用)来增强提示词。
import torch
from transformers import GenerationConfig, GPT2LMHeadModel, GPT2Tokenizer, LogitsProcessor, LogitsProcessorList
from diffusers import StableDiffusionXLPipeline
styles = {
"cinematic": "cinematic film still of {prompt}, highly detailed, high budget hollywood movie, cinemascope, moody, epic, gorgeous, film grain",
"anime": "anime artwork of {prompt}, anime style, key visual, vibrant, studio anime, highly detailed",
"photographic": "cinematic photo of {prompt}, 35mm photograph, film, professional, 4k, highly detailed",
"comic": "comic of {prompt}, graphic illustration, comic art, graphic novel art, vibrant, highly detailed",
"lineart": "line art drawing {prompt}, professional, sleek, modern, minimalist, graphic, line art, vector graphics",
"pixelart": " pixel-art {prompt}, low-res, blocky, pixel art style, 8-bit graphics",
}
words = [
"aesthetic", "astonishing", "beautiful", "breathtaking", "composition", "contrasted", "epic", "moody", "enhanced",
"exceptional", "fascinating", "flawless", "glamorous", "glorious", "illumination", "impressive", "improved",
"inspirational", "magnificent", "majestic", "hyperrealistic", "smooth", "sharp", "focus", "stunning", "detailed",
"intricate", "dramatic", "high", "quality", "perfect", "light", "ultra", "highly", "radiant", "satisfying",
"soothing", "sophisticated", "stylish", "sublime", "terrific", "touching", "timeless", "wonderful", "unbelievable",
"elegant", "awesome", "amazing", "dynamic", "trendy",
]
你可能已经注意到,在words
列表中,某些词可以配对在一起以创造更有意义的东西。例如,“高”和“质量”这两个词可以组合成“高质量”。让我们将这些词配对,并删除无法配对的词。
word_pairs = ["highly detailed", "high quality", "enhanced quality", "perfect composition", "dynamic light"]
def find_and_order_pairs(s, pairs):
words = s.split()
found_pairs = []
for pair in pairs:
pair_words = pair.split()
if pair_words[0] in words and pair_words[1] in words:
found_pairs.append(pair)
words.remove(pair_words[0])
words.remove(pair_words[1])
for word in words[:]:
for pair in pairs:
if word in pair.split():
words.remove(word)
break
ordered_pairs = ", ".join(found_pairs)
remaining_s = ", ".join(words)
return ordered_pairs, remaining_s
接下来,实现一个自定义的LogitsProcessor 类,它将words
列表中的标记分配为 0 的值,并将不在words
列表中的标记分配为负值,以便在生成过程中不会选择它们。这样,生成就会偏向于words
列表中的词。在使用列表中的某个词后,它也会被分配一个负值,因此不会再次被选中。
class CustomLogitsProcessor(LogitsProcessor):
def __init__(self, bias):
super().__init__()
self.bias = bias
def __call__(self, input_ids, scores):
if len(input_ids.shape) == 2:
last_token_id = input_ids[0, -1]
self.bias[last_token_id] = -1e10
return scores + self.bias
word_ids = [tokenizer.encode(word, add_prefix_space=True)[0] for word in words]
bias = torch.full((tokenizer.vocab_size,), -float("Inf")).to("cuda")
bias[word_ids] = 0
processor = CustomLogitsProcessor(bias)
processor_list = LogitsProcessorList([processor])
组合提示词和前面在styles
字典中定义的cinematic
风格提示词。
prompt = "a cat basking in the sun on a roof in Turkey"
style = "cinematic"
prompt = styles[style].format(prompt=prompt)
prompt
"cinematic film still of a cat basking in the sun on a roof in Turkey, highly detailed, high budget hollywood movie, cinemascope, moody, epic, gorgeous, film grain"
从Gustavosta/MagicPrompt-Stable-Diffusion 检查点加载 GPT2 分词器和模型(此特定检查点经过训练以生成提示词)以增强提示词。
tokenizer = GPT2Tokenizer.from_pretrained("Gustavosta/MagicPrompt-Stable-Diffusion")
model = GPT2LMHeadModel.from_pretrained("Gustavosta/MagicPrompt-Stable-Diffusion", torch_dtype=torch.float16).to(
"cuda"
)
model.eval()
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
token_count = inputs["input_ids"].shape[1]
max_new_tokens = 50 - token_count
generation_config = GenerationConfig(
penalty_alpha=0.7,
top_k=50,
eos_token_id=model.config.eos_token_id,
pad_token_id=model.config.eos_token_id,
pad_token=model.config.pad_token_id,
do_sample=True,
)
with torch.no_grad():
generated_ids = model.generate(
input_ids=inputs["input_ids"],
attention_mask=inputs["attention_mask"],
max_new_tokens=max_new_tokens,
generation_config=generation_config,
logits_processor=proccesor_list,
)
然后你可以组合输入提示词和生成的提示词。随意查看生成的提示词(generated_part
)、找到的词对(pairs
)和剩余的词(words
)。所有这些都打包在enhanced_prompt
中。
output_tokens = [tokenizer.decode(generated_id, skip_special_tokens=True) for generated_id in generated_ids]
input_part, generated_part = output_tokens[0][: len(prompt)], output_tokens[0][len(prompt) :]
pairs, words = find_and_order_pairs(generated_part, word_pairs)
formatted_generated_part = pairs + ", " + words
enhanced_prompt = input_part + ", " + formatted_generated_part
enhanced_prompt
["cinematic film still of a cat basking in the sun on a roof in Turkey, highly detailed, high budget hollywood movie, cinemascope, moody, epic, gorgeous, film grain quality sharp focus beautiful detailed intricate stunning amazing epic"]
最后,加载一个管道和一个具有低权重的偏移噪声 LoRA,以使用增强的提示词生成图像。
pipeline = StableDiffusionXLPipeline.from_pretrained(
"RunDiffusion/Juggernaut-XL-v9", torch_dtype=torch.float16, variant="fp16"
).to("cuda")
pipeline.load_lora_weights(
"stabilityai/stable-diffusion-xl-base-1.0",
weight_name="sd_xl_offset_example-lora_1.0.safetensors",
adapter_name="offset",
)
pipeline.set_adapters(["offset"], adapter_weights=[0.2])
image = pipeline(
enhanced_prompt,
width=1152,
height=896,
guidance_scale=7.5,
num_inference_steps=25,
).images[0]
image
提示词加权
提示词加权提供了一种强调或弱化提示词某些部分的方法,从而可以更好地控制生成的图像。一个提示词可以包含多个概念,这些概念会被转换成上下文化的文本嵌入。嵌入被模型用来调节其交叉注意力层以生成图像(阅读 Stable Diffusion 的博文 以了解其工作原理)。
提示词加权通过增加或减少与提示词中概念相对应的文本嵌入向量的尺度来工作,因为您可能并不希望模型平等地关注所有概念。准备加权提示词嵌入的最简单方法是使用 Compel,这是一个文本提示词加权和混合库。获得加权提示词嵌入后,您可以将其传递给任何具有 prompt_embeds
(以及可选的 negative_prompt_embeds
)参数的管道,例如 StableDiffusionPipeline、StableDiffusionControlNetPipeline 和 StableDiffusionXLPipeline。
如果您的首选管道没有 prompt_embeds
参数,请打开一个 issue,以便我们添加它!
本指南将向您展示如何在 🤗 Diffusers 中使用 Compel 对提示词进行加权和混合。
在开始之前,请确保您已安装最新版本的 Compel。
# uncomment to install in Colab
#!pip install compel --upgrade
在本指南中,让我们使用提示词 "a red cat playing with a ball"
使用 StableDiffusionPipeline 生成图像。
from diffusers import StableDiffusionPipeline, UniPCMultistepScheduler
import torch
pipe = StableDiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v1-4", use_safetensors=True)
pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)
pipe.to("cuda")
prompt = "a red cat playing with a ball"
generator = torch.Generator(device="cpu").manual_seed(33)
image = pipe(prompt, generator=generator, num_inference_steps=20).images[0]
image
加权
您会注意到图像中没有“球”!让我们使用 compel 来提高提示词中“球”的概念权重。创建一个 Compel
对象,并向其传递分词器和文本编码器。
from compel import Compel
compel_proc = Compel(tokenizer=pipe.tokenizer, text_encoder=pipe.text_encoder)
compel 使用 +
或 -
来增加或减少提示词中某个词的权重。要增加“球”的权重,
+
对应于值 1.1
,++
对应于 1.1^2
,依此类推。类似地,-
对应于 0.9
,--
对应于 0.9^2
。请随意尝试在您的提示词中添加更多 +
或 -
!
prompt = "a red cat playing with a ball++"
将提示词传递给 compel_proc
以创建新的提示词嵌入,这些嵌入将传递给管道。
prompt_embeds = compel_proc(prompt)
generator = torch.manual_seed(33)
image = pipe(prompt_embeds=prompt_embeds, generator=generator, num_inference_steps=20).images[0]
image
要降低提示词部分的权重,请使用 -
后缀。
prompt = "a red------- cat playing with a ball"
prompt_embeds = compel_proc(prompt)
generator = torch.manual_seed(33)
image = pipe(prompt_embeds=prompt_embeds, generator=generator, num_inference_steps=20).images[0]
image
您甚至可以在同一个提示词中增加或降低多个概念的权重。
prompt = "a red cat++ playing with a ball----"
prompt_embeds = compel_proc(prompt)
generator = torch.manual_seed(33)
image = pipe(prompt_embeds=prompt_embeds, generator=generator, num_inference_steps=20).images[0]
image
混合
您还可以通过将 .blend()
添加到提示词列表并传递一些权重来创建提示词的加权混合。您的混合可能并不总是产生您期望的结果,因为它打破了关于文本编码器功能的一些假设,所以尽情享受并进行试验!
prompt_embeds = compel_proc('("a red cat playing with a ball", "jungle").blend(0.7, 0.8)')
generator = torch.Generator(device="cuda").manual_seed(33)
image = pipe(prompt_embeds=prompt_embeds, generator=generator, num_inference_steps=20).images[0]
image
连接
连接会独立地扩散每个提示词,并通过其加权和连接其结果。在提示词列表的末尾添加 .and()
以创建连接。
prompt_embeds = compel_proc('["a red cat", "playing with a", "ball"].and()')
generator = torch.Generator(device="cuda").manual_seed(55)
image = pipe(prompt_embeds=prompt_embeds, generator=generator, num_inference_steps=20).images[0]
image
文本反转
文本反转 是一种从一些图像中学习特定概念的技术,您可以使用它来生成以该概念为条件的新图像。
创建一个管道,并使用 load_textual_inversion() 函数加载文本反转嵌入(随意浏览 Stable Diffusion Conceptualizer 以获取 100 多个经过训练的概念)。
import torch
from diffusers import StableDiffusionPipeline
from compel import Compel, DiffusersTextualInversionManager
pipe = StableDiffusionPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5", torch_dtype=torch.float16,
use_safetensors=True, variant="fp16").to("cuda")
pipe.load_textual_inversion("sd-concepts-library/midjourney-style")
Compel 提供了一个 DiffusersTextualInversionManager
类来简化文本反转的提示词加权。实例化 DiffusersTextualInversionManager
并将其传递给 Compel
类。
textual_inversion_manager = DiffusersTextualInversionManager(pipe) compel_proc = Compel( tokenizer=pipe.tokenizer, text_encoder=pipe.text_encoder, textual_inversion_manager=textual_inversion_manager)
使用 <concept>
语法将概念合并到提示词中以进行条件设置。
prompt_embeds = compel_proc('("A red cat++ playing with a ball <midjourney-style>")')
image = pipe(prompt_embeds=prompt_embeds).images[0]
image
DreamBooth
DreamBooth 是一种在给定少量主题图像进行训练的情况下生成主题的上下文化图像的技术。它类似于文本反转,但 DreamBooth 训练整个模型,而文本反转仅微调文本嵌入。这意味着您应该使用 from_pretrained() 加载 DreamBooth 模型(随意浏览 Stable Diffusion Dreambooth Concepts Library 以获取 100 多个经过训练的模型)。
import torch
from diffusers import DiffusionPipeline, UniPCMultistepScheduler
from compel import Compel
pipe = DiffusionPipeline.from_pretrained("sd-dreambooth-library/dndcoverart-v1", torch_dtype=torch.float16).to("cuda")
pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)
创建一个带有分词器和文本编码器的 Compel
类,并将您的提示词传递给它。根据您使用的模型,您需要将模型的唯一标识符合并到您的提示词中。例如,dndcoverart-v1
模型使用标识符 dndcoverart
。
compel_proc = Compel(tokenizer=pipe.tokenizer, text_encoder=pipe.text_encoder)
prompt_embeds = compel_proc('("magazine cover of a dndcoverart dragon, high quality, intricate details, larry elmore art style").and()')
image = pipe(prompt_embeds=prompt_embeds).images[0]
image
Stable Diffusion XL
Stable Diffusion XL (SDXL) 具有两个分词器和文本编码器,因此其用法略有不同。为了解决这个问题,您应该将两个分词器和编码器都传递给 Compel
类。
from compel import Compel, ReturnedEmbeddingsType
from diffusers import DiffusionPipeline
from diffusers.utils import make_image_grid
import torch
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
variant="fp16",
use_safetensors=True,
torch_dtype=torch.float16
).to("cuda")
compel = Compel(
tokenizer=[pipeline.tokenizer, pipeline.tokenizer_2] ,
text_encoder=[pipeline.text_encoder, pipeline.text_encoder_2],
returned_embeddings_type=ReturnedEmbeddingsType.PENULTIMATE_HIDDEN_STATES_NON_NORMALIZED,
requires_pooled=[False, True]
)
这次,让我们将第一个提示词中“球”的权重提高 1.5 倍,并将第二个提示词中“球”的权重降低到 0.6。 StableDiffusionXLPipeline 还需要 pooled_prompt_embeds
(以及可选的 negative_pooled_prompt_embeds
),因此您应该将它们与条件张量一起传递给管道。
# apply weights
prompt = ["a red cat playing with a (ball)1.5", "a red cat playing with a (ball)0.6"]
conditioning, pooled = compel(prompt)
# generate image
generator = [torch.Generator().manual_seed(33) for _ in range(len(prompt))]
images = pipeline(prompt_embeds=conditioning, pooled_prompt_embeds=pooled, generator=generator, num_inference_steps=30).images
make_image_grid(images, rows=1, cols=2)