dcgan_dcgan代码实现 - CSDN
精华内容
参与话题
  • 1、DCGAN的简单总结稳定的深度卷积GAN 架构指南: 所有的pooling层使用步幅卷积(判别网络)和微步幅度卷积(生成网络)进行替换。 在生成网络和判别网络上使用批处理规范化。 对于更深的架构移除全连接隐藏层。 在生成...

    首先是各种参考博客、链接等,表示感谢。

    1、参考博客1:地址

    ——以下,开始正文。

    2017/12/12 更新 解决训练不收敛的问题。

    更新在最后面部分。


    1、DCGAN的简单总结

    稳定的深度卷积GAN 架构指南:

    • 所有的pooling层使用步幅卷积(判别网络)和微步幅度卷积(生成网络)进行替换。

    • 在生成网络和判别网络上使用批处理规范化。

    • 对于更深的架构移除全连接隐藏层。

    • 在生成网络的所有层上使用RelU激活函数,除了输出层使用Tanh激活函数。

    • 在判别网络的所有层上使用LeakyReLU激活函数。

    这里写图片描述

    图: LSUN 场景模型中使用的DCGAN生成网络。一个100维度的均匀分布z映射到一个有很多特征映射的小空间范围卷积。一连串的四个微步幅卷积(在最近的一些论文中它们错误地称为去卷积),将高层表征转换为64*64像素的图像。明显,没有使用全连接层和池化层。

    2、DCGAN的实现

    DCGAN原文作者是生成了卧室图片,这里参照前面写的参考链接中,来生成动漫人物头像。生成效果如下:

    暂且先不放,因为还没开始做。

    2.1 搜集原始数据集

    首先是需要获取大量的动漫图像,这个可以利用爬虫爬取一个动漫网站:konachan.net的图片。爬虫的代码如下所示:

    import requests  # http lib
    from bs4 import BeautifulSoup  # climb lib
    import os # operation system
    import traceback # trace deviance
    
    def download(url,filename):
        if os.path.exists(filename):
            print('file exists!')
            return
        try:
            r = requests.get(url,stream=True,timeout=60)
            r.raise_for_status()
            with open(filename,'wb') as f:
                for chunk in r.iter_content(chunk_size=1024):
                    if chunk: # filter out keep-alove new chunks
                        f.write(chunk)
                        f.flush()
            return filename
        except KeyboardInterrupt:
            if os.path.exists(filename):
                os.remove(filename)
            return KeyboardInterrupt
        except Exception:
            traceback.print_exc()
            if os.path.exists(filename):
                os.remove(filename)
    
    if os.path.exists('imgs') is False:
        os.makedirs('imgs')
    
    start = 1
    end = 8000
    for i in range(start, end+1):
        url = 'http://konachan.net/post?page=%d&tags=' % i
        html = requests.get(url).text # gain the web's information
        soup =  BeautifulSoup(html,'html.parser') # doc's string and jie xi qi
        for img in soup.find_all('img',class_="preview"):# 遍历所有preview类,找到img标签
            target_url = 'http:' + img['src']
            filename = os.path.join('imgs',target_url.split('/')[-1])
            download(target_url,filename)
        print('%d / %d' % (i,end))    
    

    目标是获取1万张图像,因为自己是在CPU上跑的,而且内存太小,太多图像根本训练不起来,就先少一点训练,看看效果。

    截取部分图像如下所示:

    这里写图片描述

    现在已经有了基本的图像了,但我们的目标是生成动漫头像,不需要整张图像,而且其他的信息会干扰到训练,所以需要进行人脸检测截取人脸图像。

    2.2 人脸检测截取人脸

    通过基于opencv的人脸检测分类器,参考于lbpcascade_animeface

    首先,要使用这个分类器要先进行下载:

    wget https://raw.githubusercontent.com/nagadomi/lbpcascade_animeface/master/lbpcascade_animeface.xml

    下载完成后,运行以下代码对图像进行人脸截取。

    import cv2
    import sys
    import os.path
    from glob import glob
    
    def detect(filename,cascade_file="lbpcascade_animeface.xml"):
        if not os.path.isfile(cascade_file):
            raise RuntimeError("%s: not found" % cascade_file)
    
        cascade = cv2.CascadeClassifier(cascade_file)
        image = cv2.imread(filename)
        gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
        gray = cv2.equalizeHist(gray)
    
        faces = cascade.detectMultiScale(
            gray,
            # detector options
            scaleFactor = 1.1,
            minNeighbors = 5,
            minSize = (48,48)
        )
    
        for i,(x,y,w,h) in enumerate(faces):
            face = image[y: y+h, x:x+w, :]
            face = cv2.resize(face,(96,96))
            save_filename = '%s.jpg' % (os.path.basename(filename).split('.')[0])
            cv2.imwrite("faces/"+sace_filename,face)
    
    if __name__ == '__main__':
        if os.path.exists('faces') is False:
            os.makedirs('faces')
        file_list = glob('imgs/*.jpg')
        for filename in file_list:
            detect(filename)
    

    处理后的图像如下所示:

    这里写图片描述

    2.3 源代码解析

    参照于DCGAN-tensorflow

    总共获取11053张图像,人脸检测后得到3533张。

    一共有4个文件,分别是main.py、model.py、ops.py、utils.py。

    2.3.1 mian.py

    原代码(98行):

    import os
    import scipy.misc # 
    import numpy as np
    
    from model import DCGAN
    from utils import pp, visualize, to_json, show_all_variables
    
    import tensorflow as tf
    
    flags = tf.app.flags
    flags.DEFINE_integer("epoch", 25, "Epoch to train [25]")
    flags.DEFINE_float("learning_rate", 0.0002, "Learning rate of for adam [0.0002]")
    flags.DEFINE_float("beta1", 0.5, "Momentum term of adam [0.5]")
    flags.DEFINE_integer("train_size", np.inf, "The size of train images [np.inf]")
    flags.DEFINE_integer("batch_size", 64, "The size of batch images [64]")
    flags.DEFINE_integer("input_height", 108, "The size of image to use (will be center cropped). [108]")
    flags.DEFINE_integer("input_width", None, "The size of image to use (will be center cropped). If None, same value as input_height [None]")
    flags.DEFINE_integer("output_height", 64, "The size of the output images to produce [64]")
    flags.DEFINE_integer("output_width", None, "The size of the output images to produce. If None, same value as output_height [None]")
    flags.DEFINE_string("dataset", "celebA", "The name of dataset [celebA, mnist, lsun]")
    flags.DEFINE_string("input_fname_pattern", "*.jpg", "Glob pattern of filename of input images [*]")
    flags.DEFINE_string("checkpoint_dir", "checkpoint", "Directory name to save the checkpoints [checkpoint]")
    flags.DEFINE_string("sample_dir", "samples", "Directory name to save the image samples [samples]")
    flags.DEFINE_boolean("train", False, "True for training, False for testing [False]")
    flags.DEFINE_boolean("crop", False, "True for training, False for testing [False]")
    flags.DEFINE_boolean("visualize", False, "True for visualizing, False for nothing [False]")
    FLAGS = flags.FLAGS
    
    def main(_):
      pp.pprint(flags.FLAGS.__flags)
    
      if FLAGS.input_width is None:
        FLAGS.input_width = FLAGS.input_height
      if FLAGS.output_width is None:
        FLAGS.output_width = FLAGS.output_height
    
      if not os.path.exists(FLAGS.checkpoint_dir):
        os.makedirs(FLAGS.checkpoint_dir)
      if not os.path.exists(FLAGS.sample_dir):
        os.makedirs(FLAGS.sample_dir)
    
      #gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.333)
      run_config = tf.ConfigProto()
      run_config.gpu_options.allow_growth=True
    
      with tf.Session(config=run_config) as sess:
        if FLAGS.dataset == 'mnist':
          dcgan = DCGAN(
              sess,
              input_width=FLAGS.input_width,
              input_height=FLAGS.input_height,
              output_width=FLAGS.output_width,
              output_height=FLAGS.output_height,
              batch_size=FLAGS.batch_size,
              sample_num=FLAGS.batch_size,
              y_dim=10,
              dataset_name=FLAGS.dataset,
              input_fname_pattern=FLAGS.input_fname_pattern,
              crop=FLAGS.crop,
              checkpoint_dir=FLAGS.checkpoint_dir,
              sample_dir=FLAGS.sample_dir)
        else:
          dcgan = DCGAN(
              sess,
              input_width=FLAGS.input_width,
              input_height=FLAGS.input_height,
              output_width=FLAGS.output_width,
              output_height=FLAGS.output_height,
              batch_size=FLAGS.batch_size,
              sample_num=FLAGS.batch_size,
              dataset_name=FLAGS.dataset,
              input_fname_pattern=FLAGS.input_fname_pattern,
              crop=FLAGS.crop,
              checkpoint_dir=FLAGS.checkpoint_dir,
              sample_dir=FLAGS.sample_dir)
    
        show_all_variables()
    
        if FLAGS.train:
          dcgan.train(FLAGS)
        else:
          if not dcgan.load(FLAGS.checkpoint_dir)[0]:
            raise Exception("[!] Train a model first, then run test mode")
    
    
        # to_json("./web/js/layers.js", [dcgan.h0_w, dcgan.h0_b, dcgan.g_bn0],
        #                 [dcgan.h1_w, dcgan.h1_b, dcgan.g_bn1],
        #                 [dcgan.h2_w, dcgan.h2_b, dcgan.g_bn2],
        #                 [dcgan.h3_w, dcgan.h3_b, dcgan.g_bn3],
        #                 [dcgan.h4_w, dcgan.h4_b, None])
    
        # Below is codes for visualization
        OPTION = 1
        visualize(sess, dcgan, FLAGS, OPTION)
    
    if __name__ == '__main__':
      tf.app.run()
    

    该文件调用了model.py文件和utils.py文件。

    step0:执行main函数之前首先进行flags的解析,TensorFlow底层使用了python-gflags项目,然后封装成tf.app.flags接口,也就是说TensorFlow通过设置flags来传递tf.app.run()所需要的参数,我们可以直接在程序运行前初始化flags,也可以在运行程序的时候设置命令行参数来达到传参的目的。

    这里主要设置了:

    • epoch:迭代次数
    • learning_rate:学习速率,默认是0.002
    • beta1
    • train_size
    • batch_size:每次迭代的图像数量
    • input_height:需要指定输入图像的高
    • input_width:需要指定输入图像的宽
    • output_height:需要指定输出图像的高
    • output_width:需要指定输出图像的宽
    • dataset:需要指定处理哪个数据集
    • input_fname_pattern
    • checkpoint_dir
    • sample_dir
    • train:True for training, False for testing
    • crop:True for training, False for testing
    • visualize

    step1:首先是打印参数数据,然后判断输入图像的输出图像的宽是否指定,如果没有指定,则等于其图像的高。

    step2:然后判断checkpoint和sample的文件是否存在,不存在则创建。

    step3:然后是设置session参数。tf.ConfigProto一般用在创建session的时候,用来对session进行参数配置,详细内容可见这篇博客

    #tf.ConfigProto()的参数:
    log_device_placement=True : 是否打印设备分配日志
    allow_soft_placement=True : 如果你指定的设备不存在,允许TF自动分配设备
    tf.ConfigProto(log_device_placement=True,allow_soft_placement=True)
    
    控制GPU资源使用率:
    #allow growth
    config = tf.ConfigProto()
    config.gpu_options.allow_growth = True
    session = tf.Session(config=config, ...)
    # 使用allow_growth option,刚一开始分配少量的GPU容量,然后按需慢慢的增加,由于不会释放内存,所以会导致碎片

    step4:运行session,首先判断处理的是哪个数据集,然后对应使用不同参数的DCGAN类,这个类会在model.py文件中定义。

    step5:show所有与训练相关的变量。

    step6:判断是训练还是测试,如果是训练,则进行训练;如果不是,判断是否有训练好的model,然后进行测试,如果没有先训练,则会提示“[!] Train a model first, then run test mode”。

    step7:最后进行可视化,visualize(sess, dcgan, FLAGS, OPTION)。

    main.py主要是调用前面定义好的模型、图像处理方法,来进行训练测试,程序的入口。

    2.3.2 utils.py

    源代码(250行):

    """
    Some codes from https://github.com/Newmu/dcgan_code
    """
    from __future__ import division
    import math
    import json
    import random
    import pprint # print data_struct
    import scipy.misc
    import numpy as np
    from time import gmtime, strftime
    from six.moves import xrange
    
    import tensorflow as tf
    import tensorflow.contrib.slim as slim
    
    pp = pprint.PrettyPrinter()
    
    get_stddev = lambda x, k_h, k_w: 1/math.sqrt(k_w*k_h*x.get_shape()[-1])
    
    def show_all_variables():
      model_vars = tf.trainable_variables()
      slim.model_analyzer.analyze_vars(model_vars, print_info=True)
    
    def get_image(image_path, input_height, input_width,
                  resize_height=64, resize_width=64,
                  crop=True, grayscale=False):
      image = imread(image_path, grayscale)
      return transform(image, input_height, input_width,
                       resize_height, resize_width, crop)
    
    def save_images(images, size, image_path):
      return imsave(inverse_transform(images), size, image_path)
    
    def imread(path, grayscale = False):
      if (grayscale):
        return scipy.misc.imread(path, flatten = True).astype(np.float)
      else:
        return scipy.misc.imread(path).astype(np.float)
    
    def merge_images(images, size):
      return inverse_transform(images)
    
    def merge(images, size):
      h, w = images.shape[1], images.shape[2]
      if (images.shape[3] in (3,4)):
        c = images.shape[3]
        img = np.zeros((h * size[0], w * size[1], c))
        for idx, image in enumerate(images):
          i = idx % size[1]
          j = idx // size[1]
          img[j * h:j * h + h, i * w:i * w + w, :] = image
        return img
      elif images.shape[3]==1:
        img = np.zeros((h * size[0], w * size[1]))
        for idx, image in enumerate(images):
          i = idx % size[1]
          j = idx // size[1]
          img[j * h:j * h + h, i * w:i * w + w] = image[:,:,0]
        return img
      else:
        raise ValueError('in merge(images,size) images parameter '
                         'must have dimensions: HxW or HxWx3 or HxWx4')
    
    def imsave(images, size, path):
      image = np.squeeze(merge(images, size))
      return scipy.misc.imsave(path, image)
    
    def center_crop(x, crop_h, crop_w,
                    resize_h=64, resize_w=64):
      if crop_w is None:
        crop_w = crop_h
      h, w = x.shape[:2]
      j = int(round((h - crop_h)/2.))
      i = int(round((w - crop_w)/2.))
      return scipy.misc.imresize(
          x[j:j+crop_h, i:i+crop_w], [resize_h, resize_w])
    
    def transform(image, input_height, input_width, 
                  resize_height=64, resize_width=64, crop=True):
      if crop:
        cropped_image = center_crop(
          image, input_height, input_width, 
          resize_height, resize_width)
      else:
        cropped_image = scipy.misc.imresize(image, [resize_height, resize_width])
      return np.array(cropped_image)/127.5 - 1.
    
    def inverse_transform(images):
      return (images+1.)/2.
    
    def to_json(output_path, *layers):
      with open(output_path, "w") as layer_f:
        lines = ""
        for w, b, bn in layers:
          layer_idx = w.name.split('/')[0].split('h')[1]
    
          B = b.eval()
    
          if "lin/" in w.name:
            W = w.eval()
            depth = W.shape[1]
          else:
            W = np.rollaxis(w.eval(), 2, 0)
            depth = W.shape[0]
    
          biases = {"sy": 1, "sx": 1, "depth": depth, "w": ['%.2f' % elem for elem in list(B)]}
          if bn != None:
            gamma = bn.gamma.eval()
            beta = bn.beta.eval()
    
            gamma = {"sy": 1, "sx": 1, "depth": depth, "w": ['%.2f' % elem for elem in list(gamma)]}
            beta = {"sy": 1, "sx": 1, "depth": depth, "w": ['%.2f' % elem for elem in list(beta)]}
          else:
            gamma = {"sy": 1, "sx": 1, "depth": 0, "w": []}
            beta = {"sy": 1, "sx": 1, "depth": 0, "w": []}
    
          if "lin/" in w.name:
            fs = []
            for w in W.T:
              fs.append({"sy": 1, "sx": 1, "depth": W.shape[0], "w": ['%.2f' % elem for elem in list(w)]})
    
            lines += """
              var layer_%s = {
                "layer_type": "fc", 
                "sy": 1, "sx": 1, 
                "out_sx": 1, "out_sy": 1,
                "stride": 1, "pad": 0,
                "out_depth": %s, "in_depth": %s,
                "biases": %s,
                "gamma": %s,
                "beta": %s,
                "filters": %s
              };""" % (layer_idx.split('_')[0], W.shape[1], W.shape[0], biases, gamma, beta, fs)
          else:
            fs = []
            for w_ in W:
              fs.append({"sy": 5, "sx": 5, "depth": W.shape[3], "w": ['%.2f' % elem for elem in list(w_.flatten())]})
    
            lines += """
              var layer_%s = {
                "layer_type": "deconv", 
                "sy": 5, "sx": 5,
                "out_sx": %s, "out_sy": %s,
                "stride": 2, "pad": 1,
                "out_depth": %s, "in_depth": %s,
                "biases": %s,
                "gamma": %s,
                "beta": %s,
                "filters": %s
              };""" % (layer_idx, 2**(int(layer_idx)+2), 2**(int(layer_idx)+2),
                   W.shape[0], W.shape[3], biases, gamma, beta, fs)
        layer_f.write(" ".join(lines.replace("'","").split()))
    
    def make_gif(images, fname, duration=2, true_image=False):
      import moviepy.editor as mpy
    
      def make_frame(t):
        try:
          x = images[int(len(images)/duration*t)]
        except:
          x = images[-1]
    
        if true_image:
          return x.astype(np.uint8)
        else:
          return ((x+1)/2*255).astype(np.uint8)
    
      clip = mpy.VideoClip(make_frame, duration=duration)
      clip.write_gif(fname, fps = len(images) / duration)
    
    def visualize(sess, dcgan, config, option):
      image_frame_dim = int(math.ceil(config.batch_size**.5))
      if option == 0:
        z_sample = np.random.uniform(-0.5, 0.5, size=(config.batch_size, dcgan.z_dim))
        samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
        save_images(samples, [image_frame_dim, image_frame_dim], './samples/test_%s.png' % strftime("%Y%m%d%H%M%S", gmtime()))
      elif option == 1:
        values = np.arange(0, 1, 1./config.batch_size)
        for idx in xrange(100):
          print(" [*] %d" % idx)
          z_sample = np.zeros([config.batch_size, dcgan.z_dim])
          for kdx, z in enumerate(z_sample):
            z[idx] = values[kdx]
    
          if config.dataset == "mnist":
            y = np.random.choice(10, config.batch_size)
            y_one_hot = np.zeros((config.batch_size, 10))
            y_one_hot[np.arange(config.batch_size), y] = 1
    
            samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample, dcgan.y: y_one_hot})
          else:
            samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
    
          save_images(samples, [image_frame_dim, image_frame_dim], './samples/test_arange_%s.png' % (idx))
      elif option == 2:
        values = np.arange(0, 1, 1./config.batch_size)
        for idx in [random.randint(0, 99) for _ in xrange(100)]:
          print(" [*] %d" % idx)
          z = np.random.uniform(-0.2, 0.2, size=(dcgan.z_dim))
          z_sample = np.tile(z, (config.batch_size, 1))
          #z_sample = np.zeros([config.batch_size, dcgan.z_dim])
          for kdx, z in enumerate(z_sample):
            z[idx] = values[kdx]
    
          if config.dataset == "mnist":
            y = np.random.choice(10, config.batch_size)
            y_one_hot = np.zeros((config.batch_size, 10))
            y_one_hot[np.arange(config.batch_size), y] = 1
    
            samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample, dcgan.y: y_one_hot})
          else:
            samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
    
          try:
            make_gif(samples, './samples/test_gif_%s.gif' % (idx))
          except:
            save_images(samples, [image_frame_dim, image_frame_dim], './samples/test_%s.png' % strftime("%Y%m%d%H%M%S", gmtime()))
      elif option == 3:
        values = np.arange(0, 1, 1./config.batch_size)
        for idx in xrange(100):
          print(" [*] %d" % idx)
          z_sample = np.zeros([config.batch_size, dcgan.z_dim])
          for kdx, z in enumerate(z_sample):
            z[idx] = values[kdx]
    
          samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
          make_gif(samples, './samples/test_gif_%s.gif' % (idx))
      elif option == 4:
        image_set = []
        values = np.arange(0, 1, 1./config.batch_size)
    
        for idx in xrange(100):
          print(" [*] %d" % idx)
          z_sample = np.zeros([config.batch_size, dcgan.z_dim])
          for kdx, z in enumerate(z_sample): z[idx] = values[kdx]
    
          image_set.append(sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample}))
          make_gif(image_set[-1], './samples/test_gif_%s.gif' % (idx))
    
        new_image_set = [merge(np.array([images[idx] for images in image_set]), [10, 10]) \
            for idx in range(64) + range(63, -1, -1)]
        make_gif(new_image_set, './samples/test_gif_merged.gif', duration=8)
    
    
    def image_manifold_size(num_images):
      manifold_h = int(np.floor(np.sqrt(num_images)))
      manifold_w = int(np.ceil(np.sqrt(num_images)))
      assert manifold_h * manifold_w == num_images
      return manifold_h, manifold_w
    

    这份代码主要是定义了各种对图像处理的函数,相当于其他3个文件的头文件。

    step0:首先定义了一个pp = pprint.PrettyPrinter(),以方便打印数据结构信息,详细信息可见这篇博客

    step1:定义了get_stddev函数,是三个参数乘积后开平方的倒数,应该是为了随机化用。

    step2:定义show_all_variables()函数。首先,tf.trainable_variables返回的是需要训练的变量列表;然后用tensorflow.contrib.slim中的model_analyzer.analyze_vars打印出所有与训练相关的变量信息。用法如下:

    -代码:

    import tensorflow as tf
    import tensorflow.contrib.slim as slim
    
    x1=tf.Variable(tf.constant(1,shape=[1],dtype=tf.float32),name='x11')
    x2=tf.Variable(tf.constant(2,shape=[1],dtype=tf.float32),name='x22')
    m=tf.train.ExponentialMovingAverage(0.99,5)
    v=tf.trainable_variables()
    for i in v:
        print 233
        print i
    
    print 23333333   
    slim.model_analyzer.analyze_vars(v,print_info=True)
    print 23333333

    -结果截图如下:

    这里写图片描述

    -
    注:从step3-step11,都是在定义一些图像处理的函数,它们之间相互调用。

    step3:定义get_image(image_path,input_height,input_width,resize_height=64, resize_width=64,crop=True, grayscale=False)函数。首先根据图像路径参数读取路径,根据灰度化参数选择是否进行灰度化。然后对图像参照输入的参数进行裁剪。

    step4:定义save_images(images,size,image_path)函数。调用imsave(inverse_transform(images), size, image_path)函数并返回新图像。

    step5:定义imread(path, grayscale = False)函数。调用cipy.misc.imread()函数,判断grayscale参数是否进行范围灰度化,并进行类型转换为np.float。

    step6:定义merge_images(images, size)函数。调用inverse_transform(images)函数,并返回新图像。

    step7:定义merge(images, size)函数。首先获取image的高和宽。然后判断image是RGB图还是灰度图,以分别进行不同的处理。如果通道数是3或4,则对每一批次(如,batch_size=64)的所有图像,用0初始化一张原始图像放大8*8的图像,然后循环,依次将所有图像填入大图像,并且返回这张大图像。如果通道数是1,也是一样,只不过填入图像的时候只填一个通道的信息。如果不是上述两种情况,则抛出错误提示。

    step8:定义imsave(images, size, path)函数。首先将merge()函数返回的图像,用 np.squeeze()函数移除长度为1的轴。然后利用scipy.misc.imsave()函数将新图像保存到指定路径中。

    step9:定义center_crop(x, crop_h, crop_w,resize_h=64, resize_w=64)函数。对图像的H和W与crop的H和W相减,得到取整的值,根据这个值作为下标依据来scipy.misc.resize图像。

    step10:定义transform(image, input_height, input_width,resize_height=64, resize_width=64, crop=True)函数。对输入的图像进行裁剪,如果crop为true,则使用center_crop()函数,对图像的H和W与crop的H和W相减,得到取整的值,根据这个值作为下标依据来scipy.misc.resize图像;否则不对图像进行其他操作,直接scipy.misc.resize为64*64大小的图像。最后返回图像。

    step11:定义inverse_transform(images)函数。对图像进行翻转后返回新图像。

    总结下来,这几个函数相互调用,主要实现了3个图像操作功能:获取图像get_image(),负责读取图像,返回图像裁剪后的新图像;保存图像save_images(),负责将一个batch中所有图像保存为一张大图像并返回;图像翻转merge_images(),负责不知道怎么得翻转的,返回新图像。它们之间的相互关系如下图所示。

    这里写图片描述

    step12:定义to_json(output_path, *layers)函数。应该是获取每一层的权值、偏置值什么的,但貌似代码中没有用到这个函数,所以先不管,后面用到再说。

    step13:定义make_gif(images, fname, duration=2, true_image=False)函数。利用moviepy.editor模块来制作动图,为了可视化用的。函数又定义了一个函数make_frame(t),首先根据图像集的长度和持续的时间做一个除法,然后返回每帧图像。最后视频修剪并制作成GIF动画。

    step14:定义visualize(sess, dcgan, config, option)函数。分为0、1、2、3、4种option。如果option=0,则之间显示生产的样本‘如果option=1,根据不同数据集不一样的处理,并利用前面的save_images()函数将sample保存下来;等等。本次在main.py中选用option=1。

    step15:定义image_manifold_size(num_images)函数。首先获取图像数量的开平方后向下取整的h和向上取整的w,然后设置一个assert断言,如果h*w与图像数量相等,则返回h和w,否则断言错误提示。

    这就是全部utils.py全部内容,主要负责图像的一些基本操作,获取图像、保存图像、图像翻转,和利用moviepy模块可视化训练过程。

    2.3.3 ops.py

    源代码(105行):

    import math
    import numpy as np 
    import tensorflow as tf
    
    from tensorflow.python.framework import ops
    
    from utils import *
    
    try:
      image_summary = tf.image_summary
      scalar_summary = tf.scalar_summary
      histogram_summary = tf.histogram_summary
      merge_summary = tf.merge_summary
      SummaryWriter = tf.train.SummaryWriter
    except:
      image_summary = tf.summary.image
      scalar_summary = tf.summary.scalar
      histogram_summary = tf.summary.histogram
      merge_summary = tf.summary.merge
      SummaryWriter = tf.summary.FileWriter
    
    if "concat_v2" in dir(tf):
      def concat(tensors, axis, *args, **kwargs):
        return tf.concat_v2(tensors, axis, *args, **kwargs)
    else:
      def concat(tensors, axis, *args, **kwargs):
        return tf.concat(tensors, axis, *args, **kwargs)
    
    class batch_norm(object):
      def __init__(self, epsilon=1e-5, momentum = 0.9, name="batch_norm"):
        with tf.variable_scope(name):
          self.epsilon  = epsilon
          self.momentum = momentum
          self.name = name
    
      def __call__(self, x, train=True):
        return tf.contrib.layers.batch_norm(x,
                          decay=self.momentum, 
                          updates_collections=None,
                          epsilon=self.epsilon,
                          scale=True,
                          is_training=train,
                          scope=self.name)
    
    def conv_cond_concat(x, y):
      """Concatenate conditioning vector on feature map axis."""
      x_shapes = x.get_shape()
      y_shapes = y.get_shape()
      return concat([
        x, y*tf.ones([x_shapes[0], x_shapes[1], x_shapes[2], y_shapes[3]])], 3)
    
    def conv2d(input_, output_dim, 
           k_h=5, k_w=5, d_h=2, d_w=2, stddev=0.02,
           name="conv2d"):
      with tf.variable_scope(name):
        w = tf.get_variable('w', [k_h, k_w, input_.get_shape()[-1], output_dim],
                  initializer=tf.truncated_normal_initializer(stddev=stddev))
        conv = tf.nn.conv2d(input_, w, strides=[1, d_h, d_w, 1], padding='SAME')
    
        biases = tf.get_variable('biases', [output_dim], initializer=tf.constant_initializer(0.0))
        conv = tf.reshape(tf.nn.bias_add(conv, biases), conv.get_shape())
    
        return conv
    
    def deconv2d(input_, output_shape,
           k_h=5, k_w=5, d_h=2, d_w=2, stddev=0.02,
           name="deconv2d", with_w=False):
      with tf.variable_scope(name):
        # filter : [height, width, output_channels, in_channels]
        w = tf.get_variable('w', [k_h, k_w, output_shape[-1], input_.get_shape()[-1]],
                  initializer=tf.random_normal_initializer(stddev=stddev))
    
        try:
          deconv = tf.nn.conv2d_transpose(input_, w, output_shape=output_shape,
                    strides=[1, d_h, d_w, 1])
    
        # Support for verisons of TensorFlow before 0.7.0
        except AttributeError:
          deconv = tf.nn.deconv2d(input_, w, output_shape=output_shape,
                    strides=[1, d_h, d_w, 1])
    
        biases = tf.get_variable('biases', [output_shape[-1]], initializer=tf.constant_initializer(0.0))
        deconv = tf.reshape(tf.nn.bias_add(deconv, biases), deconv.get_shape())
    
        if with_w:
          return deconv, w, biases
        else:
          return deconv
    
    def lrelu(x, leak=0.2, name="lrelu"):
      return tf.maximum(x, leak*x)
    
    def linear(input_, output_size, scope=None, stddev=0.02, bias_start=0.0, with_w=False):
      shape = input_.get_shape().as_list()
    
      with tf.variable_scope(scope or "Linear"):
        matrix = tf.get_variable("Matrix", [shape[1], output_size], tf.float32,
                     tf.random_normal_initializer(stddev=stddev))
        bias = tf.get_variable("bias", [output_size],
          initializer=tf.constant_initializer(bias_start))
        if with_w:
          return tf.matmul(input_, matrix) + bias, matrix, bias
        else:
          return tf.matmul(input_, matrix) + bias
    

    该文件调用了utils.py文件。

    step0:首先导入tensorflow.python.framework模块,包含了tensorflow中图、张量等的定义操作。

    step1:然后是一个try…except部分,定义了一堆变量:image_summary 、scalar_summary、histogram_summary、merge_summary、SummaryWriter,都是从相应的tensorflow中获取的。如果可是直接获取,则获取,否则从tf.summary中获取。

    step2:用来连接多个tensor。利用dir(tf)判断”concat_v2”是否在里面,如果在的话,定义一个concat(tensors, axis, *args, **kwargs)函数,并返回tf.concat_v2(tensors, axis, *args, **kwargs);否则也定义concat(tensors, axis, *args, **kwargs)函数,只不过返回的是tf.concat(tensors, axis, *args, **kwargs)。其中,tf.concat使用如下:

    t1=tf.constant([[1,2,3],[4,5,6]])
    t2=tf.constant([[7,8,9],[10,11,12]])
    t3=tf.concat([t1,t2],0)
    t4=tf.concat([t1,t2],1)
    print t1
    print t2
    print t3
    print t4

    这里写图片描述

    step3:定义一个batch_norm类,包含两个函数initcall函数。首先在init(self, epsilon=1e-5, momentum = 0.9, name=”batch_norm”)函数中,定义一个name参数名字的变量,初始化self变量epsilon、momentum 、name。在call(self, x, train=True)函数中,利用tf.contrib.layers.batch_norm函数批处理规范化。

    step4:定义conv_cond_concat(x,y)函数。连接x,y与Int32型的[x_shapes[0], x_shapes[1], x_shapes[2], y_shapes[3]]维度的张量乘积。

    step5:定义conv2d(input_, output_dim, k_h=5, k_w=5, d_h=2,d_w=2, stddev=0.02,name=”conv2d”)函数。卷积函数:获取随机正态分布权值、实现卷积、获取初始偏置值,获取添加偏置值后的卷积变量并返回。

    step6:定义deconv2d(input_, output_shape,k_h=5, k_w=5, d_h=2, d_w=2, stddev=0.02,name=”deconv2d”, with_w=False):函数。解卷积函数:获取随机正态分布权值、解卷积,获取初始偏置值,获取添加偏置值后的卷积变量,判断with_w是否为真,真则返回解卷积、权值、偏置值,否则返回解卷积。

    step7:定义lrelu(x, leak=0.2, name=”lrelu”)函数。定义一个lrelu激励函数。

    step8:定义linear(input_, output_size, scope=None, stddev=0.02, bias_start=0.0, with_w=False)函数。进行线性运算,获取一个随机正态分布矩阵,获取初始偏置值,如果with_w为真,则返回xw+b,权值w和偏置值b;否则返回xw+b。

    这个文件主要定义了一些变量连接的函数、批处理规范化的函数、卷积函数、解卷积函数、激励函数、线性运算函数。

    2.3.4 model.py

    源代码(530行):

    from __future__ import division
    import os
    import time
    import math
    from glob import glob  # file path search
    import tensorflow as tf
    import numpy as np
    from six.moves import xrange
    
    from ops import *
    from utils import *
    
    def conv_out_size_same(size, stride):
      return int(math.ceil(float(size) / float(stride)))
    
    class DCGAN(object):
      def __init__(self, sess, input_height=108, input_width=108, crop=True,
             batch_size=64, sample_num = 64, output_height=64, output_width=64,
             y_dim=None, z_dim=100, gf_dim=64, df_dim=64,
             gfc_dim=1024, dfc_dim=1024, c_dim=3, dataset_name='default',
             input_fname_pattern='*.jpg', checkpoint_dir=None, sample_dir=None):
        """
    
        Args:
          sess: TensorFlow session
          batch_size: The size of batch. Should be specified before training.
          y_dim: (optional) Dimension of dim for y. [None]
          z_dim: (optional) Dimension of dim for Z. [100]
          gf_dim: (optional) Dimension of gen filters in first conv layer. [64]
          df_dim: (optional) Dimension of discrim filters in first conv layer. [64]
          gfc_dim: (optional) Dimension of gen units for for fully connected layer. [1024]
          dfc_dim: (optional) Dimension of discrim units for fully connected layer. [1024]
          c_dim: (optional) Dimension of image color. For grayscale input, set to 1. [3]
        """
        self.sess = sess
        self.crop = crop
    
        self.batch_size = batch_size
        self.sample_num = sample_num
    
        self.input_height = input_height
        self.input_width = input_width
        self.output_height = output_height
        self.output_width = output_width
    
        self.y_dim = y_dim
        self.z_dim = z_dim
    
        self.gf_dim = gf_dim
        self.df_dim = df_dim
    
        self.gfc_dim = gfc_dim
        self.dfc_dim = dfc_dim
    
        # batch normalization : deals with poor initialization helps gradient flow
        self.d_bn1 = batch_norm(name='d_bn1')
        self.d_bn2 = batch_norm(name='d_bn2')
    
        if not self.y_dim:
          self.d_bn3 = batch_norm(name='d_bn3')
    
        self.g_bn0 = batch_norm(name='g_bn0')
        self.g_bn1 = batch_norm(name='g_bn1')
        self.g_bn2 = batch_norm(name='g_bn2')
    
        if not self.y_dim:
          self.g_bn3 = batch_norm(name='g_bn3')
    
        self.dataset_name = dataset_name
        self.input_fname_pattern = input_fname_pattern
        self.checkpoint_dir = checkpoint_dir
    
        if self.dataset_name == 'mnist':
          self.data_X, self.data_y = self.load_mnist()
          self.c_dim = self.data_X[0].shape[-1]
        else:
          self.data = glob(os.path.join("./data", self.dataset_name, self.input_fname_pattern))
          imreadImg = imread(self.data[0]);
          if len(imreadImg.shape) >= 3: #check if image is a non-grayscale image by checking channel number
            self.c_dim = imread(self.data[0]).shape[-1]
          else:
            self.c_dim = 1
    
        self.grayscale = (self.c_dim == 1)
    
        self.build_model()
    
      def build_model(self):
        if self.y_dim:
          self.y= tf.placeholder(tf.float32, [self.batch_size, self.y_dim], name='y')
    
        if self.crop:
          image_dims = [self.output_height, self.output_width, self.c_dim]
        else:
          image_dims = [self.input_height, self.input_width, self.c_dim]
    
        self.inputs = tf.placeholder(
          tf.float32, [self.batch_size] + image_dims, name='real_images')
    
        inputs = self.inputs
    
        self.z = tf.placeholder(
          tf.float32, [None, self.z_dim], name='z')
        self.z_sum = histogram_summary("z", self.z)
    
        if self.y_dim:
          self.G = self.generator(self.z, self.y)
          self.D, self.D_logits = \
              self.discriminator(inputs, self.y, reuse=False)
    
          self.sampler = self.sampler(self.z, self.y)
          self.D_, self.D_logits_ = \
              self.discriminator(self.G, self.y, reuse=True)
        else:
          self.G = self.generator(self.z)
          self.D, self.D_logits = self.discriminator(inputs)
    
          self.sampler = self.sampler(self.z)
          self.D_, self.D_logits_ = self.discriminator(self.G, reuse=True)
    
        self.d_sum = histogram_summary("d", self.D)
        self.d__sum = histogram_summary("d_", self.D_)
        self.G_sum = image_summary("G", self.G)
    
        def sigmoid_cross_entropy_with_logits(x, y):
          try:
            return tf.nn.sigmoid_cross_entropy_with_logits(logits=x, labels=y)
          except:
            return tf.nn.sigmoid_cross_entropy_with_logits(logits=x, targets=y)
    
        self.d_loss_real = tf.reduce_mean(
          sigmoid_cross_entropy_with_logits(self.D_logits, tf.ones_like(self.D)))
        self.d_loss_fake = tf.reduce_mean(
          sigmoid_cross_entropy_with_logits(self.D_logits_, tf.zeros_like(self.D_)))
        self.g_loss = tf.reduce_mean(
          sigmoid_cross_entropy_with_logits(self.D_logits_, tf.ones_like(self.D_)))
    
        self.d_loss_real_sum = scalar_summary("d_loss_real", self.d_loss_real)
        self.d_loss_fake_sum = scalar_summary("d_loss_fake", self.d_loss_fake)
    
        self.d_loss = self.d_loss_real + self.d_loss_fake
    
        self.g_loss_sum = scalar_summary("g_loss", self.g_loss)
        self.d_loss_sum = scalar_summary("d_loss", self.d_loss)
    
        t_vars = tf.trainable_variables()
    
        self.d_vars = [var for var in t_vars if 'd_' in var.name]
        self.g_vars = [var for var in t_vars if 'g_' in var.name]
    
        self.saver = tf.train.Saver()
    
      def train(self, config):
        d_optim = tf.train.AdamOptimizer(config.learning_rate, beta1=config.beta1) \
                  .minimize(self.d_loss, var_list=self.d_vars)
        g_optim = tf.train.AdamOptimizer(config.learning_rate, beta1=config.beta1) \
                  .minimize(self.g_loss, var_list=self.g_vars)
        try:
          tf.global_variables_initializer().run()
        except:
          tf.initialize_all_variables().run()
    
        self.g_sum = merge_summary([self.z_sum, self.d__sum,
          self.G_sum, self.d_loss_fake_sum, self.g_loss_sum])
        self.d_sum = merge_summary(
            [self.z_sum, self.d_sum, self.d_loss_real_sum, self.d_loss_sum])
        self.writer = SummaryWriter("./logs", self.sess.graph)
    
        sample_z = np.random.uniform(-1, 1, size=(self.sample_num , self.z_dim))
    
        if config.dataset == 'mnist':
          sample_inputs = self.data_X[0:self.sample_num]
          sample_labels = self.data_y[0:self.sample_num]
        else:
          sample_files = self.data[0:self.sample_num]
          sample = [
              get_image(sample_file,
                        input_height=self.input_height,
                        input_width=self.input_width,
                        resize_height=self.output_height,
                        resize_width=self.output_width,
                        crop=self.crop,
                        grayscale=self.grayscale) for sample_file in sample_files]
          if (self.grayscale):
            sample_inputs = np.array(sample).astype(np.float32)[:, :, :, None]
          else:
            sample_inputs = np.array(sample).astype(np.float32)
    
        counter = 1
        start_time = time.time()
        could_load, checkpoint_counter = self.load(self.checkpoint_dir)
        if could_load:
          counter = checkpoint_counter
          print(" [*] Load SUCCESS")
        else:
          print(" [!] Load failed...")
    
        for epoch in xrange(config.epoch):
          if config.dataset == 'mnist':
            batch_idxs = min(len(self.data_X), config.train_size) // config.batch_size
          else:      
            self.data = glob(os.path.join(
              "./data", config.dataset, self.input_fname_pattern))
            batch_idxs = min(len(self.data), config.train_size) // config.batch_size
    
          for idx in xrange(0, batch_idxs):
            if config.dataset == 'mnist':
              batch_images = self.data_X[idx*config.batch_size:(idx+1)*config.batch_size]
              batch_labels = self.data_y[idx*config.batch_size:(idx+1)*config.batch_size]
            else:
              batch_files = self.data[idx*config.batch_size:(idx+1)*config.batch_size]
              batch = [
                  get_image(batch_file,
                            input_height=self.input_height,
                            input_width=self.input_width,
                            resize_height=self.output_height,
                            resize_width=self.output_width,
                            crop=self.crop,
                            grayscale=self.grayscale) for batch_file in batch_files]
              if self.grayscale:
                batch_images = np.array(batch).astype(np.float32)[:, :, :, None]
              else:
                batch_images = np.array(batch).astype(np.float32)
    
            batch_z = np.random.uniform(-1, 1, [config.batch_size, self.z_dim]) \
                  .astype(np.float32)
    
            if config.dataset == 'mnist':
              # Update D network
              _, summary_str = self.sess.run([d_optim, self.d_sum],
                feed_dict={ 
                  self.inputs: batch_images,
                  self.z: batch_z,
                  self.y:batch_labels,
                })
              self.writer.add_summary(summary_str, counter)
    
              # Update G network
              _, summary_str = self.sess.run([g_optim, self.g_sum],
                feed_dict={
                  self.z: batch_z, 
                  self.y:batch_labels,
                })
              self.writer.add_summary(summary_str, counter)
    
              # Run g_optim twice to make sure that d_loss does not go to zero (different from paper)
              _, summary_str = self.sess.run([g_optim, self.g_sum],
                feed_dict={ self.z: batch_z, self.y:batch_labels })
              self.writer.add_summary(summary_str, counter)
    
              errD_fake = self.d_loss_fake.eval({
                  self.z: batch_z, 
                  self.y:batch_labels
              })
              errD_real = self.d_loss_real.eval({
                  self.inputs: batch_images,
                  self.y:batch_labels
              })
              errG = self.g_loss.eval({
                  self.z: batch_z,
                  self.y: batch_labels
              })
            else:
              # Update D network
              _, summary_str = self.sess.run([d_optim, self.d_sum],
                feed_dict={ self.inputs: batch_images, self.z: batch_z })
              self.writer.add_summary(summary_str, counter)
    
              # Update G network
              _, summary_str = self.sess.run([g_optim, self.g_sum],
                feed_dict={ self.z: batch_z })
              self.writer.add_summary(summary_str, counter)
    
              # Run g_optim twice to make sure that d_loss does not go to zero (different from paper)
              _, summary_str = self.sess.run([g_optim, self.g_sum],
                feed_dict={ self.z: batch_z })
              self.writer.add_summary(summary_str, counter)
    
              errD_fake = self.d_loss_fake.eval({ self.z: batch_z })
              errD_real = self.d_loss_real.eval({ self.inputs: batch_images })
              errG = self.g_loss.eval({self.z: batch_z})
    
            counter += 1
            print("Epoch: [%2d] [%4d/%4d] time: %4.4f, d_loss: %.8f, g_loss: %.8f" \
              % (epoch, idx, batch_idxs,
                time.time() - start_time, errD_fake+errD_real, errG))
    
            if np.mod(counter, 100) == 1:
              if config.dataset == 'mnist':
                samples, d_loss, g_loss = self.sess.run(
                  [self.sampler, self.d_loss, self.g_loss],
                  feed_dict={
                      self.z: sample_z,
                      self.inputs: sample_inputs,
                      self.y:sample_labels,
                  }
                )
                save_images(samples, image_manifold_size(samples.shape[0]),
                      './{}/train_{:02d}_{:04d}.png'.format(config.sample_dir, epoch, idx))
                print("[Sample] d_loss: %.8f, g_loss: %.8f" % (d_loss, g_loss)) 
              else:
                try:
                  samples, d_loss, g_loss = self.sess.run(
                    [self.sampler, self.d_loss, self.g_loss],
                    feed_dict={
                        self.z: sample_z,
                        self.inputs: sample_inputs,
                    },
                  )
                  save_images(samples, image_manifold_size(samples.shape[0]),
                        './{}/train_{:02d}_{:04d}.png'.format(config.sample_dir, epoch, idx))
                  print("[Sample] d_loss: %.8f, g_loss: %.8f" % (d_loss, g_loss)) 
                except:
                  print("one pic error!...")
    
            if np.mod(counter, 500) == 2:
              self.save(config.checkpoint_dir, counter)
    
      def discriminator(self, image, y=None, reuse=False):
        with tf.variable_scope("discriminator") as scope:
          if reuse:
            scope.reuse_variables()
    
          if not self.y_dim:
            h0 = lrelu(conv2d(image, self.df_dim, name='d_h0_conv'))
            h1 = lrelu(self.d_bn1(conv2d(h0, self.df_dim*2, name='d_h1_conv')))
            h2 = lrelu(self.d_bn2(conv2d(h1, self.df_dim*4, name='d_h2_conv')))
            h3 = lrelu(self.d_bn3(conv2d(h2, self.df_dim*8, name='d_h3_conv')))
            h4 = linear(tf.reshape(h3, [self.batch_size, -1]), 1, 'd_h4_lin')
    
            return tf.nn.sigmoid(h4), h4
          else:
            yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])
            x = conv_cond_concat(image, yb)
    
            h0 = lrelu(conv2d(x, self.c_dim + self.y_dim, name='d_h0_conv'))
            h0 = conv_cond_concat(h0, yb)
    
            h1 = lrelu(self.d_bn1(conv2d(h0, self.df_dim + self.y_dim, name='d_h1_conv')))
            h1 = tf.reshape(h1, [self.batch_size, -1])      
            h1 = concat([h1, y], 1)
    
            h2 = lrelu(self.d_bn2(linear(h1, self.dfc_dim, 'd_h2_lin')))
            h2 = concat([h2, y], 1)
    
            h3 = linear(h2, 1, 'd_h3_lin')
    
            return tf.nn.sigmoid(h3), h3
    
      def generator(self, z, y=None):
        with tf.variable_scope("generator") as scope:
          if not self.y_dim:
            s_h, s_w = self.output_height, self.output_width
            s_h2, s_w2 = conv_out_size_same(s_h, 2), conv_out_size_same(s_w, 2)
            s_h4, s_w4 = conv_out_size_same(s_h2, 2), conv_out_size_same(s_w2, 2)
            s_h8, s_w8 = conv_out_size_same(s_h4, 2), conv_out_size_same(s_w4, 2)
            s_h16, s_w16 = conv_out_size_same(s_h8, 2), conv_out_size_same(s_w8, 2)
    
            # project `z` and reshape
            self.z_, self.h0_w, self.h0_b = linear(
                z, self.gf_dim*8*s_h16*s_w16, 'g_h0_lin', with_w=True)
    
            self.h0 = tf.reshape(
                self.z_, [-1, s_h16, s_w16, self.gf_dim * 8])
            h0 = tf.nn.relu(self.g_bn0(self.h0))
    
            self.h1, self.h1_w, self.h1_b = deconv2d(
                h0, [self.batch_size, s_h8, s_w8, self.gf_dim*4], name='g_h1', with_w=True)
            h1 = tf.nn.relu(self.g_bn1(self.h1))
    
            h2, self.h2_w, self.h2_b = deconv2d(
                h1, [self.batch_size, s_h4, s_w4, self.gf_dim*2], name='g_h2', with_w=True)
            h2 = tf.nn.relu(self.g_bn2(h2))
    
            h3, self.h3_w, self.h3_b = deconv2d(
                h2, [self.batch_size, s_h2, s_w2, self.gf_dim*1], name='g_h3', with_w=True)
            h3 = tf.nn.relu(self.g_bn3(h3))
    
            h4, self.h4_w, self.h4_b = deconv2d(
                h3, [self.batch_size, s_h, s_w, self.c_dim], name='g_h4', with_w=True)
    
            return tf.nn.tanh(h4)
          else:
            s_h, s_w = self.output_height, self.output_width
            s_h2, s_h4 = int(s_h/2), int(s_h/4)
            s_w2, s_w4 = int(s_w/2), int(s_w/4)
    
            # yb = tf.expand_dims(tf.expand_dims(y, 1),2)
            yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])
            z = concat([z, y], 1)
    
            h0 = tf.nn.relu(
                self.g_bn0(linear(z, self.gfc_dim, 'g_h0_lin')))
            h0 = concat([h0, y], 1)
    
            h1 = tf.nn.relu(self.g_bn1(
                linear(h0, self.gf_dim*2*s_h4*s_w4, 'g_h1_lin')))
            h1 = tf.reshape(h1, [self.batch_size, s_h4, s_w4, self.gf_dim * 2])
    
            h1 = conv_cond_concat(h1, yb)
    
            h2 = tf.nn.relu(self.g_bn2(deconv2d(h1,
                [self.batch_size, s_h2, s_w2, self.gf_dim * 2], name='g_h2')))
            h2 = conv_cond_concat(h2, yb)
    
            return tf.nn.sigmoid(
                deconv2d(h2, [self.batch_size, s_h, s_w, self.c_dim], name='g_h3'))
    
      def sampler(self, z, y=None):
        with tf.variable_scope("generator") as scope:
          scope.reuse_variables()
    
          if not self.y_dim:
            s_h, s_w = self.output_height, self.output_width
            s_h2, s_w2 = conv_out_size_same(s_h, 2), conv_out_size_same(s_w, 2)
            s_h4, s_w4 = conv_out_size_same(s_h2, 2), conv_out_size_same(s_w2, 2)
            s_h8, s_w8 = conv_out_size_same(s_h4, 2), conv_out_size_same(s_w4, 2)
            s_h16, s_w16 = conv_out_size_same(s_h8, 2), conv_out_size_same(s_w8, 2)
    
            # project `z` and reshape
            h0 = tf.reshape(
                linear(z, self.gf_dim*8*s_h16*s_w16, 'g_h0_lin'),
                [-1, s_h16, s_w16, self.gf_dim * 8])
            h0 = tf.nn.relu(self.g_bn0(h0, train=False))
    
            h1 = deconv2d(h0, [self.batch_size, s_h8, s_w8, self.gf_dim*4], name='g_h1')
            h1 = tf.nn.relu(self.g_bn1(h1, train=False))
    
            h2 = deconv2d(h1, [self.batch_size, s_h4, s_w4, self.gf_dim*2], name='g_h2')
            h2 = tf.nn.relu(self.g_bn2(h2, train=False))
    
            h3 = deconv2d(h2, [self.batch_size, s_h2, s_w2, self.gf_dim*1], name='g_h3')
            h3 = tf.nn.relu(self.g_bn3(h3, train=False))
    
            h4 = deconv2d(h3, [self.batch_size, s_h, s_w, self.c_dim], name='g_h4')
    
            return tf.nn.tanh(h4)
          else:
            s_h, s_w = self.output_height, self.output_width
            s_h2, s_h4 = int(s_h/2), int(s_h/4)
            s_w2, s_w4 = int(s_w/2), int(s_w/4)
    
            # yb = tf.reshape(y, [-1, 1, 1, self.y_dim])
            yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])
            z = concat([z, y], 1)
    
            h0 = tf.nn.relu(self.g_bn0(linear(z, self.gfc_dim, 'g_h0_lin'), train=False))
            h0 = concat([h0, y], 1)
    
            h1 = tf.nn.relu(self.g_bn1(
                linear(h0, self.gf_dim*2*s_h4*s_w4, 'g_h1_lin'), train=False))
            h1 = tf.reshape(h1, [self.batch_size, s_h4, s_w4, self.gf_dim * 2])
            h1 = conv_cond_concat(h1, yb)
    
            h2 = tf.nn.relu(self.g_bn2(
                deconv2d(h1, [self.batch_size, s_h2, s_w2, self.gf_dim * 2], name='g_h2'), train=False))
            h2 = conv_cond_concat(h2, yb)
    
            return tf.nn.sigmoid(deconv2d(h2, [self.batch_size, s_h, s_w, self.c_dim], name='g_h3'))
    
      def load_mnist(self):
        data_dir = os.path.join("./data", self.dataset_name)
    
        fd = open(os.path.join(data_dir,'train-images-idx3-ubyte'))
        loaded = np.fromfile(file=fd,dtype=np.uint8)
        trX = loaded[16:].reshape((60000,28,28,1)).astype(np.float)
    
        fd = open(os.path.join(data_dir,'train-labels-idx1-ubyte'))
        loaded = np.fromfile(file=fd,dtype=np.uint8)
        trY = loaded[8:].reshape((60000)).astype(np.float)
    
        fd = open(os.path.join(data_dir,'t10k-images-idx3-ubyte'))
        loaded = np.fromfile(file=fd,dtype=np.uint8)
        teX = loaded[16:].reshape((10000,28,28,1)).astype(np.float)
    
        fd = open(os.path.join(data_dir,'t10k-labels-idx1-ubyte'))
        loaded = np.fromfile(file=fd,dtype=np.uint8)
        teY = loaded[8:].reshape((10000)).astype(np.float)
    
        trY = np.asarray(trY)
        teY = np.asarray(teY)
    
        X = np.concatenate((trX, teX), axis=0)
        y = np.concatenate((trY, teY), axis=0).astype(np.int)
    
        seed = 547
        np.random.seed(seed)
        np.random.shuffle(X)
        np.random.seed(seed)
        np.random.shuffle(y)
    
        y_vec = np.zeros((len(y), self.y_dim), dtype=np.float)
        for i, label in enumerate(y):
          y_vec[i,y[i]] = 1.0
    
        return X/255.,y_vec
    
      @property
      def model_dir(self):
        return "{}_{}_{}_{}".format(
            self.dataset_name, self.batch_size,
            self.output_height, self.output_width)
    
      def save(self, checkpoint_dir, step):
        model_name = "DCGAN.model"
        checkpoint_dir = os.path.join(checkpoint_dir, self.model_dir)
    
        if not os.path.exists(checkpoint_dir):
          os.makedirs(checkpoint_dir)
    
        self.saver.save(self.sess,
                os.path.join(checkpoint_dir, model_name),
                global_step=step)
    
      def load(self, checkpoint_dir):
        import re
        print(" [*] Reading checkpoints...")
        checkpoint_dir = os.path.join(checkpoint_dir, self.model_dir)
    
        ckpt = tf.train.get_checkpoint_state(checkpoint_dir)
        if ckpt and ckpt.model_checkpoint_path:
          ckpt_name = os.path.basename(ckpt.model_checkpoint_path)
          self.saver.restore(self.sess, os.path.join(checkpoint_dir, ckpt_name))
          counter = int(next(re.finditer("(\d+)(?!.*\d)",ckpt_name)).group(0))
          print(" [*] Success to read {}".format(ckpt_name))
          return True, counter
        else:
          print(" [*] Failed to find a checkpoint")
          return False, 0
    

    这个文件就是DCGAN模型定义的函数。调用了utils.py文件和ops.py文件。

    step0:定义conv_out_size_same(size, stride)函数。大小和步幅。

    step1:然后是定义了DCGAN类,剩余代码都是在写DCGAN类,所以下面几步都是在这个类里面定义进行的。

    step2:定义类的初始化函数 init。主要是对一些默认的参数进行初始化。包括session、crop、批处理大小batch_size、样本数量sample_num、输入与输出的高和宽、各种维度、生成器与判别器的批处理、数据集名字、灰度值、构建模型函数,需要注意的是,要判断数据集的名字是否是mnist,是的话则直接用load_mnist()函数加载数据,否则需要从本地data文件夹中读取数据,并将图像读取为灰度图。

    step3:定义构建模型函数build_model(self)。

    1. 首先判断y_dim,然后用tf.placeholder占位符定义并初始化y。
    2. 判断crop是否为真,是的话是进行测试,图像维度是输出图像的维度;否则是输入图像的维度。
    3. 利用tf.placeholder定义inputs,是真实数据的向量。
    4. 定义并初始化生成器用到的噪音z,z_sum。
    5. 再次判断y_dim,如果为真,用噪音z和标签y初始化生成器G、用输入inputs初始化判别器D和D_logits、样本、用G和y初始化D_和D_logits;如果为假,跟上面一样初始化各种变量,只不过都没有标签y。
    6. 将5中的D、D_、G分别放在d_sum、d__sum、G_sum。
    7. 定义sigmoid交叉熵损失函数sigmoid_cross_entropy_with_logits(x, y)。都是调用tf.nn.sigmoid_cross_entropy_with_logits函数,只不过一个是训练,y是标签,一个是测试,y是目标。
    8. 定义各种损失值。真实数据的判别损失值d_loss_real、虚假数据的判别损失值d_loss_fake、生成器损失值g_loss、判别器损失值d_loss。
    9. 定义训练的所有变量t_vars。
    10. 定义生成和判别的参数集。
    11. 最后是保存。

    step4:定义训练函数train(self, config)。

    1. 定义判别器优化器d_optim和生成器优化器g_optim。
    2. 变量初始化。
    3. 分别将关于生成器和判别器有关的变量各合并到一个变量中,并写入事件文件中。
    4. 噪音z初始化。
    5. 根据数据集是否为mnist的判断,进行输入数据和标签的获取。这里使用到了utils.py文件中的get_image函数。
    6. 定义计数器counter和起始时间start_time。
    7. 加载检查点,并判断加载是否成功。
    8. 开始for epoch in xrange(config.epoch)循环训练。先判断数据集是否是mnist,获取批处理的大小。
    9. 开始for idx in xrange(0, batch_idxs)循环训练,判断数据集是否是mnist,来定义初始化批处理图像和标签。
    10. 定义初始化噪音z。
    11. 判断数据集是否是mnist,来更新判别器网络和生成器网络,这里就不管mnist数据集是怎么处理的,其他数据集是,运行生成器优化器两次,以确保判别器损失值不会变为0,然后是判别器真实数据损失值和虚假数据损失值、生成器损失值。
    12. 输出本次批处理中训练参数的情况,首先是第几个epoch,第几个batch,训练时间,判别器损失值,生成器损失值。
    13. 每100次batch训练后,根据数据集是否是mnist的不同,获取样本、判别器损失值、生成器损失值,调用utils.py文件的save_images函数,保存训练后的样本,并以epoch、batch的次数命名文件。然后打印判别器损失值和生成器损失值。
    14. 每500次batch训练后,保存一次检查点。

    step5:定义判别器函数discriminator(self, image, y=None, reuse=False)。

    1. 利用with tf.variable_scope(“discriminator”) as scope,在一个作用域 scope 内共享一些变量。
    2. 对scope利用reuse_variables()进行重利用。
    3. 如果为假,则直接设置5层,前4层为使用lrelu激活函数的卷积层,最后一层是使用线性层,最后返回h4和sigmoid处理后的h4。
    4. 如果为真,则首先将Y_dim变为yb,然后利用ops.py文件中的conv_cond_concat函数,连接image与yb得到x,然后设置4层网络,前3层是使用lrelu激励函数的卷积层,最后一层是线性层,最后返回h3和sigmoid处理后的h3。

    step6:定义生成器函数generator(self, z, y=None)。

    1. 利用with tf.variable_scope(“generator”) as scope,在一个作用域 scope 内共享一些变量。
    2. 根据y_dim是否为真,进行判别网络的设置。
    3. 如果为假:首先获取输出的宽和高,然后根据这一值得到更多不同大小的高和宽的对。然后获取h0层的噪音z,权值w,偏置值b,然后利用relu激励函数。h1层,首先对h0层解卷积得到本层的权值和偏置值,然后利用relu激励函数。h2、h3等同于h1。h4层,解卷积h3,然后直接返回使用tanh激励函数后的h4。
    4. 如果为真:首先也是获取输出的高和宽,根据这一值得到更多不同大小的高和宽的对。然后获取yb和噪音z。h0层,使用relu激励函数,并与1连接。h1层,对线性全连接后使用relu激励函数,并与yb连接。h2层,对解卷积后使用relu激励函数,并与yb连接。最后返回解卷积、sigmoid处理后的h2。

    step7:定义sampler(self, z, y=None)函数。

    1. 利用tf.variable_scope(“generator”) as scope,在一个作用域 scope 内共享一些变量。
    2. 对scope利用reuse_variables()进行重利用。
    3. 根据y_dim是否为真,进行判别网络的设置。
    4. 然后就跟生成器差不多,不在赘述。

    step8:定义load_mnist(self)函数。这个主要是针对mnist数据集设置的,所以暂且不考虑,过。

    step9:定义model_dir(self)函数。返回数据集名字,batch大小,输出的高和宽。

    step10:定义save(self, checkpoint_dir, step)函数。保存训练好的模型。创建检查点文件夹,如果路径不存在,则创建;然后将其保存在这个文件夹下。

    step11:定义load(self, checkpoint_dir)函数。读取检查点,获取路径,重新存储检查点,并且计数。打印成功读取的提示;如果没有路径,则打印失败的提示。

    以上,就是model.py所有内容,主要是定义了DCGAN的类,完成了生成判别网络的实现。

    2.4 训练

    现在,整个4个文件都已经分析完毕,开始运行。

    step0:由于我们使用的动漫人脸数据集,所以我们需要在源文件的路径下,建一个data文件夹,然后将放有数据的文件夹放在这个data文件夹中,如下所示。

    这里写图片描述

    这里写图片描述

    step1:运行命令如下,需要制定各种参数,如我们的输入数据的高宽,输出的高宽,是哪个数据集,是否测试、训练,运行几个epoch。

    如果你看到了此处,很好,接下来一系列的问题都是由于这里的原因导致我的训练不收敛,出来的结果乱七八糟!!这是因为,参数名称写错了!!!应该是:

    python main.py --input_height 96 --output_height 48 --dataset faces --crop True --train True --epoch 10

    下面这个参数名称是错误的!(嗯,后面我还是会再说一遍的)

    python main.py --image_size 96 --output_size 48 --dataset faces --crop True --train True --epoch 10

    这里写图片描述

    step2:中间结果

    这是第0个epoch,前3个batch:

    这里写图片描述

    新生成的文件:

    这里写图片描述

    step3:训练和测试结果

    如果你又看到这里,可以忽略,直接去结果那看,因为这里都是参数没写对,生成的不收敛的结果!

    第一个epoch:

    这里写图片描述

    第9个epoch:

    这里写图片描述

    看得出来,效果并不咋地,与参考更是相差甚远,这是因为训练数据只有3000+,而且总共训练了10个epoch。本来只是先试试,毕竟是纯cpu在跑,还有2个G,哎。

    step4:这次训练数据选了16383张,epoch==300,跑了一晚上了,今天来看才到第5个epoch,嗯,慢慢等。

    step5:重新在服务器上训练,这次选了参考博客上提供的数据集,因为前两次自己采集处理的数据集,或是因为数据集过小,训练效果差强人意,所以直接拿这个5万左右的数据集来试试。epoch==300。

    step6:效果太差了。也不知道是哪里的问题,先把结果截图放上去,等有空再查查是什么原因。(严重怀疑是我的数据集有问题,因为当时在本地跑时对数据操作过,可能出现了问题。后面有空再弄吧)

    结果标题代表第几个epoch第几个batch。

    这里写图片描述

    这里写图片描述

    这里写图片描述

    这里写图片描述

    2.5 结果

    好了,终于找到原因了,是因为参数名称写错了,没有将输入数据的高宽与输出数据的高宽由原先的108与64改为96与48,简直是太蠢了!!(此处感谢评论里某位小伙伴!要不是他说修改了参数我都没注意到)

    重新训练:

    python main.py --input_height 96 --output_height 48 --dataset faces --crop True --train True --epoch 10

    只用了10个epoch,效果就已经有点可观了,等服务器有空跑个300试试。

    换了epoch==300,先放几张已有的效果,等跑完300再把全部结果放上来。

    epoch 0

    这里写图片描述

    这里写图片描述

    epoch 5

    这里写图片描述

    epoch 10

    这里写图片描述

    epoch 20

    这里写图片描述

    epoch 100

    这里写图片描述

    这里写图片描述

    epoch 200

    这里写图片描述

    这里写图片描述

    epoch 300

    这里写图片描述

    这里写图片描述

    3、总结

    总结,不想写了,只能怪自己太弱了!

    希望早日成为调参大师(摊手┓( ´∀` )┏)

    嗯,入坑需谨慎。

    展开全文
  • DCGAN

    2019-11-20 14:33:13
    这一篇就来看看GAN 的改进版之一,DCGAN(Deep Convolutional GAN)。 1. 相比于GAN 的改进 DCGAN 相比于GAN 或者是普通CNN 的改进包含以下几个方面: (1)使用卷积和去卷积替换池化,即将pooling层convolu...

    上一篇介绍了GAN 的基本原理以及相关的概念和知识点,同时也反映出GAN 的一些缺点,比如说训练不稳定,生成过程不可控,不具备可解释性等。这一篇就来看看GAN 的改进版之一,DCGAN(Deep Convolutional GAN)。

    1. 相比于GAN 的改进

    DCGAN 相比于GAN 或者是普通CNN 的改进包含以下几个方面:
    (1)使用卷积和去卷积替换池化,即将pooling层convolutions替代
    **对于判别模型:**容许网络学习自己的空间下采样
    **对于生成模型:**容许它学习自己的空间上采样
    (2)在生成器和判别器中都添加了批量归一化操作

    • 解决初始化差的问题
    • 帮助梯度传播到每一层
    • 防止generator把所有的样本都收敛到同一点。
      (3)在CNN中移除全连接层
      (4)在generator的除了输出层外的所有层使用ReLU,输出层采用tanh。
      (5)在discriminator的所有层上使用LeakyReLU。
      在这里插入图片描述
      图: LSUN 场景模型中使用的DCGAN生成网络。一个100维度的均匀分布z映射到一个有很多特征映射的小空间范围卷积。一连串的四个微步幅卷积(在最近的一些论文中它们错误地称为去卷积),将高层表征转换为64*64像素的图像。明显,没有使用全连接层和池化层。

    2.DCGAN的实现

    2.1 搜集原始数据集

    首先是需要获取大量的动漫图像,这个可以利用爬虫爬取一个动漫网站:konachan.net的图片。爬虫的代码如下所示:

    import requests
    from bs4 import BeautifulSoup
    import os
    import traceback
    
    def download(url, filename):
        if os.path.exists(filename):
            print('file exists!')
            return
        try:
            r = requests.get(url, stream=True, timeout=60)
            r.raise_for_status()
            with open(filename, 'wb') as f:
                for chunk in r.iter_content(chunk_size=1024):
                    if chunk:  # filter out keep-alive new chunks
                        f.write(chunk)
                        f.flush()
            return filename
        except KeyboardInterrupt:
            if os.path.exists(filename):
                os.remove(filename)
            raise KeyboardInterrupt
        except Exception:
            traceback.print_exc()
            if os.path.exists(filename):
                os.remove(filename)
    
    
    if os.path.exists('imgs') is False:
        os.makedirs('imgs')
    
    start = 1
    end = 8000
    for i in range(start, end + 1):
        url = 'http://konachan.net/post?page=%d&tags=' % i
        html = requests.get(url).text
        soup = BeautifulSoup(html, 'html.parser')
        for img in soup.find_all('img', class_="preview"):
            target_url = 'http:' + img['src']
            filename = os.path.join('imgs', target_url.split('/')[-1])
            download(target_url, filename)
        print('%d / %d' % (i, end))
    

    截取部分图像如下所示:
    在这里插入图片描述
    现在已经有了基本的图像了,但我们的目标是生成动漫头像,不需要整张图像,而且其他的信息会干扰到训练,所以需要进行人脸检测截取人脸图像。

    2.2 人脸检测截取人脸

    截取头像和原文一样,直接使用github上一个基于opencv的工具:nagadomi/lbpcascade_animeface

    import cv2
    import sys
    import os.path
    from glob import glob
    
    def detect(filename, cascade_file="lbpcascade_animeface.xml"):
        if not os.path.isfile(cascade_file):
            raise RuntimeError("%s: not found" % cascade_file)
    
        cascade = cv2.CascadeClassifier(cascade_file)
        image = cv2.imread(filename)
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        gray = cv2.equalizeHist(gray)
    
        faces = cascade.detectMultiScale(gray,
                                         # detector options
                                         scaleFactor=1.1,
                                         minNeighbors=5,
                                         minSize=(48, 48))
        for i, (x, y, w, h) in enumerate(faces):
            face = image[y: y + h, x:x + w, :]
            face = cv2.resize(face, (96, 96))
            save_filename = '%s-%d.jpg' % (os.path.basename(filename).split('.')[0], i)
            cv2.imwrite("faces/" + save_filename, face)
    
    
    if __name__ == '__main__':
        if os.path.exists('faces') is False:
            os.makedirs('faces')
        file_list = glob('imgs/*.jpg')
        for filename in file_list:
            detect(filename)
    

    处理后的图像如下所示:
    在这里插入图片描述
    这样就可以用来训练了!

    2.3 源代码解析

    参照于DCGAN-tensorflow
    一共有4个文件,分别是main.py、model.py、ops.py、utils.py。

    2.3.1 main.py
    import os
    import scipy.misc
    import numpy as np
    import json
    
    from model import DCGAN
    from utils import pp, visualize, to_json, show_all_variables, expand_path, timestamp
    
    import tensorflow as tf
    
    flags = tf.app.flags
    flags.DEFINE_integer("epoch", 25, "Epoch to train [25]")
    flags.DEFINE_float("learning_rate", 0.0002, "Learning rate of for adam [0.0002]")
    flags.DEFINE_float("beta1", 0.5, "Momentum term of adam [0.5]")
    flags.DEFINE_float("train_size", np.inf, "The size of train images [np.inf]")
    flags.DEFINE_integer("batch_size", 64, "The size of batch images [64]")
    flags.DEFINE_integer("input_height", 108, "The size of image to use (will be center cropped). [108]")
    flags.DEFINE_integer("input_width", None, "The size of image to use (will be center cropped). If None, same value as input_height [None]")
    flags.DEFINE_integer("output_height", 64, "The size of the output images to produce [64]")
    flags.DEFINE_integer("output_width", None, "The size of the output images to produce. If None, same value as output_height [None]")
    flags.DEFINE_string("dataset", "celebA", "The name of dataset [celebA, mnist, lsun]")
    flags.DEFINE_string("input_fname_pattern", "*.jpg", "Glob pattern of filename of input images [*]")
    flags.DEFINE_string("data_dir", "./data", "path to datasets [e.g. $HOME/data]")
    flags.DEFINE_string("out_dir", "./out", "Root directory for outputs [e.g. $HOME/out]")
    flags.DEFINE_string("out_name", "", "Folder (under out_root_dir) for all outputs. Generated automatically if left blank []")
    flags.DEFINE_string("checkpoint_dir", "checkpoint", "Folder (under out_root_dir/out_name) to save checkpoints [checkpoint]")
    flags.DEFINE_string("sample_dir", "samples", "Folder (under out_root_dir/out_name) to save samples [samples]")
    flags.DEFINE_boolean("train", False, "True for training, False for testing [False]")
    flags.DEFINE_boolean("crop", False, "True for training, False for testing [False]")
    flags.DEFINE_boolean("visualize", False, "True for visualizing, False for nothing [False]")
    flags.DEFINE_boolean("export", False, "True for exporting with new batch size")
    flags.DEFINE_boolean("freeze", False, "True for exporting with new batch size")
    flags.DEFINE_integer("max_to_keep", 1, "maximum number of checkpoints to keep")
    flags.DEFINE_integer("sample_freq", 200, "sample every this many iterations")
    flags.DEFINE_integer("ckpt_freq", 200, "save checkpoint every this many iterations")
    flags.DEFINE_integer("z_dim", 100, "dimensions of z")
    flags.DEFINE_string("z_dist", "uniform_signed", "'normal01' or 'uniform_unsigned' or uniform_signed")
    flags.DEFINE_boolean("G_img_sum", False, "Save generator image summaries in log")
    #flags.DEFINE_integer("generate_test_images", 100, "Number of images to generate during test. [100]")
    FLAGS = flags.FLAGS
    
    def main(_):
      pp.pprint(flags.FLAGS.__flags)
      
      # expand user name and environment variables
      FLAGS.data_dir = expand_path(FLAGS.data_dir)
      FLAGS.out_dir = expand_path(FLAGS.out_dir)
      FLAGS.out_name = expand_path(FLAGS.out_name)
      FLAGS.checkpoint_dir = expand_path(FLAGS.checkpoint_dir)
      FLAGS.sample_dir = expand_path(FLAGS.sample_dir)
    
      if FLAGS.output_height is None: FLAGS.output_height = FLAGS.input_height
      if FLAGS.input_width is None: FLAGS.input_width = FLAGS.input_height
      if FLAGS.output_width is None: FLAGS.output_width = FLAGS.output_height
    
      # output folders
      if FLAGS.out_name == "":
          FLAGS.out_name = '{} - {} - {}'.format(timestamp(), FLAGS.data_dir.split('/')[-1], FLAGS.dataset) # penultimate folder of path
          if FLAGS.train:
            FLAGS.out_name += ' - x{}.z{}.{}.y{}.b{}'.format(FLAGS.input_width, FLAGS.z_dim, FLAGS.z_dist, FLAGS.output_width, FLAGS.batch_size)
    
      FLAGS.out_dir = os.path.join(FLAGS.out_dir, FLAGS.out_name)
      FLAGS.checkpoint_dir = os.path.join(FLAGS.out_dir, FLAGS.checkpoint_dir)
      FLAGS.sample_dir = os.path.join(FLAGS.out_dir, FLAGS.sample_dir)
    
      if not os.path.exists(FLAGS.checkpoint_dir): os.makedirs(FLAGS.checkpoint_dir)
      if not os.path.exists(FLAGS.sample_dir): os.makedirs(FLAGS.sample_dir)
    
      with open(os.path.join(FLAGS.out_dir, 'FLAGS.json'), 'w') as f:
        flags_dict = {k:FLAGS[k].value for k in FLAGS}
        json.dump(flags_dict, f, indent=4, sort_keys=True, ensure_ascii=False)
      
    
      #gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.333)
      run_config = tf.ConfigProto()
      run_config.gpu_options.allow_growth=True
    
      with tf.Session(config=run_config) as sess:
        if FLAGS.dataset == 'mnist':
          dcgan = DCGAN(
              sess,
              input_width=FLAGS.input_width,
              input_height=FLAGS.input_height,
              output_width=FLAGS.output_width,
              output_height=FLAGS.output_height,
              batch_size=FLAGS.batch_size,
              sample_num=FLAGS.batch_size,
              y_dim=10,
              z_dim=FLAGS.z_dim,
              dataset_name=FLAGS.dataset,
              input_fname_pattern=FLAGS.input_fname_pattern,
              crop=FLAGS.crop,
              checkpoint_dir=FLAGS.checkpoint_dir,
              sample_dir=FLAGS.sample_dir,
              data_dir=FLAGS.data_dir,
              out_dir=FLAGS.out_dir,
              max_to_keep=FLAGS.max_to_keep)
        else:
          dcgan = DCGAN(                                    #创建模型对象
              sess,
              input_width=FLAGS.input_width,                #输入108(不固定)
              input_height=FLAGS.input_height,
              output_width=FLAGS.output_width,              #输出64x64x3
              output_height=FLAGS.output_height,
              batch_size=FLAGS.batch_size,                  #batch_size=64
              sample_num=FLAGS.batch_size,
              z_dim=FLAGS.z_dim,
              dataset_name=FLAGS.dataset,
              input_fname_pattern=FLAGS.input_fname_pattern,
              crop=FLAGS.crop,                              #要不要进行crop,以图像中心点为原点截取图像
              checkpoint_dir=FLAGS.checkpoint_dir,
              sample_dir=FLAGS.sample_dir,
              data_dir=FLAGS.data_dir,
              out_dir=FLAGS.out_dir,
              max_to_keep=FLAGS.max_to_keep)
    
        show_all_variables()
    
        if FLAGS.train:
          dcgan.train(FLAGS)
        else:
          load_success, load_counter = dcgan.load(FLAGS.checkpoint_dir)
          if not load_success:
            raise Exception("Checkpoint not found in " + FLAGS.checkpoint_dir)
    
    
        # to_json("./web/js/layers.js", [dcgan.h0_w, dcgan.h0_b, dcgan.g_bn0],
        #                 [dcgan.h1_w, dcgan.h1_b, dcgan.g_bn1],
        #                 [dcgan.h2_w, dcgan.h2_b, dcgan.g_bn2],
        #                 [dcgan.h3_w, dcgan.h3_b, dcgan.g_bn3],
        #                 [dcgan.h4_w, dcgan.h4_b, None])
    
        # Below is codes for visualization
          if FLAGS.export:
            export_dir = os.path.join(FLAGS.checkpoint_dir, 'export_b'+str(FLAGS.batch_size))
            dcgan.save(export_dir, load_counter, ckpt=True, frozen=False)
    
          if FLAGS.freeze:
            export_dir = os.path.join(FLAGS.checkpoint_dir, 'frozen_b'+str(FLAGS.batch_size))
            dcgan.save(export_dir, load_counter, ckpt=False, frozen=True)
    
          if FLAGS.visualize:
            OPTION = 1
            visualize(sess, dcgan, FLAGS, OPTION, FLAGS.sample_dir)
    
    if __name__ == '__main__':
      tf.app.run()
    
    

    该文件调用了model.py文件和utils.py文件。

    step0:执行main函数之前首先进行flags的解析,TensorFlow底层使用了python-gflags项目,然后封装成tf.app.flags接口,也就是说TensorFlow通过设置flags来传递tf.app.run()所需要的参数,我们可以直接在程序运行前初始化flags,也可以在运行程序的时候设置命令行参数来达到传参的目的。

    这里主要设置了:

    • epoch:迭代次数
    • learning_rate:学习速率,默认是0.002
    • beta1
    • train_size
    • batch_size:每次迭代的图像数量
    • input_height:需要指定输入图像的高
    • input_width:需要指定输入图像的宽
    • output_height:需要指定输出图像的高
    • output_width:需要指定输出图像的宽
    • dataset:需要指定处理哪个数据集
    • input_fname_pattern
    • checkpoint_dir
    • sample_dir
    • train:True for training, False for testing
    • crop:True for training, False for testing
    • visualize
      step1:首先是打印参数数据,然后判断输入图像的输出图像的宽是否指定,如果没有指定,则等于其图像的高。

    step2:然后判断checkpoint和sample的文件是否存在,不存在则创建。

    step3:然后是设置session参数。tf.ConfigProto一般用在创建session的时候,用来对session进行参数配置,详细内容可见这篇博客

    #tf.ConfigProto()的参数:
    log_device_placement=True : 是否打印设备分配日志
    allow_soft_placement=True : 如果你指定的设备不存在,允许TF自动分配设备
    tf.ConfigProto(log_device_placement=True,allow_soft_placement=True)
    
    控制GPU资源使用率:
    #allow growth
    config = tf.ConfigProto()
    config.gpu_options.allow_growth = True
    session = tf.Session(config=config, ...)
    # 使用allow_growth option,刚一开始分配少量的GPU容量,然后按需慢慢的增加,由于不会释放内存,所以会导致碎片
    

    step4:运行session,首先判断处理的是哪个数据集,然后对应使用不同参数的DCGAN类,这个类会在model.py文件中定义。

    step5:show所有与训练相关的变量。

    step6:判断是训练还是测试,如果是训练,则进行训练;如果不是,判断是否有训练好的model,然后进行测试,如果没有先训练,则会提示“[!] Train a model first, then run test mode”。

    step7:最后进行可视化,visualize(sess, dcgan, FLAGS, OPTION)。

    main.py主要是调用前面定义好的模型、图像处理方法,来进行训练测试,程序的入口。

    2.3.2 utils.py
    """
    Some codes from https://github.com/Newmu/dcgan_code
    """
    from __future__ import division
    import math
    import json
    import random
    import pprint
    import scipy.misc
    import imageio
    import cv2
    import numpy as np
    import os
    import time
    import datetime
    from time import gmtime, strftime
    from six.moves import xrange
    from PIL import Image
    
    import tensorflow as tf
    import tensorflow.contrib.slim as slim
    
    pp = pprint.PrettyPrinter()
    
    get_stddev = lambda x, k_h, k_w: 1/math.sqrt(k_w*k_h*x.get_shape()[-1])
    
    
    def expand_path(path):
      return os.path.expanduser(os.path.expandvars(path))
    
    def timestamp(s='%Y%m%d.%H%M%S', ts=None):
      if not ts: ts = time.time()
      st = datetime.datetime.fromtimestamp(ts).strftime(s)
      return st
      
    def show_all_variables():
      model_vars = tf.trainable_variables()
      slim.model_analyzer.analyze_vars(model_vars, print_info=True)
    
    def get_image(image_path, input_height, input_width,
                  resize_height=64, resize_width=64,
                  crop=True, grayscale=False):
      image = imread(image_path, grayscale)                                             #通过图像路径读取图像
      return transform(image, input_height, input_width,                                #图像预处理,中心化,crop成64大小
                       resize_height, resize_width, crop)
    
    def save_images(images, size, image_path):
      return imsave(inverse_transform(images), size, image_path)
    
    def imread(path, grayscale = False):
      if (grayscale):
        return scipy.misc.imread(path, flatten = True).astype(np.float)
      else:
        # Reference: https://github.com/carpedm20/DCGAN-tensorflow/issues/162#issuecomment-315519747
        img_bgr = cv2.imread(path)
        # Reference: https://stackoverflow.com/a/15074748/
        img_rgb = img_bgr[..., ::-1]
        return img_rgb.astype(np.float)
    
    def merge_images(images, size):
      return inverse_transform(images)
    
    def merge(images, size):
      h, w = images.shape[1], images.shape[2]
      if (images.shape[3] in (3,4)):
        c = images.shape[3]
        img = np.zeros((h * size[0], w * size[1], c))
        for idx, image in enumerate(images):
          i = idx % size[1]
          j = idx // size[1]
          img[j * h:j * h + h, i * w:i * w + w, :] = image
        return img
      elif images.shape[3]==1:
        img = np.zeros((h * size[0], w * size[1]))
        for idx, image in enumerate(images):
          i = idx % size[1]
          j = idx // size[1]
          img[j * h:j * h + h, i * w:i * w + w] = image[:,:,0]
        return img
      else:
        raise ValueError('in merge(images,size) images parameter '
                         'must have dimensions: HxW or HxWx3 or HxWx4')
    
    def imsave(images, size, path):
      image = np.squeeze(merge(images, size))
      return imageio.imwrite(path, image)
    
    def center_crop(x, crop_h, crop_w,
                    resize_h=64, resize_w=64):
      if crop_w is None:
        crop_w = crop_h
      h, w = x.shape[:2]
      j = int(round((h - crop_h)/2.))
      i = int(round((w - crop_w)/2.))
      im = Image.fromarray(np.uint8(x[j:j+crop_h, i:i+crop_w]))
      return np.array(im.resize([resize_h, resize_w]), np.uint8(Image.BILINEAR))
    
    def transform(image, input_height, input_width, 
                  resize_height=64, resize_width=64, crop=True):
      if crop:
        cropped_image = center_crop(
          image, input_height, input_width, 
          resize_height, resize_width)
        h, w = image.shape[:2]
        j = int(round((h - input_height) / 2.))
        i = int(round((w - input_width) / 2.))
        im = Image.fromarray(np.uint8(image[j:j + input_height, i:i + input_width]))
      else:
        im = Image.fromarray(image[j:j+crop_h, i:i+crop_w])
      #处理后的图像压缩到[-1,1]区间
      return np.array(im.resize([resize_height, resize_width]), np.uint8(Image.BILINEAR))/127.5 - 1.
    
    def inverse_transform(images):
      return (images+1.)/2.
    
    def to_json(output_path, *layers):
      with open(output_path, "w") as layer_f:
        lines = ""
        for w, b, bn in layers:
          layer_idx = w.name.split('/')[0].split('h')[1]
    
          B = b.eval()
    
          if "lin/" in w.name:
            W = w.eval()
            depth = W.shape[1]
          else:
            W = np.rollaxis(w.eval(), 2, 0)
            depth = W.shape[0]
    
          biases = {"sy": 1, "sx": 1, "depth": depth, "w": ['%.2f' % elem for elem in list(B)]}
          if bn != None:
            gamma = bn.gamma.eval()
            beta = bn.beta.eval()
    
            gamma = {"sy": 1, "sx": 1, "depth": depth, "w": ['%.2f' % elem for elem in list(gamma)]}
            beta = {"sy": 1, "sx": 1, "depth": depth, "w": ['%.2f' % elem for elem in list(beta)]}
          else:
            gamma = {"sy": 1, "sx": 1, "depth": 0, "w": []}
            beta = {"sy": 1, "sx": 1, "depth": 0, "w": []}
    
          if "lin/" in w.name:
            fs = []
            for w in W.T:
              fs.append({"sy": 1, "sx": 1, "depth": W.shape[0], "w": ['%.2f' % elem for elem in list(w)]})
    
            lines += """
              var layer_%s = {
                "layer_type": "fc", 
                "sy": 1, "sx": 1, 
                "out_sx": 1, "out_sy": 1,
                "stride": 1, "pad": 0,
                "out_depth": %s, "in_depth": %s,
                "biases": %s,
                "gamma": %s,
                "beta": %s,
                "filters": %s
              };""" % (layer_idx.split('_')[0], W.shape[1], W.shape[0], biases, gamma, beta, fs)
          else:
            fs = []
            for w_ in W:
              fs.append({"sy": 5, "sx": 5, "depth": W.shape[3], "w": ['%.2f' % elem for elem in list(w_.flatten())]})
    
            lines += """
              var layer_%s = {
                "layer_type": "deconv", 
                "sy": 5, "sx": 5,
                "out_sx": %s, "out_sy": %s,
                "stride": 2, "pad": 1,
                "out_depth": %s, "in_depth": %s,
                "biases": %s,
                "gamma": %s,
                "beta": %s,
                "filters": %s
              };""" % (layer_idx, 2**(int(layer_idx)+2), 2**(int(layer_idx)+2),
                   W.shape[0], W.shape[3], biases, gamma, beta, fs)
        layer_f.write(" ".join(lines.replace("'","").split()))
    
    def make_gif(images, fname, duration=2, true_image=False):
      import moviepy.editor as mpy
    
      def make_frame(t):
        try:
          x = images[int(len(images)/duration*t)]
        except:
          x = images[-1]
    
        if true_image:
          return x.astype(np.uint8)
        else:
          return ((x+1)/2*255).astype(np.uint8)
    
      clip = mpy.VideoClip(make_frame, duration=duration)
      clip.write_gif(fname, fps = len(images) / duration)
    
    def visualize(sess, dcgan, config, option, sample_dir='samples'):
      image_frame_dim = int(math.ceil(config.batch_size**.5))
      if option == 0:
        z_sample = np.random.uniform(-0.5, 0.5, size=(config.batch_size, dcgan.z_dim))
        samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
        save_images(samples, [image_frame_dim, image_frame_dim], os.path.join(sample_dir, 'test_%s.png' % strftime("%Y%m%d%H%M%S", gmtime() )))
      elif option == 1:
        values = np.arange(0, 1, 1./config.batch_size)
        for idx in xrange(dcgan.z_dim):
          print(" [*] %d" % idx)
          z_sample = np.random.uniform(-1, 1, size=(config.batch_size , dcgan.z_dim))
          for kdx, z in enumerate(z_sample):
            z[idx] = values[kdx]
    
          if config.dataset == "mnist":
            y = np.random.choice(10, config.batch_size)
            y_one_hot = np.zeros((config.batch_size, 10))
            y_one_hot[np.arange(config.batch_size), y] = 1
    
            samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample, dcgan.y: y_one_hot})
          else:
            samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
    
          save_images(samples, [image_frame_dim, image_frame_dim], os.path.join(sample_dir, 'test_arange_%s.png' % (idx)))
      elif option == 2:
        values = np.arange(0, 1, 1./config.batch_size)
        for idx in [random.randint(0, dcgan.z_dim - 1) for _ in xrange(dcgan.z_dim)]:
          print(" [*] %d" % idx)
          z = np.random.uniform(-0.2, 0.2, size=(dcgan.z_dim))
          z_sample = np.tile(z, (config.batch_size, 1))
          #z_sample = np.zeros([config.batch_size, dcgan.z_dim])
          for kdx, z in enumerate(z_sample):
            z[idx] = values[kdx]
    
          if config.dataset == "mnist":
            y = np.random.choice(10, config.batch_size)
            y_one_hot = np.zeros((config.batch_size, 10))
            y_one_hot[np.arange(config.batch_size), y] = 1
    
            samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample, dcgan.y: y_one_hot})
          else:
            samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
    
          try:
            make_gif(samples, './samples/test_gif_%s.gif' % (idx))
          except:
            save_images(samples, [image_frame_dim, image_frame_dim], os.path.join(sample_dir, 'test_%s.png' % strftime("%Y%m%d%H%M%S", gmtime() )))
      elif option == 3:
        values = np.arange(0, 1, 1./config.batch_size)
        for idx in xrange(dcgan.z_dim):
          print(" [*] %d" % idx)
          z_sample = np.zeros([config.batch_size, dcgan.z_dim])
          for kdx, z in enumerate(z_sample):
            z[idx] = values[kdx]
    
          samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
          make_gif(samples, os.path.join(sample_dir, 'test_gif_%s.gif' % (idx)))
      elif option == 4:
        image_set = []
        values = np.arange(0, 1, 1./config.batch_size)
    
        for idx in xrange(dcgan.z_dim):
          print(" [*] %d" % idx)
          z_sample = np.zeros([config.batch_size, dcgan.z_dim])
          for kdx, z in enumerate(z_sample): z[idx] = values[kdx]
    
          image_set.append(sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample}))
          make_gif(image_set[-1], os.path.join(sample_dir, 'test_gif_%s.gif' % (idx)))
    
        new_image_set = [merge(np.array([images[idx] for images in image_set]), [10, 10]) \
            for idx in range(64) + range(63, -1, -1)]
        make_gif(new_image_set, './samples/test_gif_merged.gif', duration=8)
    
    
    def image_manifold_size(num_images):
      manifold_h = int(np.floor(np.sqrt(num_images)))
      manifold_w = int(np.ceil(np.sqrt(num_images)))
      assert manifold_h * manifold_w == num_images
      return manifold_h, manifold_w
    
    

    这份代码主要是定义了各种对图像处理的函数,相当于其他3个文件的头文件。

    step0:首先定义了一个pp = pprint.PrettyPrinter(),以方便打印数据结构信息,详细信息可见这篇博客。

    step1:定义了get_stddev函数,是三个参数乘积后开平方的倒数,应该是为了随机化用。

    step2:定义show_all_variables()函数。首先,tf.trainable_variables返回的是需要训练的变量列表;然后用tensorflow.contrib.slim中的model_analyzer.analyze_vars打印出所有与训练相关的变量信息。用法如下:

    -代码:

    import tensorflow as tf
    import tensorflow.contrib.slim as slim
    
    x1=tf.Variable(tf.constant(1,shape=[1],dtype=tf.float32),name='x11')
    x2=tf.Variable(tf.constant(2,shape=[1],dtype=tf.float32),name='x22')
    m=tf.train.ExponentialMovingAverage(0.99,5)
    v=tf.trainable_variables()
    for i in v:
        print 233
        print i
    
    print 23333333   
    slim.model_analyzer.analyze_vars(v,print_info=True)
    print 23333333
    

    -结果截图如下:
    在这里插入图片描述
    注:从step3-step11,都是在定义一些图像处理的函数,它们之间相互调用。

    step3:定义get_image(image_path,input_height,input_width,resize_height=64, resize_width=64,crop=True, grayscale=False)函数。首先根据图像路径参数读取路径,根据灰度化参数选择是否进行灰度化。然后对图像参照输入的参数进行裁剪。

    step4:定义save_images(images,size,image_path)函数。调用imsave(inverse_transform(images), size, image_path)函数并返回新图像。

    step5:定义imread(path, grayscale = False)函数。调用cipy.misc.imread()函数,判断grayscale参数是否进行范围灰度化,并进行类型转换为np.float。

    step6:定义merge_images(images, size)函数。调用inverse_transform(images)函数,并返回新图像。

    step7:定义merge(images, size)函数。首先获取image的高和宽。然后判断image是RGB图还是灰度图,以分别进行不同的处理。如果通道数是3或4,则对每一批次(如,batch_size=64)的所有图像,用0初始化一张原始图像放大8*8的图像,然后循环,依次将所有图像填入大图像,并且返回这张大图像。如果通道数是1,也是一样,只不过填入图像的时候只填一个通道的信息。如果不是上述两种情况,则抛出错误提示。

    step8:定义imsave(images, size, path)函数。首先将merge()函数返回的图像,用 np.squeeze()函数移除长度为1的轴。然后利用scipy.misc.imsave()函数将新图像保存到指定路径中。

    step9:定义center_crop(x, crop_h, crop_w,resize_h=64, resize_w=64)函数。对图像的H和W与crop的H和W相减,得到取整的值,根据这个值作为下标依据来scipy.misc.resize图像。

    step10:定义transform(image, input_height, input_width,resize_height=64, resize_width=64, crop=True)函数。对输入的图像进行裁剪,如果crop为true,则使用center_crop()函数,对图像的H和W与crop的H和W相减,得到取整的值,根据这个值作为下标依据来scipy.misc.resize图像;否则不对图像进行其他操作,直接scipy.misc.resize为64*64大小的图像。最后返回图像。

    step11:定义inverse_transform(images)函数。对图像进行翻转后返回新图像。

    step12:定义to_json(output_path, *layers)函数。应该是获取每一层的权值、偏置值什么的,但貌似代码中没有用到这个函数,所以先不管,后面用到再说。

    step13:定义make_gif(images, fname, duration=2, true_image=False)函数。利用moviepy.editor模块来制作动图,为了可视化用的。函数又定义了一个函数make_frame(t),首先根据图像集的长度和持续的时间做一个除法,然后返回每帧图像。最后视频修剪并制作成GIF动画。

    step14:定义visualize(sess, dcgan, config, option)函数。分为0、1、2、3、4种option。如果option=0,则之间显示生产的样本‘如果option=1,根据不同数据集不一样的处理,并利用前面的save_images()函数将sample保存下来;等等。本次在main.py中选用option=1。

    step15:定义image_manifold_size(num_images)函数。首先获取图像数量的开平方后向下取整的h和向上取整的w,然后设置一个assert断言,如果h*w与图像数量相等,则返回h和w,否则断言错误提示。

    这就是全部utils.py全部内容,主要负责图像的一些基本操作,获取图像、保存图像、图像翻转,和利用moviepy模块可视化训练过程。

    2.3.3 ops.py
    import math
    import numpy as np 
    import tensorflow as tf
    
    from tensorflow.python.framework import ops
    
    from utils import *
    
    try:
      image_summary = tf.image_summary
      scalar_summary = tf.scalar_summary
      histogram_summary = tf.histogram_summary
      merge_summary = tf.merge_summary
      SummaryWriter = tf.train.SummaryWriter
    except:
      image_summary = tf.summary.image
      scalar_summary = tf.summary.scalar
      histogram_summary = tf.summary.histogram
      merge_summary = tf.summary.merge
      SummaryWriter = tf.summary.FileWriter
    
    if "concat_v2" in dir(tf):
      def concat(tensors, axis, *args, **kwargs):
        return tf.concat_v2(tensors, axis, *args, **kwargs)
    else:
      def concat(tensors, axis, *args, **kwargs):
        return tf.concat(tensors, axis, *args, **kwargs)
    
    class batch_norm(object):
      def __init__(self, epsilon=1e-5, momentum = 0.9, name="batch_norm"):
        with tf.variable_scope(name):
          self.epsilon  = epsilon
          self.momentum = momentum
          self.name = name
    
      def __call__(self, x, train=True):
        return tf.contrib.layers.batch_norm(x,
                          decay=self.momentum, 
                          updates_collections=None,
                          epsilon=self.epsilon,
                          scale=True,
                          is_training=train,
                          scope=self.name)
    
    def conv_cond_concat(x, y):
      """Concatenate conditioning vector on feature map axis."""
      x_shapes = x.get_shape()
      y_shapes = y.get_shape()
      return concat([
        x, y*tf.ones([x_shapes[0], x_shapes[1], x_shapes[2], y_shapes[3]])], 3)
    
    def conv2d(input_, output_dim, 
           k_h=5, k_w=5, d_h=2, d_w=2, stddev=0.02,
           name="conv2d"):
      with tf.variable_scope(name):
        w = tf.get_variable('w', [k_h, k_w, input_.get_shape()[-1], output_dim],        #h0->h1,w[5x5x3x64]输入channel为3,输出为64
                  initializer=tf.truncated_normal_initializer(stddev=stddev))
        conv = tf.nn.conv2d(input_, w, strides=[1, d_h, d_w, 1], padding='SAME')        #stride=2
    
        biases = tf.get_variable('biases', [output_dim], initializer=tf.constant_initializer(0.0))#b.shape=64
        conv = tf.reshape(tf.nn.bias_add(conv, biases), conv.get_shape())
    
        return conv
    
    def deconv2d(input_, output_shape,                                                  #input[4x4x512]output[8x8x256]
           k_h=5, k_w=5, d_h=2, d_w=2, stddev=0.02,
           name="deconv2d", with_w=False):
      with tf.variable_scope(name):
        # filter : [height, width, output_channels, in_channels]                        #h0的filter[5x5x256x512]
        w = tf.get_variable('w', [k_h, k_w, output_shape[-1], input_.get_shape()[-1]],
                  initializer=tf.random_normal_initializer(stddev=stddev))
        
        try:
          deconv = tf.nn.conv2d_transpose(input_, w, output_shape=output_shape,
                    strides=[1, d_h, d_w, 1])                                           #strides=2,d_h=2,d_w=2
    
        # Support for verisons of TensorFlow before 0.7.0
        except AttributeError:
          deconv = tf.nn.deconv2d(input_, w, output_shape=output_shape,
                    strides=[1, d_h, d_w, 1])
    
        biases = tf.get_variable('biases', [output_shape[-1]], initializer=tf.constant_initializer(0.0))#b与输出大小一样h0到h1的b为256
        deconv = tf.reshape(tf.nn.bias_add(deconv, biases), deconv.get_shape())
    
        if with_w:
          return deconv, w, biases
        else:
          return deconv
         
    def lrelu(x, leak=0.2, name="lrelu"):
      return tf.maximum(x, leak*x)
    
    def linear(input_, output_size, scope=None, stddev=0.02, bias_start=0.0, with_w=False):#维度转换
      shape = input_.get_shape().as_list()
    
      with tf.variable_scope(scope or "Linear"):                                        #对于反卷积          对于卷积
        try:
          matrix = tf.get_variable("Matrix", [shape[1], output_size], tf.float32,       #创建矩阵100x8192    h4[8192x1]
                     tf.random_normal_initializer(stddev=stddev))                       #高斯初始化w
        except ValueError as err:
            msg = "NOTE: Usually, this is due to an issue with the image dimensions.  Did you correctly set '--crop' or '--input_height' or '--output_height'?"
            err.args = err.args + (msg,)
            raise
        bias = tf.get_variable("bias", [output_size],                                   #b.shape=8192        b4.shape=1
          initializer=tf.constant_initializer(bias_start))                              #常量初始化b
        if with_w:
          return tf.matmul(input_, matrix) + bias, matrix, bias
        else:
          return tf.matmul(input_, matrix) + bias
    
    

    该文件调用了utils.py文件。

    step0:首先导入tensorflow.python.framework模块,包含了tensorflow中图、张量等的定义操作。

    step1:然后是一个try…except部分,定义了一堆变量:image_summary 、scalar_summary、histogram_summary、merge_summary、SummaryWriter,都是从相应的tensorflow中获取的。如果可是直接获取,则获取,否则从tf.summary中获取。

    step2:用来连接多个tensor。利用dir(tf)判断”concat_v2”是否在里面,如果在的话,定义一个concat(tensors, axis, *args, **kwargs)函数,并返回tf.concat_v2(tensors, axis, *args, **kwargs);否则也定义concat(tensors, axis, *args, **kwargs)函数,只不过返回的是tf.concat(tensors, axis, *args, **kwargs)。其中,tf.concat使用如下:

    t1=tf.constant([[1,2,3],[4,5,6]])
    t2=tf.constant([[7,8,9],[10,11,12]])
    t3=tf.concat([t1,t2],0)
    t4=tf.concat([t1,t2],1)
    print t1
    print t2
    print t3
    print t4
    

    在这里插入图片描述
    step3:定义一个batch_norm类,包含两个函数init和call函数。首先在init(self, epsilon=1e-5, momentum = 0.9, name=”batch_norm”)函数中,定义一个name参数名字的变量,初始化self变量epsilon、momentum 、name。在call(self, x, train=True)函数中,利用tf.contrib.layers.batch_norm函数批处理规范化。

    step4:定义conv_cond_concat(x,y)函数。连接x,y与Int32型的[x_shapes[0], x_shapes[1], x_shapes[2], y_shapes[3]]维度的张量乘积。

    step5:定义conv2d(input_, output_dim, k_h=5, k_w=5, d_h=2,d_w=2, stddev=0.02,name=”conv2d”)函数。卷积函数:获取随机正态分布权值、实现卷积、获取初始偏置值,获取添加偏置值后的卷积变量并返回。

    step6:定义deconv2d(input_, output_shape,k_h=5, k_w=5, d_h=2, d_w=2, stddev=0.02,name=”deconv2d”, with_w=False):函数。解卷积函数:获取随机正态分布权值、解卷积,获取初始偏置值,获取添加偏置值后的卷积变量,判断with_w是否为真,真则返回解卷积、权值、偏置值,否则返回解卷积。

    step7:定义lrelu(x, leak=0.2, name=”lrelu”)函数。定义一个lrelu激励函数。

    step8:定义linear(input_, output_size, scope=None, stddev=0.02, bias_start=0.0, with_w=False)函数。进行线性运算,获取一个随机正态分布矩阵,获取初始偏置值,如果with_w为真,则返回xw+b,权值w和偏置值b;否则返回xw+b。

    这个文件主要定义了一些变量连接的函数、批处理规范化的函数、卷积函数、解卷积函数、激励函数、线性运算函数。

    2.3.4 model.py
    from __future__ import division
    from __future__ import print_function
    import os
    import time
    import math
    from glob import glob
    import tensorflow as tf
    import numpy as np
    from six.moves import xrange
    
    from ops import *
    from utils import *
    
    def conv_out_size_same(size, stride):
      return int(math.ceil(float(size) / float(stride)))
    
    def gen_random(mode, size):
        if mode=='normal01': return np.random.normal(0,1,size=size)
        if mode=='uniform_signed': return np.random.uniform(-1,1,size=size)
        if mode=='uniform_unsigned': return np.random.uniform(0,1,size=size)
    
    
    class DCGAN(object):
      def __init__(self, sess, input_height=108, input_width=108, crop=True,
             batch_size=64, sample_num = 64, output_height=64, output_width=64,
             y_dim=None, z_dim=100, gf_dim=64, df_dim=64,
             gfc_dim=1024, dfc_dim=1024, c_dim=3, dataset_name='default',
             max_to_keep=1,
             input_fname_pattern='*.jpg', checkpoint_dir='ckpts', sample_dir='samples', out_dir='./out', data_dir='./data'):
        """
    
        Args:
          sess: TensorFlow session
          batch_size: The size of batch. Should be specified before training.
          y_dim: (optional) Dimension of dim for y. [None]
          z_dim: (optional) Dimension of dim for Z. [100]
          gf_dim: (optional) Dimension of gen filters in first conv layer. [64]
          df_dim: (optional) Dimension of discrim filters in first conv layer. [64]
          gfc_dim: (optional) Dimension of gen units for for fully connected layer. [1024]
          dfc_dim: (optional) Dimension of discrim units for fully connected layer. [1024]
          c_dim: (optional) Dimension of image color. For grayscale input, set to 1. [3]
        """
        self.sess = sess
        self.crop = crop
    
        self.batch_size = batch_size
        self.sample_num = sample_num        #训练途中测试
    
        self.input_height = input_height
        self.input_width = input_width
        self.output_height = output_height
        self.output_width = output_width
    
        self.y_dim = y_dim
        self.z_dim = z_dim                  #生成网络的维度先设置成100 
    
        self.gf_dim = gf_dim                #filter的基数
        self.df_dim = df_dim
    
        self.gfc_dim = gfc_dim              #全连接向量
        self.dfc_dim = dfc_dim
    
        # batch normalization : deals with poor initialization helps gradient flow
        self.d_bn1 = batch_norm(name='d_bn1')#DCGAN在生成器和判别器中都添加了批量归一化操作(BN)
        self.d_bn2 = batch_norm(name='d_bn2')#在每个conv层和ReLU层之间
    
        if not self.y_dim:
          self.d_bn3 = batch_norm(name='d_bn3')
    
        self.g_bn0 = batch_norm(name='g_bn0')
        self.g_bn1 = batch_norm(name='g_bn1')
        self.g_bn2 = batch_norm(name='g_bn2')
    
        if not self.y_dim:
          self.g_bn3 = batch_norm(name='g_bn3')
    
        self.dataset_name = dataset_name
        self.input_fname_pattern = input_fname_pattern
        self.checkpoint_dir = checkpoint_dir
        self.data_dir = data_dir
        self.out_dir = out_dir
        self.max_to_keep = max_to_keep
    
        if self.dataset_name == 'mnist':
          self.data_X, self.data_y = self.load_mnist()
          self.c_dim = self.data_X[0].shape[-1]
        else:
          data_path = os.path.join(self.data_dir, self.dataset_name, self.input_fname_pattern)#获取数据集
          self.data = glob(data_path)
          if len(self.data) == 0:
            raise Exception("[!] No data found in '" + data_path + "'")
          np.random.shuffle(self.data)
          imreadImg = imread(self.data[0])
          if len(imreadImg.shape) >= 3: #check if image is a non-grayscale image by checking channel number
            self.c_dim = imread(self.data[0]).shape[-1]
          else:
            self.c_dim = 1
    
          if len(self.data) < self.batch_size:
            raise Exception("[!] Entire dataset size is less than the configured batch_size")
        
        self.grayscale = (self.c_dim == 1)
    
        self.build_model()
    
      def build_model(self):
        if self.y_dim:
          self.y = tf.placeholder(tf.float32, [self.batch_size, self.y_dim], name='y')
        else:
          self.y = None
    
        if self.crop:
          image_dims = [self.output_height, self.output_width, self.c_dim]
        else:
          image_dims = [self.input_height, self.input_width, self.c_dim]
    
        self.inputs = tf.placeholder(
          tf.float32, [self.batch_size] + image_dims, name='real_images')
    
        inputs = self.inputs
    
        self.z = tf.placeholder(
          tf.float32, [None, self.z_dim], name='z')                                     #生成网络最开始的噪音数据self.z[?,100]
        self.z_sum = histogram_summary("z", self.z)
    
        self.G                  = self.generator(self.z, self.y)                        #G网络,输入为self.z噪音向量
        self.D, self.D_logits   = self.discriminator(inputs, self.y, reuse=False)       #D网络,真实数据输入
        self.sampler            = self.sampler(self.z, self.y)                          #sampler测试
        self.D_, self.D_logits_ = self.discriminator(self.G, self.y, reuse=True)        #D_,生成数据输入,网络结构一样,参数不同
        
        self.d_sum = histogram_summary("d", self.D)
        self.d__sum = histogram_summary("d_", self.D_)
        self.G_sum = image_summary("G", self.G)
    
        def sigmoid_cross_entropy_with_logits(x, y):                                    #交叉熵函数
          try:
            return tf.nn.sigmoid_cross_entropy_with_logits(logits=x, labels=y)
          except:
            return tf.nn.sigmoid_cross_entropy_with_logits(logits=x, targets=y)
    
        self.d_loss_real = tf.reduce_mean(                                              #D网络对于真实输入,期望labels=1
          sigmoid_cross_entropy_with_logits(self.D_logits, tf.ones_like(self.D)))
        self.d_loss_fake = tf.reduce_mean(                                              #D网络对于生成输入,期望labels=0,(targets)
          sigmoid_cross_entropy_with_logits(self.D_logits_, tf.zeros_like(self.D_)))
        self.g_loss = tf.reduce_mean(                                                   #g_loss,期望labels=1
          sigmoid_cross_entropy_with_logits(self.D_logits_, tf.ones_like(self.D_)))
    
        self.d_loss_real_sum = scalar_summary("d_loss_real", self.d_loss_real)
        self.d_loss_fake_sum = scalar_summary("d_loss_fake", self.d_loss_fake)
                              
        self.d_loss = self.d_loss_real + self.d_loss_fake                               #d_loss
    
        self.g_loss_sum = scalar_summary("g_loss", self.g_loss)
        self.d_loss_sum = scalar_summary("d_loss", self.d_loss)
    
        t_vars = tf.trainable_variables()                                               #变量赋值                                        
    
        self.d_vars = [var for var in t_vars if 'd_' in var.name]
        self.g_vars = [var for var in t_vars if 'g_' in var.name]
    
        self.saver = tf.train.Saver(max_to_keep=self.max_to_keep)
    
      def train(self, config):
        #优化器优化loss,原始论文用的Adam优化器而非梯度下降
        d_optim = tf.train.AdamOptimizer(config.learning_rate, beta1=config.beta1) \
                  .minimize(self.d_loss, var_list=self.d_vars)
        g_optim = tf.train.AdamOptimizer(config.learning_rate, beta1=config.beta1) \
                  .minimize(self.g_loss, var_list=self.g_vars)
        try:
          tf.global_variables_initializer().run()                                       #全局变量初始化
        except:
          tf.initialize_all_variables().run()
    
        if config.G_img_sum:
          self.g_sum = merge_summary([self.z_sum, self.d__sum, self.G_sum, self.d_loss_fake_sum, self.g_loss_sum])
        else:
          self.g_sum = merge_summary([self.z_sum, self.d__sum, self.d_loss_fake_sum, self.g_loss_sum])
        self.d_sum = merge_summary(
            [self.z_sum, self.d_sum, self.d_loss_real_sum, self.d_loss_sum])
        self.writer = SummaryWriter(os.path.join(self.out_dir, "logs"), self.sess.graph)
    
        sample_z = gen_random(config.z_dist, size=(self.sample_num , self.z_dim))       #[-1,1]区间初始化噪音向量[64x100]一个batch的噪音向量
        
        if config.dataset == 'mnist':
          sample_inputs = self.data_X[0:self.sample_num]
          sample_labels = self.data_y[0:self.sample_num]
        else:
          sample_files = self.data[0:self.sample_num]
          sample = [                                                                    #获取一个batch的数据
              get_image(sample_file,
                        input_height=self.input_height,
                        input_width=self.input_width,
                        resize_height=self.output_height,
                        resize_width=self.output_width,
                        crop=self.crop,
                        grayscale=self.grayscale) for sample_file in sample_files]
          if (self.grayscale):
            sample_inputs = np.array(sample).astype(np.float32)[:, :, :, None]
          else:
            sample_inputs = np.array(sample).astype(np.float32)                         #处理后的图像压缩到[-1,1]区间,大小[64,64]
      
        counter = 1
        start_time = time.time()
        could_load, checkpoint_counter = self.load(self.checkpoint_dir)
        if could_load:                                                                  #判断之前有没有训练网络模型
          counter = checkpoint_counter                                                  #如果有,接着之前的
          print(" [*] Load SUCCESS")                                            
        else:
          print(" [!] Load failed...")                                                  #如果没有从0开始训练
    
        for epoch in xrange(config.epoch):
          if config.dataset == 'mnist':
            batch_idxs = min(len(self.data_X), config.train_size) // config.batch_size
          else:      
            self.data = glob(os.path.join(
              config.data_dir, config.dataset, self.input_fname_pattern))
            np.random.shuffle(self.data)
            batch_idxs = min(len(self.data), config.train_size) // config.batch_size
    
          for idx in xrange(0, int(batch_idxs)):
            if config.dataset == 'mnist':
              batch_images = self.data_X[idx*config.batch_size:(idx+1)*config.batch_size]
              batch_labels = self.data_y[idx*config.batch_size:(idx+1)*config.batch_size]
            else:
              batch_files = self.data[idx*config.batch_size:(idx+1)*config.batch_size]  #准备batch从0到第64个图像为第一个batch
              batch = [
                  get_image(batch_file,
                            input_height=self.input_height,
                            input_width=self.input_width,
                            resize_height=self.output_height,
                            resize_width=self.output_width,
                            crop=self.crop,
                            grayscale=self.grayscale) for batch_file in batch_files]
              if self.grayscale:
                batch_images = np.array(batch).astype(np.float32)[:, :, :, None]
              else:
                batch_images = np.array(batch).astype(np.float32)
    
            batch_z = gen_random(config.z_dist, size=[config.batch_size, self.z_dim]) \
                  .astype(np.float32)
    
            if config.dataset == 'mnist':
              # Update D network
              _, summary_str = self.sess.run([d_optim, self.d_sum],
                feed_dict={ 
                  self.inputs: batch_images,
                  self.z: batch_z,
                  self.y:batch_labels,
                })
              self.writer.add_summary(summary_str, counter)
    
              # Update G network
              _, summary_str = self.sess.run([g_optim, self.g_sum],
                feed_dict={
                  self.z: batch_z, 
                  self.y:batch_labels,
                })
              self.writer.add_summary(summary_str, counter)
    
              # Run g_optim twice to make sure that d_loss does not go to zero (different from paper)
              _, summary_str = self.sess.run([g_optim, self.g_sum],
                feed_dict={ self.z: batch_z, self.y:batch_labels })
              self.writer.add_summary(summary_str, counter)
              
              errD_fake = self.d_loss_fake.eval({
                  self.z: batch_z, 
                  self.y:batch_labels
              })
              errD_real = self.d_loss_real.eval({
                  self.inputs: batch_images,
                  self.y:batch_labels
              })
              errG = self.g_loss.eval({
                  self.z: batch_z,
                  self.y: batch_labels
              })
            else:
              # Update D network                                                        #迭代D网络
              _, summary_str = self.sess.run([d_optim, self.d_sum],                     #通过优化器run,将feed_dict添加进去
                feed_dict={ self.inputs: batch_images, self.z: batch_z })               #之前是placeholder格式
              self.writer.add_summary(summary_str, counter)
    
              # Update G network
              _, summary_str = self.sess.run([g_optim, self.g_sum],
                feed_dict={ self.z: batch_z })
              self.writer.add_summary(summary_str, counter)
    
              # Run g_optim twice to make sure that d_loss does not go to zero (different from paper)
              _, summary_str = self.sess.run([g_optim, self.g_sum],
                feed_dict={ self.z: batch_z })
              self.writer.add_summary(summary_str, counter)
              
              errD_fake = self.d_loss_fake.eval({ self.z: batch_z })
              errD_real = self.d_loss_real.eval({ self.inputs: batch_images })
              errG = self.g_loss.eval({self.z: batch_z})
    
            print("[%8d Epoch:[%2d/%2d] [%4d/%4d] time: %4.4f, d_loss: %.8f, g_loss: %.8f" \
              % (counter, epoch, config.epoch, idx, batch_idxs,
                time.time() - start_time, errD_fake+errD_real, errG))
    
            if np.mod(counter, config.sample_freq) == 0:                                #每迭代200次保存一次模型
              if config.dataset == 'mnist':
                samples, d_loss, g_loss = self.sess.run(
                  [self.sampler, self.d_loss, self.g_loss],
                  feed_dict={
                      self.z: sample_z,
                      self.inputs: sample_inputs,
                      self.y:sample_labels,
                  }
                )
                save_images(samples, image_manifold_size(samples.shape[0]),
                      './{}/train_{:08d}.png'.format(config.sample_dir, counter))
                print("[Sample] d_loss: %.8f, g_loss: %.8f" % (d_loss, g_loss)) 
              else:
                try:
                  samples, d_loss, g_loss = self.sess.run(
                    [self.sampler, self.d_loss, self.g_loss],
                    feed_dict={
                        self.z: sample_z,
                        self.inputs: sample_inputs,
                    },
                  )
                  save_images(samples, image_manifold_size(samples.shape[0]),
                        './{}/train_{:08d}.png'.format(config.sample_dir, counter))
                  print("[Sample] d_loss: %.8f, g_loss: %.8f" % (d_loss, g_loss)) 
                except:
                  print("one pic error!...")
    
            if np.mod(counter, config.ckpt_freq) == 0:
              self.save(config.checkpoint_dir, counter)
            
            counter += 1
            
      def discriminator(self, image, y=None, reuse=False):                              #判别网络正向卷积
        with tf.variable_scope("discriminator") as scope:
          if reuse:
            scope.reuse_variables()
    
          if not self.y_dim:                                                            #h0[64x64x3]
            h0 = lrelu(conv2d(image, self.df_dim, name='d_h0_conv'))
            h1 = lrelu(self.d_bn1(conv2d(h0, self.df_dim*2, name='d_h1_conv')))
            h2 = lrelu(self.d_bn2(conv2d(h1, self.df_dim*4, name='d_h2_conv')))
            h3 = lrelu(self.d_bn3(conv2d(h2, self.df_dim*8, name='d_h3_conv')))
            h4 = linear(tf.reshape(h3, [self.batch_size, -1]), 1, 'd_h4_lin')
    
            return tf.nn.sigmoid(h4), h4                                                #全连接层通过sigmoid映射到[0,1]区间上,完成判别网络
          else:
            yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])
            x = conv_cond_concat(image, yb)
    
            h0 = lrelu(conv2d(x, self.c_dim + self.y_dim, name='d_h0_conv'))
            h0 = conv_cond_concat(h0, yb)
    
            h1 = lrelu(self.d_bn1(conv2d(h0, self.df_dim + self.y_dim, name='d_h1_conv')))
            h1 = tf.reshape(h1, [self.batch_size, -1])      
            h1 = concat([h1, y], 1)
            
            h2 = lrelu(self.d_bn2(linear(h1, self.dfc_dim, 'd_h2_lin')))
            h2 = concat([h2, y], 1)
    
            h3 = linear(h2, 1, 'd_h3_lin')
            
            return tf.nn.sigmoid(h3), h3
    
      def generator(self, z, y=None):                                                   #生成网络反卷积
        with tf.variable_scope("generator") as scope:
          if not self.y_dim:                                                            #反卷积计算特征图大小
            s_h, s_w = self.output_height, self.output_width                            #64,64
            s_h2, s_w2 = conv_out_size_same(s_h, 2), conv_out_size_same(s_w, 2)         #32,32
            s_h4, s_w4 = conv_out_size_same(s_h2, 2), conv_out_size_same(s_w2, 2)       #16,16
            s_h8, s_w8 = conv_out_size_same(s_h4, 2), conv_out_size_same(s_w4, 2)       #8,8
            s_h16, s_w16 = conv_out_size_same(s_h8, 2), conv_out_size_same(s_w8, 2)     #4,4
    
            # project `z` and reshape
            #将z先连接一层fc转换为高维向量,linear将input转为output
            self.z_, self.h0_w, self.h0_b = linear(                                     #self.z_[?,8192]
                z, self.gf_dim*8*s_h16*s_w16, 'g_h0_lin', with_w=True)                  #输入为z,输出64x8x4x4=8192维度
    
            self.h0 = tf.reshape(
                self.z_, [-1, s_h16, s_w16, self.gf_dim * 8])                           #将self.z_转换为1x4x4x512
            h0 = tf.nn.relu(self.g_bn0(self.h0))                                        #要对h0进行反卷积操作,先bn,再relu  
    
            self.h1, self.h1_w, self.h1_b = deconv2d(                                   #h0->h1翻卷积4x4x512->8x8x256
                h0, [self.batch_size, s_h8, s_w8, self.gf_dim*4], name='g_h1', with_w=True)
            h1 = tf.nn.relu(self.g_bn1(self.h1))
    
            h2, self.h2_w, self.h2_b = deconv2d(                                        #h2[16x16x128]
                h1, [self.batch_size, s_h4, s_w4, self.gf_dim*2], name='g_h2', with_w=True)
            h2 = tf.nn.relu(self.g_bn2(h2))
    
            h3, self.h3_w, self.h3_b = deconv2d(                                        #h3[32x32x64]
                h2, [self.batch_size, s_h2, s_w2, self.gf_dim*1], name='g_h3', with_w=True)
            h3 = tf.nn.relu(self.g_bn3(h3))
    
            h4, self.h4_w, self.h4_b = deconv2d(                                        #h4[64x64x3],batch为64
                h3, [self.batch_size, s_h, s_w, self.c_dim], name='g_h4', with_w=True)
    
            return tf.nn.tanh(h4)
          else:
            s_h, s_w = self.output_height, self.output_width
            s_h2, s_h4 = int(s_h/2), int(s_h/4)
            s_w2, s_w4 = int(s_w/2), int(s_w/4)
    
            # yb = tf.expand_dims(tf.expand_dims(y, 1),2)
            yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])
            z = concat([z, y], 1)
    
            h0 = tf.nn.relu(
                self.g_bn0(linear(z, self.gfc_dim, 'g_h0_lin')))
            h0 = concat([h0, y], 1)
    
            h1 = tf.nn.relu(self.g_bn1(
                linear(h0, self.gf_dim*2*s_h4*s_w4, 'g_h1_lin')))
            h1 = tf.reshape(h1, [self.batch_size, s_h4, s_w4, self.gf_dim * 2])
    
            h1 = conv_cond_concat(h1, yb)
    
            h2 = tf.nn.relu(self.g_bn2(deconv2d(h1,
                [self.batch_size, s_h2, s_w2, self.gf_dim * 2], name='g_h2')))
            h2 = conv_cond_concat(h2, yb)
    
            return tf.nn.sigmoid(
                deconv2d(h2, [self.batch_size, s_h, s_w, self.c_dim], name='g_h3'))
    
      def sampler(self, z, y=None):
        with tf.variable_scope("generator") as scope:
          scope.reuse_variables()
    
          if not self.y_dim:
            s_h, s_w = self.output_height, self.output_width
            s_h2, s_w2 = conv_out_size_same(s_h, 2), conv_out_size_same(s_w, 2)
            s_h4, s_w4 = conv_out_size_same(s_h2, 2), conv_out_size_same(s_w2, 2)
            s_h8, s_w8 = conv_out_size_same(s_h4, 2), conv_out_size_same(s_w4, 2)
            s_h16, s_w16 = conv_out_size_same(s_h8, 2), conv_out_size_same(s_w8, 2)
    
            # project `z` and reshape
            h0 = tf.reshape(
                linear(z, self.gf_dim*8*s_h16*s_w16, 'g_h0_lin'),
                [-1, s_h16, s_w16, self.gf_dim * 8])
            h0 = tf.nn.relu(self.g_bn0(h0, train=False))
    
            h1 = deconv2d(h0, [self.batch_size, s_h8, s_w8, self.gf_dim*4], name='g_h1')
            h1 = tf.nn.relu(self.g_bn1(h1, train=False))
    
            h2 = deconv2d(h1, [self.batch_size, s_h4, s_w4, self.gf_dim*2], name='g_h2')
            h2 = tf.nn.relu(self.g_bn2(h2, train=False))
    
            h3 = deconv2d(h2, [self.batch_size, s_h2, s_w2, self.gf_dim*1], name='g_h3')
            h3 = tf.nn.relu(self.g_bn3(h3, train=False))
    
            h4 = deconv2d(h3, [self.batch_size, s_h, s_w, self.c_dim], name='g_h4')
    
            return tf.nn.tanh(h4)
          else:
            s_h, s_w = self.output_height, self.output_width
            s_h2, s_h4 = int(s_h/2), int(s_h/4)
            s_w2, s_w4 = int(s_w/2), int(s_w/4)
    
            # yb = tf.reshape(y, [-1, 1, 1, self.y_dim])
            yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])
            z = concat([z, y], 1)
    
            h0 = tf.nn.relu(self.g_bn0(linear(z, self.gfc_dim, 'g_h0_lin'), train=False))
            h0 = concat([h0, y], 1)
    
            h1 = tf.nn.relu(self.g_bn1(
                linear(h0, self.gf_dim*2*s_h4*s_w4, 'g_h1_lin'), train=False))
            h1 = tf.reshape(h1, [self.batch_size, s_h4, s_w4, self.gf_dim * 2])
            h1 = conv_cond_concat(h1, yb)
    
            h2 = tf.nn.relu(self.g_bn2(
                deconv2d(h1, [self.batch_size, s_h2, s_w2, self.gf_dim * 2], name='g_h2'), train=False))
            h2 = conv_cond_concat(h2, yb)
    
            return tf.nn.sigmoid(deconv2d(h2, [self.batch_size, s_h, s_w, self.c_dim], name='g_h3'))
    
      def load_mnist(self):
        data_dir = os.path.join(self.data_dir, self.dataset_name)
        
        fd = open(os.path.join(data_dir,'train-images-idx3-ubyte'))
        loaded = np.fromfile(file=fd,dtype=np.uint8)
        trX = loaded[16:].reshape((60000,28,28,1)).astype(np.float)
    
        fd = open(os.path.join(data_dir,'train-labels-idx1-ubyte'))
        loaded = np.fromfile(file=fd,dtype=np.uint8)
        trY = loaded[8:].reshape((60000)).astype(np.float)
    
        fd = open(os.path.join(data_dir,'t10k-images-idx3-ubyte'))
        loaded = np.fromfile(file=fd,dtype=np.uint8)
        teX = loaded[16:].reshape((10000,28,28,1)).astype(np.float)
    
        fd = open(os.path.join(data_dir,'t10k-labels-idx1-ubyte'))
        loaded = np.fromfile(file=fd,dtype=np.uint8)
        teY = loaded[8:].reshape((10000)).astype(np.float)
    
        trY = np.asarray(trY)
        teY = np.asarray(teY)
        
        X = np.concatenate((trX, teX), axis=0)
        y = np.concatenate((trY, teY), axis=0).astype(np.int)
        
        seed = 547
        np.random.seed(seed)
        np.random.shuffle(X)
        np.random.seed(seed)
        np.random.shuffle(y)
        
        y_vec = np.zeros((len(y), self.y_dim), dtype=np.float)
        for i, label in enumerate(y):
          y_vec[i,y[i]] = 1.0
        
        return X/255.,y_vec
    
      @property
      def model_dir(self):
        return "{}_{}_{}_{}".format(
            self.dataset_name, self.batch_size,
            self.output_height, self.output_width)
    
      def save(self, checkpoint_dir, step, filename='model', ckpt=True, frozen=False):
        # model_name = "DCGAN.model"
        # checkpoint_dir = os.path.join(checkpoint_dir, self.model_dir)
    
        filename += '.b' + str(self.batch_size)
        if not os.path.exists(checkpoint_dir):
          os.makedirs(checkpoint_dir)
    
        if ckpt:
          self.saver.save(self.sess,
                  os.path.join(checkpoint_dir, filename),
                  global_step=step)
    
        if frozen:
          tf.train.write_graph(
                  tf.graph_util.convert_variables_to_constants(self.sess, self.sess.graph_def, ["generator_1/Tanh"]),
                  checkpoint_dir,
                  '{}-{:06d}_frz.pb'.format(filename, step),
                  as_text=False)
    
      def load(self, checkpoint_dir):
        #import re
        print(" [*] Reading checkpoints...", checkpoint_dir)
        # checkpoint_dir = os.path.join(checkpoint_dir, self.model_dir)
        # print("     ->", checkpoint_dir)
    
        ckpt = tf.train.get_checkpoint_state(checkpoint_dir)
        if ckpt and ckpt.model_checkpoint_path:
          ckpt_name = os.path.basename(ckpt.model_checkpoint_path)
          self.saver.restore(self.sess, os.path.join(checkpoint_dir, ckpt_name))
          #counter = int(next(re.finditer("(\d+)(?!.*\d)",ckpt_name)).group(0))
          counter = int(ckpt_name.split('-')[-1])
          print(" [*] Success to read {}".format(ckpt_name))
          return True, counter
        else:
          print(" [*] Failed to find a checkpoint")
          return False, 0
    
    

    这个文件就是DCGAN模型定义的函数。调用了utils.py文件和ops.py文件。

    step0:定义conv_out_size_same(size, stride)函数。大小和步幅。

    step1:然后是定义了DCGAN类,剩余代码都是在写DCGAN类,所以下面几步都是在这个类里面定义进行的。

    step2:定义类的初始化函数 init。主要是对一些默认的参数进行初始化。包括session、crop、批处理大小batch_size、样本数量sample_num、输入与输出的高和宽、各种维度、生成器与判别器的批处理、数据集名字、灰度值、构建模型函数,需要注意的是,要判断数据集的名字是否是mnist,是的话则直接用load_mnist()函数加载数据,否则需要从本地data文件夹中读取数据,并将图像读取为灰度图。

    step3:定义构建模型函数build_model(self)。

    1. 首先判断y_dim,然后用tf.placeholder占位符定义并初始化y。
    2. 判断crop是否为真,是的话是进行测试,图像维度是输出图像的维度;否则是输入图像的维度。
    3. 利用tf.placeholder定义inputs,是真实数据的向量。
    4. 定义并初始化生成器用到的噪音z,z_sum。
    5. 再次判断y_dim,如果为真,用噪音z和标签y初始化生成器G、用输入inputs初始化判别器D和D_logits、样本、用G和y初始化D_和D_logits;如果为假,跟上面一样初始化各种变量,只不过都没有标签y。
    6. 将5中的D、D_、G分别放在d_sum、d__sum、G_sum。
    7. 定义sigmoid交叉熵损失函数sigmoid_cross_entropy_with_logits(x, y)。都是调用tf.nn.sigmoid_cross_entropy_with_logits函数,只不过一个是训练,y是标签,一个是测试,y是目标。
    8. 定义各种损失值。真实数据的判别损失值d_loss_real、虚假数据的判别损失值d_loss_fake、生成器损失值g_loss、判别器损失值d_loss。
    9. 定义训练的所有变量t_vars。
    10. 定义生成和判别的参数集。
    11. 最后是保存。

    step4:定义训练函数train(self, config)。

    1. 定义判别器优化器d_optim和生成器优化器g_optim。
    2. 变量初始化。
    3. 分别将关于生成器和判别器有关的变量各合并到一个变量中,并写入事件文件中。
    4. 噪音z初始化。
    5. 根据数据集是否为mnist的判断,进行输入数据和标签的获取。这里使用到了utils.py文件中的get_image函数。
    6. 定义计数器counter和起始时间start_time。
    7. 加载检查点,并判断加载是否成功。
    8. 开始for epoch in xrange(config.epoch)循环训练。先判断数据集是否是mnist,获取批处理的大小。
    9. 开始for idx in xrange(0, batch_idxs)循环训练,判断数据集是否是mnist,来定义初始化批处理图像和标签。
    10. 定义初始化噪音z。
    11. 判断数据集是否是mnist,来更新判别器网络和生成器网络,这里就不管mnist数据集是怎么处理的,其他数据集是,运行生成器优化器两次,以确保判别器损失值不会变为0,然后是判别器真实数据损失值和虚假数据损失值、生成器损失值。
    12. 输出本次批处理中训练参数的情况,首先是第几个epoch,第几个batch,训练时间,判别器损失值,生成器损失值。
    13. 每100次batch训练后,根据数据集是否是mnist的不同,获取样本、判别器损失值、生成器损失值,调用utils.py文件的save_images函数,保存训练后的样本,并以epoch、batch的次数命名文件。然后打印判别器损失值和生成器损失值。
    14. 每500次batch训练后,保存一次检查点。

    step5:定义判别器函数discriminator(self, image, y=None, reuse=False)。

    1. 利用with tf.variable_scope(“discriminator”) as scope,在一个作用域 scope 内共享一些变量。
    2. 对scope利用reuse_variables()进行重利用。
    3. 如果为假,则直接设置5层,前4层为使用lrelu激活函数的卷积层,最后一层是使用线性层,最后返回h4和sigmoid处理后的h4。
    4. 如果为真,则首先将Y_dim变为yb,然后利用ops.py文件中的conv_cond_concat函数,连接image与yb得到x,然后设置4层网络,前3层是使用lrelu激励函数的卷积层,最后一层是线性层,最后返回h3和sigmoid处理后的h3。

    step6:定义生成器函数generator(self, z, y=None)。

    1. 利用with tf.variable_scope(“generator”) as scope,在一个作用域 scope 内共享一些变量。
    2. 根据y_dim是否为真,进行判别网络的设置。
    3. 如果为假:首先获取输出的宽和高,然后根据这一值得到更多不同大小的高和宽的对。然后获取h0层的噪音z,权值w,偏置值b,然后利用relu激励函数。h1层,首先对h0层解卷积得到本层的权值和偏置值,然后利用relu激励函数。h2、h3等同于h1。h4层,解卷积h3,然后直接返回使用tanh激励函数后的h4。
    4. 如果为真:首先也是获取输出的高和宽,根据这一值得到更多不同大小的高和宽的对。然后获取yb和噪音z。h0层,使用relu激励函数,并与1连接。h1层,对线性全连接后使用relu激励函数,并与yb连接。h2层,对解卷积后使用relu激励函数,并与yb连接。最后返回解卷积、sigmoid处理后的h2。

    step7:定义sampler(self, z, y=None)函数。

    1. 利用tf.variable_scope(“generator”) as scope,在一个作用域 scope 内共享一些变量。
    2. 对scope利用reuse_variables()进行重利用。
    3. 根据y_dim是否为真,进行判别网络的设置。
    4. 然后就跟生成器差不多,不在赘述。

    step8:定义load_mnist(self)函数。这个主要是针对mnist数据集设置的,所以暂且不考虑,过。

    step9:定义model_dir(self)函数。返回数据集名字,batch大小,输出的高和宽。

    step10:定义save(self, checkpoint_dir, step)函数。保存训练好的模型。创建检查点文件夹,如果路径不存在,则创建;然后将其保存在这个文件夹下。

    step11:定义load(self, checkpoint_dir)函数。读取检查点,获取路径,重新存储检查点,并且计数。打印成功读取的提示;如果没有路径,则打印失败的提示。

    以上,就是model.py所有内容,主要是定义了DCGAN的类,完成了生成判别网络的实现。

    3.结果

    运行

    python main.py --input_height 96 --input_width 96 --output_height 48 --output_width 48 --dataset faces_ --input_fname_pattern "*.jpg" -crop --train --epoch 100
    

    第1个epoch
    在这里插入图片描述
    第5个epoch
    在这里插入图片描述
    第10个epoch
    在这里插入图片描述
    第20个epoch
    在这里插入图片描述
    第200epoch
    在这里插入图片描述

    展开全文
  • 学习笔记:GAN和DCGAN入门

    万次阅读 多人点赞 2018-07-25 19:42:40
    GAN的全称为Generative Adversarial ...DCGAN将GAN的概念扩展到卷积神经网络中,可以生成质量较高的图片样本。GAN和DCGAN在各个领域都有广泛的应用,这篇文章首先会介绍他们的原理,再介绍如何在TensorFlow中使用D...

    GAN的全称为Generative Adversarial Networks,意为对抗生成网络。原始的GAN是一种无监督学习方法,它巧妙地利用“对抗”的思想来学习生成式模型,一旦训练完成后可以生成全新的数据样本。DCGAN将GAN的概念扩展到卷积神经网络中,可以生成质量较高的图片样本。GAN和DCGAN在各个领域都有广泛的应用,这篇文章首先会介绍他们的原理,再介绍如何在TensorFlow中使用DCGAN生成图像,关于GAN和DCGAN的更多项目会在接下来的章节中进行介绍。

    GAN的原理

    GAN的原理其实非常简单。可以把GAN看成数据生成工具,这里以生成图片数据为例进行讲解,实际GAN可以应用到任何类型的数据。

    假设有两个网络,生成网络G(Generator)和判别网络D(Discriminator)
    他们的功能分别是:

    • G负责生成图片,它接收一个随机的噪声z,通过该噪声生成图片,将生成的图片记为G(z)
    • D负责判别一张图片是不是“真实的”。它的输入时xx代表一张图片,输出D(x)表示x为真实图片的概率,如果为,代表真实图片的概率为%,而输出为,代表不可能是真实的图片。

    在训练过程中,生成网络G的目标是尽量生成真实的图片去欺骗判别网络D,而D的目标是尽量把生成的图片和真实的图片区分开来。这样,G和D构成了一个动态的“博弈”,这就是GAN的基本思想。

    最后博弈的结果是什么?在理想的状态下,G可以生成足以“以假乱真”的图片G(z)。对于D来说,他难以判定G生成的图片究竟是不是真实的,因此D(G(z))=0.5。此时得到了一个生成式的模型G,他可以用来生成图片。

    下面就用数学化的语言来描述这个过程。假设用于训练的真实图片数据是x,图片数据的分布为Pdata(x),之前说G能够“生成图片”,实际G可以学习到真实的数据分布Pdata(x)。噪声z的分布设为pz(z)pz(z)是已知的,而Pdata(x)是未知的。在理想情况下,G(z)的分布应该尽可能接近Pdata(x),G将已知分布的z变量映射到了未知分布x变量上。

    根据交叉熵损失,可以构造下面的损失函数
    V(D,G)=ExPdata(x)[lnD(x)]+EzPz(z)[ln(1D(G(z)))]
    损失函数中的ExPdata(x)是指在训练数据x中取得真实样本,而EzPz(z)是指从已知的噪声分布中提取的样本。对于这个损失函数,需要认识下面几点:

    • 整个式子由两项构成。x表示真实图片,z表示输入G网络的噪声,而G(z)表示G网络生成的图片。
    • D(x)表示D网络判断真实图片是否真实的概率(因为x是真实的,所以对于D来说,这个值越接近1越好)。而D(G(z))是为了D判断G生成的图片是否真实的概率。
    • G的目的:G应该希望自己生成的图片“越接近真实越好”。也就是说,G希望D(G(z))尽可能得大,这是V(D,G)会变小。
    • D的目的:D的能力越强,D(x)应该越大,D(G(x))应该越小。因此D的目的和G不同,D希望V(D,G)越大越好。

    在实际训练中,使用梯度下降法,对D和G交替做优化即可,详细的步骤为:

    1. 从已知的噪声分布Pz(z)中选出一些样本z(1),z(2),...,z(m)
    2. 从训练数据中选出同样个数的真实图片x(1),x(2),...,x(m)
    3. 设判别器D的参数为θd,求出损失关于参数的梯度1mi=1m[lnD(xi)+ln(1D(G(zi)))],对θd更新时加上该梯度。
    4. 设生成器G的参数为θg,求出损失关于参数的梯度1mi=1m[ln(1D(G(zi)))],对θg更新时减去该梯度。

    在上面的步骤中,每对D的参数更新一次,便接着更新一次G的参数。有时还可以对D的参数更新k次后再更新一次G的参数,这些要根据训练的实际情况进行调整。另外,要注意的是,由于D是希望损失越大越好,G是希望损失损失越小越好,所以他们是一个加上梯度,一个是减去梯度。

    当训练完成后,可以从Pzz随机取出一个噪声,经过G运算后可以生成符合Pdata(x)的新样本。

    2 DCGAN的原理

    DCGAN的全称是Deep Convolutional Generative Adversarial Networks ,
    意即深度卷积对抗生成网络,它是由Alec Radford在论文Unsupervised
    Representation Learning with Deep Convolutional Generative Adversarial
    Networks中提出的。从名字上来看,它是在GAN的基础上增加深度卷积网
    络结构,专门生成图像样本。下面一起来学习DCGAN的原理。

    上一节详细介绍了D 、G 的输入输出租损失的走义,但关于D 、G 本身的结构并没高做过多的介绍。事实上, GAN 并没再对D 、G 的具体结构做出任何限制。DCGAN中的D 、G 的含义以及损失都和原始GAN中完全一致,但是它在D和G中采用了较为特殊的结构,以便对图片进行有效建模。

    对于判别器D,它的输入是一张图像,输出是这张图像为真实图像的概率。在DCGAN中,判别器D的结构是一个卷积神经网络,输入的图像经过若干层卷积后得到一个卷积特征,将得到的特征送入Logistic函数,输出可以看作是概率。

    对于生成器G ,它的网络结构如图8-1所示。
    这里写图片描述

    G的输入时一个100维的向量z。它是之前所说的噪声向量。G网络的第一层实际是一个全连接层,将100维的向量变成一个4x4x1024维的向量,从第二层开始,使用转置卷积做上采样,逐渐减少通道数,最后得到的输出为64x64x3,即输出一个三通道的宽和高都为64的图像。

    此外,G、D还有一些其他的实现细节:

    • 不采用任何池化层( Pooling Layer ),在判别器D 中,用带有步长( Stride)的卷积来代替池化层。
    • 在G 、D 中均使用Batch Normalization帮助模型收敛。
    • 在G中,激活函数除了最后一层都使用ReLU 函数,而最后一层使用tanh函数。使用tanh函数的原因在于最后一层要输出图像,而图像的像素值是有一个取值范围的,如0~255 。ReLU函数的输出可能会很大,而tanh函数的输出是在-1~1之间的,只要将tanh函数的输出加1再乘以127.5可以得到0~255 的像素值。
    • 在D 中,激活函数都使用Leaky ReLU作为激活函数。

    以上是DCGAN中D和G的结构,损失的定义以及训练的方法和第1节中描述的完全一致。Alec Radford使用DCGAN在LSUN数据集上进行无监督学习, LSUN是一个场景理解图像数据集,主要包含了卧室、固房、客厅、教室等场景图像。在LSUN的卧室数据集上,DCGAN生成的图像如图8-2所示。
    这里写图片描述

    除了使用G生成图像之外,还可以将G的输入信号z看作生成图像的一种表示。假设图片A对应的输入为zA,图片B对应的输入为zB,可以在zAzB之间做插值,并使用G生成每一个插值对应的图片,对应的结果如图8-3所示。每一行的最左边可以看做图片A,而每一行的最右边可以看做是图片B,DCGAN可以让生成的图像以比较自然的方式从A过渡到B,并保证每一张过度图片都是卧室的图片。如图8-3所示的第六航中,一键没有窗户的卧室逐渐变化成了一间有窗户的卧室,在第四行中,一间有点事的卧室逐渐变化成了一间没有电视的卧室,原来电视的位置被窗帘取代,所有这些图片都是机器自动生成的。
    这里写图片描述

    实验证明,不仅可以对输入信号z进行过渡插值,还可以对它进行复杂运算。如图8-4所示,用代表“露出笑容的女性”的z,减去“女性”,再加上“男性”,最后得到了“露出笑容的男性”。
    这里写图片描述

    3 在TensorFlow中用DCGAN生成图像

    本节会以GitHub上的一个DCGAN项目介绍TensorFlow中的DCGAN实现。利用该代码主要去完成两件事,一是生成MNIST手写数字,二是在自己的数据集上训练。还会穿插讲解该项目的数据读入方法、数据可视化方法。

    3.1 生成MNIST图像

    先做一个简单的小实验:生成MNIST手写数字。
    运行如下代码会下载MNIST数据集到data/mnist文件夹中。

    python download.py mnist

    download.py 依赖一个名为tqdm的库,如果运行报错,可以先使用pip
    install tqdm安装该库。

    注意:当下载数据集时,如果出现网络问题导致下载中断, 在再次下载时必须先删除data/mnist 文件夹,否则download.py 会自动跳过下载。

    下载完成后,使用下面的命令即可开始训练:

    python main.py --dataset mnist --input_height=28 --output_height=28 --train

    参数的含义会在下面的小节中进行详细的介绍,先来关注运行该命令后屏幕显示的信息:

    Epoch: [ 3] [   5/  15] time: 152.4979, d_loss: 1.39733350, g_loss: 0.68659568
    Epoch: [ 3] [   6/  15] time: 155.5141, d_loss: 1.39340806, g_loss: 0.68581676
    Epoch: [ 3] [   7/  15] time: 158.4942, d_loss: 1.39538455, g_loss: 0.68858492
    Epoch: [ 3] [   8/  15] time: 161.3817, d_loss: 1.39494920, g_loss: 0.68842071
    Epoch: [ 3] [   9/  15] time: 164.2292, d_loss: 1.40010333, g_loss: 0.67908889
    Epoch: [ 3] [  10/  15] time: 167.2779, d_loss: 1.40040839, g_loss: 0.68134904

    Epoch[3][10/15]表示当前为第3个epoch,每个epoch内有15步,当前为第0步。默认会在MNIST数据集运行25个epoch。每个一段时间,程序会把生成的模型保存在checkpoint/mnist_64_28_28、文件夹中。此外,每隔100步,程序都会使用当前的G生成图像样本,并将图像保存在samples文件夹中。这些自动生成的图像以train开头,如train_20_0299.png表示是第20个epoch第299步生成的图像。根据这些图像,可以得知当前生成G的性能,从而决定是否可以停止训练。

    运行完25个epoch时,生成的效果如图8-5所示。

    3.2 使用自己的数据集训练

    本节介绍如何使用自己的图片数据集进行训练。首先需要准备好图片数据将它们裁剪到统一大小。在数据目录chapter_8_data中已经准备好了一个动漫人物头像数据集faces.zip 。在源代码的data目录中新建一个anime目录(如果没有data目录可以自行新建) ,并将faces.zip中所高的图像文件解压到anime目录中。最后形成的项目结构为:
    这里写图片描述

    在项目根目录中运行下面的命令即可开始训练:

    python main.py --input_height 96 --input_width 96 \
        --output_height 48 --output_width 48 \
        --dataset anime --crop --train \
        --epoch 300 --input_fname_pattern ".jpg"

    这里将参数设置为一共会训练300个epoch,实际可能并不需要那么多,读者同样可以观察samples文件夹下生成的样本图像来决定应该训练多少个
    epoch 。

    在训练1个epoch后,产生的样本图像如图8-6所示,此时只有模糊的边框(产生的图片在samples文件夹中)。
    这里写图片描述

    在训练5个epoch后,产生的样本如图8-7所示。
    这里写图片描述

    在训练50个epoch 后,产生的样本如图8-8所示,此时模型已经基本收敛了。
    这里写图片描述

    使用已经训练好的模型进行测试的对应命令为:

    python main.py --input_height 96 --input_width 96 \
        --output_height 48 --output_width 48 \
        --dataset anime --crop

    3.3 程序结构分析:如何将图像读入模型

    如果对第3.1、3.2节中的命令仍有所疑惑,本节会结合程序源码,对这些输入参数进行详细的分析。项目所有的功能入口为文件main.py,因此,先来看下main.py的大体结构。在这个文件中,首先定义了一些参数,然后将参数统一保存到变量FLAGS中,接着根据这些参数调用DCGAN(),新建一个模型,并保存到变量dcgan中。接下来的代码为:

    # 如果参数中指定为train,那么调用train方法进行训练
    if FLAGS.train:
      dcgan.train(FLAGS)
    else:
        # 如果不需训练,直接去载入已经训练好的模型
      if not dcgan.load(FLAGS.checkpoint_dir)[0]:
        raise Exception("[!] Train a model first, then run test mode")
    
    # Below is codes for visualization
    # 无论是进行训练还是直接执行,都会调用visualize方法进行可视化
    OPTION = 2
    visualize(sess, dcgan, FLAGS, OPTION)

    根据这段代码,在输入命令时,如果指定了–train,会进行训练,如果不指定–train,会载入己保存的模型,无论是进行训练还是不进行训练,都会调用visualize方法进行可视化。

    以上是该项目的整体逻辑。下面介绍输入的命令行和输入图像有关的参数处理。–input_height、–input_width、–output_height、–output_width 、–dataset、–crop、–input_fname_pattern 这些参数。

    首先–dataset、–input_fname_pattern 两个参数。在model.py中,找到下列代码:

    # mnist单独处理
    if self.dataset_name == 'mnist':
      self.data_X, self.data_y = self.load_mnist()
      self.c_dim = self.data_X[0].shape[-1]
    else:
      # 在训练时,使用self.data中的数据
      # 是data、dataset_name、self.input_fname_pattern
      self.data = glob(os.path.join("E:\datasets", self.dataset_name, self.input_fname_pattern))
      # 检查图片的通道数。一般是3通道彩色图
      imreadImg = imread(self.data[0]);
      if len(imreadImg.shape) >= 3: #check if image is a non-grayscale image by checking channel number
        self.c_dim = imread(self.data[0]).shape[-1]
      else:
        self.c_dim = 1

    对于MNIST数据,程序是使用一个load_mnist()函数单独处理的。而对
    于自己的数据集3 程序会在data 文件夹下根据dataset和input_fname pattern两个变量找图像文件。这里的self.dataset_name是输入参数dataset,
    self.input_fname_pattern是输入参数input_fname pattern 。如输入dataset 为anime 、input_fname_pattern 为.jpg ,程序会自动寻找路径为data/anime/ .jpg的所有图片,即data/anime目录下的所有jpg图像。

    读入所高图片的文件名后,又会做哪些操作呢?这涉及–input_height 、–input_width 、–crop 、–output_height 、–output_width五个参数。首先要说明的一点是,如果输入时不指定–input_width,那么它的值会和–input_height的值相同;同样,如果不指定–output_width,那么它的值会和–output_height相同。即main.py中的:

    if FLAGS.input_width is None:
      FLAGS.input_width = FLAGS.input_height
    if FLAGS.output_width is None:
      FLAGS.output_width = FLAGS.output_height

    读入的图片文件名首先经过以下操作(该部分代码在model.py 中):

    # mnist单独操作
    if config.dataset == 'mnist':
    batch_images = self.data_X[idx*config.batch_size:(idx+1)*config.batch_size]
    batch_labels = self.data_y[idx*config.batch_size:(idx+1)*config.batch_size]
    else:
    # self.data是所有图像文件名,batch_files是取出一个batch_size文件的文件名
    batch_files = self.data[idx*config.batch_size:(idx+1)*config.batch_size]
    # 调用get_image函数对每个图像进行处理
    batch = [
        get_image(batch_file,
                  input_height=self.input_height,
                  input_width=self.input_width,
                  resize_height=self.output_height,
                  resize_width=self.output_width,
                  crop=self.crop,
                  grayscale=self.grayscale) for batch_file in batch_files]
    # 区分灰度图和彩色图
    if self.grayscale:
      batch_images = np.array(batch).astype(np.float32)[:, :, :, None]
    else:
      batch_images = np.array(batch).astype(np.float32)

    self.data是之前说的存放所有图像文件路径的列表,每次都从该列表中取出batch_size大小的子集batch_files,对于batch_files中的每一个文件路径,调用get_image函数进行处理。

    get_image函数在utils.py中,在此直接列出所有用到的函数:

    # get_image读入图像后直接使用transform函数
    def get_image(image_path, input_height, input_width,
                  resize_height=64, resize_width=64,
                  crop=True, grayscale=False):
      image = imread(image_path, grayscale)
      return transform(image, input_height, input_width,
                       resize_height, resize_width, crop)
    
    
    # transform函数
    def transform(image, input_height, input_width, 
                  resize_height=64, resize_width=64, crop=True):
      if crop:
      # 中心crop之后resize
        cropped_image = center_crop(
          image, input_height, input_width, 
          resize_height, resize_width)
      else:
      # 直接resize
        cropped_image = scipy.misc.imresize(image, [resize_height, resize_width])
        # 标准化处理
      return np.array(cropped_image)/127.5 - 1.
    
    # 中心crop,再进行缩放
    def center_crop(x, crop_h, crop_w,
                    resize_h=64, resize_w=64):
      if crop_w is None:
        crop_w = crop_h
      h, w = x.shape[:2]
      j = int(round((h - crop_h)/2.))
      i = int(round((w - crop_w)/2.))
      return scipy.misc.imresize(
          x[j:j+crop_h, i:i+crop_w], [resize_h, resize_w])

    get_image函数实际调用了transform 函数。transform 函数又使用了
    center_crop函数。而center_crop函数的功能是:在图片中心截取高为crop_h像素,宽为crop_w像素的图片,再缩放为resize_h乘resize_w的大小。

    再看transform函数,对输入图像的处理有两种方法。当指定–crop后,会调用center_crop函数。根据调用关系,这里的input_height和input_width是输入的–input_height和–input_ width参数,而resize_height和resize_width是输入的–output_height和–output_width参数。因此,实际是在图像中心截
    取高为input_height乘以input_width的小块,并放缩到output_ height乘以
    output_width的大小。此外,如果不指定参数–crop,不去截取图像,而是直接缩放到output_height乘output_width 。

    这样的话,之前的执行指令非常好理解了,下面的命令:

    python main.py --input_height 96 --input_width 96 \
        --output_height 48 --output_width 48 \
        --dataset anime --crop --train --epoch 300 --input_fname_pattern "*.jpg"

    对应的含义是:

    • 找出data/anime/下所有jpg格式的图像。
    • 将这些图像中心截取96x96的小块,并缩放到48 ×48像素。
    • 因为有–train参数, 所以执行训练。

    最后还有一个参数–epoch没解释,这个参数含义很好理解,代表执行的epoch数目。

    3.4 程序结构分析:可视化方法

    在训练好模型或者载入已有模型后,都会调用visualize方法进行可视化,即main.py中的如下代码:

    OPTION = 0
    visualize(sess, dcgan, FLAGS, OPTION)

    visualize函数在utils.py中。简单查看后可以发现该函数的输入参数option支持0、1、2、3、4一共5个值。在main. py 中直接更改OPTION的值可以使用不同的可视化方法。这里以option=0和option=1为例进行介绍。

    option=0的可视化方法:

    # image_fname_dim是batch_size开方之后向上取整的值
    image_frame_dim = int(math.ceil(config.batch_size**.5))
    if option == 0:
      # 生成batch_size个z噪声
      z_sample = np.random.uniform(-0.5, 0.5, size=(config.batch_size, dcgan.z_dim))
      # 根据batch_size个z噪声生成batch_size张图片
      samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
      # 将所有图片拼合成一张图片
      # 这一张图片的格式为image_frame_dim乘以image_frame_dim
      save_images(samples, [image_frame_dim, image_frame_dim], './samples/test_%s.png' % strftime("%Y%m%d%H%M%S", gmtime()))

    程序首先根据batch_size的值计算出一个image_frame_dim 。这个值实际上是batch_size开方后再向上取整的结果。如默认的batch_size为64, 那么对应的image_frame_dim值是8 。

    接着随机生成一些躁声z并保存为变量z_sample,它的形状为( batch size,z dim ),后者z_dim是单个噪声本身具有的维度,默认为100,这也和原始论文中的网络结构保持一致。在默认情况下,将生成一个形状为( 64, 100)的z_sample,z_sample中的每个值都在-0.5~0.5 之间。将它送入网络中,可以得到64张图像并放在samples中,最后调用save_images函数将64张图像组合为一张8*8的图像,如图8-9所示。
    这里写图片描述

    再看option=1的可视化方法:

    elif option == 1:
      # values是和batch_size等长的向量,从0~1递增
      values = np.arange(0, 1, 1./config.batch_size)
      # 会生成100张图片
      for idx in xrange(100):
        print(" [*] %d" % idx)
        # 这里的z_sample大多数都是0
        z_sample = np.zeros([config.batch_size, dcgan.z_dim])
        # 实际上是把z_sample的第idx列变成values
        for kdx, z in enumerate(z_sample):
          z[idx] = values[kdx]
    
        if config.dataset == "mnist":
          # 对mnist分开处理
          y = np.random.choice(10, config.batch_size)
          y_one_hot = np.zeros((config.batch_size, 10))
          y_one_hot[np.arange(config.batch_size), y] = 1
    
          samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample, dcgan.y: y_one_hot})
        else:
          samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
    
        save_images(samples, [image_frame_dim, image_frame_dim], './samples/test_arange_%s.png' % (idx))

    option=1的可视化方法会生成100张和option=0中差不多的图片。每个
    z_sample中的数字大多数都是0,某中第idx( idx 从0~99)列变成一个事先
    定义好的向量values。因此每个z_sample中各个图片对应的改变很小。图
    8-10展示了使用option=1进行可视化生成的图片(变化比较细微) 。

    这里写图片描述

    剩下的几种可视化方法我们可以自行参阅源码进行分析。注意option=2 、
    3、4 的几种方法都依赖一个名为moviepy的库。可以使用pip install moviep y安装,并保证import moviepy.editor as mpy不会出错。

    4 总结

    本章首先讲解了GAN和DCGAN的原理,接着介绍了一个非常有趣的项目:在TensorFlow中利用DCGAN生成图片。最后,以输入图像和可视化方法两部分为例,分析了DCGAN 项目的源码。希望通过这篇文章的介绍,掌握GAN的思想以及DCGAN的使用方法。

    展开全文
  • 从头开始GAN【论文】(二) —— DCGAN

    万次阅读 多人点赞 2018-11-16 16:17:48
    这一篇就来看看GAN 的改进版之一,DCGAN(Deep Convolutional GAN)。 1. 网络结构 DCGAN 的判别器和生成器都使用了卷积神经网络(CNN)来替代GAN 中的多层感知机,同时为了使整个网络可微,拿掉了CNN 中的池化...

    上一篇介绍了GAN 的基本原理以及相关的概念和知识点,同时也反映出GAN 的一些缺点,比如说训练不稳定,生成过程不可控,不具备可解释性等。这一篇就来看看GAN 的改进版之一,DCGAN(Deep Convolutional GAN)。

    1. 网络结构

    DCGAN 的判别器和生成器都使用了卷积神经网络(CNN)来替代GAN 中的多层感知机,同时为了使整个网络可微,拿掉了CNN 中的池化层,另外将全连接层以全局池化层替代以减轻计算量。

    2. 去卷积(反卷积,Deconvolution)

    从上图中可以看到,生成器G 将一个100 维的噪音向量扩展成64 * 64 * 3 的矩阵输出,整个过程采用的是微步卷积的方式。作者在文中将其称为fractionally-strided convolutions,并特意强调不是deconvolutions。关于上采样和去卷积等概念,我特意找了一些资料来看看他们的区别是什么,相关内容整理在下面。

    上采样和去卷积是同一个概念,它的目的是将经过池化层以后缩小的矩阵扩大到一定的大小,比如说从3 * 3 扩大到5 * 5,如下图所示:

    而去卷积(链接:反卷积)又包含转置卷积和微步卷积,两者的区别在于padding 的方式不同,看看下面这张图片就可以明白了:

    另外还有一个概念叫空洞卷积(Dilated Convolution)。在pixel-wise 目标检测任务上有一个常见的场景就是需要通过去卷积来会恢复经过池化层以后的矩阵大小,先池化再去卷积的过程实际上损失了很多信息,而这个过程的真实目的实际上就是为了通过缩小矩阵size 的方式来增大感受野,那能不能跳过先缩小在增大的过程直接进行感受野放大呢?这个就是所谓的空洞卷积,更详细的内容可以参考这个链接:空洞卷积

    3. 训练方法

    DCGAN 的训练方法跟GAN 是一样的,分为以下三步:

    (1)for k steps:训练D 让式子【logD(x) + log(1 - D(G(Z)) (G keeps still)】的值达到最大

    (2)保持D 不变,训练G 使式子【logD(G(z))】的值达到最大

    (3)重复step(1)和step(2)直到G 与D 达到纳什均衡

    4. 相比于GAN 的改进

    DCGAN 相比于GAN 或者是普通CNN 的改进包含以下几个方面:

    (1)使用卷积和去卷积代替池化层

    (2)在生成器和判别器中都添加了批量归一化操作

    (3)去掉了全连接层,使用全局池化层替代

    (4)生成器的输出层使用Tanh 激活函数,其他层使用RELU

    (5)判别器的所有层都是用LeakyReLU 激活函数

    5. 漫游隐空间

    通过使用插值微调噪音输入z 的方式可以导致隐空间结构发生变化从而引导生成图像发生语义上的平滑过度,比如说从有窗户到没窗户,从有电视到没电视等等。

    6. 语义遮罩

    通过标注窗口,并判断激活神经元是否在窗口内的方式来找出影响窗户形成的神经元,将这些神经元的权重设置为0,那么就可以导致生成的图像中没有窗户。从下图可以看到,上面一行图片都是有窗户的,下面一行通过语义遮罩的方式拿掉了窗户,但是空缺的位置依然是平滑连续的,使整幅图像的语义没有发生太大的变化。

    7. 矢量算法

    在向量算法中有一个很经典的例子就是【vector("King") - vector("Man") + vector("Woman") = vector("Queue")】,作者将该思想引入到图像生成当中并得到了以下实验结果:【smiling woman - neutral woman + neutral man = smiling man】

     

    推荐阅读(很nice 的一篇文章):Image Completion with Deep Learning in TensorFlow

    推荐参考(超棒的去卷积动图):https://github.com/vdumoulin/conv_arithmetic

    展开全文
  • DCGAN实现

    2019-05-06 12:44:38
    DCGAN实现 DCGAN实现 代码 dcgan.py #!/usr/bin/env python # -*- coding: utf-8 -*- import os import math import argparse import cv2 import numpy as np import tensorflow as tf # D...
  • 【GAN】DCGAN

    2019-08-16 14:46:14
    生成式对抗网络(GAN)是近年来大热的深度学习模型。... 如何在Tensorflow跑DCGAN的代码,生成如题图所示的动漫头像,附送数据集哦 :-) 一、GAN原理介绍 说到GAN第一篇要看的paper当然是Ian Go...
  • DCGAN——菜鸟实现

    千次阅读 2019-01-09 12:15:21
    收获 ​ 在复现这个项目以前,我算是一个完全不入门的人。 ​ 所以,第一遍我花里很大的精力取读懂代码,第二遍根据视频去理清思路,第三遍根据tensorflow框架的学习去代码中寻找他的身影。所以,整个项目下来,写的...
  • 深度学习项目实战-对抗生成网络

    千人学习 2020-06-24 14:45:35
    进入学习群,获取唐宇迪老师...后项目实战对抗生成网络的升级版DCGAN,大家都可以基于DCGAN生成出来任何你喜欢的数据。 课程代码基于Tensorflow框架,案例与项目课时会通过debug的形式详解代码中每行的用法。
  • DCGAN结构解读

    千次阅读 2017-10-13 22:11:25
    DCGAN的原文里面给出的结构如下: 这是G的结构,而D则是完全相反的: 刚看论文的时候觉得结构很清晰,但是实际写代码的时候并不是很能够清楚表示,相信有很多人也是这种感觉,因此这边做一个分析,以便后面的同学...
  • DCGAN+pytorch+代码详解

    千次阅读 2019-03-21 16:53:49
    pytorch 官方给的例子,思路很清晰。 https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html 简单的例子,可以自己动手做一下。更清楚GAN的一个工作原理。
  • GANs入门系列之(三)pytorch实现DCGAN

    千次阅读 2019-01-28 21:35:17
    pytorch官方实现的说明 1.DCGAN简介 2.参数说明/运行说明 3.数据加载 4.Generator 5.Discriminator 6.训练过程 7.完整代码
  • 深度学习DCGAN自己的数据集实现

    千次阅读 热门讨论 2019-10-22 17:02:19
    按照各位大佬博客,我也利用萌妹子头像训练了一下DCGAN。本人电脑CPU的 4GB 啊啊啊,100epoch跑了一天。不过看到最后生成了清晰的萌妹子头像还是很开心的。。。。。 我github上的这篇文章的代码地址:...
  • 对抗网络DCGAN 生成图片的应用

    千次阅读 2020-04-29 13:43:51
    DCGAN原理介绍 我们知道深度学习中对图像处理应用最好的模型是CNN,那么如何把CNN与GAN结合?DCGAN是这方面最好的尝试之一(论文地址:[1511.06434] Unsupervised Representation Learning with Deep Convolutional ...
  • 各位大神,DCGAN训练后生成的图像是很多小图片拼成的,如何使生成的图片是单张的,而不是很多图像组合一块的。
  • Windows下用DCGAN训练自己的数据集

    千次阅读 热门讨论 2019-03-31 13:57:37
    DCGAN是在TensorFlow框架下训练数据,所以在使用DCGAN前先配置好TensorFlow环境,可以参考这篇博客win10+cuda9.2+TensorFlow安装。 下载DCGAN源码,百度链接:DCGAN源码,提取码:nd61 2. 训练自己的数据集 将...
  • 使用DCGAN训练celeba生成图像的结果

    千次阅读 热门讨论 2019-04-12 16:43:00
    # 第一次写博客 不知道该写些什么有什么意见或则想法的可以留言
  • GAN和DCGAN的讨论

    千次阅读 2017-11-11 16:08:27
    1. GAN对噪声z的分布有要求吗?常用哪些分布? 一般没有特别要求,常用有高斯分布、均匀分布。噪声的维数至少要达到数据流形的内在维数,才能产生足够... 2. GAN的 adversarial 体现在哪里? G和D的博弈,G需要尽量贴
  • DCGAN数据集:mnist、CelebA、lsun

    千次阅读 2018-07-20 17:09:15
    carpedm20/DCGAN-tensorflow : https://github.com/carpedm20/DCGAN-tensorflow mnist数据集: http://yann.lecun.com/exdb/mnist/ CelebA数据集: ...
  • 深度卷积对抗生成网络(DCGAN)

    万次阅读 2020-09-09 20:43:13
    卷积神经网络在有监督学习中的各项任务上都有很好的表现,但在无监督学习领域,却比较少。本文介绍的算法将有监督学习中的CNN和无监督学习中的GAN结合到了一起。
  • DCGAN-TensorFlow-样例程序(新手推荐)

    千次阅读 2018-03-16 13:05:07
    下面这个亲测成功:https://github.com/carpedm20/DCGAN-tensorflowmnist数据包大部分情况需要另外下载好放到新建的data文件夹中(其他数据包暂未接触,见后续更新)。mnist数据包下载地址:...
1 2 3 4 5 ... 20
收藏数 3,536
精华内容 1,414
关键字:

dcgan