开发环境:Google Colab

卷积 GAN - Celeba 生成人脸图像

14_gan_cnn_celeba.ipynb

  • 挂载驱动器以访问 google drive 中的文件

    1. # mount Drive to access data files
    2. from google.colab import drive
    3. drive.mount('./mount')
  • 导入包 ```python import torch import torch.nn as nn from torch.utils.data import Dataset

import h5py import pandas, numpy, random import matplotlib.pyplot as plt

  1. - 标准的CUDA检查和设置步骤
  2. ```python
  3. # 检查CUDA是否可用,如果可用,则设置默认的rensor类型为cuda
  4. # 使用CUDA需要在上方工具栏“代码执行程序-更改运行时类型-硬件加速器”改为GPU
  5. if torch.cuda.is_available():
  6. torch.set_default_tensor_type(torch.cuda.FloatTensor)
  7. print("using cuda:", torch.cuda.get_device_name(0))
  8. pass
  9. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
  10. device

辅助函数

  1. # 生成随机数据
  2. def generate_random_image(size):
  3. random_data = torch.rand(size) # 生成的数据符合0-1区间内的均匀分布
  4. return random_data
  5. def generate_random_seed(size):
  6. random_data = torch.randn(size) # 生成的数据符合均值为0,方差为1的正态分布
  7. return random_data
  1. # 将三维张量转换为一维张量(eg. 将大小为(218, 178, 3)的三维图像张量重塑成一个长度为218*178*3的一维张量)
  2. # modified from https://github.com/pytorch/vision/issues/720
  3. class View(nn.Module):
  4. def __init__(self, shape):
  5. super().__init__()
  6. self.shape = shape,
  7. def forward(self, x):
  8. return x.view(*self.shape)
  1. # 将一个给定的numpy图像中裁剪为指定的长和宽,裁剪的区域位于输入图像的正中央
  2. def crop_centre(img, new_width, new_height):
  3. height, width, _ = img.shape
  4. startx = width // 2 - new_width // 2 # 注意使用整数除法//
  5. starty = height // 2 - new_height // 2
  6. return img[ starty: starty + new_height, startx: startx + new_width, :]

数据集类

  1. # 数据集类
  2. class CelebADataset(Dataset):
  3. def __init__(self, file):
  4. self.file_object = h5py.File(file, 'r')
  5. self.dataset = self.file_object['img_align_celeba']
  6. pass
  7. def __len__(self):
  8. return len(self.dataset)
  9. def __getitem__(self, index):
  10. if(index >= len(self.dataset)):
  11. raise IndexError()
  12. img = numpy.array(self.dataset[str(index)+'.jpg'])
  13. # 将图像裁剪为128*128大小的正方形图像
  14. img = crop_centre(img, 128, 128)
  15. return torch.cuda.FloatTensor(img).permute(2, 0, 1).view(1, 3, 128, 128) / 255.0 # 使得结果范围在0-1
  16. # 最终返回一个四维张量的形式:(批次大小,通道数,高度,宽度)
  17. # 原本numpy数组的形式为(高度,宽度,通道数3)的三维张量,因此先用permute(2, 0, 1)将numpy数组重新排序为(通道数3,高度,宽度)
  18. # 再使用view(1, 3, 128, 128)增加一个额外的维度,代表批量大小,这里为1
  19. def plot_image(self, index):
  20. if(index >= len(self.dataset)):
  21. raise IndexError()
  22. img = numpy.array(self.dataset[str(index)+'.jpg'])
  23. # 将图像裁剪为128*128大小的正方形图像
  24. img = crop_centre(img, 128, 128)
  25. plt.imshow(img, interpolation='nearest')
  26. pass
  27. pass
  1. # 创建数据集对象
  2. celeba_dataset = CelebADataset('mount/My Drive/Colab Notebooks/pytorch_gan/celeba_data/celeba_aligned_small.h5py')
  1. # 检查数据集中的图像 & 检查图像是否正确被裁减
  2. celeba_dataset.plot_image(43) # 索引范围是0-19999

鉴别器网络

celeba_gan_cnn_1.PNG

  1. class Discriminator(nn.Module):
  2. def __init__(self):
  3. # 初始化pytorch父类
  4. super().__init__()
  5. # 定义神经网络层
  6. self.model = nn.Sequential(
  7. # 与其输入形状为 (1, 3, 128, 128)
  8. nn.Conv2d(3, 256, kernel_size=8, stride=2), # 输入3通道彩色图像,应用256个卷积核,输出256个特征图,卷积核大小8*8,步长2,因此输出特征图大小为61*61
  9. nn.BatchNorm2d(256), # 对图层中的每个通道进行标准化
  10. nn.LeakyReLU(0.2),
  11. nn.Conv2d(256, 256, kernel_size=8, stride=2), # 输入256通道(256个特征图),应用256个卷积核,输出256个特征图,卷积核大小8*8,步长2,因此输出特征图大小为27*27
  12. nn.BatchNorm2d(256), # 对图层中的每个通道进行标准化
  13. nn.LeakyReLU(0.2),
  14. nn.Conv2d(256, 3, kernel_size=8, stride=2), # 输入256通道(256个特征图),应用3个卷积核,输出3个特征图,卷积核大小8*8,步长2,因此输出特征图大小为10*10
  15. nn.LeakyReLU(0.2),
  16. View(3*10*10), # 输入(1,3,10,10)的三维特征图,转换为含100个值的一维张量
  17. nn.Linear(3*10*10, 1), # 全连接层,将300个值所见到一个鉴别器的输出值
  18. nn.Sigmoid()
  19. )
  20. # 创建损失函数
  21. self.loss_function = nn.BCELoss() # 二元交叉熵BCELoss()
  22. # 创建优化器,使用随机梯度下降
  23. self.optimiser = torch.optim.Adam(self.parameters(), lr=0.0001) # 使用Adam优化器
  24. # 计数器和进程记录
  25. self.counter = 0
  26. self.progress = []
  27. pass
  28. def forward(self, inputs):
  29. # 直接运行模型
  30. return self.model(inputs)
  31. def train(self, inputs, targets):
  32. # 计算网络的输出
  33. outputs = self.forward(inputs)
  34. # 计算损失值
  35. loss = self.loss_function(outputs, targets)
  36. # 每训练10次增加计数器
  37. self.counter += 1
  38. if(self.counter % 10 == 0):
  39. self.progress.append(loss.item())
  40. pass
  41. if(self.counter % 10000 == 0):
  42. print("counter = ", self.counter)
  43. pass
  44. # 归零梯度,反向传播,并更新权重
  45. self.optimiser.zero_grad()
  46. loss.backward()
  47. self.optimiser.step()
  48. pass
  49. def plot_progress(self): # 打印训练过程
  50. df = pandas.DataFrame(self.progress, columns=['loss'])
  51. df.plot(ylim=(0), figsize=(16, 8), alpha=0.1, marker='.', grid=True, yticks=(0, 0.25, 0.5, 1.0, 5.0))
  52. pass
  53. pass

测试鉴别器

  1. %%time
  2. # 测试鉴别器能否鉴别真实数据和随机噪声
  3. # 训练鉴别器
  4. D = Discriminator()
  5. D.to(device) # 将模型移到cuda设备
  6. for image_data_tensor in celeba_dataset:
  7. # 真实数据
  8. D.train(image_data_tensor, torch.cuda.FloatTensor([1.0]))
  9. # 生成数据
  10. D.train(generate_random_image((1, 3, 128, 128)), torch.cuda.FloatTensor([0.0]))
  11. pass
  1. # 绘制鉴别器的损失图
  2. D.plot_progress()
  1. # 运行鉴别器,检查其能否区分真实图像和随机图像
  2. for i in range(4):
  3. image_data_tensor = celeba_dataset[random.randint(0, 20000)]
  4. print(D.forward(image_data_tensor).item())
  5. pass
  6. for i in range(4):
  7. print(D.forward(generate_random_image((1, 3, 128, 128))).item())
  8. pass

生成器网络

celeba_gan_cnn_2.PNG

  1. class Generator(nn.Module):
  2. def __init__(self):
  3. # 初始化pytorch父类
  4. super().__init__()
  5. # 定义神经网络层
  6. self.model = nn.Sequential(
  7. # 输入是一个一维数组(100个种子)
  8. nn.Linear(100, 3*11*11),
  9. nn.LeakyReLU(0.2),
  10. # 转换成四维
  11. View((1, 3, 11, 11)),
  12. nn.ConvTranspose2d(3, 256, kernel_size=8, stride=2), # 转置卷积,256个卷积核
  13. nn.BatchNorm2d(256),
  14. nn.LeakyReLU(0.2),
  15. nn.ConvTranspose2d(256, 256, kernel_size=8, stride=2),
  16. nn.BatchNorm2d(256),
  17. nn.LeakyReLU(0.2),
  18. nn.ConvTranspose2d(256, 3, kernel_size=8, stride=2, padding=1), # 因为输出要红绿蓝三通道,因此使用三个转置卷积核;padding用于从中间网格中去掉外围的方格
  19. nn.BatchNorm2d(3),
  20. nn.Sigmoid()
  21. # 输出为 (1, 3, 128, 128)
  22. )
  23. # 不需要损失函数!!!
  24. # 创建优化器,使用随机梯度下降
  25. self.optimiser = torch.optim.Adam(self.parameters(), lr=0.0001) # 使用Adam优化器
  26. # 计数器和进程记录
  27. self.counter = 0
  28. self.progress = []
  29. pass
  30. def forward(self, inputs):
  31. # 直接运行模型
  32. return self.model(inputs)
  33. def train(self, D, inputs, targets):
  34. # 计算网络的输出
  35. g_outputs = self.forward(inputs)
  36. # 训练生成器需要鉴别器的损失值
  37. # 将生成器网络的输出输入到鉴别器
  38. d_outputs = D.forward(g_outputs)
  39. # 计算鉴别器的损失值
  40. loss = D.loss_function(d_outputs, targets)
  41. # 每训练10次增加计数器
  42. self.counter += 1
  43. if(self.counter % 10 == 0):
  44. self.progress.append(loss.item())
  45. pass
  46. # 鉴别器训练中不打印,这样可以通过真实的训练数据更准确地反应训练进度
  47. # 归零梯度,反向传播,并更新权重
  48. self.optimiser.zero_grad()
  49. loss.backward()
  50. self.optimiser.step()
  51. pass
  52. def plot_progress(self): # 打印训练过程
  53. df = pandas.DataFrame(self.progress, columns=['loss'])
  54. df.plot(ylim=(0), figsize=(16, 8), alpha=0.1, marker='.', grid=True, yticks=(0, 0.25, 0.5, 1.0, 5.0))
  55. pass
  56. pass

检查生成器输出

  1. # 检查生成器的输出大小,并确保运行没有错误
  2. G = Generator()
  3. # 将模型转存到CUDA设备
  4. G.to(device)
  5. output = G.forward(generate_random_seed(100))
  6. img = output.detach().permute(0, 2, 3, 1).view(128, 128, 3).cpu().numpy() # 用detach将输出图像与pytorch的计算图分离,存回CPU并转换成numpy数组
  7. # 上面permute(0,2,3,1)将numpy数组(批量大小,通道数3,高,宽)重新排序为(批量大小,高,宽,通道数3);view(128, 128, 3)则去掉了批量大小这一维度,将numpy转为三维张量(高,宽,通道数)
  8. plt.imshow(img, interpolation='none', cmap='Blues')

训练GAN

  1. %%time
  2. # 创建鉴别器和生成器
  3. D = Discriminator()
  4. D.to(device)
  5. G= Generator()
  6. G.to(device)
  7. epochs = 1
  8. for epoch in range(epochs):
  9. print("epoch = ", epoch + 1)
  10. # 训练鉴别器和生成器
  11. for image_data_tensor in celeba_dataset:
  12. # 第一步:用真实样本训练鉴别器
  13. D.train(image_data_tensor, torch.cuda.FloatTensor([1.0]))
  14. # 第二步:用生成样本训练鉴别器
  15. # 使用detach()以避免计算生成器G中的梯度,节省计算成本
  16. D.train(G.forward(generate_random_seed(100)).detach(), torch.cuda.FloatTensor([0.0]))
  17. # 第三步:训练生成器
  18. G.train(D, generate_random_seed(100), torch.cuda.FloatTensor([1.0]))
  19. pass
  20. pass
  1. # 绘制鉴别器的损失图
  2. D.plot_progress()
  1. # 绘制生成器的损失图
  2. G.plot_progress()
  3. # 实际上,鉴别器和生成器的二元交叉熵损失值最理想的情况是ln2=0.693

运行生成器

  1. # 从训练好的生成器绘制一些输出
  2. # 在3行2列的网格中绘制生成图像(检查生成图像的多样性)
  3. f, axarr = plt.subplots(2, 3, figsize=(16, 8))
  4. for i in range(2):
  5. for j in range(3):
  6. output = G.forward(generate_random_seed(100))
  7. img = output.detach().permute(0, 2, 3, 1).view(128, 128, 3).cpu().numpy()
  8. axarr[i, j].imshow(img, interpolation='none', cmap='Blues')
  9. pass
  10. pass

条件式 GAN - MNIST 生成指定数字图像

16_cgan_mnist.ipynb

  • 挂载驱动器以访问 google drive 中的文件 ```python

    mount Drive to access data files

from google.colab import drive drive.mount(‘./mount’)

  1. - 导入包
  2. ```python
  3. import torch
  4. import torch.nn as nn
  5. from torch.utils.data import Dataset
  6. import pandas, numpy, random
  7. import matplotlib.pyplot as plt

数据类

  1. class MnistDataset(Dataset): # 对于继承自Dataset的数据集,需要提供__len__()函数和__getitem__()函数
  2. def __init__(self, csv_file):
  3. self.data_df = pandas.read_csv(csv_file, header=None)
  4. pass
  5. def __len__(self): # 获取数据集大小(返回数据集中的项目总数)
  6. return len(self.data_df)
  7. def __getitem__(self, index): # 通过索引获取数据集中的项目
  8. # 目标图像(标签)
  9. label = self.data_df.iloc[index, 0]
  10. target = torch.zeros((10))
  11. target[label] = 1.0 # 转换为one-hot形式的标签向量
  12. # 图像数据,取值范围是0-255,标准化为0-1
  13. image_values = torch.FloatTensor(self.data_df.iloc[index, 1:].values) / 255.0
  14. # 返回标签、图像数据张量以及目标张量
  15. return label, image_values, target
  16. def plot_image(self, index): # 通过索引号,绘制对应编号的图
  17. img = self.data_df.iloc[index, 1:].values.reshape(28, 28)
  18. plt.title("label = " + str(self.data_df.iloc[index, 0]))
  19. plt.imshow(img, interpolation='none', cmap='Blues')
  20. pass
  21. pass
  1. # 测试Dataset类是否可以正常工作
  2. # 加载数据
  3. mnist_dataset = MnistDataset('mount/My Drive/Colab Notebooks/pytorch_gan/mnist_data/mnist_train.csv')
  1. # 检查数据包含图像
  2. mnist_dataset.plot_image(17)

生成随机数据的辅助函数

  1. # 生成随机数据的函数
  2. def generate_random_image(size):
  3. random_data = torch.rand(size)
  4. return random_data
  5. def generate_random_seed(size):
  6. random_data = torch.randn(size)
  7. return random_data
  8. # size here must only be an integer
  9. def generate_random_one_hot(size):
  10. label_tensor = torch.zeros((size))
  11. random_idx = random.randint(0,size-1)
  12. label_tensor[random_idx] = 1.0
  13. return label_tensor

鉴别器类

  1. class Discriminator(nn.Module):
  2. def __init__(self):
  3. # 初始化pytorch父类
  4. super().__init__()
  5. # 定义神经网络层
  6. self.model = nn.Sequential(
  7. nn.Linear(784+10, 200), # 输入:图像张量28*28=784 + 标签张量长度10
  8. nn.LeakyReLU(0.02),
  9. nn.LayerNorm(200),
  10. nn.Linear(200, 1),
  11. nn.Sigmoid()
  12. )
  13. # 创建损失函数
  14. self.loss_function = nn.BCELoss()
  15. # 创建优化器,使用随机梯度下降
  16. self.optimiser = torch.optim.Adam(self.parameters(), lr=0.0001) # 改良4:使用Adam优化器
  17. # 计数器和进程记录
  18. self.counter = 0
  19. self.progress = []
  20. pass
  21. def forward(self, image_tensor, label_tensor): # 扩展forward()函数,使其同时接收图像张量和标签张量,并将它们拼接起来
  22. # 拼接种子和标签
  23. inputs = torch.cat((image_tensor, label_tensor))
  24. return self.model(inputs)
  25. def train(self, inputs, label_tensor, targets): # 扩展:接收标签张量
  26. # 计算网络的输出
  27. outputs = self.forward(inputs, label_tensor) # 同时接收图像张量和标签张量
  28. # 计算损失值
  29. loss = self.loss_function(outputs, targets)
  30. # 每训练10次增加计数器
  31. self.counter += 1
  32. if(self.counter % 10 == 0):
  33. self.progress.append(loss.item())
  34. pass
  35. if(self.counter % 10000 == 0):
  36. print("counter = ", self.counter)
  37. pass
  38. # 归零梯度,反向传播,并更新权重
  39. self.optimiser.zero_grad()
  40. loss.backward()
  41. self.optimiser.step()
  42. pass
  43. def plot_progress(self): # 打印训练过程
  44. df = pandas.DataFrame(self.progress, columns=['loss'])
  45. df.plot(ylim=(0), figsize=(16, 8), alpha=0.1, marker='.', grid=True, yticks=(0, 0.25, 0.5, 1.0, 5.0))
  46. pass
  47. pass

测试鉴别器

  1. %%time
  2. # 测试鉴别器,确保其至少能将真实图像与随机噪声区分开
  3. # 训练鉴别器,奖励鉴别器将真实的训练数据判别为真,也就是输出1.0;将伪造的生成数据判别为假,也就是输出0.0
  4. D = Discriminator()
  5. for label, image_data_tensor, label_tensor in mnist_dataset:
  6. # 真实数据
  7. D.train(image_data_tensor, label_tensor, torch.FloatTensor([1.0]))
  8. # 生成数据
  9. D.train(generate_random_image(784), generate_random_one_hot(10), torch.FloatTensor([0.0]))
  10. pass
  1. # 绘制训练过程中的损失值变化
  2. D.plot_progress()
  3. # 损失之下降并一直保持接近0的值,这正是我们希望达到的效果
  1. # 随机选取一些训练集中的图像和随机噪声图像,分别作为输入来测试训练后的鉴别器
  2. for i in range(4):
  3. label, image_data_tensor, label_tensor = mnist_dataset[random.randint(0, 6000)]
  4. print(D.forward(image_data_tensor, label_tensor).item())
  5. pass
  6. for i in range(4):
  7. print(D.forward(generate_random_image(784), generate_random_one_hot(10)).item())
  8. pass

生成器类

gan_mnist_1.PNG
条件式GAN架构:要让训练后的GAN生成器生成一个指定数字的图像,因此生成器和鉴别器的输入都在图像数据的基础上加入了类型标签
cgan_mnist.PNG

  1. class Generator(nn.Module):
  2. def __init__(self):
  3. # 初始化pytorch父类
  4. super().__init__()
  5. # 定义神经网络层
  6. self.model = nn.Sequential(
  7. nn.Linear(100+10, 200),
  8. nn.LeakyReLU(0.02),
  9. nn.LayerNorm(200),
  10. nn.Linear(200, 784),
  11. nn.Sigmoid()
  12. )
  13. # 不需要损失函数!!!
  14. # 创建优化器,使用随机梯度下降
  15. self.optimiser = torch.optim.Adam(self.parameters(), lr=0.0001)
  16. # 计数器和进程记录
  17. self.counter = 0
  18. self.progress = []
  19. pass
  20. def forward(self, seed_tensor, label_tensor):
  21. # 拼接种子和标签
  22. inputs = torch.cat((seed_tensor, label_tensor))
  23. return self.model(inputs)
  24. def train(self, D, inputs, label_tensor, targets):
  25. # 计算网络的输出
  26. g_outputs = self.forward(inputs, label_tensor)
  27. # 训练生成器需要鉴别器的损失值
  28. # 将生成器网络的输出输入到鉴别器
  29. d_outputs = D.forward(g_outputs, label_tensor)
  30. # 计算鉴别器的损失值
  31. loss = D.loss_function(d_outputs, targets)
  32. # 每训练10次增加计数器
  33. self.counter += 1
  34. if(self.counter % 10 == 0):
  35. self.progress.append(loss.item())
  36. pass
  37. # 鉴别器训练中不打印,这样可以通过真实的训练数据更准确地反应训练进度
  38. # 归零梯度,反向传播,并更新权重
  39. self.optimiser.zero_grad()
  40. loss.backward()
  41. self.optimiser.step()
  42. pass
  43. # 这个函数不应该放在类里把,应该单独拎出来,下面imshow那个G.forward,G是哪来的?
  44. def plot_images(self, label):
  45. label_tensor = torch.zeros((10))
  46. label_tensor[label] = 1.0
  47. # 在3行2列的网格中生成图像
  48. f, axarr = plt.subplots(2, 3, figsize=(16, 8))
  49. for i in range(2):
  50. for j in range(3):
  51. axarr[i, j].imshow(G.forward(generate_random_seed(100), label_tensor).detach().cpu().numpy().reshape(28, 28), interpolation='none', cmap='Blues')
  52. pass
  53. pass
  54. pass
  55. def plot_progress(self): # 打印训练过程
  56. df = pandas.DataFrame(self.progress, columns=['loss'])
  57. df.plot(ylim=(0), figsize=(16, 8), alpha=0.1, marker='.', grid=True, yticks=(0, 0.25, 0.5, 1.0, 5.0))
  58. pass
  59. pass

检查生成器输出

  1. # 训练生成器前,先检查生成器输出格式是否正确
  2. G = Generator()
  3. output = G.forward(generate_random_seed(100), generate_random_one_hot(10))
  4. img = output.detach().numpy().reshape(28, 28)
  5. plt.imshow(img, interpolation='none', cmap='Blues')

训练GAN

  1. %%time
  2. # 训练GAN
  3. # 创建鉴别器和生成器
  4. D = Discriminator()
  5. G = Generator()
  6. epochs = 4
  7. # 训练鉴别器和生成器
  8. for epoch in range(epochs):
  9. print("epoch = ", epoch + 1)
  10. for label, image_data_tensor, label_tensor in mnist_dataset:
  11. # 第一步:用真实样本训练鉴别器
  12. D.train(image_data_tensor, label_tensor, torch.FloatTensor([1.0]))
  13. # 为鉴别器生成一个随机独热标签(用生成图像训练鉴别器时,对生成器和鉴别器输入同一标签张量)
  14. random_label = generate_random_one_hot(10)
  15. # 第二步:用生成样本训练鉴别器
  16. # 使用detach()以避免计算生成器G中的梯度,节省计算成本
  17. D.train(G.forward(generate_random_seed(100), random_label).detach(), random_label, torch.FloatTensor([0.0]))
  18. # 为生成器另外生成一个随机独热标签
  19. random_label = generate_random_one_hot(10)
  20. # 第三步:训练生成器(输入鉴别器对象和随机单值输入来训练生成器)
  21. G.train(D, generate_random_seed(100), random_label, torch.FloatTensor([1.0]))
  22. pass
  23. pass
  1. # 绘制鉴别器在训练中的损失值变化图
  2. D.plot_progress()
  3. # 损失值迅速下降到接近于0,并一直保持在很低的位置。训练期间,损失值偶尔发生跳跃。这说明生成器和鉴别器之间仍然没有取得平衡
  1. # 绘制生成器训练过程中的损失值变化图
  2. G.plot_progress()
  3. # 损失值先是上升,表示在训练早期生成器落后于鉴别器。之后,损失值下降并保持在3左右。记住,与MSELoss不同,BCELoss没有1.0的上限

运行生成器

  1. # 试验训练后的生成器会生成什么样的图像
  2. # 由于不同的随机种子应当生成不同的图像,所以绘制多幅输出图像并查看
  3. label = 9
  4. label_tensor = torch.zeros((10))
  5. label_tensor[label] = 1.0
  6. # 在3行2列的网格中生成图像
  7. f, axarr = plt.subplots(2, 3, figsize=(16, 8))
  8. for i in range(2):
  9. for j in range(3):
  10. output = G.forward(generate_random_seed(100), label_tensor)
  11. img = output.detach().cpu().numpy().reshape(28, 28)
  12. axarr[i, j].imshow(img, interpolation='none', cmap='Blues')
  13. pass
  14. pass
  15. # 由图可知,生成的图像不是随机噪声,而是有某种形状
  16. # 但这些图像看起来都相同,这种现象称为“模式崩溃”

总结

  • 训练 GAN 时的理想状态:生成器与鉴别器达到平衡,生成器已经学会了生成看起来足以以假乱真的数据,使得鉴别器无法区分真实数据与生成器生成的数据
  • 训练 GAN 时的理想损失值:(即生成器和鉴别器达到平衡时的损失值)

    • 均方误差 MSE Loss:0.25
      • 即鉴别器无法从伪造数据中识别真实数据,就无法确定输出0.0还是1.0,索性就输出0.5,因此均方误差为 《Pytorch生成对抗网络编程》塔里克·拉希德 - 图5
    • 二元交叉熵 BCE Lossln2 ≈ 0.693
  • GAN 不会学习记忆训练数据中的实例,GAN 学习的是训练数据中每个元素出现的可能性(即概率分布)

  • 模式崩溃

    • eg. 使用 MNIST 数据集训练 GAN,希望生成所有 10 个数字,也就是说希望能学习到所有 10 个数字图像的概率分布
    • 但,有可能训练出的GAN只会生成其中一种数字,也就是模式崩溃,即生成器只学会了一类图像的概率分布
  • 梯度下降并不适合对抗博弈