目录
以上都是我什么新思路也没有了,或者有也无法实现从而吐吐槽发泄一下罢了(建议直接跳过哦)
先说点废话:
我为什么去研究生成式ai,众所周知现在ai绘画越来越发达,作为一个无知啥也不懂的大学生,我自大的想要创造一种能够高概率分辨一张图片是否是ai生成的代码,我从一开始就没想把重点放在提取图片特征方面,因为这种它只能是一种治标不治本的方法,现在的ai图片特征五花八门,人们可以自己训练画风,训练模型,让通过提取特征来区分是否是ai绘画根本没有任何意义。于是我就想从神经网络入手想尝试从根本上找出有没有什么规律或者因为是通过数学算法制出的图片会不会留下什么痕迹甚至是是否能通过ai的图像能够提取出一种函数,而自然图像提取不出来区分,但现在看来属实可笑了。我也有点心灰意冷,但也有些憧憬。在生成式ai越来越发达的时代,真的找不出一种真正从根本上辨别的方法吗,虽然现在有些ai制图还是看起来油腻或者有其他的缺点,但优化到完美只是一个时间问题到那个时候艺术又会是什么样,会是百花齐放吗,人人都可以利用ai让一张图片以自己喜欢的画风存在,而绘画真正成为一种乐趣,会不会因为此从而更让人们的创造力得以表现,艺术家们创造出更新奇的画风,人们的审美发生更多元的变化,那个时候世界又会变成什么样会因为创造力的提升而导致科技更加爆发式的增长,人类因为更具有创造性而更加具有个体独立性,特殊性,创造力提升,生产力提升,每个人都因自身特殊性而更加有生存的意义
还是说反而思路闭塞,艺术家们也不愿意去创造新格,而是将世界上的画风全部版权化从而阻止普通人去使用,或者是利用其版权牟利,由此带来负面影响ai也无法获得新的学习数据,导致集体退步,还是说反而ai利用自己的学习能力产生了创造性从而发生无法预知的事情
以上都是我什么新思路也没有了,或者有也无法实现从而吐吐槽发泄一下罢了(建议直接跳过哦)
接下来是GAN 的基础教程
首先先通过论文了解一下GAN
Abstract
生成对抗网络,顾名思义。主打的就是一个对抗的思想,这个理论也符合自然界的情况
“物竞天择,适者生存” 高中生物有这么一个例子 猎豹追捕🦌,🦌为了生存锻炼出了强大有力的肢体从而可以跑的更快以逃脱追捕,而猎豹为了还能捕猎到跑的更快的🦌,它也会变得越来越快,当然这是宏观感受,毕竟自然选择是基因突变自然进行淘汰筛选,只不过单就结果论是这么个感受,你可以类比成神经网络也自然选择也就是不适应进化的神经元死掉喽。(具体的后面讲)
而论文中的这个段落讲的就是,我们提出了一个新的生成网络也就是GAN,生成对抗网络,里面自然有俩,一个是生成器G,一个是判别器D。
生成器目的是根据输入的噪声(也就是一些数据,可以是有规律的符合正态分布的,也可能是随机的)然后这个生成器将这个噪声通过神经网络,它的目标是生成出跟自身训练集十分相像的图片,也就是它要最大化判别器出错的概率。
判别器目的是最小化判断出错的概率,这就体现出了一个对抗的过程
然后回馈,更新权重就是一个神经网络普遍流程了
这里因为是直接由生成器进行生成,所以并不需要对每一步进行推理,来预测后面的数据,所以不需要用到马尔科夫链或展开近似推理网络,但注意一点这是早期论文中提到说不用,我经过查找发现这俩是可以在GAN 基础上进行拓展的 ,感兴趣的自己研究
1. Introduction
这一段是讲了一下在那个时候深度学习的发展,基于反向传播和丢弃算法,分层模型快速发展,像是什么数据的概率分布例如,处理自然图像,音频,自然语言处理(这些都是近几年热点,人工只能发展太快了),但是呢,由于最大似然估计极其相关策略中出现许多难以处理的概率计算和近似,并且之前的生成网络也难以利用分段线性单元的好处,深度生成模型的影响较小,于是提出了一个新的模型来避开这些困难(这里是说过去因为生成模型设计上的问题导致出现了一些问题很影响这个模型的效率,所以才提出了这个新的模型也就GAN)
(图片取自网络)
说完这个论文作者也举了个例子就是造假钞,一组是伪造者试图制造假币并在不被发现的情况下使用它(生成器),而另一组就是警察,试图发现假币(判别器),让他们相互竞争驱使两个团队改进各自方法直到赝品与真品无法区分
2. Relate work
这里讲了过去的一些该领域一些成果一些问题,感兴趣的自己看看吧
3 .Adversarial nets
这里的重点一个是这个公式一个就是这个图
复杂的就不讲了这个公式是什么呢:其实它就是很眼熟的
torch.nn.BCELoss() 这个损失函数应该大家都用过吧处理2分类问题会用到 如果去讲这个损失函数一时半会讲不清可以就这么理解:z是我们输入的噪声,data是真实数据的概率分布。 D()是判别器判断传入图片是否是生成的,返回值在【0, 1】 g(z) 生成器返回值是一组图像数据 在这个损失函数里面我们想让它尽可能趋近于0 于是在0到1 的log函数里 为了分析这个问题由于二进制,log的底数暂且当作2, 在0-1区间 也就是公式的后半段有关输入量是噪声的,在这个区间取值是负数所以我们希望它的值越小越好,也就对应我们之前说的生成器要尽可能让判别器出错,判别器尽可能不出错如果不理解公式,无所谓理解最开头说的生成对抗的道理就够。
他这个图片就是说 最开始可能无法让G很好的学习,所以在学习的早期G(绿线)较差的时候 D可以拒绝具有高置信度的样本,因为于训练数据明显不同,然后进行更新能发现G越来越贴合虚线最后近乎重合我们再看D(蓝色虚线)刚开始很不稳定,无法区分,再到b->c图像前半段强后半段弱,在学习早期能提供更强的梯度前期强从而促进G的成长G成长D的能力不足反过来又会促进D的成长,直到d图达到一个最终状态二者都不能提高而此时也就是判别器几乎无法判断真假的时候
再往后都是一些理论验证,我们这里只是先理解GAN,好奇怎么验证的去网上查GAN的论文,一查就能查到
代码展示
import torch.nn as nn
import torch
import numpy as np
from torchvision import datasets
from torch.utils.data import DataLoader
from torchvision import transforms
import torchvision.transforms as T
transforms = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, ), (0.5, ))])
dataset = datasets.MNIST(root="/dataset", train=True, download=True, transform=transforms)
DataLoader = DataLoader(dataset, shuffle=True, batch_size=64, num_workers=8)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
class Options:
def __init__(self):
self.latent_dim = 100
self.lr = 0.001
self.batch_size = 64
self.num_epochs = 20
self.b1 = 0.7
self.b2 = 0.7
opt = Options()
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, 784),
nn.Tanh()
)
def forward(self, x):
return self.model(x)
class Discriminator(nn.Module):
def __init__(self):
super(Discriminator, self).__init__()
self.model = nn.Sequential(
nn.Linear(784, 512),
nn.Linear(512, 256),
nn.LeakyReLU(0.2, inplace=True),
nn.Linear(256, 1),
nn.Sigmoid(),
)
def forward(self, img):
img = self.model(img)
return img
loss = torch.nn.BCELoss()
generate = Generator()
discriminator = Discriminator()
generate.to(device)
loss.to(device)
discriminator.to(device)
optimizer_G = torch.optim.Adam(generate.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
optimizer_D = torch.optim.Adam(discriminator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
to_pil = T.ToPILImage()
if __name__ == '__main__':
for epoch in range(100):
for batch_idx, data in enumerate(DataLoader):
image, label = data[0].to(device), data[1].to(device)
z = (torch.randn(image.size(0), opt.latent_dim).detach()).to(device)
valid = torch.full((image.size(0), 1), 1).float().to(device)
fake = torch.full((image.size(0), 1), 0).float().to(device)
optimizer_G.zero_grad()
G_img = generate(z)
imgs = torch.reshape(G_img, (image.size(0), 1, 28, 28))
img_list = [to_pil(img) for img in imgs]
for i, img in enumerate(img_list):
img.save("G_img_{}.png".format(i))
loss_G = loss(discriminator(G_img), valid)
loss_G.backward()
optimizer_G.step()
optimizer_D.zero_grad()
loss_D_f = loss(discriminator(G_img.detach()), fake)
loss_D_t = loss(discriminator(image.view(image.size(0), -1)), valid)
loss_D = (loss_D_f + loss_D_t) / 2
loss_D.backward()
optimizer_D.step()
optimizer_D.zero_grad()
optimizer_G.zero_grad()
print('[%d][batch %d] [d_loss: %.6f] [g_loss: %.6f]' % (epoch, batch_idx, loss_G.item(), loss_D.item()))
部分代码讲解
1.生成器
非常基础一个生成器就是输入噪声然后通过线性层最后变为1*28*28因为我们这里使用的是mnist比较简单的数据集,图像都是灰度的channle只有一层,况且也没必要去用卷积网络来写这个最基础版本的,这个就是为了简单易懂
里面用到几个 BatchNorm1d 批量归一化神经网络批量数据,计算均值方差输入标准化,可以加速训练提高网络容错和稳定
leakRelu 缓解relu神经元死亡问题,优点也就是relu的优点
2. 判别器
这个更简单需要注意的就是返回值sigmoid处理保证在01之间因为他是一个概率,同时如果不在01之间 bceloss会报错的
3.训练过程
1. z 就是我们传入的噪声是一个我这里用的是,数据批量大小*100的随机张量, 值得注意就是虽然我们数据处理的批次是64 但是最后会有没组成64个一批次的所以我们才用数据批量大小*100的随机张量
2.vaild 真 fake 假 都是数据批量大小*100的张量 真是1, 假是0
3.G的loss 就是它生成的图片用判别器判断后与1的loss 他希望的是loss趋近于0 也就是越来越接近1(真)
4. D 的loss分为两个 一个是生成图片与假的loss 它希望自己能越来越擅长判断出生成图片
另一个就是 真实图片与真的loss 他希望自己能够擅长判断出原始图片
二者进行求均值也就是D要越来越擅长区分图片是否是G生成的
4. 结果
这是第95epoch 生成图片的结果可以看到已经很接近原来数据集了
5.总结
生成对抗网络要的就是理解这个对抗的过程,利用判别器求loss来优化G和D就是GAN 的主要思想
GAN 算是一个基础在此基础上去学些cyGAN那些会更好
但我个人感觉现在这些ai已经很成熟了,以后的发展可能也仅仅是优化细节扩大应用面了,所以重要的是理解原理就好了