半监督学习是一个具有挑战性的问题,它需要在一个包含少量标记样例和大量未标记样例的数据集中训练一个分类器。
生成式对抗网络(GAN)是一种有效利用大型未标记数据集,通过图像鉴别器模型训练图像生成器模型的体系结构。在某些情况下,鉴别器模型可以作为开发分类器模型的起点。
半监督GAN(或称SGAN)模型是GAN体系结构的扩展,它涉及同时训练监督鉴别器、非监督鉴别器和生成器模型。结果是一个监督分类模型,可以很好地推广到不可见的例子和一个生成器模型,可以输出来自该领域的图像的可信的例子。
在本教程中,您将了解如何从头开始开发一个半监督的生成式对抗网络。
完成本教程后,您将知道:
- 半监督GAN是GAN体系结构的扩展,用于训练分类器模型,同时利用有标记和无标记数据。
- 在Keras中,至少有三种实现半监督GAN中监督和非监督鉴别器模型的方法。
- 如何在MNIST和负载上从无到有地训练半监督GAN,并使用训练好的分类器进行预测。
Let’s get started
教程概述
本教程分为四个部分;它们是:
1、半监督GAN是什么?
2、如何实现半监督鉴别器模型
3、如何为MNIST开发半监督GAN
4、如何加载和使用最终的SGAN分类器模型
半监督GAN是什么?
半监督学习是指需要一个预测模型,并且有标记的例子很少,而没有标记的例子很多的问题。
最常见的例子是一个分类预测建模问题,其中可能有一个非常大的数据集的例子,但只有一小部分有目标标签。该模型必须从小的标记示例集合中学习,并以某种方式利用更大的未标记示例数据集,以便在将来推广到对新示例进行分类。
半监督GAN(有时简称SGAN)是用于解决半监督学习问题的生成式对抗网络体系结构的扩展。
"这项工作的主要目标之一是提高生成式对抗性网络在半监督学习中的有效性(在这种情况下,通过对其他未标记示例的学习来提高监督任务的性能,即分类)。"
— Improved Techniques for Training GANs, 2016.
传统GAN中的鉴别器被训练来预测给定图像是真实的(来自数据集)还是伪造的(生成的),从而使它能够从未标记的图像中学习特征。然后,在为同一数据集开发分类器时,可以通过转移学习将该鉴别器作为起点使用,从而使监督预测任务受益于GAN的非监督训练。
在半监督GAN中,将识别器模型更新为预测K+1类,其中K为预测问题中的类数,并为新的“伪”类添加额外的类标签。它涉及到同时对无监督GAN任务和有监督分类任务直接训练鉴别器模型。
"我们将生成模型G和鉴别器D训练在一个数据集上,该数据集的输入属于N个类中的一个。在训练时,D用来预测N+1个类中输入属于哪个类,其中增加一个额外的类来对应G的输出。"
— Semi-Supervised Learning with Generative Adversarial Networks, 2016.
因此,鉴别器训练有两种模式:监督模式和非监督模式。
- 无监督训练:在无监督模式下,识别器以与传统GAN相同的方式进行训练,以预测样本是真还是假。
- 监督训练:在监督模式下,训练鉴别器预测真实例子的类别标签。
在无监督模式下的训练允许模型从大量未标记的数据集中学习有用的特征提取功能,而在监督模式下的训练允许模型使用提取的特征并应用类标签。
这样的结果是一个分类器模型,它可以在标准问题上获得最新的结果,比如MNIST,当它只训练很少的有标记的例子时,比如几十个,几百个,或者1000个。此外,该训练过程还可以使生成器模型输出的图像质量更好。
例如,Augustus Odena 在2016年的论文题为“Semi-Supervised Learning with Generative Adversarial Networks”展示了如何GAN-trained分类器能够执行以及或比一个独立的CNN模型MNIST手写数字识别任务训练时25岁,50岁,100年和1000年标签的例子。

引自:具有生成式对抗网络的半监督学习标题
OpenAI的Tim Salimans等人在他们2016年发表的论文 “Improved Techniques for Training GANs”中,使用半监督的GAN(包括MNIST)完成了当时最先进的图像分类任务。
如何实现半监督鉴别器模型
有许多方法可以实现半监督GAN的鉴别器模型。
在本节中,我们将回顾三种候选方法。
传统的鉴别器模型
考虑一个标准GAN模型的鉴别器模型。
它必须以一个图像作为输入,并预测它是真的还是假的。更具体地说,它预测输入图像是真实的可能性。输出层使用sigmoid激活函数来预测[0,1]中的概率值,模型通常使用二元交叉熵损失函数进行优化。
例如,我们可以定义一个简单的识别器模型,该模型以灰度图像为输入,大小为28×28像素,预测图像的真实概率。我们可以使用最佳实践和采样图像使用卷积层与2×2的步幅和 leaky ReLU 激活函数。
下面的define_discriminator()函数实现了这个功能,并定义了我们的标准鉴别器模型。
# example of defining the discriminator model
from keras.models import Model
from keras.layers import Input
from keras.layers import Dense
from keras.layers import Conv2D
from keras.layers import LeakyReLU
from keras.layers import Dropout
from keras.layers import Flatten
from keras.optimizers import Adam
from keras.utils.vis_utils import plot_model
# define the standalone discriminator model
def define_discriminator(in_shape=(28,28,1)):
# image input
in_image = Input(shape=in_shape)
# downsample
fe = Conv2D(128, (3,3), strides=(2,2), padding='same')(in_image)
fe = LeakyReLU(alpha=0.2)(fe)
# downsample
fe = Conv2D(128, (3,3), strides=(2,2), padding='same')(fe)
fe = LeakyReLU(alpha=0.2)(fe)
# downsample
fe = Conv2D(128, (3,3), strides=(2,2), padding='same')(fe)
fe = LeakyReLU(alpha=0.2)(fe)
# flatten feature maps
fe = Flatten()(fe)
# dropout
fe = Dropout(0.4)(fe)
# output layer
d_out_layer = Dense(1, activation='sigmoid')(fe)
# define and compile discriminator model
d_model = Model(in_image, d_out_layer)
d_model.compile(loss='binary_crossentropy', optimizer=Adam(lr=0.0002, beta_1=0.5))
return d_model
# create model
model = define_discriminator()
# plot the model
plot_model(model, to_file='discriminator_plot.png', show_shapes=True, show_layer_names=True)
运行该示例将创建鉴别器模型的一个图,清楚地显示输入图像的28x28x1形状和单个概率值的预测。

使用共享权重的独立鉴别器模型
从标准的GAN鉴别器模型开始,我们可以更新它来创建两个共享特征提取权值的模型。
具体来说,我们可以定义一个分类器模型来预测输入图像是真还是假,然后定义第二个分类器模型来预测给定模型的类。
- Binary Classifier Model. 预测图像的真伪,在输出层使用sigmoid激活函数,并利用binary cross entropy loss function.进行优化。
- Multi-Class Classifier Model. 预测了图像的类别、输出层中的softmax激活函数,并利用categorical cross entropy loss function进行了优化。
这两个模型都有不同的输出层,但是共享所有的特征提取层。这意味着对其中一个分类器模型的更新将影响两个模型。
下面的示例首先创建具有二进制输出的传统鉴别器模型,然后重用特征提取层并创建一个新的多类预测模型,在本例中为10个类。
# example of defining semi-supervised discriminator model
from keras.models import Model
from keras.layers import Input
from keras.layers import Dense
from keras.layers import Conv2D
from keras.layers import LeakyReLU
from keras.layers import Dropout
from keras.layers import Flatten
from keras.optimizers import Adam
from keras.utils.vis_utils import plot_model
# define the standalone supervised and unsupervised discriminator models
def define_discriminator(in_shape=(28,28,1), n_classes=10):
# image input
in_image = Input(shape=in_shape)
# downsample
fe = Conv2D(128, (3,3), strides=(2,2), padding='same')(in_image)
fe = LeakyReLU(alpha=0.2)(fe)
# downsample
fe = Conv2D(128, (3,3), strides=(2,2), padding='same')(fe)
fe = LeakyReLU(alpha=0.2)(fe)
# downsample
fe = Conv2D(128, (3,3), strides=(2,2), padding='same')(fe)
fe = LeakyReLU(alpha=0.2)(fe)
# flatten feature maps
fe = Flatten()(fe)
# dropout
fe = Dropout(0.4)(fe)
# unsupervised output
d_out_layer = Dense(1, activation='sigmoid')(fe)
# define and compile unsupervised discriminator model
d_model = Model(in_image, d_out_layer)
d_model.compile(loss='binary_crossentropy', optimizer=Adam(lr=0.0002, beta_1=0.5))
# supervised output
c_out_layer = Dense(n_classes, activation='softmax')(fe)
# define and compile supervised discriminator model
c_model