AI摄影的未来?
介绍
当听到"人工智能","机器学习"或"机器人"这两个词时,大多数人倾向于想象一个行走的,会说话的机器人,它看起来像科幻电影中的某些东西,并立即假定它在遥远的未来。
人工智能已经在我们身边多年了,目前就在你的智能手机中(Siri!),汽车的GPS系统,甚至在你读完这篇文章之后,它还会为你推荐类似的文章。然而,在过去几年中,没有哪个领域比计算机视觉更受其影响。
随着技术的发展,以超高分辨率视觉上吸引人的图像变得越来越普遍。人们不再需要使用Photoshop和CorelDRAW等工具来增强和改变他们的图像。AI已经被用于图像增强和操作的每个方面,以便产生最佳图像。然而,最新出现的想法实际上是使用AI来综合生成图像。
您可能已经看过的几乎所有图像都是拍摄的照片,或者由生气勃勃的人手动创建。可能有数百种手动生成图像的工具,但它们确实需要人才来操作这个过程。但是,想象一下从头开始绘制的计算机程序,无论你告诉它什么。微软的Drawing Bot可能是第一个也是唯一个能够实现这一目标的技术。想象一下在不久的将来,你可以在智能手机上下载应用程序并给它一些指示,例如"我想要一张我站在艾菲尔铁塔旁边的图像"。"(确保你正确地说出来)。
生成对抗网络(GANs)
" GANs are the most interesting idea in the last 10 years in ML "—— Yann LeCun
生成这种合成图像的基础在于生成对抗网络。自从Ian Goodfellow及其同行在2014年发现并发布他们的研究论文以来,GANs一直是深度学习中最迷人和最广泛使用的方面之一。这项技术的无穷无尽的应用,也就是所谓的对抗性训练的核心,不仅包括计算机视觉领域,还包括数据分析,机器人技术和预测模型。
那么关于GANs的重大事件是什么?
生成性对抗网络属于一组生成模型。这意味着他们的工作是在完全自动化的过程中创建或"生成"新数据。
生成的图像
顾名思义,GAN实际上由两个相互竞争的个体神经网络组成(以对抗的方式)。一个称为生成器的神经网络生成由随机噪声产生的新数据实例,而另一个神经网络(判别器)则评估它们的真实性。换句话说,判别器决定它所检查的每个数据实例是否属于实际训练数据集。
一个简单的例子
假设你的任务是制作一幅与一位非常著名的艺术家制作的画作相同的画作。不幸的是,你不知道这位艺术家是谁,也没看过他的任何一幅画作。你的任务是伪造一幅画并将其作为原件之一出现在拍卖会上。所以,你决定尝试一下。你需要的只是一些颜料和画布,对吧?然而,拍卖商不希望人们出售一些随机的东西,只想要真品,所以他们已经成功聘请了一名侦探,首先验证拍卖会上展示的所有物品。幸运的是,侦探有他自己的著名艺术家原画的样本,当你呈现你的随机画时,他立刻就知道这与原作不同。
他拒绝了,你决定再试一次。但是这一次,你有一些有用的提示,当他评估你的画布时,侦探会忽略这幅画应该是什么样子。
现在当你再次尝试运气时,这幅画应该会好一点。但侦探仍然不相信并再次拒绝你。因此,你一次又一次地尝试,每次使用某种形式的反馈来改变绘画,它会变得越来越好。(我们假设侦探可以随着你无休止地回来验证。)最后,一千次左右的尝试,你终于能够想出一些接近完美复制品的东西。当侦探看着他的样画时,他不确定你给他的是否是其中之一,甚至是与著名艺术家具有相同风格和笔触的其他东西。
GANs工作的过程是什么?
将相同的思维过程应用于神经网络的组合,GANs的训练包括以下步骤:
基本GAN的框架生成器最初接收一些随机噪声并将其传递给判别器。由于判别器已经访问了真实图像的数据集,因此它将它们与从生成器接收的图像进行比较并评估其真实性。由于初始图像只是随机噪声,因此将被评估为假的。生成器通过改变其参数来不断尝试运气,以便生成开始变得更好的图像。随着训练的进行,两个网络都变得更加智能:生成假图像的生成器和检测它们的判别器。最终,生成器设法创建与真实图像的数据集中的图像无法区分的图像。判别器不够聪明,无法判断给定的图像是真实的还是假的。此时,训练结束,生成的图像是我们的最终结果。
我们自己的GAN生成的汽车徽标的图像
这是PyTorch中实现的基本生成网络。import argparse
import os
import numpy as np
import math
import torchvision.transforms as transforms
from torchvision.utils import save_image
from torch.utils.data import DataLoader
from torchvision import datasets
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
import torch
os.makedirs("images", exist_ok=True)
parser = argparse.ArgumentParser()
parser.add_argument("--n_epochs", type=int, default=200, help="number of epochs of training")
parser.add_argument("--batch_size", type=int, default=64, help="size of the batches")
parser.add_argument("--lr", type=float, default=0.0002, help="adam: learning rate")
parser.add_argument("--b1", type=float, default=0.5, help="adam: decay of first order momentum of gradient")
parser.add_argument("--b2", type=float, default=0.999, help="adam: decay of first order momentum of gradient")
parser.add_argument("--n_cpu", type=int, default=8, help="number of cpu threads to use during batch generation")
parser.add_argument("--latent_dim", type=int, default=100, help="dimensionality of the latent space")
parser.add_argument("--img_size", type=int, default=28, help="size of each image dimension")
parser.add_argument("--channels", type=int, default=1, help="number of image channels")
parser.add_argument("--sample_interval", type=int, default=400, help="interval betwen image samples")
opt = parser.parse_args()
print(opt)
img_shape = (opt.channels, opt.img_size, opt.img_size)
cuda = True if torch.cuda.is_available() else False
class Generator(nn.Module):
def __init__(self):
super(Generator, self).__init__()
def block(in_feat, out_feat, normalize=True):
layers = [nn.Linear(in_feat, out_feat)]
if normalize:
layers.append(nn.BatchNorm1d(out_feat, 0.8))
layers.append(nn.LeakyReLU(0.2, inplace=True))
return layers
self.model = nn.Sequential(
*block(opt.latent_dim, 128, normalize=False),
*block(128, 256),
*block(256, 512),
*block(512, 1024),
nn.Linear(1024, int(np.prod(img_shape))),
nn.Tanh()
)
def forward(self, z):
img = self.model(z)
img = img.view(img.size(0), *img_shape)
return img
class Discriminator(nn.Module):
def __init__(self):
super(Discriminator, self).__init__()
self.model = nn.Sequential(
nn.Linear(int(np.prod(img_shape)), 512),
nn.LeakyReLU(0.2, inplace=True),
nn.Linear(512, 256),
nn.LeakyReLU(0.2, inplace=True),
nn.Linear(256, 1),
nn.Sigmoid()
)
def forward(self, img):
img_flat = img.view(img.size(0), -1)
validity = self.model(img_flat)
return validity
# Loss function
adversarial_loss = torch.nn.BCELoss()
# Initialize generator and discriminator
generator = Generator()
discriminator = Discriminator()
if cuda:
generator.cuda()
discriminator.cuda()
adversarial_loss.cuda()
# Configure data loader
os.makedirs("../../data/mnist", exist_ok=True)
dataloader = torch.utils.data.DataLoader(
datasets.MNIST("../../data/mnist", train=True, download=True,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])),
batch_size=opt.batch_size, shuffle=True)
# Optimizers
optimizer_G = torch.optim.Adam(generator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
optimizer_D = torch.optim.Adam(discriminator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
Tensor = torch.cuda.FloatTensor if cuda else torch.FloatTensor
# ----------
# Training
# ----------
for epoch in range(opt.n_epochs):
for i, (imgs, _) in enumerate(dataloader):
# Adversarial ground truths
valid = Variable(Tensor(imgs.size(0), 1).fill_(1.0), requires_grad=False)
fake = Variable(Tensor(imgs.size(0), 1).fill_(0.0), requires_grad=False)
# Configure input
real_imgs = Variable(imgs.type(Tensor))
# -----------------
# Train Generator
# -----------------
optimizer_G.zero_grad()
# Sample noise as generator input
z = Variable(Tensor(np.random.normal(0, 1, (imgs.shape[0], opt.latent_dim))))
# Generate a batch of images
gen_imgs = generator(z)
# Loss measures generator"s ability to fool the discriminator
g_loss = adversarial_loss(discriminator(gen_imgs), valid)
g_loss.backward()
optimizer_G.step()
# ---------------------
# Train Discriminator
# ---------------------
optimizer_D.zero_grad()
# Measure discriminator"s ability to classify real from generated samples
real_loss = adversarial_loss(discriminator(real_imgs), valid)
fake_loss = adversarial_loss(discriminator(gen_imgs.detach()), fake)
d_loss = (real_loss + fake_loss) / 2
d_loss.backward()
optimizer_D.step()
print ("[Epoch %d/%d] [Batch %d/%d] [D loss: %f] [G loss: %f]" % (epoch, opt.n_epochs, i, len(dataloader),
d_loss.item(), g_loss.item()))
batches_done = epoch * len(dataloader) + i
if batches_done % opt.sample_interval == 0:
save_image(gen_imgs.data[:25], "images/%d.png" % batches_done, nrow=5, normalize=True)
优点和缺点
与所有技术一样,GANs也有自己独特的优点和缺点。让我们总结其中的一些,而不是深入细节。
以下是使用GANs的一些潜在优势GANs并不总是需要有标签的例子来训练。比其他生成模型更快地生成样本,因为它们不需要按顺序生成样本中的不同条目。它们更容易训练生成模型,生成模型依赖于对数配分函数梯度的蒙特卡罗近似。因为蒙特卡罗方法在高维空间中不能很好地工作,所以这种生成模型对于像ImageNet训练这样的现实任务来说效果不佳。
同样,还有以下缺点:他们没有引入任何确定性偏差。像变分自编码器这样的某些生成方法会引入确定性偏差,因为它们优化了对数似然的下限,而不是似然本身。与GANs相比,这似乎导致VAEs学习生成模糊样本。GANs特别难以训练。这些网络试图优化的函数是一个基本上没有封闭形式的损失函数(不像对数损失或平方误差等标准损失函数)。因此,优化这种损失函数非常困难,并且需要对网络结构和训练协议进行大量的反复试验。特别是对于图像生成,没有适当的措施来评估准确性。由于合成图像看起来可以通过计算机本身,因此实际结果是一个非常主观的主题,并且取决于人类观察者。相反,我们有像Inception Score和Frechet Inception Distance这样的函数来度量它们的性能。
GANs的应用
我们可以使用GANs做的所有惊人的东西列表。在其所有潜在用途中,GANs已经在计算机视觉领域中找到了大量应用。
文本到图像转换
这个概念有几种实现,例如TAC-GAN - 文本条件辅助分类器生成对抗网络。它们用于从文本描述中合成图像。
左:TAC-GAN的结构,右:通过向网络提供一行文本产生的结果
风格转换
GANs在风格转换等概念中很受欢迎。它包括使用称为CGAN(条件生成对抗网络)的特殊类型的GAN进行图像到图像的转换。绘画和概念设计从未如此简单。然而,虽然GAN可以从它的草图中完成像这个钱包这样简单的绘图,但绘制更复杂的东西,如完美的人脸,目前还不是GANs的强项。事实上,对于某些物体来说,它的结果非常可怕。
CGAN pix2pix的结果
图像修复和图像生成
在修复和生成中可以看到生成网络的两个令人兴奋的应用。第一种包括图像内的填充或噪声,这可能被视为图像修复。例如,给定具有残次或间隙的图像,GAN能够以"拼接"方式对其进行校正。另一方面,修复涉及使用网络自己的学习来想象一个图像在其当前边界之外可能会是什么样子。
图像修复(左)和生成(右)的结果
面部合成
由于生成网络,面部合成是可能的,这涉及以不同角度生成单个面部图像。这就是为什么面部识别不需要数百个脸部样本,但可以使用一个样本。NVIDIA最近使用他们的GAN 2.0使用Celeba Hq数据集生成高清分辨率的人造脸,这是高分辨率合成图像生成的第一个例子。
由先进的GAN生成的想象中的名人的面孔
Ganimtion
改变面部表情。GANimation是一项使用PyTorch 的研究成果,它将自己定义为"从一张图像中提取具有解剖学意义的面部动画"。
GANimation的实现
从绘画到摄影
使用GANs使图像更逼真的另一个例子是简单地将绘画变成照片。这是使用称为CycleGAN的特殊类型的GAN完成的,它使用两个生成器和两个判别器。我们称一个生成器G,它将图像从X域转换为Y域。另一个生成器被称为F,它将图像从Y转换到X。每个生成器都有一个相应的判别器,该判别器试图将其合成的图像与真实图像区分开来。
CycleGAN的成果
我们该如何做?
机器学习和GANs肯定会在不久的将来对成像和摄影产生巨大影响。目前,该技术能够从文本输入生成简单图像。然而,在可预见的未来,它不仅能够创建高分辨率的精准图像,还能够创建整个视频。想象一下,通过简单地将脚本输入GAN生成整部电影。不仅如此,每个人都可以使用简单的交互式应用程序来创建自己的电影(甚至可以设定为自己作为主演!)。
令人印象深刻的技术也意味着可能用于恶意的目的。完美假图像还需要一种识别和检测它们的方法。需要对这种图像的产生进行监管。目前,GANs已经被用于制作虚假视频或"深度赝品",这些假视频或"深度赝品"正以消极的方式被使用,比如制作名人的假色情视频,或者让人们在不知情的情况下发表言论。将音频和视频合成技术提供给普通大众的后果是可怕的。
人工图像生成是一种双刃技术,特别是在人们普遍知道它的时候。生成性对抗网络是一个非常有用和危险的工具。它将重塑技术世界的事实是肯定的,但它将如何这样做,我们只能思考。