- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
理论知识
在条件GAN中,判别器只用来判断图像的真和假,到了条件GAN中,图像本身其实是有标签的,这时候我们可能会想,为什么不直接让判别器输出图像的标签呢?本节要探究的SGAN就实现了这样一个GAN网络。
SGAN将GAN拓展到半监督学习,通过强制判别器D
来输出类别标签来实现。
SGAN在一个数据集上训练一个生成器G
和一个判别器D
,输入是N类中的一个,在训练的时候,判别器D
也被用于预测输入是属于N+1类中的哪一个,这个N+1是对应了生成器G
的输出,这里的判别器D
同时也充当起了分类器C
的效果。
经过实验发现,这种方法可以用于训练效果更好的判别器D
,并且可以比普通的GAN产生更加高质量的样本。
Semi-Supervised GAN有如下成果:
- 作者对GANs做了一个新的扩展,允许它同时学习一个生成模型和一个分类器,我们把这个拓展称为半监督GAN或者SGAN
- 实验结果表明,SGAN在有限数据集上比没有生成部分的基准分类器提升了分类性能
- 实验结果表明,SGAN可以显著地提升生成样本的质量并降低生成器的训练时间
对比生成效果发现,SGAN比普通的DCGAN算法的结果更好。
模型实现
引用、配置参数
import argparse
import os
import numpy as np
import math
from torchvision import datasets, transforms
from torchvision.utils import save_image
from torch.utils.data import DataLoader
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
import torch
# 创建结果输出目录,没有就新增,有就跳过
os.makedirs('images', exist_ok=True)
# 参数
n_epochs = 50 # 训练轮数
batch_size = 64 # 每个批次的样本数量
lr = 0.0002 # 学习率
b1 = 0.5 # Adam优化器的第一个动量衰减参数
b2 = 0.999 # Adam 优化器的第二个动量衰减参数
n_cpu = 8 # 用于批次生成的CPU线程数
latent_dim = 100 # 潜在空间的维度
num_classes = 10 # 数据集的类别数
img_size = 32 # 每个图像的尺寸(高度和宽度相等)
channels = 1 # 图像的通道数(灰度图像通道数为1)
sample_interval = 400 # 图像采样间隔
# 全局设备
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
初始化权重
def weights_init_normal(m):
classname = m.__class__.__name__
if classname.find('Conv')!= -1:
torch.nn.init.normal_(m.weight.data, 0.0, 0.02)
elif classname.find('BatchNorm') != -1:
torch.nn.init.normal_(m.weight.data, 1.0, 0.02)
torch.nn.init.normal_(m.bias.data, 0.0)
定义算法模型
# 生成器
class Generator(nn.Module):
def __init__(self):
super().__init__()
# 创建一个标签嵌入层,用于将条件标签映射到潜在空间
self.label_emb = nn.Embedding(num_classes, latent_dim)
# 初始化图像尺寸, 用于上采样之前
self.init_size = img_size //4
# 第一个全连接层,将随机噪声映射到合适的维度
self.l1 = nn.Sequential(nn.Linear(latent_dim, 128*self.init_size**2))
# 生成器的卷积块
self.conv_blocks = nn.Sequential(
nn.BatchNorm2d(128),
nn.Upsample(scale_factor=2),
nn.Conv2d(128, 128, 3, stride=1, padding=1),
nn.BatchNorm2d(128, 0.8),
nn.LeakyReLU(0.2, inplace=True),
nn.Upsample(scale_factor=2),
nn.Conv2d(128, 64, 3, stride=1, padding=1),
nn.BatchNorm2d(64, 0.8),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(64, channels, 3, stride=1, padding=1),
nn.Tanh(),
)
def forward(self, noise):
out = self.l1