带标签的infogan及其代码解析

如前所述,infogan的提出的目标在于非监督生成模型,然而我们人类总是对数据有一些确定的知识(如图片表达得数字为几之类的),如果将这些确定的知识结合infogan进行半监督的学习,生成的数据的效果必然会有所提高。

和传统生成模型相似,infogan的结构如图1所示,实质上infogan在结构上和gan相比就是输入上增加了几维潜在变量,而在分类网络后增加了一个识别网络Q用以估计这些潜在变量,其中潜在变量可以是离散的,也可以是连续的。


图1:infogan和gan的结构,图源自Katrina Evtimova和Andrew Drozdov

而infogan的训练也和gan训练类似,采用的是对抗训练的方式,先固定D训练G尽量的去生成符合加入变量C属性的数据来愚弄D,然后训练D和Q来识别图像的真伪和C。而对于如mnist这样的数据库,我们是明显知道数据表达得数字是0到9中的某一类的,我们在输入真实图像时可以加入其标签作为潜在变量C中离散变量的一部分。

这里我们解析的代码基于由Namju Kim开发的sugartensor,代码可在github上下载。

import sugartensor as tf
import numpy as np

sugartensor包有一个好处就在于其可直接插入sugartensor不用再import tensorflow了。

batch_size = 32   # batch size
num_category = 10  # total categorical factor
num_cont = 2  # total continuous factor
num_dim = 50  # total latent dimension

#
# inputs
#

# MNIST input tensor ( with QueueRunner )
data = tf.sg_data.Mnist(batch_size=batch_size)

# input images
x = data.train.image

# generator labels ( all ones )
y = tf.ones(batch_size, dtype=tf.sg_floatx)

# discriminator labels ( half 1s, half 0s )
y_disc = tf.concat(0, [y, y * 0])

这里作者的输入数据维度选择和infogan文章中是一模一样的。对于产生的数据,g的label和d的label是相反的。

z_cat = tf.multinomial(tf.ones((batch_size, num_category), dtype=tf.sg_floatx) / num_category, 1).sg_squeeze().sg_int()

# random seed = random categorical variable + random uniform
z = z_cat.sg_one_hot(depth=num_category).sg_concat(target=tf.random_uniform((batch_size, num_dim-num_category)))

# random continuous variable
z_cont = z[:, num_category:num_category+num_cont]

# category label
label = tf.concat(0, [data.train.label, z_cat])

产生噪声z和隐含变量c,c中含有含有离散变量,离散变量z_cat用于规定产生的数据属于0到9之间的某个数,这里由于输入的数据我们已知其表达的是哪个数了,所以可以给输入数据加入标签并和z_cat拼接在一起。

with tf.sg_context(name='generator', size=4, stride=2, act='relu', bn=True):
    gen = (z.sg_dense(dim=1024)
           .sg_dense(dim=7*7*128)
           .sg_reshape(shape=(-1, 7, 7, 128))
           .sg_upconv(dim=64)
           .sg_upconv(dim=1, act='sigmoid', bn=False))

# add image summary
tf.sg_summary_image(gen)

#
# create discriminator & recognizer
#

# create real + fake image input
xx = tf.concat(0, [x, gen])

with tf.sg_context(name='discriminator', size=4, stride=2, act='leaky_relu'):
    # shared part
    shared = (xx.sg_conv(dim=64)
              .sg_conv(dim=128)
              .sg_flatten()
              .sg_dense(dim=1024))
    # shared recognizer part
    recog_shared = shared.sg_dense(dim=128)
    # discriminator end
    disc = shared.sg_dense(dim=1, act='linear').sg_squeeze()
    # categorical recognizer end
    recog_cat = recog_shared.sg_dense(dim=num_category, act='linear')
    # continuous recognizer end
    recog_cont = recog_shared[batch_size:, :].sg_dense(dim=num_cont, act='sigmoid')
    recog_conts = recog_shared[batch_size:, :].sg_dense(dim=num_cont, act='sigmoid')

这里短短几行代码,作者构建了infogan的网络结构,与原作稍稍不同,我们对于潜在变量C多加入了两维的输出用于估计连续变量的方差。,损失函数如下。

loss_disc = tf.reduce_mean(disc.sg_bce(target=y_disc))  # discriminator loss
loss_gen = tf.reduce_mean(disc.sg_reuse(input=gen).sg_bce(target=y))  # generator loss
loss_recog = tf.reduce_mean(recog_cat.sg_ce(target=label)) \
             + tf.reduce_mean(0.5*tf.identity(tf.square(recog_cont-z_cont)/recog_conts)))  # recognizer loss

train_disc = tf.sg_optim(loss_disc + loss_recog, lr=0.0001, category='discriminator')  # discriminator train ops
train_gen = tf.sg_optim(loss_gen + loss_recog, lr=0.001, category='generator')  # generator train ops

对于离散潜在变量,Q的识别损失为其输出与潜在变量之间的交叉熵。而对于连续变量,Q的识别误差我们改为估计方差为recog_conts时的带权重的l_2范数,训练过程

def alt_train(sess, opt):
    l_disc = sess.run([loss_disc, train_disc])[0]  # training discriminator
    l_gen = sess.run([loss_gen, train_gen])[0]  # training generator
    return np.mean(l_disc) + np.mean(l_gen)

# do training
alt_train(log_interval=10, max_ep=30, ep_size=data.train.num_batch, early_stop=False)

结果

你可能感兴趣的:(神经网络)