动手实现会写数字的神经网络—半监督学习和生成式对抗网络介绍

在1889年,梵高画了这个美丽的艺术品:星月夜。如今,我的GAN模型只使用20%的标签数据,学会了画MNIST数字!它是怎么实现的?让我们动手做做看。

半监督学习

大多数深度学习分类器需要大量的标签样本才能很好地泛化,但获取这些数据是的过程往往很艰难。为了解决这个限制,半监督学习被提出,它是利用少量标记数据和大量未标记数据的分类技术。许多机器学习研究人员发现,将未标记数据与少量标记数据结合使用时,可以显著提高学习准确性。在半监督学习中,GAN(生成式对抗网络)表现出了很大的潜力,其中分类器可以用很少的标签数据取得良好的表现。

GAN的背景

GAN是深度生成模型的一种。它们特别有趣,因为它们没有明确表示数据所在空间的概率分布。而是通过从中抽取样本,提供了一些不直接与这种概率分布不直接相关的方法。

普通GAN架构

GAN的基本原理是在两个“玩家”之间建立一场比赛:

生成器(G):取随机噪声z作为输入并输出图像x。它的参数被调整以让它产生的假图像从判别器中获得高分。

判别器(D):获取图像X作为输入,并输出一个反映了它对于这是否是真实图像的信心得分。它的参数被调整为:当有真实图像馈送时反馈高分,并且发生器馈送假图像时会反馈低分。

现在,让我们来稍微讨论一下GAN最重要的应用之一,半监督学习。

直觉

普通判别器架构只有一个输出神经元用于分类R / F概率(对/错)。我们同时训练两个网络并在训练完成后丢弃判别器,因为它仅用于改进发生器。

对于半监督任务,除了R / F神经元之外,判别器现在将具有10个用于MNIST数字分类的神经元。而且,这次他们的角色会改变,我们可以在训练后丢弃生成器,其唯一目标是生成未标记的数据以提高判别器的性能。

现在判别器成为了11个类的分类器,其中1个神经元(R / F神经元)代表假数据输出,另外10个代表具有类的实际数据。你必须牢记以下几点:

当来自数据集的真的无监督(或者说标签)数据被馈送时,要保证R / F神经元输出标签= 0

当来自发生器的假的无监督数据被馈送时,要保证R / F神经元输出标签= 1

当真实有监督数据被馈送时,要保证R / F输出标签= 0并且相应的标签输出= 1

不同数据来源的组合将有助于判别器的分类更精确。

架构

现在我们动手进行编码。

判别器

下面的架构与DCGAN 论文中提出的架构类似。我们使用跨卷积(strided convolutions)来减少特征向量的维度,而不是任何池化层,并且为所有层应用一系列的leaky_relu,dropout和BN来稳定学习。输入层和最后一层中BN被舍弃(为了特征匹配)。最后,我们执行全局平均池化(Global Average Pooling)以取得特征向量空间维度上的平均值。这可以将张量维度压缩为单个值。在扁平化了特征之后,为了多类输出增加一个11个类的稠密层和softmax激活函数。

01def discriminator(x, dropout_rate= 0., is_training= True, reuse= False):

02   # input x -> n+1 classes

03   with tf.variable_scope('Discriminator', reuse= reuse):

04     # x = ?*64*64*1

05     

06     #Layer 1

07     conv1= tf.layers.conv2d(x,128, kernel_size= [4,4], strides= [2,2],

08                             padding= 'same', activation= tf.nn.leaky_relu, name= 'conv1')# ?*32*32*128

09     #No batch-norm for input layer

10     dropout1= tf.nn.dropout(conv1, dropout_rate)

11     

12     #Layer2

13     conv2= tf.layers.conv2d(dropout1,256, kernel_size= [4,4], strides= [2,2],

14                             padding= 'same', activation= tf.nn.leaky_relu, name= 'conv2')# ?*16*16*256

15     batch2= tf.layers.batch_normalization(conv2, training= is_training)

16     dropout2= tf.nn.dropout(batch2, dropout_rate)

17     

18     #Layer3

19     conv3= tf.layers.conv2d(dropout2,512, kernel_size= [4,4], strides= [4,4],

20                             padding= 'same', activation= tf.nn.leaky_relu, name= 'conv3')# ?*4*4*512

21     batch3= tf.layers.batch_normalization(conv3, training= is_training)

22     dropout3= tf.nn.dropout(batch3, dropout_rate)

23       

24     # Layer 4

25     conv4= tf.layers.conv2d(dropout3,1024, kernel_size=[3,3], strides=[1,1],

26                              padding='valid',activation= tf.nn.leaky_relu, name='conv4')# ?*2*2*1024

27     # No batch-norm as this layer's op will be used in feature matching loss

28     # No dropout as feature matching needs to be definite on logits

29 

30     # Layer 5

31     # Note: Applying Global average pooling       

32     flatten= tf.reduce_mean(conv4, axis= [1,2])

33     logits_D= tf.layers.dense(flatten, (1 + num_classes))

34     out_D= tf.nn.softmax(logits_D)    

35   return flatten,logits_D,out_D

发生器

发生器架构旨在模仿判别器的空间输出。使用部分跨卷积来增加表示的空间维度。噪声的四维张量的输入z被馈送,它经过转置卷积,relu,BN(输出层除外)和dropout操作。最后,tanh激活将输出图像映射到(-1,1)范围内。

01def generator(z, dropout_rate= 0., is_training= True, reuse= False):

02    # input latent z -> image x

03    with tf.variable_scope('Generator', reuse= reuse):

04      #Layer 1

05      deconv1= tf.layers.conv2d_transpose(z,512, kernel_size= [4,4],

06                                         strides= [1,1], padding= 'valid',

07                                        activation= tf.nn.relu, name= 'deconv1')# ?*4*4*512

08      batch1= tf.layers.batch_normalization(deconv1, training= is_training)

09      dropout1= tf.nn.dropout(batch1, dropout_rate)

10      

11      #Layer 2

12      deconv2= tf.layers.conv2d_transpose(dropout1,256, kernel_size= [4,4],

13                                         strides= [4,4], padding= 'same',

14                                        activation= tf.nn.relu, name= 'deconv2')# ?*16*16*256

15      batch2= tf.layers.batch_normalization(deconv2, training= is_training)

16      dropout2= tf.nn.dropout(batch2, dropout_rate)

17        

18      #Layer 3

19      deconv3= tf.layers.conv2d_transpose(dropout2,128, kernel_size= [4,4],

20                                         strides= [2,2], padding= 'same',

21                                        activation= tf.nn.relu, name= 'deconv3')# ?*32*32*256

22      batch3= tf.layers.batch_normalization(deconv3, training= is_training)

23      dropout3= tf.nn.dropout(batch3, dropout_rate)

24      

25      #Output layer

26      deconv4= tf.layers.conv2d_transpose(dropout3,1, kernel_size= [4,4],

27                                        strides= [2,2], padding= 'same',

28                                        activation= None, name= 'deconv4')# ?*64*64*1

29      out= tf.nn.tanh(deconv4)

30    return out

模型损失

我们首先通过将实际标签附加为零来准备整个批次的扩展标签。这样做是为了在标记数据馈送时,R / F神经元的输出为0。未标记数据的判别器损失可以被认为是一个二元sigmoid损失,通过将R / F神经元输出为1声明假图像,而真实图像输出为0。

01### Discriminator loss ###

02   # Supervised loss -> which class the real data belongs to   

03   temp= tf.nn.softmax_cross_entropy_with_logits_v2(logits= D_real_logit,

04                                                 labels= extended_label)

05   # Labeled_mask and temp are of same size = batch_size where temp is softmax cross_entropy calculated over whole batch

06 

07   D_L_Supervised= tf.reduce_sum(tf.multiply(temp,labeled_mask))/ tf.reduce_sum(labeled_mask)

08   

09   # Multiplying temp with labeled_mask gives supervised loss on labeled_mask

10   # data only, calculating mean by dividing by no of labeled samples

11   

12   # Unsupervised loss -> R/F   

13   D_L_RealUnsupervised= tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(

14           logits= D_real_logit[:,0], labels= tf.zeros_like(D_real_logit[:,0], dtype=tf.float32)))

15   

16   D_L_FakeUnsupervised= tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(

17           logits= D_fake_logit[:,0], labels= tf.ones_like(D_fake_logit[:,0], dtype=tf.float32)))

18   

19   D_L= D_L_Supervised+ D_L_RealUnsupervised+ D_L_FakeUnsupervised

发生器损失是fake_image损失与特征匹配损失的组合,前者错误的将R / F神经元输出断言为0,后者惩罚训练数据上一组特征的平均值与生成样本中这组特征的平均值之间的平均绝对误差

01             ### Generator loss ###               

02# G_L_1 -> Fake data wanna be real

03 

04G_L_1= tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(

05        logits= D_fake_logit[:,0],labels= tf.zeros_like(D_fake_logit[:,0], dtype=tf.float32)))

06 

07# G_L_2 -> Feature matching

08data_moments= tf.reduce_mean(D_real_features, axis= 0)

09sample_moments= tf.reduce_mean(D_fake_features, axis= 0)

10G_L_2= tf.reduce_mean(tf.square(data_moments-sample_moments))

11 

12G_L= G_L_1+ G_L_2

训练

训练图像从[batch_size,28,28,1]调整为[batch_size,64,64,1]以适应发生器和判别器架构。计算损失,准确性和生成样本,并观察每个周期的改进。

01for epochin range(epochs):

02  train_accuracies, train_D_losses, train_G_losses= [], [], []

03  for itin range(no_of_batches):

04  

05  batch= mnist_data.train.next_batch(batch_size, shuffle= False)

06  # batch[0] has shape: batch_size*28*28*1        

07  batch_reshaped= tf.image.resize_images(batch[0], [64,64]).eval()

08  # Reshaping the whole batch into batch_size*64*64*1 for disc/gen architecture

09  batch_z= np.random.normal(0,1, (batch_size,1,1, latent))

10  mask= get_labeled_mask(labeled_rate, batch_size)

11                

12  train_feed_dict= {x : scale(batch_reshaped), z : batch_z,

13                              label : batch[1], labeled_mask : mask,

14                               dropout_rate :0.7, is_training :True}

15  #The label provided in dict are one hot encoded in 10 classes

16                

17  D_optimizer.run(feed_dict= train_feed_dict)

18  G_optimizer.run(feed_dict= train_feed_dict)

19                

20  train_D_loss= D_L.eval(feed_dict= train_feed_dict)

21  train_G_loss= G_L.eval(feed_dict= train_feed_dict)

22  train_accuracy= accuracy.eval(feed_dict= train_feed_dict)

23          

24  train_D_losses.append(train_D_loss)

25  train_G_losses.append(train_G_loss)

26  train_accuracies.append(train_accuracy)

27          

28  tr_GL= np.mean(train_G_losses)

29  tr_DL= np.mean(train_D_losses)

30  tr_acc= np.mean(train_accuracies)      

31  

32  print ('After epoch: '+ str(epoch+1)+ ' Generator loss: '

33                       + str(tr_GL)+ ' Discriminator loss: ' + str(tr_DL)+ ' Accuracy: ' + str(tr_acc))

34        

35  gen_samples= fake_data.eval(feed_dict= {z : np.random.normal(0,1, (25,1,1, latent)), dropout_rate :0.7, is_training :False})

36  # Dont train batch-norm while plotting => is_training = False

37  test_images= tf.image.resize_images(gen_samples, [64,64]).eval()

38  show_result(test_images, (epoch+ 1), show= True, save= False, path= '')

结论

由于GPU的限制,训练已完成5个周期和20%的 labeled_rate。想要获得更好的结果,建议使用较小的label_rate的训练更多周期。

完整代码:https://github.com/raghav64/SemiSuper_GAN/blob/master/SSGAN.py

训练结果

本文为编译作品,转载请注明出处。更多内容关注微信公众号:atyun_com

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容