一、实现目标
上篇文章写完LLM的Agent之后,流程应该是进入到了SDXL的“文生图”“图生图”阶段了
目标很明确,使用SDXL为ChatGLM生成的内容进行配图,说明:大部分使用SD模型的大神都是使用SD模型配套的开源WebUI,因为我主打一个折腾,所以自己使用diffusers库开发自己的应用
1.要根据不同内容生成不同“风格”的图片,比如:电影风格,摄影风格,动漫风格,水墨风格,彩绘风格....
2.某些特定场景下需要有人物出境,模型预训练的时候的人物原型并不能满足“私有AIGC”这个flag,所以必须微调模型打造自己的人物角色
3.某些时候我希望能够进行多风格融合,比如人物写真融入彩绘,原始模型无法完成,所以必须通过代码进行loRA模型融合
需求目标明确之后,就开始实现吧...
二、实现路径
2.1、sdxl原始模型接入
2.1.1 模型下载
从huggingface下载SDXL模型(SDXL模型相较于SD模型,在prompt上更具友好性,XL是通过类自然语言进行提示词解析(也支持Tag标记),而SD是通过Tag标记进行解析,而且XL在图像处理的细腻度上个人感觉比SD更好)
下载地址:
https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0
https://huggingface.co/stabilityai/stable-diffusion-xl-refiner-1.0
为什么会有两个地址?,因为SDXL模型分为base模型和refiner模型,base是基础模型,refiner是精炼模型,意思就是一张图首先通过base进行处理,然后交给refiner进行精加工,最后产生最终的图片。
从huggingface上拔下来的一张流程图当然,这两个模型并不是必须结合在一起使用,通常情况下可以只使用Base模型,由Base模型直接产出1024x1024的最终图,也是没有任何问题的,后面我会给大家展示一下结合使用和分开使用的代码和最终图片效果
2.1.2 加载模型
模型下载到本地之后,可以通过diffusers库对模型进行加载
from diffusers import DiffusionPipeline, StableDiffusionXLImg2ImgPipeline
base模型加载:
def load_model(self):
if self.base_model is None:
# 这里并不是一定要制定vae,如果不指定vae,DiffusionPipeline会加载sdxl模型内部原本的vae
self.vae = AutoencoderKL.from_pretrained(
f"{self.model_id}\\vae_fix", torch_dtype=torch.float16)
# 初始化模型
self.base_model = DiffusionPipeline.from_pretrained(
self.model_id, # 模型存放本地磁盘路径
vae=self.vae, # 指定模型使用的vae组件,可选项,不是必须指定
torch_dtype=torch.float16, # 加载半精度的模型参数,显存够大的可以忽略
use_safetensors=True, # 是否允许使用saftensor格式的参数文件
variant="fp16" # 加载半精度的模型参数,显存够大的可以忽略
)
# 在GPU上进行推理 显存够大的可以直接上CUDA推理
if self.is_cuda is True:
self.base_model.to("cuda")
# GPU+CPU联合推理
else:
self.base_model.enable_model_cpu_offload()
在我这篇文章中就不详细解释SD模型内部的unet,vae之类的概念了,有兴趣的同学可以自己去查一查,在这里我主要讲怎么用,科普类的东西如果有时间,我后面专门讲。这里我只简单介绍一些为什么我要单独指定vae,因为,在sdxl的官方微调文章里说明了sdxl原本的vae数值并不稳定,如果涉及到微调建议使用madebyollin/sdxl-vae-fp16-fix,为了打造自己的私有人物角色,所以我必须微调,进而在这里指定了vae
refiner模型加载:
def load_model(self):
if self.is_combine_base is True: # 如果要和base模型结合使用
# 处理base模型 SD_Base_Model是自己写的一个封装base模型的类
if self.base_model is None:
self.base_model = SD_Base_Model.instance(
n_steps=self.n_steps, # 模型在处理图片时迭代次数
high_noise_frac=self.high_noise_frac, # 结合n_steps使用表示,base模型处理百分比
is_cuda=self.is_cuda, # 是否在cuda上推理
H=self.H / 2, # 图片在base模型中的高
W=self.W / 2) # 图片在base模型中的宽
if self.base_model.base_model is None:
self.base_model.load_model()
# 处理refinerModel
if self.refiner_model is None:
self.refiner_model = DiffusionPipeline.from_pretrained(
self.model_id,
torch_dtype=torch.float16,
variant="fp16",
use_safetensors=True,
text_encoder_2=self.base_model.base_model.text_encoder_2, # 提示词编码器使用base模型的编码器
vae=self.base_model.base_model.vae) # vae使用base模型的vae
else: # 如果单独使用refiner,通常情况是"图片生成图片"的场景
self.base_model = None
if self.refiner_model is None:
self.refiner_model = StableDiffusionXLImg2ImgPipeline.from_pretrained(
self.model_id,
torch_dtype=torch.float16,
variant="fp16",
use_safetensors=True)
if self.is_cuda is True:
self.refiner_model.to("cuda")
else:
self.refiner_model.enable_model_cpu_offload()
注意:单独使用refiner的话,建议使用StableDiffusionXLImg2ImgPipeline类进行加载,通常实在以图生图的场景下使用
另外再单独说一个参数:high_noise_frac,这个参数的意思是,在base与refiner结合使用的情况下,如果总的图像处理迭代次数为50,high_noise_frac为0.8的话,那么在base中将进行50*0.8=40次迭代,在refiner中进行剩下的10次迭代
base模型单独使用时生成图片方法定义:
# 使用基础模型生成图片 返回PIL图片
def get_image_by_single_prompt(self,query: str, image_count: int = 1,
negative_prompt: str = None):
# seed = 1337
# generator = torch.Generator("cuda").manual_seed(seed)
# self.base_model 就是DiffusionPipeline.from_pretrained()这个方法返回的对象,
# 参见上面base模型加载片段
images = self.base_model(query,
num_inference_steps=self.n_steps,
guidance_scale=self.guidance_scale, # 数值越高越prompt越相符合
num_images_per_prompt=image_count, # 针对每一条prompt生成多少张图片
negative_prompt=negative_prompt, # 负面提示词,告诉模型在生成图像时,不要生成什么
height=self.H,
width=self.W).images
return images
这个方法返回的是一个PIL图片集合,可以直接使用PIL的save方法将图片保存到本地,这个方法适用于base模型单独使用的时候
from PIL import Image...for i in range(len(images)):
images[i].save(f"D:\\pics\\image_{i}.jpg", "JPEG")
refiner模型单独使用时生成图片方法定义:
def get_image_to_image_single_prompt(self,
query: str,
image_url: str = None,
image_count: int = 1,
negative_prompt: str = None):
target_size: tuple[int, int] = (self.H, self.W)
init_image = load_image(image_url).convert("RGB")
# self.refiner_model就是StableDiffusionXLImg2ImgPipeline.from_pretrained()这个方法返回的对象,
# 参见上面refiner模型加载片段
images = self.refiner_model(prompt=query,
image=init_image,
num_inference_steps=self.n_steps,
guidance_scale=self.guidance_scale,
negative_prompt=negative_prompt,
num_images_per_prompt=image_count,
target_size