学习Cyclegan笔记

GAN模型是啥?(李沐老师的视频)

机器学习的模型

1 分辨模型

2 生成模型

minmax对抗游戏 纳什均衡

生成器跟判别器一样

最好使得生成器跟判别器更新的速度差不多

gan的收敛需要很多约束

好的我们现在知道了gan模型的全程是生成式对抗模型,其实就是一个生成器跟判别器的博弈的过程,生成器想要生成跟输入媲美的内容,判别器想要识别是真的图片还是生成器生成的内容,然后再这种竞争之中,双方的性能都越来越好,直到达到一种均衡

那么这种均衡是怎么体现出来的呢

也就是说判别器有%50的可能识别对,也有%50的可能识别错

我们接下来看cyclegan

Abstract

图像到图像的转换的目标是学习图像之间的映射

问题1:为什么配对训练数据不可用

问题2:什么叫没有成对示例

问题3:为什么映射是高度欠约束的

问题4:包括集合样式迁移、对象变形、季节迁移、照片增强等是啥

Introduction

本文提出的是捕获一个图像集合的特殊特征,弄清楚是怎么从这个特征转化为另一个的,然后这些都没有配对训练示例

说的是人话么

那我有点奇怪,就是这个判别器是怎么知道冬天图像的样子的呢

那我明白了,就是生成器也不知道冬天是啥样的,但是会根据判别器的反馈来调整,直到判别器也辨认不出来

样式迁移就是模仿风格

对象变形就是把马变成斑马

属性迁移就就是调整图像参数

照片增强就是修复损坏照片或提高照片的清晰度

什么问题呢?就是很难独立地优化对抗目标,还有就是所有输入都对应相同的输出,我们就要加入更多的结构了,最好是一致的,就比如说英语翻译到了法语,然后再翻译回来,跟原文是一样的

Related work

对抗性损失的概念使得图像逼真,无法区分

讲的是coupledgan 基于源图像计算出值,用来符合目标域的元素

所以这段话讲的是有配对的翻译还是无配对的呢

我们的公式好在哪里呢?

1 我们的公式不依赖预定义的相似函数,也没有假设低维的嵌入空间

2 方法更通用

什么是对偶学习

那其实cyclegan也是受到对偶学习的启发啊

循环一致性

就是上面说的,转换来转换去是一样的

神经风格迁移

感觉像是跟泛化性差不多的东西? 这种想法错了!!!

并不是一个模型对应不同的风格,而是在域中不仅仅是对特定的图像结合,可以对域内的多个元素进行操作

图像风格迁移是一种通过将一张图像的风格应用到另一张图像的内容上,从而生成一张新图像的技术。这种技术通常基于深度学习模型,特别是卷积神经网络(CNN)。

Formulation

我们的目标是学习到X域到Y域的映射函数

有两个术语

对抗性损失 周期一致性损失

3.1. Adversarial Loss

符号的含义

为什么判别器里面有着生成器的公式

梯度为什么会消失

为什么使用log函数,是因为防止梯度消失

sigmoid饱和区是啥

3.2. Cycle Consistency Loss

cyclegan的创新点是啥,是怎么实现的,有什么作用?

总期望

Implementation

作者把负对数似然估计法变成了最小二乘估计法

model oscillation 模型震荡

为什么要加入图像缓冲区

调参

Results

CycleGAN通过定量指标(AMT、FCN、分类精度)和定性对比(视觉结果、消融实验)综合评估,证明了其在非配对图像翻译中的优越性,尤其在保留语义和生成真实感方面

5.1.1 Evaluation Metrics

AMT perceptual studies

FCN score

Semantic segmentation metrics

基线使用的模型如下

Cyclegan代码解释

1.导入库

import torch

import torch.nn as nn

import torch.optim as optim

from torch.utils.data import DataLoader

from torchvision import datasets, transforms

import matplotlib.pyplot as plt

import os

import itertools

2 定义生成器跟判别器

class Generator(nn.Module):

def __init__(self, input_channels, output_channels):

super(Generator, self).__init__()

self.model = nn.Sequential(

nn.Conv2d(input_channels, 64, kernel_size=7, stride=1, padding=3),

nn.InstanceNorm2d(64),

nn.ReLU(inplace=True),

nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1),

nn.InstanceNorm2d(128),

nn.ReLU(inplace=True),

nn.Conv2d(128, 256, kernel_size=3, stride=2, padding=1),

nn.InstanceNorm2d(256),

nn.ReLU(inplace=True),

nn.ConvTranspose2d(256, 128, kernel_size=3, stride=2, padding=1, output_padding=1),

nn.InstanceNorm2d(128),

nn.ReLU(inplace=True),

nn.ConvTranspose2d(128, 64, kernel_size=3, stride=2, padding=1, output_padding=1),

nn.InstanceNorm2d(64),

nn.ReLU(inplace=True),

nn.Conv2d(64, output_channels, kernel_size=7, stride=1, padding=3),

nn.Tanh()

)

def forward(self, x):

return self.model(x)

生成器完毕

class Discriminator(nn.Module):

def __init__(self, input_channels):

super(Discriminator, self).__init__()

self.model = nn.Sequential(

nn.Conv2d(input_channels, 64, kernel_size=4, stride=2, padding=1),

nn.LeakyReLU(0.2, inplace=True),

nn.Conv2d(64, 128, kernel_size=4, stride=2, padding=1),

nn.InstanceNorm2d(128),

nn.LeakyReLU(0.2, inplace=True),

nn.Conv2d(128, 256, kernel_size=4, stride=2, padding=1),

nn.InstanceNorm2d(256),

nn.LeakyReLU(0.2, inplace=True),

nn.Conv2d(256, 512, kernel_size=4, stride=1, padding=1),

nn.InstanceNorm2d(512),

nn.LeakyReLU(0.2, inplace=True),

nn.Conv2d(512, 1, kernel_size=4, stride=1, padding=1)

)

def forward(self, x):

return self.model(x)

判别器搞定

代码讲解

先经过一个卷积核大小为7*7的卷积操作,再经过一个InstanceNorm的操作,关于这个Norm操作,我发现它还是蛮有讲究的

批次归一化,层归一化,实例归一化,组归一化

生成器

GAN中使用的是实例归一化InstanceNorm,它的好处就是对每个通道单独计算方差跟均值,这样的话虽然它忽略了与其他样本跟通道的关系,但是保留了样本间的独立性,

输入图像的风格保留下来了,因为输出图像的风格只受到输入图像的影响,而不是整体数据集的,因此针对单个通道进行归一化的InstanceNorm更好

接着就是对特征图反复的进行卷积操作,归一化,relu函数

总共有六层

最后使用tanh函数

判别器

代码跟生成器差不多,就是把relu换为了Leakyrelu(为了增强鲁棒性),然后没有tanh函数

3 定义CycleGAN 模型

class CycleGAN(nn.Module):

def __init__(self, input_channels, output_channels):

super(CycleGAN, self).__init__()

self.G_A2B = Generator(input_channels, output_channels)

self.G_B2A = Generator(input_channels, output_channels)

self.D_A = Discriminator(input_channels)

self.D_B = Discriminator(input_channels)

def forward(self, x):

fake_B = self.G_A2B(x)

fake_A = self.G_B2A(fake_B)

return fake_A, fake_B

输入x,生成的图片为fake_B 然后把fake_B再生成回去,作为循环一致性损失的样本

4 定义损失函数跟优化器

def train_cyclegan(cyclegan, dataloader_A, dataloader_B, num_epochs=200, lr=0.0002, device='cuda'):

cyclegan = cyclegan.to(device)

criterion_GAN = nn.MSELoss()

criterion_cycle = nn.L1Loss()

criterion_identity = nn.L1Loss()

optimizer_G = optim.Adam(itertools.chain(cyclegan.G_A2B.parameters(), cyclegan.G_B2A.parameters()), lr=lr, betas=(0.5, 0.999))

optimizer_D_A = optim.Adam(cyclegan.D_A.parameters(), lr=lr, betas=(0.5, 0.999))

optimizer_D_B = optim.Adam(cyclegan.D_B.parameters(), lr=lr, betas=(0.5, 0.999))

for epoch in range(num_epochs):

for i, (real_A, real_B) in enumerate(zip(dataloader_A, dataloader_B)):

real_A = real_A.to(device)

real_B = real_B.to(device)

训练生成器

# Train Generators

optimizer_G.zero_grad()

# Identity loss

same_B = cyclegan.G_A2B(real_B)

loss_identity_B = criterion_identity(same_B, real_B)

same_A = cyclegan.G_B2A(real_A)

loss_identity_A = criterion_identity(same_A, real_A)

# GAN loss

fake_B = cyclegan.G_A2B(real_A)

pred_fake_B = cyclegan.D_B(fake_B)

loss_GAN_A2B = criterion_GAN(pred_fake_B, torch.ones_like(pred_fake_B))

fake_A = cyclegan.G_B2A(real_B)

pred_fake_A = cyclegan.D_A(fake_A)

loss_GAN_B2A = criterion_GAN(pred_fake_A, torch.ones_like(pred_fake_A))

真实图像想让自己接近1

# Cycle loss

recovered_A = cyclegan.G_B2A(fake_B)

loss_cycle_A = criterion_cycle(recovered_A, real_A)

recovered_B = cyclegan.G_A2B(fake_A)

loss_cycle_B = criterion_cycle(recovered_B, real_B)

# Total loss

loss_G = loss_GAN_A2B + loss_GAN_B2A + loss_cycle_A + loss_cycle_B + loss_identity_A + loss_identity_B

loss_G.backward()

optimizer_G.step()

判别器的训练

# Train Discriminator A

optimizer_D_A.zero_grad()

pred_real_A = cyclegan.D_A(real_A)

loss_D_real_A = criterion_GAN(pred_real_A, torch.ones_like(pred_real_A))

pred_fake_A = cyclegan.D_A(fake_A.detach())

loss_D_fake_A = criterion_GAN(pred_fake_A, torch.zeros_like(pred_fake_A))

loss_D_A = (loss_D_real_A + loss_D_fake_A) * 0.5

loss_D_A.backward()

optimizer_D_A.step()

# Train Discriminator B

optimizer_D_B.zero_grad()

pred_real_B = cyclegan.D_B(real_B)

loss_D_real_B = criterion_GAN(pred_real_B, torch.ones_like(pred_real_B))

pred_fake_B = cyclegan.D_B(fake_B.detach())

loss_D_fake_B = criterion_GAN(pred_fake_B, torch.zeros_like(pred_fake_B))

loss_D_B = (loss_D_real_B + loss_D_fake_B) * 0.5

loss_D_B.backward()

optimizer_D_B.step()

虚假图像则偏向于0

if i % 100 == 0:

print(f"Epoch [{epoch}/{num_epochs}] Batch [{i}/{len(dataloader_A)}] "

f"Loss D_A: {loss_D_A.item()} Loss D_B: {loss_D_B.item()} "

f"Loss G: {loss_G.item()}")

torch.save(cyclegan.state_dict(), 'cyclegan.pth')

是怎做到最小化跟最大化的?

ones_like 和 zeros_like

5 数据加载和预处理

transform = transforms.Compose([

transforms.Resize((256, 256)),

transforms.ToTensor(),

transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))

])

dataset_A = datasets.ImageFolder(root='path_to_dataset_A', transform=transform)

dataset_B = datasets.ImageFolder(root='path_to_dataset_B', transform=transform)

dataloader_A = DataLoader(dataset_A, batch_size=1, shuffle=True)

dataloader_B = DataLoader(dataset_B, batch_size=1, shuffle=True)

6 训练模型

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

cyclegan = CycleGAN(input_channels=3, output_channels=3)

train_cyclegan(cyclegan, dataloader_A, dataloader_B, num_epochs=200, device=device)

7 测试模型

def test_cyclegan(cyclegan, dataloader_A, device='cuda'):

cyclegan.load_state_dict(torch.load('cyclegan.pth'))

cyclegan = cyclegan.to(device)

cyclegan.eval()

with torch.no_grad():

for i, real_A in enumerate(dataloader_A):

real_A = real_A.to(device)

fake_B = cyclegan.G_A2B(real_A)

fake_A = cyclegan.G_B2A(fake_B)

plt.figure(figsize=(10, 5))

plt.subplot(1, 3, 1)

plt.title("Real A")

plt.imshow(real_A.cpu().squeeze().permute(1, 2, 0) * 0.5 + 0.5)

plt.subplot(1, 3, 2)

plt.title("Fake B")

plt.imshow(fake_B.cpu().squeeze().permute(1, 2, 0) * 0.5 + 0.5)

plt.subplot(1, 3, 3)

plt.title("Recovered A")

plt.imshow(fake_A.cpu().squeeze().permute(1, 2, 0) * 0.5 + 0.5)

plt.show()

if i == 5:

break

test_cyclegan(cyclegan, dataloader_A, device=device)

8 运行代码+输出结果

引申 为何GAN能够生成图像呢?

CycleGAN实战的主要目标是通过使用CycleGAN算法来进行图像转换。CycleGAN是基于unconditional GAN和conditional GAN的算法,其中包含两个生成器和两个判别器。其核心思想是通过循环一致性来实现图像的转换。 在CycleGAN中,首先使用一个生成器G将原始输入图像x转换为目标域中的图像Y^。然后,使用另一个生成器F将生成的图像Y^转换回原始域中的图像x^。这个过程的目的是尽可能让原始输入图像x和经过两次转换得到的图像x^相似,从而实现循环一致性。同样地,也可以使用另一个生成器F将目标域中的图像y转换回原始域中的图像X^,并通过两次转换尽可能使得原始输入图像y和经过两次转换得到的图像Y^相似。 CycleGAN的训练过程中,除了循环一致性损失外,还包括对生成器和判别器的对抗性损失。通过使用这些损失函数,CycleGAN可以学习到如何进行跨域图像转换,例如将马转换为斑马或将夏天的景色转换为冬天的景色。 在CycleGAN实战中,可以使用已经训练好的模型来进行图像转换。通过将原始输入图像输入到生成器G中,可以得到目标域中的转换图像Y^。同样地,也可以将目标域中的图像输入到生成器F中,得到原始域中的转换图像x^。通过这种方式,可以实现不同域之间的图像转换,从而获得有趣的结果。 总的来说,CycleGAN实战是通过使用CycleGAN算法来实现图像的跨域转换,其中包括循环一致性和对抗性损失的训练过程。通过使用已经训练好的模型,可以将图像从一个域转换为另一个域,获得有趣的效果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [【Pytorch】Cycle GAN实战(一):风格转换--真实风景图像转换为VanGogh风格](https://blog.csdn.net/qq_44031210/article/details/120113727)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [(五)cycleGAN论文笔记与实战](https://blog.csdn.net/qq_41845478/article/details/107553633)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值