自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

飛槳PaddlePaddle手把手教你完成圖像生成任務(wù)

企業(yè)動態(tài)
本文中,我們利用DCGAN生成了手寫數(shù)字圖片,您可以嘗試更換數(shù)據(jù)集生成符合個人需求的圖片,完成您的圖像生成任務(wù),快來試試吧!

生成對抗網(wǎng)絡(luò)(Generative Adversarial Network,簡稱GAN)是非監(jiān)督式學(xué)習(xí)的一種方法,通過讓兩個神經(jīng)網(wǎng)絡(luò)相互博弈的方式進行學(xué)習(xí)。

生成對抗網(wǎng)絡(luò)由兩種子網(wǎng)絡(luò)組成:一個生成器與一個判別期。生成器從潛在空間(latent space)中隨機采樣作為輸入,其輸出結(jié)果需要盡量模仿訓(xùn)練集中的真實圖像。判別器的輸入為真實圖像或生成網(wǎng)絡(luò)的輸出圖像,其目的是將生成器的輸出圖像從真實圖像中盡可能分辨出來。而生成器則要盡可能地欺騙判別器。兩個網(wǎng)絡(luò)相互對抗、不斷調(diào)整參數(shù),提升自己的能力。

生成對抗網(wǎng)絡(luò)常用于生成以假亂真的圖片,常用場景有手寫體生成、人臉合成、風(fēng)格遷移、圖像修復(fù)等。此外,該方法還被用于生成視頻、三維物體模型等。

項目地址:

https://github.com/PaddlePaddle/book/tree/develop/09.gan

效果展示

 

先來看一下DCGAN的最終效果:將 MNIST 數(shù)據(jù)集輸入網(wǎng)絡(luò)進行訓(xùn)練,經(jīng)過19輪訓(xùn)練后的結(jié)果如下圖,前8行是真實圖片的樣子,后8行是網(wǎng)絡(luò)生成的圖像效果。可以看到,生成的圖片已經(jīng)非常接近真實圖片的樣子。

 

模型概覽

GAN

GAN是一種通過對抗的方式,去學(xué)習(xí)數(shù)據(jù)分布的生成模型。以生成圖片為例說明:

  • 生成器(G)接收一個隨機的噪聲z,盡可能的生成近似樣本的圖片,記為G(z)。
  • 判別器(D)接收一張輸入圖片x,盡可以去判別該圖像是真實圖片還是網(wǎng)絡(luò)生成的假圖片,判別器的輸出 D(x) 代表 x 為真實圖片的概率。如果 D(x)=1 說明判別器認為該輸入一定是真實圖片,如果 D(x)=0 說明判別器認為該輸入一定是假圖片。

在訓(xùn)練的過程中,兩個網(wǎng)絡(luò)互相對抗,最終形成了一個動態(tài)的平衡,上述過程用公式可以被描述為:

在最理想的情況下,G 可以生成與真實圖片極其相似的圖片G(z),而 D 很難判斷這張生成的圖片是否為真,對圖片的真假進行隨機猜測,即 D(G(z))=0.5。

下圖展示了生成對抗網(wǎng)絡(luò)的訓(xùn)練過程,假設(shè)在訓(xùn)練開始時,真實樣本分布、生成樣本分布以及判別模型分別是圖中的黑線、綠線和藍線。在訓(xùn)練開始時,判別模型是無法很好地區(qū)分真實樣本和生成樣本的。接下來當(dāng)我們固定生成模型,而優(yōu)化判別模型時,優(yōu)化結(jié)果如第二幅圖所示,可以看出,這個時候判別模型已經(jīng)可以較好地區(qū)分生成圖片和真實圖片了。第三步是固定判別模型,改進生成模型,試圖讓判別模型無法區(qū)分生成圖片與真實圖片,在這個過程中,可以看出由模型生成的圖片分布與真實圖片分布更加接近,這樣的迭代不斷進行,直到最終收斂,生成分布和真實分布重合,判別模型無法區(qū)分真實圖片與生成圖片。

圖:GAN 訓(xùn)練過程

但是在實際過程中,很難得到這個完美的平衡點,關(guān)于GAN的收斂理論還在持續(xù)不斷的研究中。

DCGAN

DCGAN是深層卷積網(wǎng)絡(luò)與 GAN 的結(jié)合,其基本原理與 GAN 相同,只是將生成器和判別器用兩個卷積網(wǎng)絡(luò)(CNN)替代。為了提高生成樣本的質(zhì)量和網(wǎng)絡(luò)的收斂速度,論文中的 DCGAN 在網(wǎng)絡(luò)結(jié)構(gòu)上進行了一些改進:

  • 取消 pooling 層:在網(wǎng)絡(luò)中,所有的pooling層使用步幅卷積(strided convolutions)(判別器)和微步幅度卷積(fractional-strided convolutions)(生成器)進行替換。
  • 加入 batch normalization:在生成器和判別器中均加入batchnorm。
  • 使用全卷積網(wǎng)絡(luò):去掉了FC層,以實現(xiàn)更深的網(wǎng)絡(luò)結(jié)構(gòu)。
  • 激活函數(shù):在生成器(G)中,最后一層使用Tanh函數(shù),其余層采用 ReLu 函數(shù) ; 判別器(D)中都采用LeakyReLu。

圖:DCGAN中的生成器(G)

快速開始

本文的DCGAN任務(wù)依賴于 Paddle Fluid v1.3 及以上版本,請參考官網(wǎng)安裝指南進行安裝。

數(shù)據(jù)準(zhǔn)備

本文使用數(shù)據(jù)規(guī)模較小的MNIST 訓(xùn)練生成器和判別器,該數(shù)據(jù)集可通過paddle.dataset模塊自動下載到本地。

加載包

首先加載 Paddle Fluid和其他相關(guān)包:

  1. import sys 
  2.  
  3. import os 
  4.  
  5. import matplotlib 
  6.  
  7. import PIL 
  8.  
  9. import six 
  10.  
  11. import numpy as np 
  12.  
  13. import math 
  14.  
  15. import time 
  16.  
  17. import paddle 
  18.  
  19. import paddle.fluid as fluid 
  20.  
  21. matplotlib.use('agg'
  22.  
  23. import matplotlib.pyplot as plt 
  24.  
  25. import matplotlib.gridspec as gridspec 
  26.  
  27. from __future__ import absolute_import 
  28.  
  29. from __future__ import division 
  30.  
  31. from __future__ import print_function 

定義輔助工具

定義 plot 函數(shù),將圖像生成過程可視化:

  1. def plot(gen_data): 
  2.  
  3. pad_dim = 1 
  4.  
  5. paded = pad_dim + img_dim 
  6.  
  7. gen_data = gen_data.reshape(gen_data.shape[0], img_dim, img_dim) 
  8.  
  9. n = int(math.ceil(math.sqrt(gen_data.shape[0]))) 
  10.  
  11. gen_data = (np.pad( 
  12.  
  13. gen_data, [[0, n * n - gen_data.shape[0]], [pad_dim, 0], [pad_dim, 0]], 
  14.  
  15. 'constant').reshape((n, n, paded, paded)).transpose((0213)) 
  16.  
  17. .reshape((n * paded, n * paded))) 
  18.  
  19. fig = plt.figure(figsize=(88)) 
  20.  
  21. plt.axis('off'
  22.  
  23. plt.imshow(gen_data, cmap='Greys_r', vmin=-1, vmax=1
  24.  
  25. return fig 

定義超參數(shù)

  1. gf_dim = 64 # 生成器的feature map的基礎(chǔ)通道數(shù)量,生成器中所有的feature map的通道數(shù)量都是基礎(chǔ)通道數(shù)量的倍數(shù) 
  2.  
  3. df_dim = 64 # 判別器的feature map的基礎(chǔ)通道數(shù)量,判別器中所有的feature map的通道數(shù)量都是基礎(chǔ)通道數(shù)量的倍數(shù) 
  4.  
  5. gfc_dim = 1024 * 2 # 生成器的全連接層維度 
  6.  
  7. dfc_dim = 1024 # 判別器的全連接層維度 
  8.  
  9. img_dim = 28 # 輸入圖片的尺寸 
  10.  
  11. NOISE_SIZE = 100 # 輸入噪聲的維度 
  12.  
  13. LEARNING_RATE = 2e-4 # 訓(xùn)練的學(xué)習(xí)率 
  14.  
  15. epoch = 20 # 訓(xùn)練的epoch數(shù) 
  16.  
  17. output = "./output_dcgan" # 模型和測試結(jié)果的存儲路徑 
  18.  
  19. use_cudnn = False # 是否使用cuDNN 
  20.  
  21. use_gpu=False # 是否使用GPU訓(xùn)練 

 

定義網(wǎng)絡(luò)結(jié)構(gòu)

bn 層

調(diào)用 fluid.layers.batch_norm 接口實現(xiàn)bn層,激活函數(shù)默認使用ReLu。

  1. def bn(x, name=None, act='relu'): 
  2.  
  3. return fluid.layers.batch_norm( 
  4.  
  5. x, 
  6.  
  7. param_attr=name + '1'
  8.  
  9. bias_attr=name + '2'
  10.  
  11. moving_mean_name=name + '3'
  12.  
  13. moving_variance_name=name + '4'
  14.  
  15. name=name, 
  16.  
  17. act=act) 

卷積層

調(diào)用 fluid.nets.simple_img_conv_pool 實現(xiàn)卷積池化組,卷積核大小為3x3,池化窗口大小為2x2,窗口滑動步長為2,激活函數(shù)類型由具體網(wǎng)絡(luò)結(jié)構(gòu)指定。

  1. def conv(x, num_filters, name=None, act=None): 
  2.  
  3. return fluid.nets.simple_img_conv_pool( 
  4.  
  5. input=x, 
  6.  
  7. filter_size=5
  8.  
  9. num_filters=num_filters, 
  10.  
  11. pool_size=2
  12.  
  13. pool_stride=2
  14.  
  15. param_attr=name + 'w'
  16.  
  17. bias_attr=name + 'b'
  18.  
  19. use_cudnn=use_cudnn, 
  20.  
  21. act=act) 

全連接層

  1. def fc(x, num_filters, name=None, act=None): 
  2.  
  3. return fluid.layers.fc(input=x, 
  4.  
  5. size=num_filters, 
  6.  
  7. act=act, 
  8.  
  9. param_attr=name + 'w'
  10.  
  11. bias_attr=name + 'b'

轉(zhuǎn)置卷積層

在生成器中,需要用隨機采樣值生成全尺寸圖像,dcgan使用轉(zhuǎn)置卷積層進行上采樣,在Fluid中,我們調(diào)用 fluid.layers.conv2d_transpose 實現(xiàn)轉(zhuǎn)置卷積。

  1. def deconv(x, 
  2.  
  3. num_filters, 
  4.  
  5. name=None, 
  6.  
  7. filter_size=5
  8.  
  9. stride=2
  10.  
  11. dilation=1
  12.  
  13. padding=2
  14.  
  15. output_size=None, 
  16.  
  17. act=None): 
  18.  
  19. return fluid.layers.conv2d_transpose( 
  20.  
  21. input=x, 
  22.  
  23. param_attr=name + 'w'
  24.  
  25. bias_attr=name + 'b'
  26.  
  27. num_filters=num_filters, 
  28.  
  29. output_size=output_size, 
  30.  
  31. filter_size=filter_size, 
  32.  
  33. stride=stride, 
  34.  
  35. dilation=dilation, 
  36.  
  37. padding=padding, 
  38.  
  39. use_cudnn=use_cudnn, 
  40.  
  41. act=act) 

判別器

判別器使用真實數(shù)據(jù)集和生成器生成的假圖片共同進行訓(xùn)練,在訓(xùn)練過程中盡量使真實數(shù)據(jù)集的輸出結(jié)果為1,生成的假圖片輸出結(jié)果為0。本文中實現(xiàn)的判別器由兩個卷積池化層和兩個全連接層組成,其中最后一個全連接層的神經(jīng)元個數(shù)為1,輸出一個二分類結(jié)果。

  1. def D(x): 
  2.  
  3. x = fluid.layers.reshape(x=x, shape=[-112828]) 
  4.  
  5. x = conv(x, df_dim, act='leaky_relu',name='conv1'
  6.  
  7. x = bn(conv(x, df_dim * 2,name='conv2'), act='leaky_relu',name='bn1'
  8.  
  9. x = bn(fc(x, dfc_dim,name='fc1'), act='leaky_relu',name='bn2'
  10.  
  11. x = fc(x, 1, act='sigmoid',name='fc2'
  12.  
  13. return x 

生成器

生成器由兩組帶BN的全連接層和兩組轉(zhuǎn)置卷積層組成,網(wǎng)絡(luò)輸入為隨機的噪聲數(shù)據(jù),最后一層轉(zhuǎn)置卷積的卷積核數(shù)為1,表示輸出為灰度圖片。

  1. def G(x): 
  2.  
  3. x = bn(fc(x, gfc_dim,name='fc3'),name='bn3'
  4.  
  5. x = bn(fc(x, gf_dim * 2 * img_dim // 4 * img_dim // 4,name='fc4'),name='bn4') 
  6.  
  7. x = fluid.layers.reshape(x, [-1, gf_dim * 2, img_dim // 4, img_dim // 4]) 
  8.  
  9. x = deconv(x, gf_dim * 2, act='relu', output_size=[1414],name='deconv1'
  10.  
  11. x = deconv(x, num_filters=1, filter_size=5, padding=2, act='tanh', output_size=[2828],name='deconv2'
  12.  
  13. x = fluid.layers.reshape(x, shape=[-128 * 28]) 
  14.  
  15. return x 

損失函數(shù)

損失函數(shù)使用 sigmoid_cross_entropy_with_logits

  1. def loss(x, label): 
  2.  
  3. return fluid.layers.mean( 
  4.  
  5. fluid.layers.sigmoid_cross_entropy_with_logits(x=x, label=label)) 

創(chuàng)建Program

  1. d_program = fluid.Program() 
  2.  
  3. dg_program = fluid.Program() 
  4.  
  5. # 定義判別真實圖片的program 
  6.  
  7. with fluid.program_guard(d_program): 
  8.  
  9. # 輸入圖片大小為28*28=784 
  10.  
  11. img = fluid.layers.data(name='img', shape=[784], dtype='float32'
  12.  
  13. # 標(biāo)簽shape=1 
  14.  
  15. label = fluid.layers.data(name='label', shape=[1], dtype='float32'
  16.  
  17. d_logit = D(img) 
  18.  
  19. d_loss = loss(d_logit, label) 
  20.  
  21. # 定義判別生成圖片的program 
  22.  
  23. with fluid.program_guard(dg_program): 
  24.  
  25. noise = fluid.layers.data( 
  26.  
  27. name='noise', shape=[NOISE_SIZE], dtype='float32'
  28.  
  29. # 噪聲數(shù)據(jù)作為輸入得到生成圖片 
  30.  
  31. g_img = G(x=noise) 
  32.  
  33. g_program = dg_program.clone() 
  34.  
  35. g_program_test = dg_program.clone(for_test=True) 
  36.  
  37. # 判斷生成圖片為真實樣本的概率 
  38.  
  39. dg_logit = D(g_img) 
  40.  
  41. # 計算生成圖片被判別為真實樣本的loss 
  42.  
  43. dg_loss = loss( 
  44.  
  45. dg_logit, 
  46.  
  47. fluid.layers.fill_constant_batch_size_like( 
  48.  
  49. input=noise, dtype='float32', shape=[-11], value=1.0)) 

使用adam作為優(yōu)化器,分別優(yōu)化判別真實圖片的loss和判別生成圖片的loss。

  1. opt = fluid.optimizer.Adam(learning_rate=LEARNING_RATE) 
  2.  
  3. opt.minimize(loss=d_loss) 
  4.  
  5. parameters = [p.name for p in g_program.global_block().all_parameters()] 
  6.  
  7. opt.minimize(loss=dg_loss, parameter_list=parameters) 

數(shù)據(jù)集 Feeders 配置

下一步,我們開始訓(xùn)練過程。paddle.dataset.mnist.train()用做訓(xùn)練數(shù)據(jù)集。這個函數(shù)返回一個reader——飛槳(PaddlePaddle)中的reader是一個Python函數(shù),每次調(diào)用的時候返回一個Python yield generator。

下面shuffle是一個reader decorator,它接受一個reader A,返回另一個reader B。reader B 每次讀入buffer_size條訓(xùn)練數(shù)據(jù)到一個buffer里,然后隨機打亂其順序,并且逐條輸出。

batch是一個特殊的decorator,它的輸入是一個reader,輸出是一個batched reader。在飛槳(PaddlePaddle)里,一個reader每次yield一條訓(xùn)練數(shù)據(jù),而一個batched reader每次yield一個minibatch。

  1. batch_size = 128 # Minibatch size 
  2.  
  3. train_reader = paddle.batch( 
  4.  
  5. paddle.reader.shuffle( 
  6.  
  7. paddle.dataset.mnist.train(), buf_size=60000), 
  8.  
  9. batch_size=batch_size) 

創(chuàng)建執(zhí)行器

  1. if use_gpu: 
  2.  
  3. exe = fluid.Executor(fluid.CUDAPlace(0)) 
  4.  
  5. else: 
  6.  
  7. exe = fluid.Executor(fluid.CPUPlace()) 
  8.  
  9. exe.run(fluid.default_startup_program()) 

開始訓(xùn)練

訓(xùn)練過程中的每一次迭代,生成器和判別器分別設(shè)置自己的迭代次數(shù)。為了避免判別器快速收斂到0,本文默認每迭代一次,訓(xùn)練一次判別器,兩次生成器。

  1. t_time = 0 
  2.  
  3. losses = [[], []] 
  4.  
  5. # 判別器的迭代次數(shù) 
  6.  
  7. NUM_TRAIN_TIMES_OF_DG = 2 
  8.  
  9. # 最終生成圖像的噪聲數(shù)據(jù) 
  10.  
  11. const_n = np.random.uniform( 
  12.  
  13. low=-1.0, high=1.0
  14.  
  15. size=[batch_size, NOISE_SIZE]).astype('float32'
  16.  
  17. for pass_id in range(epoch): 
  18.  
  19. for batch_id, data in enumerate(train_reader()): 
  20.  
  21. if len(data) != batch_size: 
  22.  
  23. continue 
  24.  
  25. # 生成訓(xùn)練過程的噪聲數(shù)據(jù) 
  26.  
  27. noise_data = np.random.uniform( 
  28.  
  29. low=-1.0, high=1.0
  30.  
  31. size=[batch_size, NOISE_SIZE]).astype('float32'
  32.  
  33. # 真實圖片 
  34.  
  35. real_image = np.array(list(map(lambda x: x[0], data))).reshape( 
  36.  
  37. -1784).astype('float32'
  38.  
  39. # 真實標(biāo)簽 
  40.  
  41. real_labels = np.ones( 
  42.  
  43. shape=[real_image.shape[0], 1], dtype='float32'
  44.  
  45. # 虛假標(biāo)簽 
  46.  
  47. fake_labels = np.zeros( 
  48.  
  49. shape=[real_image.shape[0], 1], dtype='float32'
  50.  
  51. total_label = np.concatenate([real_labels, fake_labels]) 
  52.  
  53. s_time = time.time() 
  54.  
  55. # 虛假圖片 
  56.  
  57. generated_image = exe.run(g_program, 
  58.  
  59. feed={'noise': noise_data}, 
  60.  
  61. fetch_list={g_img})[0
  62.  
  63. total_images = np.concatenate([real_image, generated_image]) 
  64.  
  65. # D 判斷虛假圖片為假的loss 
  66.  
  67. d_loss_1 = exe.run(d_program, 
  68.  
  69. feed={ 
  70.  
  71. 'img': generated_image, 
  72.  
  73. 'label': fake_labels, 
  74.  
  75. }, 
  76.  
  77. fetch_list={d_loss})[0][0
  78.  
  79. # D 判斷真實圖片為真的loss 
  80.  
  81. d_loss_2 = exe.run(d_program, 
  82.  
  83. feed={ 
  84.  
  85. 'img': real_image, 
  86.  
  87. 'label': real_labels, 
  88.  
  89. }, 
  90.  
  91. fetch_list={d_loss})[0][0
  92.  
  93. d_loss_n = d_loss_1 + d_loss_2 
  94.  
  95. losses[0].append(d_loss_n) 
  96.  
  97. # 訓(xùn)練生成器 
  98.  
  99. for _ in six.moves.xrange(NUM_TRAIN_TIMES_OF_DG): 
  100.  
  101. noise_data = np.random.uniform( 
  102.  
  103. low=-1.0, high=1.0
  104.  
  105. size=[batch_size, NOISE_SIZE]).astype('float32'
  106.  
  107. dg_loss_n = exe.run(dg_program, 
  108.  
  109. feed={'noise': noise_data}, 
  110.  
  111. fetch_list={dg_loss})[0][0
  112.  
  113. losses[1].append(dg_loss_n) 
  114.  
  115. t_time += (time.time() - s_time) 
  116.  
  117. if batch_id % 10 == 0 and not run_ce: 
  118.  
  119. if not os.path.exists(output): 
  120.  
  121. os.makedirs(output) 
  122.  
  123. # 每輪的生成結(jié)果 
  124.  
  125. generated_images = exe.run(g_program_test, 
  126.  
  127. feed={'noise': const_n}, 
  128.  
  129. fetch_list={g_img})[0
  130.  
  131. # 將真實圖片和生成圖片連接 
  132.  
  133. total_images = np.concatenate([real_image, generated_images]) 
  134.  
  135. fig = plot(total_images) 
  136.  
  137. msg = "Epoch ID={0} Batch ID={1} D-Loss={2} DG-Loss={3}\n ".format( 
  138.  
  139. pass_id, batch_id, 
  140.  
  141. d_loss_n, dg_loss_n) 
  142.  
  143. print(msg) 
  144.  
  145. plt.title(msg) 
  146.  
  147. plt.savefig( 
  148.  
  149. '{}/{:04d}_{:04d}.png'.format(output, pass_id, 
  150.  
  151. batch_id), 
  152.  
  153. bbox_inches='tight'
  154.  
  155. plt.close(fig) 

打印特定輪次的生成結(jié)果:

  1. def display_image(epoch_no,batch_id): 
  2.  
  3. return PIL.Image.open('output_dcgan/{:04d}_{:04d}.png'.format(epoch_no,batch_id)) 
  4.  
  5. # 觀察第10個epoch,460個batch的生成圖像: 
  6.  
  7. display_image(10,460

 

總結(jié)

DCGAN采用一個隨機噪聲向量作為輸入,輸入通過與CNN類似但是相反的結(jié)構(gòu),將輸入放大成二維數(shù)據(jù)。采用這種結(jié)構(gòu)的生成模型和CNN結(jié)構(gòu)的判別模型,DCGAN在圖片生成上可以達到相當(dāng)可觀的效果。本文中,我們利用DCGAN生成了手寫數(shù)字圖片,您可以嘗試更換數(shù)據(jù)集生成符合個人需求的圖片,完成您的圖像生成任務(wù),快來試試吧!

更多內(nèi)容,可點擊文末閱讀原文或查看:

https://github.com/PaddlePaddle/book/tree/develop/09.gan

ps:最后給大家推薦一個GPU福利-Tesla V100免費算力!配合PaddleHub能讓模型原地起飛~掃碼下方二維碼申請~

 

 

責(zé)任編輯:張燕妮 來源: 51CTO官微
相關(guān)推薦

2014-08-08 13:22:54

測試手機站點移動設(shè)備

2021-11-09 06:55:03

水印圖像開發(fā)

2011-01-10 14:41:26

2011-05-03 15:59:00

黑盒打印機

2021-07-14 09:00:00

JavaFX開發(fā)應(yīng)用

2021-02-26 11:54:38

MyBatis 插件接口

2011-02-22 13:46:27

微軟SQL.NET

2021-12-28 08:38:26

Linux 中斷喚醒系統(tǒng)Linux 系統(tǒng)

2022-01-08 20:04:20

攔截系統(tǒng)調(diào)用

2023-04-26 12:46:43

DockerSpringKubernetes

2022-12-07 08:42:35

2022-07-27 08:16:22

搜索引擎Lucene

2022-03-14 14:47:21

HarmonyOS操作系統(tǒng)鴻蒙

2020-04-14 10:20:12

MySQL數(shù)據(jù)庫死鎖

2016-04-27 09:49:16

用戶模型產(chǎn)品總結(jié)

2021-08-04 08:55:02

Socket Java開發(fā)

2020-07-09 08:59:52

if else模板Service

2009-11-09 14:57:37

WCF上傳文件

2011-01-06 10:39:25

.NET程序打包

2024-10-16 11:40:47

點贊
收藏

51CTO技術(shù)棧公眾號