13 从零开始学习生成对抗网络(GAN)

13 从零开始学习生成对抗网络(GAN)

本教程将深入探讨生成对抗网络(GAN)的进阶技术。我们将从基本概念出发,逐步引入更复杂的主题,并附上相关代码示例,以帮助你更好地理解和应用这些知识。

1. 生成对抗网络基础回顾

生成对抗网络(GAN)由两个主要组件组成:

  • 生成器(Generator):负责生成假数据,以逼近真实数据分布。
  • 判别器(Discriminator):负责区分真实数据和假数据。

这两个模型通过对抗过程进行训练:生成器试图优化其生成的数据,使其更像真实数据,而判别器则尝试提高其区分能力。

2. GAN进阶技术

2.1 条件生成对抗网络(Conditional GAN)

条件生成对抗网络(CGAN)是一种扩展的GAN,它允许我们在生成数据时引入条件信息,如标签或其他数据类型。这样,生成器可以生成特定类型的数据。

2.1.1 CGAN架构

CGAN的生成器和判别器都会接收条件信息。例如,给定一个类别标签,生成器生成该类别的样本,判别器则需要判断输入样本是否与条件匹配。

2.1.2 代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import tensorflow as tf
from tensorflow.keras import layers

def build_generator(latent_dim, num_classes):
model = tf.keras.Sequential()
model.add(layers.Input(shape=(latent_dim,)))
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(784, activation='sigmoid'))
return model

def build_discriminator(num_classes):
model = tf.keras.Sequential()
model.add(layers.Input(shape=(784,)))
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
return model

2.2 深度卷积生成对抗网络(DCGAN)

深度卷积生成对抗网络(DCGAN)使用卷积神经网络(CNN)取代多层感知机(MLP),适用于图像生成。

2.2.1 DCGAN架构

  • 生成器使用反卷积(转置卷积)层生成图片。
  • 判别器使用卷积层区分真实和生成的图片。

2.2.2 代码示例

1
2
3
4
5
6
7
8
def build_dc_generator(latent_dim):
model = tf.keras.Sequential()
model.add(layers.Dense(256, input_dim=latent_dim))
model.add(layers.Reshape((16, 16, 1)))
model.add(layers.Conv2DTranspose(128, kernel_size=3, strides=2, padding='same', activation='relu'))
model.add(layers.Conv2DTranspose(64, kernel_size=3, strides=2, padding='same', activation='relu'))
model.add(layers.Conv2DTranspose(1, kernel_size=3, padding='same', activation='sigmoid'))
return model

2.3 生成对抗网络的训练技巧

  • 平衡生成器与判别器的能力:确保两者在训练过程中保持平衡,避免判别器过于强大(这样生成器会难以学习)或者过于弱。
  • 使用标签平滑:在判别器的训练中,对真实标签进行微调,降低其为1的绝对值,提高模型的鲁棒性。

2.4 生成对抗网络的高级主题

2.4.1 CycleGAN

CycleGAN用于无监督学习,针对没有成对样本的情况下进行图像到图像的转换,例如将马转化为斑马等。

2.4.2 StyleGAN

StyleGAN是一种新型架构,允许生成更高质量的图像,并且能够控制图像的特征(风格、纹理等)。

2.5 示范案例:使用CGAN生成手写数字

以下是一个使用CGAN生成手写数字的简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import numpy as np

# 加载数据(如MNIST)
(x_train, y_train), _ = tf.keras.datasets.mnist.load_data()
x_train = x_train.astype(np.float32) / 255.0

# 定义输入与标签的形状
latent_dim = 100
num_classes = 10

# 生成器与判别器实例化
generator = build_generator(latent_dim, num_classes)
discriminator = build_discriminator(num_classes)

# 训练过程
for epoch in range(epochs):
for batch in range(num_batches):
noise = np.random.normal(0, 1, (batch_size, latent_dim))
labels = np.random.randint(0, num_classes, batch_size)

# 生成假样本
gen_samples = generator.predict([noise, labels])

# 训练判别器
discriminator.train_on_batch(real_samples, np.ones((batch_size, 1)))
discriminator.train_on_batch(gen_samples, np.zeros((batch_size, 1)))

# 训练生成器
generator.train_on_batch([noise, labels], np.ones((batch_size, 1)))

# 生成样本
generated_images = generator.predict(noise)

3. 总结

本教程详细介绍了进阶的生成对抗网络技术,包括条件生成对抗网络、深度卷积生成对抗网络以及一些训练技巧。希望这些内容能够帮助你更深入地理解和应用生成对抗网络。

如需进一步深入研究,建议查阅相关研究论文或书籍,继续实践和实验。在实际应用中,GAN的创新和发展日新月异,保持学习和关注会让你走在前沿。

14 从零学生成式对抗网络 (GAN) 教程 - 条件 GAN (Conditional GAN)

14 从零学生成式对抗网络 (GAN) 教程 - 条件 GAN (Conditional GAN)

条件生成对抗网络(Conditional GAN,简称CGAN)是一种GAN的变种,它允许我们通过提供条件信息来生成特定类型的数据。这是通过在生成器和判别器中添加条件标签实现的。

1. 理解条件 GAN

与传统的GAN不同,CGAN在生成数据时引入了额外的信息,例如类别标签。这使得模型能够生成特定类别的样本。例如,在图像生成任务中,我们可以输入特定的数字标签(如 0-9),模型则会生成相应的数字图像。

1.1 结构概述

CGAN模型包含两个主要的部分:

  • 生成器(Generator):接收随机噪声 z 和条件信息 y(如类别标签),输出生成的数据 G(z | y)
  • 判别器(Discriminator):接收真实数据和生成数据连同条件信息 y,输出数据为真实的概率 D(x | y)

2. 条件 GAN 的数学基础

令:

  • x 表示真实样本
  • y 表示标签
  • z 表示随机噪声

在CGAN中,我们的目标是最大化对抗损失,可以表示为:

$$
\min_G \max_D V(D, G) = \mathbb{E}{x,y}[ \log D(x | y)] + \mathbb{E}{z,y}[\log(1 - D(G(z | y) | y))]
$$

其中,第一项是对真实样本的判别,第二项是对生成样本的判别。

3. 实现条件 GAN

我们将使用 TensorFlowKeras 来实现一个简单的条件 GAN。以下是生成手写数字图像(MNIST数据集)的示例。

3.1 导入必要的库

1
2
3
4
5
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

3.2 加载和预处理数据

我们使用 MNIST 数据集,其中每个样本都有一个数字标签。

1
2
3
4
5
6
7
# 加载MNIST数据集
(x_train, y_train), (_, _) = keras.datasets.mnist.load_data()
x_train = x_train / 255.0 # 归一化至[0, 1]
x_train = np.expand_dims(x_train, axis=-1) # 扩展维度

# 获取数据的维度
num_classes = 10

3.3 构建生成器

生成器将随机噪声和条件标签作为输入,并生成图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def build_generator():
noise_shape = (100,)
label_shape = (num_classes,)

noise_input = layers.Input(shape=noise_shape)
label_input = layers.Input(shape=label_shape)

# 连接输入
model_input = layers.Concatenate()([noise_input, label_input])

x = layers.Dense(256, activation='relu')(model_input)
x = layers.Dense(512, activation='relu')(x)
x = layers.Dense(1024, activation='relu')(x)
output = layers.Dense(28 * 28 * 1, activation='tanh')(x) # 输出28x28图像

output = layers.Reshape((28, 28, 1))(output)

generator = keras.models.Model([noise_input, label_input], output)
return generator

generator = build_generator()
generator.summary()

3.4 构建判别器

判别器接收图像和标签,预测图像的真实性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def build_discriminator():
image_shape = (28, 28, 1)
label_shape = (num_classes,)

image_input = layers.Input(shape=image_shape)
label_input = layers.Input(shape=label_shape)

# 连接输入
flat_image = layers.Flatten()(image_input)
model_input = layers.Concatenate()([flat_image, label_input])

x = layers.Dense(512, activation='relu')(model_input)
x = layers.Dense(256, activation='relu')(x)
output = layers.Dense(1, activation='sigmoid')(x)

discriminator = keras.models.Model([image_input, label_input], output)
discriminator.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
return discriminator

discriminator = build_discriminator()
discriminator.summary()

3.5 训练条件 GAN

训练过程中,我们需要交替训练生成器和判别器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def train_cgan(epochs, batch_size):
for epoch in range(epochs):
for _ in range(x_train.shape[0] // batch_size):
# 选择随机样本
idx = np.random.randint(0, x_train.shape[0], batch_size)
real_images = x_train[idx]
real_labels = y_train[idx]

# 生成假样本
noise = np.random.normal(0, 1, (batch_size, 100))
random_labels = np.random.randint(0, num_classes, batch_size)
random_labels_one_hot = keras.utils.to_categorical(random_labels, num_classes)

generated_images = generator.predict([noise, random_labels_one_hot])

# 训练判别器
d_loss_real = discriminator.train_on_batch([real_images, keras.utils.to_categorical(real_labels, num_classes)], np.ones((batch_size, 1)))
d_loss_fake = discriminator.train_on_batch([generated_images, random_labels_one_hot], np.zeros((batch_size, 1)))
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)

# 训练生成器
noise = np.random.normal(0, 1, (batch_size, 100))
random_labels = np.random.randint(0, num_classes, batch_size)
random_labels_one_hot = keras.utils.to_categorical(random_labels, num_classes)

g_loss = combined.train_on_batch([noise, random_labels_one_hot], np.ones((batch_size, 1)))

# 打印损失
print(f"{epoch}/{epochs} [D loss: {d_loss[0]:.4f}, acc.: {100 * d_loss[1]:.2f}] [G loss: {g_loss:.4f}]")

train_cgan(epochs=10000, batch_size=64)

3.6 生成图像

最后,我们可以使用生成器生成与指定标签相对应的图像。

def generate_images(num_images):
    noise = np.random.normal(0, 1, (num_images, 100))
    random_labels = np.random.randint(0, num_classes, num_images)
    random_labels_one_hot
15 ACGAN (Auxiliary Classifier GAN) 详细教程

15 ACGAN (Auxiliary Classifier GAN) 详细教程

1. 简介

ACGAN(辅助分类器生成对抗网络)是一种改进的生成对抗网络(GAN)模型,通过引入一个辅助分类器来增强生成图片的质量和多样性。与传统的GAN仅生成随机噪声图像不同,ACGAN不仅生成图像,还同时输出一个图片所对应的类别标签。

2. ACGAN的工作原理

ACGAN有两个主要组件:

  • **生成器 (Generator)**:负责生成图像。
  • **判别器 (Discriminator)**:不仅判断图像是否真实,还输出图像的类别标签。

2.1 生成器

生成器接收一个随机噪声向量 z 和目标类别标签 y。其目标是通过这些输入生成一个看起来像真实图像的输出。

2.2 判别器

判别器接收真实图像或生成器生成的图像,并输出两个信息:

  1. 图像是真实的还是伪造的(二分类)。
  2. 图像的类别(多分类)。

判别器的损失函数考虑到了这两个部分。

3. 损失函数

ACGAN的损失函数由两部分组成:

3.1 生成器损失

生成器损失目标是让生成的图像被判别器判定为真实,同时希望分类器给出正确的类别。其损失函数形式为:

1
L_G = -E_{z \sim p_z(z)} [ \log D(G(z, y)) + \log P(y | G(z, y)) ]

3.2 判别器损失

判别器损失也同样有两个部分:对真实图像的损失和对生成图像的损失,其损失函数形式为:

1
2
L_D = -E_{x \sim p_{data}(x)} [ \log D(x) + \log P(y | x) ] 
-E_{z \sim p_z(z)} [ \log(1 - D(G(z, y))) + \log P(y | G(z, y)) ]

4. 案例:使用PyTorch实现ACGAN

下面是一个简单的ACGAN实现示例。我们将使用PyTorch框架生成MNIST手写数字。

4.1 导入库

1
2
3
4
5
6
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt

4.2 超参数设置

1
2
3
4
5
6
# 超参数
batch_size = 64
lr = 0.0002
num_epochs = 100
num_classes = 10
latent_size = 100

4.3 数据加载

1
2
3
4
5
6
7
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))
])

train_dataset = torchvision.datasets.MNIST(root='./data', train=True, transform=transform, download=True)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)

4.4 生成器模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Generator(nn.Module):
def __init__(self):
super(Generator, self).__init__()
self.model = nn.Sequential(
nn.Linear(latent_size + num_classes, 256),
nn.ReLU(),
nn.Linear(256, 512),
nn.ReLU(),
nn.Linear(512, 784),
nn.Tanh()
)

def forward(self, z, labels):
inputs = torch.cat((z, labels), dim=1)
return self.model(inputs).view(-1, 1, 28, 28)

4.5 判别器模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Discriminator(nn.Module):
def __init__(self):
super(Discriminator, self).__init__()
self.model = nn.Sequential(
nn.Linear(784 + num_classes, 512),
nn.LeakyReLU(0.2),
nn.Linear(512, 256),
nn.LeakyReLU(0.2),
nn.Linear(256, 1), # 判别真假
nn.Sigmoid()
)
self.classifier = nn.Linear(256, num_classes) # 分类输出

def forward(self, x, labels):
inputs = torch.cat((x.view(-1, 784), labels), dim=1)
features = self.model(inputs)
class_output = self.classifier(features)
return features, class_output

4.6 训练模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def train():
G = Generator().cuda()
D = Discriminator().cuda()

criterion = nn.BCELoss()
criterion_label = nn.CrossEntropyLoss()

optimizer_G = optim.Adam(G.parameters(), lr=lr)
optimizer_D = optim.Adam(D.parameters(), lr=lr)

for epoch in range(num_epochs):
for i, (images, labels) in enumerate(train_loader):
batch_size = images.size(0)
images = images.cuda()
labels = torch.eye(num_classes)[labels].cuda() # one-hot编码

# 训练判别器
optimizer_D.zero_grad()

# 真实标签和生成标签
real_labels = torch.ones(batch_size, 1).cuda()
fake_labels = torch.zeros(batch_size, 1).cuda()

outputs, class_outputs = D(images, labels)
d_loss_real = criterion(outputs, real_labels)
d_loss_class_real = criterion_label(class_outputs, labels.argmax(dim=1))

z = torch.randn(batch_size, latent_size).cuda()
fake_images = G(z, labels).detach()
outputs, class_outputs = D(fake_images, labels)
d_loss_fake = criterion(outputs, fake_labels)
d_loss_class_fake = criterion_label(class_outputs, labels.argmax(dim=1))

d_loss = d_loss_real + d_loss_fake + d_loss_class_real + d_loss_class_fake
d_loss.backward()
optimizer_D.step()

# 训练生成器
optimizer_G.zero_grad()
outputs, class_outputs = D(fake_images, labels)
g_loss = criterion(outputs, real_labels) + criterion_label(class_outputs, labels.argmax(dim=1))
g_loss.backward()
optimizer_G.step()

print(f'Epoch [{epoch}/{num_epochs}], D Loss: {d_loss.item():.4f}, G Loss: {g_loss.item():.4f}')

train()

4.7 结果展示

def show_generated_images(generator, num_images=25):
    z = torch.randn(num_images, latent_size).cuda()
    label = torch.eye(num_classes)[torch.randint(0, num_classes, (num_images,))].cuda()
    generated_images = generator(z,