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

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

一篇文章教會你AI繪畫

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

一、前言

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

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


二、GAN

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


2.1 生成

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

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


2.2 專家與贗品

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

上面提到的造假專家就是G網(wǎng)絡(luò),也就是Generator;而鑒別專家就是D網(wǎng)絡(luò),也就是Discriminator。它們在互相對抗中學習,最終成為各自領(lǐng)域的專家,這就是GAN的思想。


2.3 Generator

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

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


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


輸入一個維度為100的向量,輸出一張64×64×3的圖像,其PyTorch實現(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中非常重要的一個角色,它是一個接受一個圖片輸入的網(wǎng)絡(luò),輸入的圖像會包含一部分真實圖像real(我們收集的動漫圖像),還會包含一部分虛假圖像fake(Generator生成的圖像),然后輸出一個結(jié)果。這個結(jié)果可以是fake是真實圖像的概率,也可以是fake的類別(0表示假,1表示真)。對于Discriminator而言,它的目的就是調(diào)整網(wǎng)絡(luò)參數(shù),讓網(wǎng)絡(luò)知道fake圖像是假的。


關(guān)于Discriminator的結(jié)構(gòu),并沒有非常固定的約束,通常是一個卷積網(wǎng)絡(luò)。這里同樣參考DCGAN,這里實現(xiàn)PyTorch的一個實現(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ò)了。

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

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

· 第二步:固定Discriminator,訓練Generator,讓Generator生成的圖像能夠瞞過Discriminator

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

· 第四步:用Generator生成圖像

上述步驟可以看作下圖:


以上就是GAN網(wǎng)絡(luò)的訓練過程。其實就是Generator和Discriminator交替訓練的過程,其PyTorch實現(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ān)于DCGAN的代碼實現(xiàn)可以參考


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

三、Conditional GAN

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


3.1 Generator

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


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

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


3.2 Discriminator

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

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

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

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


四、Stable Diffusion

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

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

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

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


因為U-Net是在低維空間上工作的,因此Latent Diffusion快速有效。Stable Diffusion的整體流程如下圖:

五、體驗

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

1. 戴帽子拿劍的貓

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


2. 梵高星空

其中第一個效果圖和原作場景有幾分相似的地方,而其余幾幅畫則不太一樣


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

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


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


六、總結(jié)

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

熱門課程推薦

熱門資訊

請綁定手機號

x

同學您好!

您已成功報名0元試學活動,老師會在第一時間與您取得聯(lián)系,請保持電話暢通!
確定