激情六月丁香婷婷|亚洲色图AV二区|丝袜AV日韩AV|久草视频在线分类|伊人九九精品视频|国产精品一级电影|久草视频在线99|在线看的av网址|伊人99精品无码|午夜无码视频在线

高校合作1:010-59833514 ?咨詢電話:400-810-1418 服務(wù)與監(jiān)督電話:400-810-1418轉(zhuǎn)接2

一篇文章教會(huì)你AI繪畫(huà)

發(fā)布時(shí)間:2023-12-07 20:27:15 瀏覽量:149次

一、前言

最近AI繪畫(huà)讓人工智能再次走進(jìn)大眾視野。在人工智能發(fā)展早起,一直認(rèn)為人工智能能實(shí)現(xiàn)的功能非常有限。通常都是些死板的東西,像是下棋、問(wèn)答之類的,不具有創(chuàng)造性。那時(shí)的人們應(yīng)該想不到現(xiàn)在的AI已經(jīng)能夠繪畫(huà)、譜曲、作詩(shī)了。這些曾被認(rèn)為是人類獨(dú)有的東西,如今也被AI涉獵了。今天我們要討論的就是現(xiàn)今大火的AI繪畫(huà),我們來(lái)看看AI是不是真的有了創(chuàng)造力,還是只是不停的搬運(yùn)。

可以實(shí)現(xiàn)AI繪畫(huà)的模型有很多種,這里我們討論Conditional GAN和Stable Diffusion兩種模型?,F(xiàn)在已經(jīng)有了對(duì)應(yīng)的商業(yè)版本,比如昆侖天宮的AI繪圖就是采用了Stable Diffusion分支模型。


二、GAN

這里我們討論Conditional GAN(Generative Adversarial Network)實(shí)現(xiàn)AI的原理。在講Conditional GAN之前,我們來(lái)看看GAN是怎么回事。


2.1 生成

生成網(wǎng)絡(luò)一直被認(rèn)為是賦予AI創(chuàng)造力的突破口,生成包括文本生成、圖像生成、音頻生成等。

GAN是一種比較成熟的生成網(wǎng)絡(luò),通常用來(lái)生成圖像。GAN有許多變種,包括DCGAN、CycleGAN等。


2.2 專家與贗品

GAN的中文名叫生成對(duì)抗網(wǎng)絡(luò),在提到GAN時(shí)經(jīng)常會(huì)用兩個(gè)對(duì)立的角色來(lái)舉例。一個(gè)是造假專家,專門(mén)負(fù)責(zé)制作贗品;另一個(gè)是鑒別專家,專門(mén)負(fù)責(zé)鑒定贗品。他們最開(kāi)始都不是專家,而是在對(duì)抗中學(xué)習(xí),最終造假專家能夠制造出人都難以識(shí)別出來(lái)的贗品。最終我們會(huì)拋棄鑒別專家,讓造假專家為我們服務(wù)。

上面提到的造假專家就是G網(wǎng)絡(luò),也就是Generator;而鑒別專家就是D網(wǎng)絡(luò),也就是Discriminator。它們?cè)诨ハ鄬?duì)抗中學(xué)習(xí),最終成為各自領(lǐng)域的專家,這就是GAN的思想。


2.3 Generator

下面我們以生成動(dòng)漫頭像的例子來(lái)討論GAN網(wǎng)絡(luò)的Generator和Discriminator。

首先討論Generator,它在GAN中充當(dāng)造假的作用,也是用它來(lái)生成圖像。Generator接收一個(gè)隨機(jī)變量,這個(gè)隨機(jī)變量滿足一種特定的簡(jiǎn)單分布,比如高斯分布。接收輸入的隨機(jī)變量后,網(wǎng)絡(luò)經(jīng)過(guò)運(yùn)算生成一個(gè)非常長(zhǎng)的向量,我們可以把這個(gè)向量reshape成w×h×3,也就是彩色圖像。


Generator的具體結(jié)構(gòu)可以是多種多樣的,通常是以卷積為基礎(chǔ)的網(wǎng)絡(luò)。比如在DCGAN中,Generator由5層反卷積組成,其網(wǎng)絡(luò)結(jié)構(gòu)如下圖:


輸入一個(gè)維度為100的向量,輸出一張64×64×3的圖像,其PyTorch實(shí)現(xiàn)如下:

class Generator(nn.Module):
    def __init__(self, ngpu):
        super(Generator, self).__init__()
        self.ngpu = ngpu
        self.main = nn.Sequential(
            # input is Z, going into a convolution
            nn.ConvTranspose2d( nz, ngf * 8, 4, 1, 0, bias=False),
            nn.BatchNorm2d(ngf * 8),
            nn.ReLU(True),
            # state size. (ngf*8) x 4 x 4
            nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ngf * 4),
            nn.ReLU(True),
            # state size. (ngf*4) x 8 x 8
            nn.ConvTranspose2d( ngf * 4, ngf * 2, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ngf * 2),
            nn.ReLU(True),
            # state size. (ngf*2) x 16 x 16
            nn.ConvTranspose2d( ngf * 2, ngf, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ngf),
            nn.ReLU(True),
            # state size. (ngf) x 32 x 32
            nn.ConvTranspose2d( ngf, nc, 4, 2, 1, bias=False),
            nn.Tanh()
            # state size. (nc) x 64 x 64
        )

    def forward(self, input):
        return self.main(input)


2.4 Discriminator

Discriminator是GAN中非常重要的一個(gè)角色,它是一個(gè)接受一個(gè)圖片輸入的網(wǎng)絡(luò),輸入的圖像會(huì)包含一部分真實(shí)圖像real(我們收集的動(dòng)漫圖像),還會(huì)包含一部分虛假圖像fake(Generator生成的圖像),然后輸出一個(gè)結(jié)果。這個(gè)結(jié)果可以是fake是真實(shí)圖像的概率,也可以是fake的類別(0表示假,1表示真)。對(duì)于Discriminator而言,它的目的就是調(diào)整網(wǎng)絡(luò)參數(shù),讓網(wǎng)絡(luò)知道fake圖像是假的。


關(guān)于Discriminator的結(jié)構(gòu),并沒(méi)有非常固定的約束,通常是一個(gè)卷積網(wǎng)絡(luò)。這里同樣參考DCGAN,這里實(shí)現(xiàn)PyTorch的一個(gè)實(shí)現(xiàn):

class Discriminator(nn.Module):
    def __init__(self, ngpu):
        super(Discriminator, self).__init__()
        self.ngpu = ngpu
        self.main = nn.Sequential(
            # input is (nc) x 64 x 64
            nn.Conv2d(nc, ndf, 4, 2, 1, bias=False),
            nn.LeakyReLU(0.2, inplace=True),
            # state size. (ndf) x 32 x 32
            nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 2),
            nn.LeakyReLU(0.2, inplace=True),
            # state size. (ndf*2) x 16 x 16
            nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 4),
            nn.LeakyReLU(0.2, inplace=True),
            # state size. (ndf*4) x 8 x 8
            nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 8),
            nn.LeakyReLU(0.2, inplace=True),
            # state size. (ndf*8) x 4 x 4
            nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False),
            nn.Sigmoid()
        )

    def forward(self, input):
        return self.main(input)

這里比較特別的就是LeakyReLU的使用。


2.5 GAN

有了Generator和Discriminator就可以組成GAN網(wǎng)絡(luò)了。

最開(kāi)始Generator和Discriminator是兩個(gè)懵懂小孩,Generator不知道如何生成,Discriminator也不知道如何辨別。GAN網(wǎng)絡(luò)的訓(xùn)練分為下面幾個(gè)步驟。

· 第一步:訓(xùn)練Discriminator網(wǎng)絡(luò),此時(shí)Generator提供的照片都是噪聲,先訓(xùn)練Discriminator可以讓 Discriminator知道如何區(qū)分真實(shí)圖像和噪聲

· 第二步:固定Discriminator,訓(xùn)練Generator,讓Generator生成的圖像能夠瞞過(guò)Discriminator

· 第三步:再循環(huán)訓(xùn)練Discriminator-Generator,直到Generator生成的圖像能夠滿足我們的需求

· 第四步:用Generator生成圖像

上述步驟可以看作下圖:


以上就是GAN網(wǎng)絡(luò)的訓(xùn)練過(guò)程。其實(shí)就是Generator和Discriminator交替訓(xùn)練的過(guò)程,其PyTorch實(shí)現(xiàn)如下:

# Create the generator
netG = Generator(ngpu).to(device)
if (device.type == 'cuda') and (ngpu > 1):
    netG = nn.DataParallel(netG, list(range(ngpu)))
netG.apply(weights_init)

# Create the Discriminator
netD = Discriminator(ngpu).to(device)

if (device.type == 'cuda') and (ngpu > 1):
    netD = nn.DataParallel(netD, list(range(ngpu)))
netD.apply(weights_init)

criterion = nn.BCELoss()

fixed_noise = torch.randn(64, nz, 1, 1, device=device)
real_label = 1.
fake_label = 0.
optimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))
optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))

# Training Loop

# Lists to keep track of progress
img_list = []
G_losses = []
D_losses = []
iters = 0

print("Starting Training Loop...")
# For each epoch
for epoch in range(num_epochs):
    # For each batch in the dataloader
    for i, data in enumerate(dataloader, 0):

        ############################
        # (1) Update D network: maximize log(D(x)) + log(1 - D(G(z)))
        ###########################
        ## Train with all-real batch
        netD.zero_grad()
        # Format batch
        real_cpu = data[0].to(device)
        b_size = real_cpu.size(0)
        label = torch.full((b_size,), real_label, dtype=torch.float, device=device)
        # Forward pass real batch through D
        output = netD(real_cpu).view(-1)
        # Calculate loss on all-real batch
        errD_real = criterion(output, label)
        # Calculate gradients for D in backward pass
        errD_real.backward()
        D_x = output.mean().item()

        ## Train with all-fake batch
        # Generate batch of latent vectors
        noise = torch.randn(b_size, nz, 1, 1, device=device)
        # Generate fake image batch with G
        fake = netG(noise)
        label.fill_(fake_label)
        # Classify all fake batch with D
        output = netD(fake.detach()).view(-1)
        # Calculate D's loss on the all-fake batch
        errD_fake = criterion(output, label)
        # Calculate the gradients for this batch, accumulated (summed) with previous gradients
        errD_fake.backward()
        D_G_z1 = output.mean().item()
        # Compute error of D as sum over the fake and the real batches
        errD = errD_real + errD_fake
        # Update D
        optimizerD.step()

        ############################
        # (2) Update G network: maximize log(D(G(z)))
        ###########################
        netG.zero_grad()
        label.fill_(real_label)  # fake labels are real for generator cost
        # Since we just updated D, perform another forward pass of all-fake batch through D
        output = netD(fake).view(-1)
        # Calculate G's loss based on this output
        errG = criterion(output, label)
        # Calculate gradients for G
        errG.backward()
        D_G_z2 = output.mean().item()
        # Update G
        optimizerG.step()

        # Output training stats
        if i % 50 == 0:
            print('[%d/%d][%d/%d]\tLoss_D: %.4f\tLoss_G: %.4f\tD(x): %.4f\tD(G(z)): %.4f / %.4f'
                  % (epoch, num_epochs, i, len(dataloader),
                     errD.item(), errG.item(), D_x, D_G_z1, D_G_z2))

        # Save Losses for plotting later
        G_losses.append(errG.item())
        D_losses.append(errD.item())

        # Check how the generator is doing by saving G's output on fixed_noise
        if (iters % 500 == 0) or ((epoch == num_epochs-1) and (i == len(dataloader)-1)):
            with torch.no_grad():
                fake = netG(fixed_noise).detach().cpu()
            img_list.append(vutils.make_grid(fake, padding=2, normalize=True))

        iters += 1

經(jīng)過(guò)一段時(shí)間的訓(xùn)練后,我們就可以生成一些動(dòng)漫圖像了。關(guān)于DCGAN的代碼實(shí)現(xiàn)可以參考


https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html#
sphx-glr-beginner-dcgan-faces-tutorial-py。

三、Conditional GAN

通過(guò)上面的GAN網(wǎng)絡(luò),我們可以生成動(dòng)漫圖像。但是這個(gè)生成是不可控的,我們只知道它生成的是動(dòng)漫圖像,至于圖像內(nèi)容我們無(wú)法得知。我們無(wú)法根據(jù)描述來(lái)生成圖像,這個(gè)是GAN網(wǎng)絡(luò)的局限,因此提出一種變形叫Conditional GAN,這種GAN網(wǎng)絡(luò)可以解決上面的問(wèn)題。


3.1 Generator

Conditional GAN不同于GAN的地方在于其Generator和Discriminator接收參數(shù)的數(shù)量不同。Generator在接收隨機(jī)變量的同時(shí)還接收一個(gè)“思想向量”,這個(gè)思想向量可以是對(duì)句子的一個(gè)編碼。此時(shí)我們的Generator的結(jié)構(gòu)變成了輸入兩個(gè)向量,輸出一個(gè)圖像的網(wǎng)絡(luò)。


比如上圖,我們將red eyes這個(gè)句子轉(zhuǎn)化成向量交給Generator,然后讓它生成紅眼的動(dòng)漫圖像。通過(guò)修改x我們可以得到不同的圖像,又因?yàn)閦這個(gè)隨機(jī)變量的存在,我們即使給同樣的x也可以得到不同的圖像。

為了能讓網(wǎng)絡(luò)學(xué)習(xí)到文字和描述之間的關(guān)系,我們需要準(zhǔn)備好(文字描述-圖像)這種組合的數(shù)據(jù)集。


3.2 Discriminator

Discriminator同樣需要輸入兩個(gè)向量,分別是Generator生成的圖像和輸入到Generator的x,然后輸出是否正確。

交給Generator的訓(xùn)練數(shù)據(jù)需要把(正確描述-正確圖像)作為類別1,把(正確描述,不正確圖像)、(正確描述,正確圖像,但圖像和描述不匹配)作為類別0。

如果不包含(正確描述,正確圖像,但圖像和描述不匹配)作為訓(xùn)練數(shù)據(jù),我們的網(wǎng)絡(luò)得不到很好的結(jié)果。

知道了Generator和Discriminator網(wǎng)絡(luò)后,我們可以使用和GAN類似的方式進(jìn)行訓(xùn)練,最后的Generator就是我們的AI畫(huà)師了。我們給它文字描述,它給我們返回一張對(duì)應(yīng)的圖。


四、Stable Diffusion

Stable Diffusion和Conditional GAN有很多相似的地方,因?yàn)槎伎梢杂脕?lái)解決Text-to-image的問(wèn)題,因此模型都是接收一個(gè)文本以及影響圖像的高斯噪聲。只不過(guò)使用的網(wǎng)絡(luò)結(jié)構(gòu)有所區(qū)別,而且Stable Diffusion引入了Latent Diffusion,讓訓(xùn)練更加順利。

Latent Diffusion包括了三個(gè)部分,分別是自編碼器、U-Net、Text-Encoder。

其中自編碼器包括編碼器和解碼器兩部分。編碼器的輸出會(huì)交給U-Net進(jìn)行處理。而U-Net得輸出則會(huì)交給解碼器。

U-Net在接收編碼器輸入的同時(shí),還接收一個(gè)句子的向量。這個(gè)句向量由Text-Encoder給出。下圖是U-Net的結(jié)構(gòu)。


因?yàn)閁-Net是在低維空間上工作的,因此Latent Diffusion快速有效。Stable Diffusion的整體流程如下圖:

五、體驗(yàn)

現(xiàn)在有許多現(xiàn)成的平臺(tái)可以AI繪畫(huà),相比GAN,Stable Diffusion要更擅長(zhǎng)繪畫(huà),這里可以用昆侖天宮的天工巧繪(SkyPaint)來(lái)進(jìn)行一個(gè)簡(jiǎn)單的體驗(yàn),該平臺(tái)使用的就是Stable Diffusion分支模型。下面是幾個(gè)測(cè)試的例子。

1. 戴帽子拿劍的貓

我原本的設(shè)想是得到近似穿長(zhǎng)靴的貓一樣的圖像,下面幾個(gè)結(jié)果有一些穿長(zhǎng)靴的貓的韻味


2. 梵高星空

其中第一個(gè)效果圖和原作場(chǎng)景有幾分相似的地方,而其余幾幅畫(huà)則不太一樣


3. 阿拉斯加千年不化的雪山 一架紅色直升機(jī)正在起飛

這次的描述包含很多細(xì)節(jié),紅色直升機(jī),起飛等。從下面的結(jié)果來(lái)看AI把握了這些細(xì)節(jié),每張圖都沒(méi)有太多違和感,不過(guò)細(xì)看螺旋槳還是有一些不太滿意的地方。


大家可以自己去嘗試一下AI繪圖的效果。


六、總結(jié)

從Conditional GAN的實(shí)現(xiàn)來(lái)AI繪畫(huà)并不是簡(jiǎn)單的照搬,在訓(xùn)練Conditional GAN的時(shí)候,我們?cè)谧龅臅r(shí)學(xué)習(xí)到圖像的分布。對(duì)于一張64×64×3的8bit圖,可以有12288^256種組合,而這么多組合里面只有極小一部分是我們需要的圖像,而Generator網(wǎng)絡(luò)就是把z從一個(gè)簡(jiǎn)單的分布(比如高斯分布),映射一個(gè)復(fù)雜的分布(圖像的分布)。當(dāng)學(xué)習(xí)到這個(gè)分布后,我們只需要從z的分布中采樣一個(gè)點(diǎn),就可以對(duì)應(yīng)到一張圖像。這就是我們Generator在做的事情。

熱門(mén)課程推薦

熱門(mén)資訊

請(qǐng)綁定手機(jī)號(hào)

x

同學(xué)您好!

您已成功報(bào)名0元試學(xué)活動(dòng),老師會(huì)在第一時(shí)間與您取得聯(lián)系,請(qǐng)保持電話暢通!
確定