1.4半监督生成对抗网络(SGAN)
SGAN是一种半监督生成对抗网络,结合生成器和鉴别器进行训练。其核心在于利用少量标签数据和大量无标签数据进行学习,通过生成器生成伪样本和鉴别器区分真伪样本,同时鉴别器需对真实样本进行多分类以提高准确性。SGAN的鉴别器不仅用于二分类(真伪区分),还用于多分类(类别预测),通过监督学习提升生成器的逼真度。实验结果表明,SGAN在训练集上达到了100%的分类准确率,在测试集上获得了89%的精度。
1.SGAN简介
半监督学习仅用于为训练数据集的一小部分分配类别标签。数据内部的潜在结构为半监督学习提供了基础,使其能够从标注数据集中抽取小样本,并对未知样例进行精确分类。
要使半监督学习有效,标签数据和无标签数据必须来自相同分布。
半监督生成对抗网络是一种生成对抗网络,其分类器为多分类器,不仅能够区分正反两类,而是能够识别N+1类,其中N为训练数据集的类别数,生成模块生成的假样本属于特定类别。

相对于其他生成对抗网络,SGAN的核心关注点是鉴别器。在训练过程中,该网络最终目标是成为仅依赖部分标签数据的半监督分类器,其性能表现与全监督分类器相近,即每个样本都带有标签的半监督分类器。
1.2架构图

生成器负责将随机噪音转换为伪样本;鉴别器接收有标签的真实图像(x,y)、无标签的真实图像(x)以及生成器生成的伪图像(x*)。首先使用sigmoid函数区分真伪,接着采用softmax进行类别划分。
2.代码实现
2.1导入声明
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from keras import backend as K
from keras.datasets import mnist
from keras.layers import (Activation, BatchNormalization, Concatenate, Dense,
Dropout, Flatten, Input, Lambda, Reshape)
from keras.layers.advanced_activations import LeakyReLU
from keras.layers.convolutional import Conv2D, Conv2DTranspose
from keras.models import Model, Sequential
from keras.optimizer_v2 import adam as Adam
from tensorflow.keras.utils import to_categorical
2.2模型输入维度
img_rows = 28
img_cols = 28
channels = 1
# 输入图像维度
img_shape = (img_rows, img_cols, channels)
# 噪声向量的大小,用作生成器的输入
z_dim = 100
# 数据集中类别的数量
num_classes = 10
2.3数据集
此处采用的是MNIST数据集,该数据集总共包含有50000张图片,每张图片都带有标签。但其中仅选取一部分图片用于训练,其余图片则假设没有标签。
class Dataset:
def __init__(self, num_labeled):
# 训练中使用的有标签图像的数量
self.num_labeled = num_labeled
# 加载MINST数据集
(self.x_train, self.y_train), (self.x_test,
self.y_test) = mnist.load_data()
def preprocess_imgs(x):
# 灰度像素值从[0,255]缩放到[-1,1]
x = (x.astype(np.float32) - 127.5) / 127.5
# 将图像尺寸扩展到宽*高*通道数
x = np.expand_dims(x, axis=3)
return x
def preprocess_labels(y):
return y.reshape(-1, 1)
# 训练数据
self.x_train = preprocess_imgs(self.x_train)
self.y_train = preprocess_labels(self.y_train)
# 测试数据
self.x_test = preprocess_imgs(self.x_test)
self.y_test = preprocess_labels(self.y_test)
def batch_labeled(self, batch_size):
#获取随机批量有标签图像及其标签
idx = np.random.randint(0, self.num_labeled, batch_size)
imgs = self.x_train[idx]
labels = self.y_train[idx]
return imgs, labels
def batch_unlabeled(self, batch_size):
# 获取随机批量的无标签图像
idx = np.random.randint(self.num_labeled, self.x_train.shape[0],
batch_size)
imgs = self.x_train[idx]
return imgs
def training_set(self):
x_train = self.x_train[range(self.num_labeled)]
y_train = self.y_train[range(self.num_labeled)]
return x_train, y_train
def test_set(self):
return self.x_test, self.y_test
# 要使用的有标签样本的数量
num_labeled = 100
dataset = Dataset(num_labeled)
2.4生成器
此处的生成器网络与DCGAN网络具有相同的结构,通过转置卷积层,将低维的随机噪声向量映射到高维的空间,从而生成与目标图像尺寸(28281)一致的图像。
def build_generator(z_dim):
model = Sequential()
# 通过一个全连接层改变输入为一个7*7*256的张量
model.add(Dense(256 * 7 * 7, input_dim=z_dim))
model.add(Reshape((7, 7, 256)))
# 转置卷积层,张量从7*7*256变为14*14*128
model.add(Conv2DTranspose(128, kernel_size=3, strides=2, padding='same'))
# 批归一化
model.add(BatchNormalization())
# Leaky ReLU 的激活函数
model.add(LeakyReLU(alpha=0.01))
# 转置卷积层,张量从14*14*128变为14*14*64
model.add(Conv2DTranspose(64, kernel_size=3, strides=1, padding='same'))
# 拟归一化
model.add(BatchNormalization())
# Leaky ReLU 的激活函数
model.add(LeakyReLU(alpha=0.01))
#转置卷积层,张量从14*14*64变为28*28*1
model.add(Conv2DTranspose(1, kernel_size=3, strides=2, padding='same'))
# 带有tanh激活函数的输出层
model.add(Activation('tanh'))
return model
2.5鉴别器
有着双重目标:
区别真实样本和伪样本。(使用sigmoid函数)
对于真实样本,还要对其标签进行分类。(使用softmax函数)
2.5.1核心鉴别网络
添加一个dropout,通过在训练过程中随机丢弃神经元来防止过拟合。
def build_discriminator_net(img_shape):
model = Sequential()
# 卷积层,张量从28*28*1变成14*14*32
model.add(
Conv2D(32,
kernel_size=3,
strides=2,
input_shape=img_shape,
padding='same'))
# Leaky ReLU 的激活函数
model.add(LeakyReLU(alpha=0.01))
# 卷积层,张量从14*14*32到7*7*64
model.add(
Conv2D(64,
kernel_size=3,
strides=2,
input_shape=img_shape,
padding='same'))
# 批归一化
model.add(BatchNormalization())
# Leaky ReLU 激活函数
model.add(LeakyReLU(alpha=0.01))
# 卷积层,张量从7*7*64变成3*3128
model.add(
Conv2D(128,
kernel_size=3,
strides=2,
input_shape=img_shape,
padding='same'))
# 批归一化
model.add(BatchNormalization())
# Leaky ReLU 激活函数
model.add(LeakyReLU(alpha=0.01))
# Droupout正则
model.add(Dropout(0.5))
# 将张量展平
model.add(Flatten())
# 于unm_classes神经元完全连接的层
model.add(Dense(num_classes))
return model
2.5.2有监督的多分类鉴定器
def build_discriminator_supervised(discriminator_net):
model = Sequential()
model.add(discriminator_net)
# Softmax 激活函数, 输出真实类别的预测概率分布
model.add(Activation('softmax'))
return model
2.5.3无监督的二分类鉴定器
def build_discriminator_unsupervised(discriminator_net):
model = Sequential()
model.add(discriminator_net)
def predict(x):
# 将真实类别的分布转换为二元真——假概率
prediction = 1.0 - (1.0 /
(K.sum(K.exp(x), axis=-1, keepdims=True) + 1.0))
return prediction
model.add(Lambda(predict))
return model
2.6构建模型
def build_gan(generator, discriminator):
model = Sequential()
# 结合生成器和鉴定器模型
model.add(generator)
model.add(discriminator)
return model
鉴定器
# 核心鉴定器网络:在有监督和无监督中共享
discriminator_net = build_discriminator_net(img_shape)
#构建有监督鉴定器
discriminator_supervised = build_discriminator_supervised(discriminator_net)
discriminator_supervised.compile(loss='categorical_crossentropy',
metrics=['accuracy'],
optimizer=Adam.Adam())
# 构建无监督鉴定器
discriminator_unsupervised = build_discriminator_unsupervised(discriminator_net)
discriminator_unsupervised.compile(loss='binary_crossentropy',
optimizer=Adam.Adam())
# 构建生成器
generator = build_generator(z_dim)
# 生成器训练时保持鉴定器参数不变
discriminator_unsupervised.trainable = False
# 构建固定参数的鉴别器,以训练生成器
# 鉴别器使用无监督版本
gan = build_gan(generator, discriminator_unsupervised)
gan.compile(loss='binary_crossentropy', optimizer=Adam.Adam())
2.7训练
supervised_losses = []
iteration_checkpoints = []
def train(iterations, batch_size, sample_interval):
# 真实图像的标签:全为1
real = np.ones((batch_size, 1))
# 伪图像的标签:全为0
fake = np.zeros((batch_size, 1))
for iteration in range(iterations):
# -------------------------
# 训练鉴定器
# -------------------------
# 获得标签样本
imgs, labels = dataset.batch_labeled(batch_size)
# 独热编码标签
labels = to_categorical(labels, num_classes=num_classes)
# 获得无标签样本
imgs_unlabeled = dataset.batch_unlabeled(batch_size)
# 生成一批伪图像
z = np.random.normal(0, 1, (batch_size, z_dim))
gen_imgs = generator.predict(z)
# 训练有标签的真实样本
d_loss_supervised, accuracy = discriminator_supervised.train_on_batch(imgs, labels)
#训练无标签的真实样本
d_loss_real = discriminator_unsupervised.train_on_batch(
imgs_unlabeled, real)
# 训练伪样本
d_loss_fake = discriminator_unsupervised.train_on_batch(gen_imgs, fake)
d_loss_unsupervised = 0.5 * np.add(d_loss_real, d_loss_fake)
# ---------------------
# 训练生成器
# ---------------------
# 生成一批次假图像
z = np.random.normal(0, 1, (batch_size, z_dim))
gen_imgs = generator.predict(z)
# 训练生成器
g_loss = gan.train_on_batch(z, np.ones((batch_size, 1)))
if (iteration + 1) % sample_interval == 0:
# 保存鉴别器的有监督分类损失,以便绘制损失曲线
supervised_losses.append(d_loss_supervised)
iteration_checkpoints.append(iteration + 1)
# 输出训练过程
print(
"%d [D loss supervised: %.4f, acc.: %.2f%%] [D loss unsupervised: %.4f] [G loss: %f]"
% (iteration + 1, d_loss_supervised, 100 * accuracy,
d_loss_unsupervised, g_loss))
2.8. 训练模型
# 设置超参数
iterations = 8000
batch_size = 32
sample_interval = 800
train(iterations, batch_size, sample_interval)
2.9画出鉴定器的监督损失
losses = np.array(supervised_losses)
plt.figure(figsize=(15, 5))
plt.plot(iteration_checkpoints, losses, label="Discriminator loss")
plt.xticks(iteration_checkpoints, rotation=90)
plt.title("Discriminator – Supervised Loss")
plt.xlabel("Iteration")
plt.ylabel("Loss")
plt.legend()
2.10.模型训练和测试准确率
x, y = dataset.training_set()
y = to_categorical(y, num_classes=num_classes)
# 在测试集上计算分类准确率
_, accuracy = discriminator_supervised.evaluate(x, y)
print("Training Accuracy: %.2f%%" % (100 * accuracy))
此处生成的结果是100%的数据(SGAN在训练过程中仅使用了100个有标签的样本进行有监督训练,这可能完全记住了训练数据集)
x, y = dataset.test_set()
y = to_categorical(y, num_classes=num_classes)
# 计算在测试集上的精度
_, accuracy = discriminator_supervised.evaluate(x, y)
print("Test Accuracy: %.2f%%" % (100 * accuracy))
达到了89%
