扩散模型实战(十二):使用调度器DDIM反转来优化图像编辑

 推荐阅读列表:

 扩散模型实战(一):基本原理介绍

扩散模型实战(二):扩散模型的发展

扩散模型实战(三):扩散模型的应用

扩散模型实战(四):从零构建扩散模型

扩散模型实战(五):采样过程

扩散模型实战(六):Diffusers DDPM初探

扩散模型实战(七):Diffusers蝴蝶图像生成实战

扩散模型实战(八):微调扩散模型

扩散模型实战(九):使用CLIP模型引导和控制扩散模型

扩散模型实战(十):Stable Diffusion文本条件生成图像大模型

扩散模型实战(十一):剖析Stable Diffusion Pipeline各个组件

一、配置环境

# !pip install -q transformers diffusers accelerateimport torchimport requestsimport torch.nn as nnimport torch.nn.functional as Ffrom PIL import Imagefrom io import BytesIOfrom tqdm.auto import tqdmfrom matplotlib import pyplot as pltfrom torchvision import transforms as tfmsfrom diffusers import StableDiffusionPipeline, DDIMScheduler# 定义接下来将要用到的函数def load_image(url, size=None):    response = requests.get(url,timeout=0.2)    img = Image.open(BytesIO(response.content)).convert('RGB')    if size is not None:        img = img.resize(size)    return imgdevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")

二、加载预训练过的Stable Diffusion Pipeline

        加载预训练pipeline并配置DDIM调度器,而后进行一次采样,代码如下:

# 载入一个管线pipe = StableDiffusionPipeline.from_pretrained("runwayml/stable- diffusion-v1-5").to(device) # 配置DDIM调度器pipe.scheduler = DDIMScheduler.from_config(pipe.scheduler.config) # 从中采样一次,以保证代码运行正常prompt = 'Beautiful DSLR Photograph of a penguin on the beach,  golden hour'negative_prompt = 'blurry, ugly, stock photo'im = pipe(prompt, negative_prompt=negative_prompt).images[0]im.resize((256, 256)) # 调整至有利于查看的尺寸

三、DDIM采样

给定任意时刻t,加噪后的图像公式如下所示:

下面是绘制加噪alpha的随时间步的变化:

# 绘制'alpha','alpha'(即α)在DDPM论文中被称为'alpha bar'(即α)。# 为了能够清晰地表现出来,我们# 选择使用Diffusers中的alphas_cumprod函数来得到alphas)timesteps = pipe.scheduler.timesteps.cpu()alphas = pipe.scheduler.alphas_cumprod[timesteps]plt.plot(timesteps, alphas, label='alpha_t');plt.legend();

标准DDIM(https://arxiv.org/abs/2010.02502)采样的实现代码如下所示:

# 采样函数(标准的DDIM采样)@torch.no_grad()def sample(prompt, start_step=0, start_latents=None,           guidance_scale=3.5, num_inference_steps=30,           num_images_per_prompt=1, do_classifier_free_ guidance=True,           negative_prompt='', device=device):# 对文本提示语进行编码    text_embeddings = pipe._encode_prompt(            prompt, device, num_images_per_prompt,             do_classifier_free_guidance, negative_prompt    )# 配置推理的步数    pipe.scheduler.set_timesteps(num_inference_steps, device=device) # 如果没有起点,就创建一个随机的起点    if start_latents is None:       start_latents = torch.randn(1, 4, 64, 64, device=device)       start_latents *= pipe.scheduler.init_noise_sigma     latents = start_latents.clone()     for i in tqdm(range(start_step, num_inference_steps)):            t = pipe.scheduler.timesteps[i]# 如果正在进行CFG,则对隐层进行扩展    latent_model_input = torch.cat([latents] * 2)  if do_classifier_free_guidance else latents    latent_model_input = pipe.scheduler.scale_model_input(latent_       model_input, t)# 预测残留的噪声    noise_pred = pipe.unet(latent_model_input, t, encoder_hidden_       states=text_embeddings).sample# 进行引导    if do_classifier_free_guidance:            noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)            noise_pred = noise_pred_uncond + guidance_scale *                (noise_pred_text - noise_pred_uncond)     # 使用调度器更新步骤        # latents = pipe.scheduler.step(noise_pred, t, latents). # prev_sample # 现在不用调度器,而是自行实现        prev_t = max(1, t.item() - (1000//num_inference_steps)) # t-1        alpha_t = pipe.scheduler.alphas_cumprod[t.item()]        alpha_t_prev = pipe.scheduler.alphas_cumprod[prev_t]        predicted_x0 = (latents - (1-alpha_t).sqrt()*noise_pred) /            alpha_t.sqrt()        direction_pointing_to_xt = (1-alpha_t_prev).sqrt()*noise_           pred        latents = alpha_t_prev.sqrt()*predicted_x0 + direction_           pointing_to_xt# 后处理     images = pipe.decode_latents(latents)    images = pipe.numpy_to_pil(images)     return images# 生成一张图片,测试一下采样函数,效果如图7-4所示sample('Watercolor painting of a beach sunset', negative_prompt=    negative_prompt, num_inference_steps=50)[0].resize((256, 256))

四、DDIM反转

       反转的目标是”颠倒“采样的过程。我们最终想得到”带噪“的隐式表示。如果将其用作采样过程的起点,那么生成的图像将是原始图像。

       我们现在首先来加载一张图像,来看看DDIM反转如何做?有什么效果?

#图片来源:https://www.pexels.com/photo/a-beagle-on-green-grass- # field-8306128/(代码中使用对应的JPEG文件链接)input_image = load_image('https://images.pexels.com/photos/ 8306128/pexels-photo-8306128.jpeg', size=(512, 512))

       我们使用一个包含无分类器引导的文本Prompt来进行反转操作,代码如下:

input_image_prompt = "Photograph of a puppy on the grass"

       接下来,我们将这幅PIL图像转换为一系列隐式表示,这些隐式表示将被用作反转操作的起点。

# 使用VAE进行编码with torch.no_grad(): latent = pipe.vae.encode(tfms.functional.to_   tensor(input_image).unsqueeze(0).to(device)*2-1)l = 0.18215 * latent.latent_dist.sample()

       我们使用invert函数进行反转,可以看出invert与上面的sample函数非常类似,但是invert函数是朝相反的方向移动的:从t=0开始,想噪声更多的方向移动的,而不是在更新隐式层的过程中那样噪声越来越少。我们可以利用预测的噪声来撤回一步更新操作,并从t移动到t+1。

## 反转@torch.no_grad()def invert(start_latents, prompt, guidance_scale=3.5,           num_inference_steps=80,num_images_per_prompt=1,            do_classifier_free_guidance=True, negative_prompt='',            device=device): # 对提示文本进行编码    text_embeddings = pipe._encode_prompt(      prompt, device, num_images_per_prompt,      do_classifier_free_guidance, negative_prompt     )     # 已经指定好起点     latents = start_latents.clone()     # 用一个列表保存反转的隐层     intermediate_latents = []     # 配置推理的步数     pipe.scheduler.set_timesteps(num_inference_steps,device=device)      # 反转的时间步     timesteps = reversed(pipe.scheduler.timesteps)      for i in tqdm(range(1, num_inference_steps), total=num_         inference_steps-1): # 跳过最后一次迭代     if i >= num_inference_steps - 1: continue      t = timesteps[i] # 如果正在进行CFG,则对隐层进行扩展      latent_model_input = torch.cat([latents] * 2) if do_        classifier_free_guidance else latents     latent_model_input = pipe.scheduler.scale_model_        input(latent_model_input, t)     # 预测残留的噪声      noise_pred = pipe.unet(latent_model_input, t, encoder_        hidden_states=text_embeddings).sample     # 进行引导     if do_classifier_free_guidance:        noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)        noise_pred = noise_pred_uncond + guidance_scale *        (noise_pred_text - noise_pred_uncond)current_t = max(0, t.item() - (1000//num_inference_steps))#tnext_t = t # min(999, t.item() + (1000//num_inference_steps)) # t+1alpha_t = pipe.scheduler.alphas_cumprod[current_t]alpha_t_next = pipe.scheduler.alphas_cumprod[next_t] # 反转的更新步(重新排列更新步,利用xt-1(当前隐层)得到xt(新的隐层))latents = (latents - (1-alpha_t).sqrt()*noise_pred)*(alpha_t_next.   sqrt()/alpha_t.sqrt()) + (1-alpha_t_next).sqrt()*noise_pred# 保存intermediate_latents.append(latents)return torch.cat(intermediate_latents)

将invert函数应用于上述小狗的图片,得到图片的一系列隐式表示。

inverted_latents = invert(l, input_image_prompt,num_inference_steps=50)inverted_latents.shape
# 输出torch.Size([48, 4, 64, 64])

将得到的最终隐式表示作为起点噪声,尝试新的采样过程。

# 解码反转的最后一个隐层with torch.no_grad():  im = pipe.decode_latents(inverted_latents[-1].unsqueeze(0))pipe.numpy_to_pil(im)[0]

通过调用call方法将反转隐式表示输入给Pipeline。

pipe(input_image_prompt, latents=inverted_latents[-1][None],      num_inference_steps=50, guidance_scale=3.5).images[0]

      看到生成的图片是不是有点蒙了,这不是刚开始输入的图片呀?

      这是因为DDIM反转需要一个重要的假设-在时刻t预测的噪声与在时刻t+1预测的噪声相同,但这个假设在反转50步或100步是不成立的。

       我们既可以使用更多的时间步来得到更准确的反转,也可以采取”作弊“的方法,直接从相应反转过程50步中的第20步的隐式表示开始。

# 设置起点的原因start_step=20sample(input_image_prompt, start_latents=inverted_latents[-(start_step+1)][None], start_step=start_step, num_inference_steps=50)[0]

      经过这一折腾,生成的图片和原始图片很接近了,那为什么要这么做呢?

       因为我们现在想用一个新的文本Prompt来生成图片。我们想要得到一张除了与Prompt相关以外,其他内容都与原始图片大致相同的图片。例如,将小狗换成小猫,得到的结果如下所示:

# 使用新的文本提示语进行采样start_step=10new_prompt = input_image_prompt.replace('puppy', 'cat')sample(new_prompt, start_latents=inverted_latents[-(start_step+1)]       [None],start_step=start_step, num_inference_steps=50)[0]

       到此为止,读者可能有一些疑问,比如为什么不直接使用Img2Img?为什么要反转?为什么不直接对输入图像添加噪声,然后用新的Prompt直接”去噪“呢?

       其实是可以采用上述方法做的,但是生成的效果对添加的噪声量十分敏感,噪声量大时会生成十分夸张的图片,噪声量小时生成的图片几乎没有变化。

start_step = 10num_inference_steps=50pipe.scheduler.set_timesteps(num_inference_steps)noisy_l = pipe.scheduler.add_noise(l, torch.randn_like(l), pipe.   scheduler.timesteps[start_step])sample(new_prompt, start_latents=noisy_l, start_step=start_step,     num_inference_steps=num_inference_steps)[0]

五、DDIM反转整体方案

      将上述代码封装到一个简单函数中,并输入一张图片和两个文本Prompt,便可以得到一张通过反转修改后的图片。

def edit(input_image, input_image_prompt, edit_prompt, num_steps=100, start_step=30,guidance_scale=3.5):    with torch.no_grad(): latent = pipe.vae.encode(tfms.functional.      to_tensor(input_image).unsqueeze(0).to(device)*2-1)    l = 0.18215 * latent.latent_dist.sample()    inverted_latents = invert(l, input_image_prompt,num_inference_       steps=num_steps)    final_im = sample(edit_prompt, start_latents=inverted_latents[       -(start_step+1)][None],start_step=start_step, num_inference_       steps=num_steps,guidance_scale=guidance_scale)[0]    return final_imAnd in action: # 实际操作edit(input_image, 'A puppy on the grass', 'an old grey dog on  the grass', num_steps=50,start_step=10) 

修改一下Prompt和参数来看看效果如何不同

edit(input_image, 'A puppy on the grass', 'A blue dog on the lawn',  num_steps=50,start_step=12, guidance_scale=6) 

得到如下图片

更多迭代能够得到更好的表现,我们可以测试一下

# 更多步的反转测试edit(input_image, 'A puppy on the grass', 'A puppy on the grass',     num_steps=350, start_step=1)

我们换一张图片进行测试一下看看效果

原始图片如下所示:

# 图片来源:https://www.pexels.com/photo/girl-taking-photo-1493111/ # (代码中使用对应的JPEG文件链接)face = load_image('https://images.pexels.com/photos/1493111/pexels- photo-1493111.jpeg', size=(512, 512))
edit(face, 'A photograph of a face', 'A photograph of a face with sunglasses', num_steps=250, start_step=30, guidance_scale=3.5)

生成的效果如下所示:

PS:读者可以通过测试不同的Prompt来观察生成的效果,强烈建议了解一下Null-text Inversion:一个基于DDIM来优化空文本(无条件Prompt)的反转过程,有更准确的反转过程与更好的编辑效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/173621.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机毕业设计|基于SpringBoot+MyBatis框架的电脑商城的设计与实现(用户上传头像+用户收货管理)

计算机毕业设计|基于SpringBootMyBatis框架的电脑商城的设计与实现(用户上传头像) 该项目分析着重于设计和实现基于SpringBootMyBatis框架的电脑商城。首先,通过深入分析项目所需数据,包括用户、商品、商品类别、收藏、订单、购物…

NX二次开发UF_CURVE_ask_parameterization 函数介绍

文章作者:里海 来源网站:https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_parameterization Defined in: uf_curve.h int UF_CURVE_ask_parameterization(tag_t object, double param_range [ 2 ] , int * periodicity ) overview 概述 Retu…

国产Ai大模型和chtgpt3.5的比较

下面是针对国产大模型,腾讯混元,百度文心一言,阿里通义千问和chatgpt的比较,最基础的对一篇文章的单词书进行统计,只有文心一言和chatgpt回答差不多,阿里和腾讯差太多了

深度学习中的注意力机制:原理、应用与实践

深度学习中的注意力机制:原理、应用与实践 摘要: 本文将深入探讨深度学习中的注意力机制,包括其原理、应用领域和实践方法。我们将通过详细的解析和代码示例,帮助读者更好地理解和应用注意力机制,从而提升深度学习模…

Vue快速实践总结 · 下篇

文章目录 组件间通信方式父 --> 子通信props插槽 子 --> 父通信(自定义事件)任意组件通信全局事件总线消息订阅与发布 Vuex工作原理运行环境简单使用GettersmapState与mapGettersmapActions与mapMutations模块化 命名空间 VueRouter路由的作用与分…

OpenWrt Lan口上网设置

LAN口上网设置 连接上openwrt,我用的 倍控N5105,eth0,看到Openwrt的IP是10.0.0.1 在 网络 -> 网口配置 -> 设置好 WAN 口和 LAN 口 初次使用经常重置 openwrt 所以我设置的是 静态IP模式 - 网络 -> 防火墙 -> 常规设置 ->…

7.私信列表 + 发送列表

目录 1.私信列表 1.1 数据访问层 1.2 业务层 1.3 表现层 1.4 私信详情 2.发送列表 2.1 数据访问层 2.2 业务层 2.3 表现层 2.4 设置已读状态 1.私信列表 私信列表:查询当前用户的会话列表,每个会话只显示一条最新的私信、支持分页列表私信详情…

苹果cms搭建教程附带免费模板

准备工作: 一台服务器域名源码安装好NGINX+PHP7.0+MYSQL5.5 安装php7.0的扩展,fileinfo和 sg11,不安装网站会搭建失败。 两个扩展都全部安装好了之后 点击-服务-重载配置 这样我们的网站环境就配置完成啦 下载苹果cms 苹果cms程序github链接:选择mac10!下载即可 http…

部署Jenkins

一、介绍 Jenkins 、Jenkins概念 Jenkins是一个功能强大的应用程序,允许持续集成和持续交付项目,无论用的是什么平台。这是一个免费的源代码,可以处理任何类型的构建或持续集成。集成Jenkins可以用于一些测试和部署技术。Jenkins是一种软件允…

自建CA实战之 《0x02 Nginx 配置 https双向认证》

自建CA实战之 《0x02 Nginx 配置 https双向认证》 上一章节我们已经实现了Nginx上配置https单向认证,主要场景为客户端验证服务端的身份,但是服务端不验证客户端的身份。 本章节我们将实现Nginx上配置https双向认证,主要场景为客户端验证服…

基于单片机仿指针显示的电子时钟设计

**单片机设计介绍, 基于51单片机超声波测距汽车避障系统 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机仿指针显示的电子时钟是一种利用单片机控制器和LED或LCD显示屏幕来模拟传统时钟指针的显示效果的设计…

PubMedBERT:生物医学自然语言处理领域的特定预训练模型

今年大语言模型的快速发展导致像BERT这样的模型都可以称作“小”模型了。Kaggle LLM比赛LLM Science Exam 的第四名就只用了deberta,这可以说是一个非常好的成绩了。所以说在特定的领域或者需求中,大语言模型并不一定就是最优的解决方案,“小…

01:编译lua及C调用

我们今天在windows平台编译lua,生成 lua动态库,lua.exe,luac.exe 我把这个目录上传到giee,使用下面命令获取它: git clone gitgitee.com:jameschenbo/lua_c_application.git 或者直接访问:访问网页 目录结构如下: build.cmd 是…

【离散数学】——期末刷题题库(一阶逻辑基本概念)

🎃个人专栏: 🐬 算法设计与分析:算法设计与分析_IT闫的博客-CSDN博客 🐳Java基础:Java基础_IT闫的博客-CSDN博客 🐋c语言:c语言_IT闫的博客-CSDN博客 🐟MySQL&#xff1a…

开发测试利器之Fiddler网络调试工具详细安装使用教程(包含汉化脚本)

一、Fiddler简介 Fiddler 是一款功能强大的网络调试工具,可以帮助开发人员和测试人员分析和调试网络流量。它通过截取计算机和服务器之间的HTTP/HTTPS请求,并提供详细的请求和响应信息来帮助我们理解和诊断网络通信。 Fiddler 可以用于各种用途&#x…

k8s环境排查nginx转发nacos请求失败问题

一、问题背景 k8s部署两个服务,一个nginx,一个nacos, 服务信息如下(nacos有两个端口): 服务 serviceNameservice类型porttargetPort nodePortnginxmonitor-cp-nginxNodePort808031082nacosmonitor-cp-nacosClusterIP88488848-98489848- ng的default.conf配置文件…

获得文件MD5——校验完整性 window 和 Linux下操作

目录 引出window下获得文件MD5Linux下获得文件MD5单个文件整个目录下所有文件检查MD5 总结 引出 1.Windows 10 自带了一个命令行程序 certutil可以 获取文件的 MD5 值; 2.Linux下md5sum命令获得文件MD5值; window下获得文件MD5 Windows 10 自带了一个命…

性价比高的护眼灯,好用护眼的护眼台灯推荐

7月27日,2023第七届家居品牌大会在北京启幕,现场发布了“2022—2023家居十大质量事件”,其中“护童、爱果乐齐曝抽检不合格”事件上榜。 在“读写台灯”品类上,本次抽查检验11个省(区、市)84家企业生产的11…

【开源】基于JAVA的天然气工程运维系统

项目编号: S 022 ,文末获取源码。 \color{red}{项目编号:S022,文末获取源码。} 项目编号:S022,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统角色分类2.2 核心功能2.2.1 流程…

程序的机器级表示

程序的机器级表示 有关CSAPP第三章一些我关注到的重点的记录 操作指令 .c->.exe的流程 1.选项 -E : 预编译过程,处理宏定义和include,并作语法检查 gcc -E hello.c -o hello.i #将hello.c预处理输出为hello.i文件2.选项 -S : 编译过程,生成通用…