如何用一句话凭空生成一幅画
source link: https://mp.weixin.qq.com/s?__biz=MjM5ODkzMzMwMQ%3D%3D&%3Bmid=2650429918&%3Bidx=2&%3Bsn=c8d851627dd0a7e313e7d74817f2d7e7
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
如何用一句话凭空生成一幅画
The following article is from Social Listening与文本挖掘 Author Scottish Fold Cats
这两天被OpenAI的DALL.E 2刷屏了,它的文本到图像生成程序,因为根据该模型生成的图像的颗粒度非常细致、逼真,完全达到了商业可用的级别。
下面看看一些由它生成的示例,效果十分惊艳!
一位宇航员骑着一匹白马:
戴着贝雷帽和黑色高领毛衣的柴犬:
一碗看起来像怪物的汤,它用毛线织成的:
泰迪熊作为疯狂的科学家在混合闪光的化学品,蒸汽朋克风格:
看了上面栩栩如生的“画作”,是不是也想动手来一幅?网上类似的开源代码很多,但是都太复杂,安装环境也比较麻烦。鉴于此,我们来点简单的,几十行代码来操作。
这个操作很简单:
我们要做的是使用CLIP对输入的提示文本进行语义表示提取,生成一个embedding,然后持续迭代地一个512x512像素的网格化图像,然后使该网格化图像无限逼近匹配提示文本的embedding。
CLIP 是openAI 在去年年初发布的一个多模态模型,能够从自然语言标注数据中学到有价值的视觉概念,并且和GPT-2/3一样拥有zero-shot的能力。它旨在将相似的图像和文本对嵌入到相似的向量空间中(例如,一个苹果的照片将与文本 "苹果 "相似)。通过在原始像素数据和它的嵌入上使用梯度下降,我们可以使它与任何指定的文本嵌入相匹配。
简而言之,笔者接下来要做的是:
对一些文本提示(也就是我们想要生成的图像的文字描述)进行编码,作为我们的目标向量
创建一个随机初始化的图像,然后将该图像的编码向量与我们的目标进行比较
计算误差,并使用torch的optimizer进行反向传播计算,减少提示文本嵌入和图像嵌入之间的相似度误差
好了,让我进行实战coding环节!
首先安装clip官方库:
pip install git+https://github.com/openai/CLIP
加载必要的库:
from tqdm.notebook import trange
import torch
from torchvision import transforms
import clip
参数设定:
device='cuda' # 使用显卡
cutn = 16 # 图像增强的数量
shape = (256, 256) # 图像的大小,即图像的高度和宽度
lr = 0.03 # 学习率
steps = 600 # 模型运行的步数,即我们将改进多少次图像
clip_model = "Chinese_ViT-B_32" # 中文版模型
prompt = "奇幻森林" # 提示语
值得注意的是上面的cutn参数,它表示我们要向clip模型展示多少个输入图像的增强部分,最好大于2,它将创造一些看起来像噪音但对嵌入相似性有好处的东西。
模型部分:
image=torch.rand((1, 3, shape[0], shape[1]), device=device, requires_grad=True)
opt=torch.optim.Adam((image,),lr)
f=transforms.Compose([lambda x:torch.clamp((x+1)/2,min=0,max=1),transforms.RandomAffine(degrees=60, translate=(0.1, 0.1)),transforms.RandomGrayscale(p=0.2),
transforms.Lambda(lambda x: x + torch.randn_like(x) * 0.01),transforms.Resize(224)])
m=clip.load(clip_model, jit=False)[0].eval().requires_grad_(False).to(device)
embedding=m.encode_text(clip.tokenize(prompt).to(device))
def total_variation_loss(img):
yv = torch.pow(img[:,:,1:,:]-img[:,:,:-1,:], 2).sum()
xv = torch.pow(img[:,:,:,1:]-img[:,:,:,:-1], 2).sum()
return (yv+xv)/(1*3*shape[0]*shape[1])
def spherical_distance_loss(x, y):
return (torch.nn.functional.normalize(x, dim=-1) - torch.nn.functional.normalize(y, dim=-1)).norm(dim=-1).div(2).arcsin().pow(2).mul(2).mean()
前两行定义了我们的image图像和我们的优化器optimizer.。
下一行创建了将增强我们的图像的东西,使用Torchvision进行转换。
接下来我们下载/加载CLIP模型,并创建提升文本的嵌入。
最后两个方法用来计算损失:第一个方法告诉我们我们的图像有多 "丝滑(Smooth)",下一个函数告诉我们图像表示离文本嵌入到底有多远。平滑度(Smooth)是必要的,因为否则图像可能看起来太像噪音了,它将更有可能试图搞乱CLIP的生成结果,以创建一个嘈杂但高分的图像,而不是一个“丝滑”的自然高分辨率图像。
开始实际的图像生成:
for i in trange(steps):
opt.zero_grad()
clip_in = m.encode_image(torch.cat([f(image.add(1).div(2)) for _ in range(cutn)]))
loss = spherical_distance_loss(clip_in, embedding.unsqueeze(0)) + (image - image.clamp(-1, 1)).pow(2).mean()/2 + total_variation_loss(image)
loss.backward()
opt.step()
第1行创建了一个循环,重复后面的每一段代码,无论我们提供多少步(使用 trange 而不是 range,控制台中会显示一个进度条,以便我们实时看到模型生成进度~)。
第2行 为优化器在训练循环中积累和应用梯度的方式提供了更多的灵活度。
第3行将图像表示到与clip生成提示文本表示投射到同一向量空间中,因此我们可以在下一行中对它们进行比较......在这里对它们进行相似度比较。另外,在下一行,我们添加了一个 "范围损失(Range Loss)"(确保图像处于我们需要的数值中)和总变化损失,这与我们的 "平滑度 "相反,任何高的数值都会受到这个惩罚。
最后两行使用 Adam optimizer来改变图像的方式,使其损失下降。
图像可视化呈现:
transforms.ToPILImage()(image.squeeze(0).clamp(-1,1)/2+.5)
看起来,这个效果太拉胯!
我们需要换个高级点的操作~
基于Pyramid的Text2image 2.0版本
Image Pyramid(图像金字塔)实际上将图片表示为一系列不同频率的图像,这些图像保留了不同的细节信息,因为这个特性,又叫多分辨率或者多尺度。它同时利用低层特征高分辨率和高层特征的高语义信息,通过融合这些不同层的特征达到较好的预测效果。
import torch
import torchvision.transforms as transforms
from tqdm.notebook import trange
import clip
import sys
from pytorch_lamb import Lamb, log_lamb_rs
device = 'cuda'
cutn = 96
prompt = "奇幻森林"
class Pyramid:
def __init__(self, sizes, device='cpu', interpolation=transforms.InterpolationMode.BILINEAR):
self.layers = []
self.weights = []
self.activation = torch.nn.Tanh()
self.sizes = sizes
for i in sizes:
self.layers.append(torch.randn((1,3,i,i), device=device, requires_grad=True))
self.weights.append(torch.rand((1,), device=device, requires_grad=True))
self.fix = transforms.Resize(sizes[-1], interpolation)
def flatten(self):
return self.activation(torch.cat([self.fix(self.layers[i]).unsqueeze(0)*self.weights[i] for i in range(len(self.layers))], dim=0).sum(0)/len(self.layers))
def to_pil(self):
return transforms.ToPILImage()(self.flatten().squeeze(0).clamp(-1,1)/2+0.5)
class RandomRandomCrop(torch.nn.Module):
def __init__(self, min_size, max_size):
super().__init__()
self.min_size = min_size
self.max_size = max_size
def forward(self, x):
import random
return transforms.RandomCrop(random.randint(self.min_size, self.max_size))(x)
model = clip.load("Chinese_ViT-B_32", jit=False)[0].float().to(device)
image = Pyramid((1,2,4,8,12,16,20,24,28,32,36,40,48,64,68,72,96,100,128,132,136,140,144,148,152,157,192,224,256,320,384,448,512,784,1024), device=device)
augment = transforms.Compose([RandomRandomCrop(int(image.sizes[-1]*0.3),image.sizes[-1]),transforms.RandomRotation(30),transforms.Resize(model.visual.input_resolution),lambda x: x+torch.randn_like(x)*0.01])
optimizer = Lamb(image.layers, lr=0.05, weight_decay= 0.01, betas=(.9, .999), adam= 'lamb')
encoding = model.encode_text(clip.tokenize(prompt).to(device))
def total_variation_loss(img):
return (torch.pow(img[:,:,1:,:]-img[:,:,:-1,:], 2).sum()+torch.pow(img[:,:,:,1:]-img[:,:,:,:-1], 2).sum())/(1*3*image.sizes[-1]*image.sizes[-1])
def spherical_distance_loss(x, y):
return (torch.nn.functional.normalize(x, dim=-1)-torch.nn.functional.normalize(y, dim=-1)).norm(dim=-1).div(2).arcsin().pow(2).mul(2).mean()
def run(encoding, steps):
for i in trange(steps):
optimizer.zero_grad()
render = image.flatten()
img = model.encode_image(torch.cat([augment(render) for cut in range(cutn)], dim=0))
loss = spherical_distance_loss(img, encoding) + total_variation_loss(render) + (render-render.clamp(-1,1)).pow(2).mean()
loss.backward(retain_graph=True)
torch.nn.utils.clip_grad_norm_(image.layers, 1.0)
optimizer.step()
if i%10==0:
print(loss.detach().item())
image.to_pil().save("output.png")
image2 = image.to_pil()
image2.save("output.png")
return image2
painting = run(model.encode_text(clip.tokenize( prompt).to(device))*0.5+encoding*0.5, 250)
最终生成的“奇幻森林”:
是不是效果好太多了!
下面是其他提示语生成的图像,大家来欣赏欣赏~
“深海中绽放的玫瑰花”:
“绢画风格-沙漠中的绿洲”:
“油画风格-沙漠中的绿洲”:
“吉卜力画风-沙漠中的绿洲”:
写在最后
这些生成的效果当然不是最好的,使用其他更复杂的算法,比如VGGAN+dual CLIP、基于Latent Diffusion的图像合成等,加上海量的图文数据对进行训练,效果会十分逼真,比如这些:
“国画风格-深山藏古寺”
“深夜,一大群人在抢菜”
“梵高画风 - 一大群人在晚上疯狂抢菜”
“盛夏午后,一缕微光透过茂密的原始森林,照射到地上”
“月光如流水一般,静静地泻在这一片叶子和花上。薄薄的青雾浮起在荷塘里。叶子和花仿佛在牛乳中洗过一样;又像笼着轻纱的梦...”
“国画风格 --- 巴黎圣母院”
进技术交流群请添加AINLP小助手微信(id: ainlper)
请备注具体方向+所用到的相关技术点
关于AINLP
AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括文本摘要、智能问答、聊天机器人、机器翻译、自动生成、知识图谱、预训练模型、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLPer(id:ainlper),备注工作/研究方向+加群目的。
阅读至此了,分享、点赞、在看三选一吧🙏
Recommend
-
123
揭秘|大爷凭空消失!隐身衣问世?真相原来如此“隐身衣”真的被造出来了?近日,网上热传一段“隐身衣”视频,一位老大爷拿着一块有隐形功能的布,当布挡住身体时,人就真的消失了,只能看到背后的花花草草。有传言说这是浙大老师发明的“国产量子隐身衣”,
-
102
不要凭空污人清白啊
-
90
上周跟我司产品经理马杰克在吃饭的时候闲聊瞎扯,我忽然问他要是让你用一句话证明你是干产品的,你会说什么? 他说产品经理这个职位比较复杂,不好讲,要懂用户、知需求,了解场景巴拉巴拉跟我说一大堆…… 马杰克:卒。 为了听听大家的想法,于是我就在人人都是产品...
-
42
-
45
“现阶段哪个平台不刷单,可能最先死的就是它。”
-
46
来源:SME科技故事原标题:看着这些图像在眼前凭空消失,你的大脑为何还相信眼见为实?有句话说得好,眼见为实,我们常以为自己亲眼所见的世界就是真实的。然而,网络上总是流传着很多图片能够瞒天过海般地骗过你的双眼。即便你清楚了其中的奥秘,却也压根没法法说...
-
42
如何用一句话恰如其分地描述这幅图,我先来抛砖引玉~~~~~~~~ - 1、听说隔壁家的猪老大不听话被宰了,我一定要做个听话的猪宝宝。2、猪老二翻墙后看到了自己的未来。3、请问哪里有卖减肥药?
-
29
文/涵的硅谷成长笔记因为我在硅谷工作嘛,就总有朋友啊、学弟学妹们问我:美国各大科技企业都咋招人?今天这篇文章,我就来集中回答一下。让你,最快只需要答一个问题,就能轻松加入他们的Team。来来来,咱走起来!哦,先说好,可不许生气哦~(对了,刚才是有一个...
-
14
作者| 哈希派-LucyCheng “区块#74638的交易输出实在是太奇怪了!92233720368.54277039 BTC?是UINT64_MAX吗?”——2010年8月15日,比...
-
9
V2EX › 程序员 怎么凭空生成随机数? hxd · 15 分钟前 · 145 次点击 不依凭任何...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK