01_ddim_inversion_CN

news2024/10/7 18:30:48

DDIM反转

设置

# !pip install -q transformers diffusers accelerate
import torch
import requests
import torch.nn as nn
import torch.nn.functional as F
from PIL import Image
from io import BytesIO
from tqdm.auto import tqdm
from matplotlib import pyplot as plt
from torchvision import transforms as tfms
from diffusers import StableDiffusionPipeline, DDIMScheduler

# Useful function for later
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 img
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

加载一个已训练的pipeline

# Load a pipeline
pipe = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5").to(device)
# Set up a DDIM scheduler:
pipe.scheduler = DDIMScheduler.from_config(pipe.scheduler.config)

```python
# Sample an image to make sure it is all working
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)) # resize for convenient viewing

在这里插入图片描述

DDIM取样过程

在给定时间 t t t, 带噪图像 x t x_t xt 是原始图像( x 0 x_0 x0)与一些噪声 ( ϵ \epsilon ϵ)的叠加。这是在DDIM论文中 x t x_t xt的定义式,我们把它引用到此节里:

x t = α t x 0 + 1 − α t ϵ x_t = \sqrt{\alpha_t}x_0 + \sqrt{1-\alpha_t}\epsilon xt=αt x0+1αt ϵ

ϵ \epsilon ϵ 是一些归一方差的高斯噪声
α t \alpha_t αt (‘alpha’)在DDPM论文中也被叫做 α ˉ \bar{\alpha} αˉ (‘alpha_bar’),被用来定义噪声调度器(scheduler)。在扩散模型中,alpha调度器是被计算出来并排序存储在scheduler.alphas_cumprod中。这令人费解,我理解!我们把这些值画出来,然后在下文中我们会使用DDIM的标注方式。

# Plot 'alpha' (alpha_bar in DDPM language, alphas_cumprod in diffusers for clarity)
timesteps = pipe.scheduler.timesteps.cpu()
alphas = pipe.scheduler.alphas_cumprod[timesteps]
plt.plot(timesteps, alphas, label='alpha_t');
plt.legend();

最初(timestep 0 ,图中左侧)是从一个无噪的干净图像开始, α t = 1 \alpha_t = 1 αt=1。当我们到达更高的timesteps,我们得到一个几乎全是噪声的图像, α t \alpha_t αt也几乎下降到0。

在采样过程,我们从timestep1000的纯噪声开始,慢慢地向timestep0前进。为了计算采样轨迹中的下一时刻( x t − 1 x_{t-1} xt1因为我们是从后向前移动)的值,我们预测噪声( ϵ θ ( x t ) \epsilon_\theta(x_t) ϵθ(xt),这就是我们模型的输出),用它来预测出无噪的图片 x 0 x_0 x0。在这之后我们用这个预测结果朝着’ x t x_t xt的方向’方向移动一小步。最终,我们可以加一些带 σ t \sigma_t σt系数的额外噪声。这是论文中与上述操作相关的章节内容:

在这里插入图片描述

好,我们有了在可控量度噪声下,从 x t x_t xt 移动到 x t − 1 x_{t-1} xt1的公式。今天我们所说案例是不需要再额外添加噪声的 - 即完全确定的DDIM采样。我们来看看这些是如何用代码表达的。

# Sample function (regular 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):
  
    # Encode prompt
    text_embeddings = pipe._encode_prompt(
            prompt, device, num_images_per_prompt, do_classifier_free_guidance, negative_prompt
    )

    # Set num inference steps
    pipe.scheduler.set_timesteps(num_inference_steps, device=device)

    # Create a random starting point if we don't have one already
    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]

        # expand the latents if we are doing classifier free guidance
        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)

        # predict the noise residual
        noise_pred = pipe.unet(latent_model_input, t, encoder_hidden_states=text_embeddings).sample

        # perform guidance
        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)


        # Normally we'd rely on the scheduler to handle the update step:
        # latents = pipe.scheduler.step(noise_pred, t, latents).prev_sample

        # Instead, let's do it ourselves:
        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

    # Post-processing
    images = pipe.decode_latents(latents)
    images = pipe.numpy_to_pil(images)

    return images
# Test our sampling function by generating an image
sample('Watercolor painting of a beach sunset', negative_prompt=negative_prompt, num_inference_steps=50)[0].resize((256, 256))

在这里插入图片描述

看看你是否能把这些代码和论文中的公式对应起来。注意 σ \sigma σ=0是因为我们只注意 无-额外-噪声 的场景,所以我们略去了公式中的那部分。

反转

反转的目标就是’颠倒’取样的过程。我们想最终得到一个带噪的隐式(latent),如果把它作为我们正常取样过程的起始点,结果将生成一副原图像。

这里我们先加载一个原始图像,当然你也可以生成一副图像来代替。

# https://www.pexels.com/photo/a-beagle-on-green-grass-field-8306128/
input_image = load_image('https://images.pexels.com/photos/8306128/pexels-photo-8306128.jpeg', size=(512, 512))
input_image

在这里插入图片描述

我们可以用包含随意分类指引(classifier-free-guidance)的prompt来做反转操作,输入一个图片的描述:

input_image_prompt = "Photograph of a puppy on the grass"

接下来我们来把这个PIL图像变成一些列隐式,它们会被用来当作反转的起点:

# encode with 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()

好了,到有趣的部分了。这个函数看起来和上面的取样函数很像,但我们在timesteps上是在向相反的方向移动,从t=0开始,向越来越多的噪声前进。代替更新隐式时噪声会越来越少,我们估计所预测出的噪声,用它来撤回一步更新操作,把它们从t移动到t+1。

## Inversion
@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):
  
    # Encode prompt
    text_embeddings = pipe._encode_prompt(
            prompt, device, num_images_per_prompt, do_classifier_free_guidance, negative_prompt
    )

    # latents are now the specified start latents
    latents = start_latents.clone()

    # We'll keep a list of the inverted latents as the process goes on
    intermediate_latents = []

    # Set num inference steps
    pipe.scheduler.set_timesteps(num_inference_steps, device=device)

    # Reversed timesteps <<<<<<<<<<<<<<<<<<<<
    timesteps = reversed(pipe.scheduler.timesteps)

    for i in tqdm(range(1, num_inference_steps), total=num_inference_steps-1):

        # We'll skip the final iteration
        if i >= num_inference_steps - 1: continue

        t = timesteps[i]

        # expand the latents if we are doing classifier free guidance
        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)

        # predict the noise residual
        noise_pred = pipe.unet(latent_model_input, t, encoder_hidden_states=text_embeddings).sample

        # perform guidance
        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))#t
        next_t = t # min(999, t.item() + (1000//num_inference_steps)) # t+1
        alpha_t = pipe.scheduler.alphas_cumprod[current_t]
        alpha_t_next = pipe.scheduler.alphas_cumprod[next_t]

        # Inverted update step (re-arranging the update step to get x(t) (new latents) as a function of x(t-1) (current latents)
        latents = (latents - (1-alpha_t).sqrt()*noise_pred)*(alpha_t_next.sqrt()/alpha_t.sqrt()) + (1-alpha_t_next).sqrt()*noise_pred


        # Store
        intermediate_latents.append(latents)
            
    return torch.cat(intermediate_latents)

把它在小狗图片的隐式表达上运行,我们可以在反转的中间过程得到一系列的隐式:

inverted_latents = invert(l, input_image_prompt,num_inference_steps=50)
inverted_latents.shape
  0%|          | 0/49 [00:00<?, ?it/s]





torch.Size([48, 4, 64, 64])

我们可以来看一下最终的隐式 - 希望这可以作为我们尝试新的取样过程的起点噪声:

# Decode the final inverted latents:
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]

  0%|          | 0/50 [00:00<?, ?it/s]

在这里插入图片描述

但这里我们遇见了第一个问题:这 并不是我们一开始的那张图片!这是因为DDIM的反转依赖一个重要的假设,在t时刻预测的噪声与t+1时刻会是相同的 - 这在我们只反转50或100步时是不陈立的。我们可以寄希望于更多的timesteps开得到一个更准确的反转,但我们也可以’作弊’一下,就是说直接从做对应反转过程中的第20/50步的隐式开始:

# The reason we want to be able to specify start step
start_step=20
sample(input_image_prompt, start_latents=inverted_latents[-(start_step+1)][None], 
       start_step=start_step, num_inference_steps=50)[0]

  0%|          | 0/30 [00:00<?, ?it/s]

在这里插入图片描述

距离我们的输入图像已经很接近了!我们为什么要这么做?嗯,这是因为如果我们现在若要用一个新的prompt来生成图像,我们会得到一个匹配于源图像,除了,与新prompt相关的内容。例如,把’小狗’替换为’猫’,我们能看到一只猫在几乎一样草地背景上:

# Sampling with a new prompt
start_step=10
new_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]
  0%|          | 0/40 [00:00<?, ?it/s]

在这里插入图片描述

为什么不直接用 img2img?

为什么要做反转,不是多此一举吗?为什么不直接对输入图片加入噪声,然后用新的promt直接来去噪呢?我们可以这么做,但这会带来一个到处都被改变得夸张得多的照片(如果我们加入了很多噪声),或哪也没怎么变的图像(如果加了太少的噪声)。来自己试试:

start_step = 10
num_inference_steps=50
pipe.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]
  0%|          | 0/40 [00:00<?, ?it/s]

在这里插入图片描述

注意背景和草坪有着非常大的变化。

把它们都组装起来

来把我们目前所写的代码都组装在一个简单的函数里,输入一张图像和两个prompts,就会得到一个通过反转得到的修改后的图片:

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_im

And in action:
实际操作起来:

edit(input_image, 'A puppy on the grass', 'an old grey dog on the grass', num_steps=50, start_step=10)
  0%|          | 0/49 [00:00<?, ?it/s]



  0%|          | 0/40 [00:00<?, ?it/s]

在这里插入图片描述

edit(input_image, 'A puppy on the grass', 'A blue dog on the lawn', num_steps=50, start_step=12, guidance_scale=6)
  0%|          | 0/49 [00:00<?, ?it/s]



  0%|          | 0/38 [00:00<?, ?it/s]

在这里插入图片描述

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

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

相关文章

离散Hopfield神经网络分类——高校科研能力评价

大家好&#xff0c;我是带我去滑雪&#xff01; 高校科研能力评价的重要性在于它对高等教育和科研体系的有效运作、发展和提高质量具有深远的影响。良好的科研能力评价可以帮助高校识别其在不同领域的强项和薄弱点&#xff0c;从而制定战略&#xff0c;改进教学和科研&#xff…

C语言求解:有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位(约瑟夫问题)

完整代码&#xff1a; /* 有n个人围成一圈&#xff0c;顺序排号。从第一个人开始报数&#xff08;从1到3报数&#xff09;&#xff0c;凡报到3的人 退出圈子&#xff0c;问最后留下的是原来第几号的那位*/ #include<stdio.h>//约瑟夫问题 //递推关系f(n)(f(n-1)2)\mod n…

白嫖阿里云服务器,速看!数量不多

白嫖阿里云服务器攻略来了&#xff0c;在阿里云免费试用中心可以申请免费服务器&#xff0c;但是阿里云百科不建议选择免费的&#xff0c;只有3个月使用时长&#xff0c;选择99元服务器不是更香&#xff0c;2核2G配置3M固定带宽&#xff0c;一年99元&#xff0c;重点是新老用户…

使用screw一键生成数据库文档

今天分享一个好用的工具&#xff0c;screw&#xff0c;他是一款国产开源软件支持将数据库库表结构一键生成html、word、markdown文档&#xff0c;非常的好用&#xff0c;项目详情页&#xff1a;https://gitee.com/leshalv/screw 背景 我们项目的开发模式是甲方、乙方这种&…

k8s、数据存储

数据存储的概念 容器磁盘上的文件的生命周期是短暂的&#xff0c;这就使得在容器中运行重要应用时会出现一些问题。首先&#xff0c;当容器崩溃时&#xff0c;kubelet 会重启它&#xff0c;但是容器中的文件将丢失——容器以干净的状态&#xff08;镜像最初的状态&#xff09;…

ChatGPT风潮再起!最新国内产品一网打尽,畅游指南曝光!

一、国内类chatgpt产品 在人工智能领域&#xff0c;自然语言处理&#xff08;NLP&#xff09;是一个重要的方向&#xff0c;涉及到语音识别、文本生成、机器翻译、问答系统等多个应用场景。近年来&#xff0c;随着深度学习技术的发展&#xff0c;NLP也取得了突破性的进展&#…

PTE-DI 练习 + 模板

目录 重点&#xff1a;平时练习的模板&#xff0c;一定要滚瓜烂熟 25秒准备时间 要想拿还不错的分数&#xff0c;fluency 一定要足够地高 Preparation 1.Look at the title and other words inthe image 2.Look for simple information(highest/lowest) Speak 1.Speak …

断点续传-http中Header参数Range(分段请求基础)

文章目录 Range请求头信息介绍RangeIf-Range 响应头Content-RangeAccept-Ranges 需要用到几个http头 rangeif-rangecontent-rangeaccept-range 断点续传的优缺点 好处&#xff1a;防止大文件下载过程出现网络异常&#xff0c;而前功尽弃。缺点&#xff1a;要发起多次请求&…

Bean的循环依赖问题

2023.11.10 通俗来讲&#xff0c;循环依赖指的是一个实例或多个实例存在相互依赖的关系&#xff08;类之间循环嵌套引用&#xff09;。比如&#xff1a;丈夫类Husband&#xff0c;妻子类Wife。Husband中有Wife的引用。Wife中有Husband的引用。 正常调用这两对象不会出现问题&am…

【华为数通HCIP | 网络工程师】821-BGP 组播高频题与解析(1)

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

echarts 圆环图 高亮事件 切换 中心文字

createEcharts() {let chartDom this.$refs.echartsthis.Echarts echarts.init(chartDom)let option {title: {text: 128, //主标题文本subtext: 总数, //副标题文本left: center,top: 32%,textStyle: {fontFamily: Montserrat-MediumItalic,fontSize: 30,color: #fff,align…

百度搜索深度学习模型业务及优化实践

作者 | Xin 导读 百度搜索架构部模型架构组&#xff0c;致力于将最新的人工智能技术以更低的成本被百度数亿用户体验到。这个过程中会面临非常多的系统、工程层面的问题&#xff0c;甚至在深度学习模型领域&#xff0c;我们看到越来越多的工作并不拘泥于工程本身。 本文主要分享…

Jython可以运行在java中的python

什么是Jython Jython是一种Python语言在Java平台上的实现。它旨在让Python程序员能够在Java平台上编写Python程序&#xff0c;同时还可以利用Java类库中的所有功能。 与传统的Python相比&#xff0c;Jython具有以下几个特点&#xff1a; 可以直接访问Java类库&#xff1a;因为…

C++结构体定义 创建 赋值 结构体数组

结构体是什么&#xff1f; struct是自定义数据类型&#xff0c;是一些类型集合组成的一个类型。结构体的定义方式 #include<iostream> using namespace std;struct Student {string name;int age;int score; };创建结构体变量并赋值 方式一&#xff0c;先创建结构体变…

无线通信测量仪器-4945B/C 无线电通信综合测试仪

01 4945B/C 无线电通信综合测试仪 产品综述&#xff1a; 4945B/4945C无线电通信综合测试仪是多功能、便携式无线电综合测试类仪器&#xff0c;基于软件无线电架构&#xff0c;集成了跳频信号发生与分析、矢量信号发生与解调分析、模拟调制信号发生与解调分析、音频信号发生与…

SAP 20策略测试简介

20策略相信也有很多小伙伴使用过,与50最大的不同之处就在于20策略是不能做计划独立需求的。 我看一下系统中20 策略的配置图,可以看到独立需求这里的配置都是空的。 1、我们开始测试准备物料 成品物料AB4 原材料:100197 2、创建BOM—CS01 3、创建主配方—c201 ,离散制造…

Perl语言用多线程爬取商品信息并做可视化处理

首先&#xff0c;我们需要使用Perl的LWP::UserAgent模块来发送HTTP请求。然后&#xff0c;我们可以使用HTML::TreeBuilder模块来解析HTML文档。在这个例子中&#xff0c;我们将使用BeautifulSoup模块来解析HTML文档。 #!/usr/bin/perl use strict; use warnings; use LWP::User…

如何结合内网穿透实现公网远程访问Linux AMH服务器管理面板

文章目录 1. Linux 安装AMH 面板2. 本地访问AMH 面板3. Linux安装Cpolar4. 配置AMH面板公网地址5. 远程访问AMH面板6. 固定AMH面板公网地址 AMH 是一款基于 Linux 系统的服务器管理面板&#xff0c;它提供了一系列的功能&#xff0c;包括网站管理、FTP 管理、数据库管理、DNS 管…

机器学习---多分类SVM、支持向量机分类

1. 多分类SVM 1.1 基本思想 Grammer-singer多分类支持向量机的出发点是直接用超平面把样本空间划分成M个区域&#xff0c;其 中每个区域对应一个类别的输入。如下例&#xff0c;用从原点出发的M条射线把平面分成M个区域&#xff0c;下图画 出了M3的情形&#xff1a; 1.2 问题…

银行卡转账记录p图软件,建设邮政工商招商农业,易语言回执单生成开发!

花了好长时间设计出来了这么一个软件&#xff0c;当然各个功能我都做了防范处理界面还有生成的图片都有对应的水印提示&#xff0c;做不了啥坏事&#xff0c;这里就是分享下原理和代码还有运行逻辑&#xff0c;仅此而已&#xff0c;软件加了一个画板&#xff0c;画面上面的图片…