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

推荐阅读列表:

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

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

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

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

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

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

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

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

上篇文章中介绍了如何微调扩散模型,有时候微调的效果仍然不能满足需求,比如图片编辑,3D模型输出等都需要对生成的内容进行控制,本文将初步探索一下如何控制扩散模型的输出。

我们将使用在LSUM bedrooms数据集上训练并在WikiArt数据集上微调的模型,首先加载模型来查看一下模型的生成效果:

!pip install -qq diffusers datasets accelerate wandb open-clip-torch

 

import numpy as npimport torchimport torch.nn.functional as Fimport torchvisionfrom datasets import load_datasetfrom diffusers import DDIMScheduler, DDPMPipelinefrom matplotlib import pyplot as pltfrom PIL import Imagefrom torchvision import transformsfrom tqdm.auto import tqdm

 

device = (    "mps"    if torch.backends.mps.is_available()    else "cuda"    if torch.cuda.is_available()    else "cpu")

 

# 载入一个预训练过的管线pipeline_name = "johnowhitaker/sd-class-wikiart-from-bedrooms"image_pipe = DDPMPipeline.from_pretrained(pipeline_name).to(device) # 使用DDIM调度器,仅用40步生成一些图片scheduler = DDIMScheduler.from_pretrained(pipeline_name)scheduler.set_timesteps(num_inference_steps=40) # 将随机噪声作为出发点x = torch.randn(8, 3, 256, 256).to(device) # 使用一个最简单的采样循环for i, t in tqdm(enumerate(scheduler.timesteps)):    model_input = scheduler.scale_model_input(x, t)    with torch.no_grad():        noise_pred = image_pipe.unet(model_input, t)["sample"]    x = scheduler.step(noise_pred, t, x).prev_sample # 查看生成结果,如图5-10所示grid = torchvision.utils.make_grid(x, nrow=4)plt.imshow(grid.permute(1, 2, 0).cpu().clip(-1, 1) * 0.5 + 0.5)

图片

       正如上图所示,模型可以生成一些图片,那么如何进行控制输出呢?下面我们以控制图片生成绿色风格为例介绍AIGC模型控制:

       思路是:定义一个均方误差损失函数,让生成的图片像素值尽量接近目标颜色;

 

def color_loss(images, target_color=(0.1, 0.9, 0.5)):    """给定一个RGB值,返回一个损失值,用于衡量图片的像素值与目标颜色相差多少; 这里的目标颜色是一种浅蓝绿色,对应的RGB值为(0.1, 0.9, 0.5)"""    target = (        torch.tensor(target_color).to(images.device) * 2 - 1    )  # 首先对target_color进行归一化,使它的取值区间为(-1, 1)     target = target[        None, :, None, None    ]  # 将所生成目标张量的形状改为(b, c, h, w),以适配输入图像images的 # 张量形状    error = torch.abs(        images - target    ).mean()  # 计算图片的像素值以及目标颜色的均方误差    return error

接下来,需要修改采样循环操作,具体操作步骤如下:

  1. 创建输入图像X,并设置requires_grad设置为True;

  2. 计算“去噪”后的图像X0;

  3. 将“去噪”后的图像X0传递给损失函数;

  4. 计算损失函数对输入图像X的梯度;

  5. 在使用调度器之前,先用计算出来的梯度修改输入图像X,使输入图像X朝着减少损失值的方向改进

实现上述步骤有两种方法:

方法一:从UNet中获取噪声预测,并将输入图像X的requires_grad属性设置为True,这样可以充分利用内存(因为不需要通过扩散模型追踪梯度),但是这会导致梯度的精度降低;

方法二:先将输入图像X的requires_grad属性设置为True,然后传递给UNet并计算“去噪”后的图像X0;

下面分别看一下这两种方法的效果:

 

# 第一种方法 # guidance_loss_scale用于决定引导的强度有多大guidance_loss_scale = 40  # 可设定为5~100的任意数字 x = torch.randn(8, 3, 256, 256).to(device) for i, t in tqdm(enumerate(scheduler.timesteps)):     # 准备模型输入    model_input = scheduler.scale_model_input(x, t)     # 预测噪声    with torch.no_grad():        noise_pred = image_pipe.unet(model_input, t)["sample"]     # 设置x.requires_grad为True    x = x.detach().requires_grad_()     # 得到“去噪”后的图像    x0 = scheduler.step(noise_pred, t, x).pred_original_sample     # 计算损失值    loss = color_loss(x0) * guidance_loss_scale    if i % 10 == 0:        print(i, "loss:", loss.item())     # 获取梯度    cond_grad = -torch.autograd.grad(loss, x)[0]     # 使用梯度更新x    x = x.detach() + cond_grad     # 使用调度器更新x    x = scheduler.step(noise_pred, t, x).prev_sample# 查看结果grid = torchvision.utils.make_grid(x, nrow=4)im = grid.permute(1, 2, 0).cpu().clip(-1, 1) * 0.5 + 0.5Image.fromarray(np.array(im * 255).astype(np.uint8))

 

# 输出0 loss: 29.3701839447021510 loss: 12.11665058135986320 loss: 11.64170455932617230 loss: 11.78276252746582

图片

 

# 第二种方法:在模型预测前设置好x.requires_gradguidance_loss_scale = 40x = torch.randn(4, 3, 256, 256).to(device) for i, t in tqdm(enumerate(scheduler.timesteps)):     # 首先设置好requires_grad    x = x.detach().requires_grad_()    model_input = scheduler.scale_model_input(x, t)     # 预测    noise_pred = image_pipe.unet(model_input, t)["sample"]     # 得到“去噪”后的图像    x0 = scheduler.step(noise_pred, t, x).pred_original_sample     # 计算损失值    loss = color_loss(x0) * guidance_loss_scale    if i % 10 == 0:        print(i, "loss:", loss.item())     # 获取梯度    cond_grad = -torch.autograd.grad(loss, x)[0]     # 根据梯度修改x    x = x.detach() + cond_grad     # 使用调度器更新x    x = scheduler.step(noise_pred, t, x).prev_sample  grid = torchvision.utils.make_grid(x, nrow=4)im = grid.permute(1, 2, 0).cpu().clip(-1, 1) * 0.5 + 0.5Image.fromarray(np.array(im * 255).astype(np.uint8))

 

# 输出0 loss: 27.6226882934570310 loss: 16.84250640869140620 loss: 15.5464210510253930 loss: 15.545379638671875

图片

       从上图看出,第二种方法效果略差,但是第二种方法的输出更接近训练模型所使用的数据,也可以通过修改guidance_loss_scale参数来增强颜色的迁移效果。

CLIP控制图像生成

       虽然上述方式可以引导和控制图像生成某种颜色,但现在LLM更主流的方式是通过Prompt(仅仅打几行字描述需求)来得到自己想要的图像,那么CLIP是一个不错的选择。CLIP是有OpenAI开发的图文匹配大模型,由于这个过程是可微分的,所以可以将其作为损失函数来引导扩散模型。

使用CLIP控制图像生成的基本流程如下

  1. 使用CLIP模型对Prompt表示为512embedding向量;

  2. 在扩散模型的生成过程中需要多次执行如下步骤:

    1)生成多个“去噪”图像;

    2)对生成的每个“去噪”图像用CLIP模型进行embedding,并对Prompt embedding和图像的embedding进行对比;

    3)计算Prompt和“去噪”后图像的梯度,使用这个梯度先更新输入图像X,然后再使用调度器更新X;

    加载CLIP模型

 

import open_clip clip_model, _, preprocess = open_clip.create_model_and_transforms(    "ViT-B-32", pretrained="openai")clip_model.to(device) # 图像变换:用于修改图像尺寸和增广数据,同时归一化数据,以使数据能够适配CLIP模型 tfms = torchvision.transforms.Compose(    [        torchvision.transforms.RandomResizedCrop(224),# 随机裁剪        torchvision.transforms.RandomAffine(5),       # 随机扭曲图片        torchvision.transforms.RandomHorizontalFlip(),# 随机左右镜像, # 你也可以使用其他增广方法        torchvision.transforms.Normalize(            mean=(0.48145466, 0.4578275, 0.40821073),            std=(0.26862954, 0.26130258, 0.27577711),        ),    ]) # 定义一个损失函数,用于获取图片的特征,然后与提示文字的特征进行对比def clip_loss(image, text_features):    image_features = clip_model.encode_image(        tfms(image)    )  # 注意施加上面定义好的变换    input_normed = torch.nn.functional.normalize(image_features.       unsqueeze(1), dim=2)    embed_normed = torch.nn.functional.normalize(text_features.       unsqueeze(0), dim=2)    dists = (        input_normed.sub(embed_normed).norm(dim=2).div(2).           arcsin().pow(2).mul(2)    )  # 使用Squared Great Circle Distance计算距离    return dists.mean()

      下面是引导模型生成图像的过程,步骤与上述类似,只需要把color_loss()替换成CLIP的损失函数

 

prompt = "Red Rose (still life), red flower painting" # 读者可以探索一下这些超参数的影响guidance_scale = 8n_cuts = 4 # 这里使用稍微多一些的步数scheduler.set_timesteps(50) # 使用CLIP从提示文字中提取特征text = open_clip.tokenize([prompt]).to(device)with torch.no_grad(), torch.cuda.amp.autocast():    text_features = clip_model.encode_text(text) x = torch.randn(4, 3, 256, 256).to(    device)  for i, t in tqdm(enumerate(scheduler.timesteps)):     model_input = scheduler.scale_model_input(x, t)     # 预测噪声    with torch.no_grad():        noise_pred = image_pipe.unet(model_input, t)["sample"]     cond_grad = 0     for cut in range(n_cuts):         # 设置输入图像的requires_grad属性为True        x = x.detach().requires_grad_()         # 获得“去噪”后的图像        x0 = scheduler.step(noise_pred, t, x).pred_original_sample         # 计算损失值        loss = clip_loss(x0, text_features) * guidance_scale         # 获取梯度并使用n_cuts进行平均        cond_grad -= torch.autograd.grad(loss, x)[0] / n_cuts     if i % 25 == 0:        print("Step:", i, ", Guidance loss:", loss.item())     # 根据这个梯度更新x    alpha_bar = scheduler.alphas_cumprod[i]    x = (        x.detach() + cond_grad * alpha_bar.sqrt()    )  # 注意这里的缩放因子     # 使用调度器更新x    x = scheduler.step(noise_pred, t, x).prev_sample  grid = torchvision.utils.make_grid(x.detach(), nrow=4)im = grid.permute(1, 2, 0).cpu().clip(-1, 1) * 0.5 + 0.5Image.fromarray(np.array(im * 255).astype(np.uint8))

 

# 输出Step: 0 , Guidance loss: 7.418107986450195Step: 25 , Guidance loss: 7.085518836975098

图片

       上述生成的图像虽然不够完美,但可以调整一些超参数,比如梯度缩放因子alpha_bar.sqrt(),虽然理论上存在所谓的正确的缩放这些梯度方法,但在实践中仍需要实验来检验,下面介绍一些常用的方案:

 

plt.plot([1 for a in scheduler.alphas_cumprod], label="no scaling")plt.plot([a for a in scheduler.alphas_cumprod], label="alpha_bar")plt.plot([a.sqrt() for a in scheduler.alphas_cumprod],     label="alpha_bar.sqrt()")plt.plot(    [(1 - a).sqrt() for a in scheduler.alphas_cumprod], label="(1-     alpha_bar).sqrt()")plt.legend()

图片

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

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

相关文章

01.智慧商城——项目介绍与初始化

智慧商城 - 授课大纲 接口文档:https://apifox.com/apidoc/shared-12ab6b18-adc2-444c-ad11-0e60f5693f66/doc-2221080 演示地址:http://cba.itlike.com/public/mweb/#/ 01. 项目功能演示 1.明确功能模块 启动准备好的代码,演示移动端面…

UITableView的style是UITableViewStyleGrouped

一般情况下,UITableViewStylePlain和UITableViewStyleGrouped是UITableView常用到的style, 之前都是用到的时候,遇到问题直接用度娘,差不多就够用了,今天在修复UI提出的间隙问题,来回改,总觉得…

MathType2024最新word公式编辑器

使用word进行论文编写时,常需要使用公式编辑器,但有些word中并没有公式编辑器,这时应该怎么办呢?本文将围绕word里没有公式编辑器怎么办,word中的公式编辑器怎么用的内容进行介绍。 一、word里没有公式编辑器怎么办 …

【ES6标准入门】JavaScript中的模块Module语法的使用细节:export命令和imprt命令详细使用,超级详细!!!

😁 作者简介:一名大四的学生,致力学习前端开发技术 ⭐️个人主页:夜宵饽饽的主页 ❔ 系列专栏:JavaScript进阶指南 👐学习格言:成功不是终点,失败也并非末日,最重要的是继…

SpringBoot实现IP地址归属地查询

SpringBoot实现IP地址归属地查询 功能特性 标准化的数据格式 每个 IP 数据段的 region 信息都固定了格式: 国家|区域|省份|城市|ISP,只有中国的数据绝大部分精确到了城市,其他国家部分数据只能定位到国家,后前的选项全部是 0。…

OpenCV图像纹理

LBP描述 LBP(Local Binary Pattern,局部二值模式)是一种用来描述图像局部纹理特征的算子;它具有旋转不变性和灰度不变性等显著的优点。它是首先由T. Ojala, M.Pietikinen, 和D. Harwood 在1994年提出,用于纹理特征提取…

第四代智能井盖传感器:万宾科技助力城市安全

在繁华喧嚣的城市里人来人往,井盖作为基础设施的一个组成部分在路面上分布范围广。然而这些看似普通的井盖却存在着位移、水浸的风险,可能给我们的生活带来诸多不便,更会威胁到我们的人身安全。如何有效监测和管理井盖的状态,成为…

【云原生-Kurbernetes篇】K8s的存储卷/数据卷+PV与PVC

这是一个目录标题 一、Kurbernetes中的存储卷1.1 为什么需要存储卷?1.2 存储卷概述1.2.1 简介1.2.2 volume字段 1.3 常用的存储卷类型1.3.1 emptyDir(临时存储卷)1.3.2 hostPath(节点存储卷)1.3.3 nfs1.3.4 cephfs 二、…

RocketMQ的适用场景有哪些?

程序员的公众号:源1024,获取更多资料,无加密无套路! 最近整理了一波电子书籍资料,包含《Effective Java中文版 第2版》《深入JAVA虚拟机》,《重构改善既有代码设计》,《MySQL高性能-第3版》&…

UE基础篇六:音频

导语: 通过实现一个小游戏,来学会音频,最后效果 入门 下载启动项目并解压缩。通过导航到项目文件夹并打开SkywardMuffin.uproject来打开项目。 按播放开始游戏。游戏的目标是在不坠落的情况下触摸尽可能多的云。单击鼠标左键跳到第一朵云。 游戏很放松,不是吗?为了强调…

php接口api数据签名及验签

api数据签名作用&#xff1a;通过使用签名可以验证数据在传输过程中是否被篡改或修改。接收方可以使用相同的签名算法和密钥对接收到的数据进行验证&#xff0c;如果验证失败则表明数据被篡改过 1、数据发送方进行接口签名并传输签名字段 <?php // 请求URL $url "h…

Python采集某网站数据实现简单可视化效果, webpack Js逆向解密

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 环境使用 : Python 3.8 Pycharm nodejs 模块使用: requests --> pip install requests execjs --> pip install PyExecJS csv 采集网站的数据如何去实现: 一. 数据来源分析 <√> 明确需求: 明确采集的网…

如何使用ArcGIS Pro制作粉饰效果

在地图上&#xff0c;如果某个部分比较重要&#xff0c;直接的制图不能将其凸显出来&#xff0c;如果想要突出显示重要部分&#xff0c;可以通过粉饰效果来实现&#xff0c;这里为大家介绍一下方法&#xff0c;希望能对你有所帮助。 数据来源 本教程所使用的数据是从水经微图…

vue2项目修改编译巨慢

前言&#xff1a;我们的一个vue项目在给新同事后他说编译贼慢&#xff0c;一个小修改项5分钟才能自动编译成功&#xff0c;我把项目放到新电脑上也巨慢&#xff0c;升级了nodejs好使了一些&#xff0c;但还是慢&#xff0c;最后引入webpack后巨快&#xff0c; 在项目的package…

如何在 Linux 上部署 RabbitMQ

如何在 Linux 上部署 RabbitMQ 文章目录 如何在 Linux 上部署 RabbitMQ安装 Erlang从预构建的二进制包安装从源代码编译 Erlang RabbitMQ 的安装使用 RabbitMQ Assistant 连接 RabbitMQ Assistant 是一款优秀的RabbitMQ 可视化管理工具&#xff0c;提供丰富的管理功能。下载地址…

ClickHouse的表引擎

1 表引擎的使用 表引擎是ClickHouse的一大特色。可以说&#xff0c; 表引擎决定了如何存储表的数据。包括&#xff1a; 数据的存储方式和位置&#xff0c;写到哪里以及从哪里读取数据。支持哪些查询以及如何支持。并发数据访问。索引的使用&#xff08;如果存在&#xff09;。是…

使用IDEA 将Eclipse java工程转为maven格式

使用IDEA 将Eclipse java工程转为maven格式 ①使用idea打开项目&#xff0c;在项目根目录下右键选择 Add Framework Support 选择 maven &#xff0c;引入maven ②找到项目中的.classpath文件或者lib目录 根据.classpath文件或者lib目录中列举的jar包名&#xff0c;将其依次手…

大模型在数据分析场景下的能力评测|进阶篇

做数据分析&#xff0c;什么大模型比较合适&#xff1f; 如何调优大模型&#xff0c;来更好地做数据计算和洞察分析&#xff1f; 如何降低整体成本&#xff0c;同时保障分析体验&#xff1f;10月25日&#xff0c;我们发布了数据分析场景下的大模型能力评测框架&#xff08;点击…

【Web】在前端中,HTML<meta>标签

<meta>实例 <head><meta name"description" content"免费在线教程"><meta name"keywords" content"HTML,CSS,XML,JAVASCRIPT"><meta name"author" content"runoob"><meta char…

静态共享代理和静态独享有哪些区别?怎么选择?

在软件开发中&#xff0c;静态共享代理&#xff08;Static Proxy&#xff09;和静态独享&#xff08;Monostatic&#xff09;是两种常见的软件设计模式。这两种模式在实现方式、使用场景以及优缺点上存在一定的差异&#xff0c;下面将详细介绍它们的区别以及如何进行选择。 一、…