pix2pix代码学习

前言

因为最近再做新的项目,需要用到GAN网络做一些新的东西。前文里已经简单的介绍了CGAN(Condition GAN)的思想。使用CGAN比较有意思的一篇论文就是pix2pix,进行图像风格迁移的一篇论文。
Image-to-Image Translation with Conditional Adversarial Networks
关于这篇论文的tensorflow版本的文章:https://affinelayer.com/pix2pix/, github地址:https://github.com/affinelayer/pix2pix-tensorflow
其任务主要就是如下图
image.png

我们期望通过输入这种风格的图像,生成目标风格类似的真实图像。
下面就简单的进行代码详解。

参数表

其实这部分在代码中很详尽了,简单的贴一下对应的中文翻译吧。
image.png
其中根据github中的readme,需要确认的参数主要为--input_dir(输入路径,也就是你的训练数据的路径)
--mode(你的运行模式,分为[train, test, export],我们对数据集进行训练,所以选择train),--output_dir (训练结果的输出路径,包含了checkpoint等权重文件,配置文件汇总,日志文件) --max_epochs(最大训练轮次),--which_direction(AtoB or BtoA,根据自己需求选择,我们是从像素级图像,生成真实风格,则为BtoA,反之。)

函数解析

def load_examples()主要是为了载入数据集,这一部分逻辑其实没有什么难点,主要记录我再看这段代码学习到的新的技能。

input_paths = glob.glob(os.path.join(a.input_dir, "*.jpg"))

这个函数主要是glob的用法。glob.glob()返回所有匹配文件的路径列表。它只有一个参数pathname,定义了文件匹配规制。比如这里的os.path.join(a.input_dir, "*.jpg")其路径的结果是result = {str}'facades/train/.jpg',也就是路径下所有的jpg文件,所以input_paths = glob.glob(os.path.join(a.input_dir, "*.jpg"))最后返回的就是一个该路径下,所有jpg文件的路径,如图所示。image.png
这里只展示了部分。

tf数据读入模块

decode = tf.image.decode_jpgor decode = tf.image.decode_png都是产生对于图像的解码器,也就是tensorflow中特有的函数,将图像,根据内容解码为数组。

path_queue = tf.train.string_input_producer(input_paths, shuffle=a.mode == "train")
reader = tf.WholeFileReader()
paths, contents = reader.read(path_queue)
raw_input = decode(contents)
raw_input = tf.image.convert_image_dtype(raw_input, dtype=tf.float32)

tf.train.string.input_producer是将文件名列表制作为一个先入先出的队列,方便数据的读取。
那么同理read = tf.WholeFileReader()就是生成一个文件阅读器,来根据生成文件队列读取文件。
raw_input = decode(contents)就是通过上文生成的解码器,对读入图片进行解码。
raw_input = tf.image.convert_image_dtype(raw_input, dtype=tf.float32)将图像的数据格式转化。
参考链接:http://wiki.jikexueyuan.com/project/tensorflow-zh/how_tos/reading_data.html

接下来就要介绍一下tensorflow中tf.control_dependencies, tf.identify
对于tf.control_dependencies,这个管理里,只有当里面的操作是一个op(tensorflow里的概念)时才会生效,也就是先执行传入的参数op,在执行里面的op。下面举个例子来说明一下,例子来源:https://stackoverflow.com/questions/34877523/in-tensorflow-what-is-tf-identity-used-for

x = tf.Variable(0.0)
x_plus_1 = tf.assign_add(x, 1)

with tf.control_dependencies([x_plus_1]):
    y = x
init = tf.initialize_all_variables()

with tf.Session() as session:
    init.run()
    for i in xrange(5):
        print(y.eval())

那么熟悉代码的同学,期望得到的是1 2 3 4 5但是实际结果是1 1 1 1 1这就是因为tf.control_dependencies需要的是一个op,那么修改代码如下:

x = tf.Variable(0.0)
x_plus_1 = tf.assign_add(x, 1)

with tf.control_dependencies([x_plus_1]):
    y = tf.identity(x)
init = tf.initialize_all_variables()

with tf.Session() as session:
    init.run()
    for i in xrange(5):
        print(y.eval())

在这里只是添加了y = tf.identity(x),结果符合预期。那么tf.identity的作用就是返回一个一模一样的op,这样会新添加一个新的节点给graph中去,这时control_dependenices就会生效。

代码部分就没有什么其他难点了(emmmmm)

网络结构

这里就贴一下我制作的PPT的内容吧,包括了生成器的网络结果,以及判别器的网络结构

image.png
生成器的结构,一个典型的U-Net风格,且结合了低频和高频信息。

image.png

判别器的结构,分为real_discriminatorfake_discriminator

损失函数

生成器损失函数

gen_loss_GAN = tf.reduce_mean(-tf.log(predict_fake + EPS))
gen_loss_L1 = tf.reduce_mean(tf.abs(targets - outputs))
gen_loss = gen_loss_GAN * a.gan_weight + gen_loss_L1 * a.l1_weight

判别器损失函数

discrim_tvars = [var for var in tf.trainable_variables() if var.name.startswith("discriminator")]
discrim_optim = tf.train.AdamOptimizer(a.lr, a.beta1)
discrim_grads_and_vars = discrim_optim.compute_gradients(discrim_loss, var_list=discrim_tvars)
discrim_train = discrim_optim.apply_gradients(discrim_grads_and_vars)

以上,是不是不难,自己悟去吧!哈哈哈哈!!!

emmmm,这部分有代码问题的欢迎找我交流讨论呢。

发表新评论