用TensorFlow生成抽象图案艺术

    2018-02-11 17:26  浏览次数:198

钻石永恒(2016)

Web Demo

这是在TensorFlow中探索组合模式生成网络的系列文章中的第一篇,我在github回购上做了一些代码供参考。也许,当numpy能完成这件事情的时候,用TensorFlow来执行CPPN可能看起来有些过头,但是我们稍后将继续开展这项工作。

介绍

在最近基于神经网络的图像生成技术中,通常情况下,发生器网络会尝试一次性绘制整个图像。例如,如果输出图像的期望分辨率是256x256,那么神经网络的最后一层将具有黑白图像专用的65536个值。由于这些算法的内存和可扩展性的限制,将输出分辨率提高到现代图像分辨率(2880x1800) 可能是不可行的。

在这篇文章中,我将描述一个非常简单的方法让神经网络来生成高分辨率的图像。神经网络的工作并不是将每个像素一次性生成,而是根据像素的地址生成单个像素的强度或颜色。然后,在期望输出图像中,通过一次访问一个像素的网络,整张图像就可以被生成了。这种方法可以用来生成分辨率非常高的图像,由于受限于内存,它提供了一种简单的方式来放大和缩小图像,并共享一些分形属性。

网上已经有一些演示试验了这个技术,包括karpathy的convnetjs 绘图演示,以及生成神经网络艺术神经图的 javascript实现。

在这篇文章中,我将介绍如何用这个简单的技术在TensorFlow中生成随机的抽象艺术。我想在TensorFlow中实现这个功能的原因是,它可以作为未来利用TensorFlow的机器学习功能基础,来做一些更有趣的工作。因此这种类型的生成网络也可以用来创建非随机图片,我将在以后的帖子解释这一点。

组成模式生成网络描述

 

我们制作的CPPN原理图

我已经在早前的文章中解释了CPPN的基础知识,当CPPN和NEAT算法结合起来时,可进化生成神经网络的结构。

实质上,CPPN只是一个函数,c=f(x,y),它定义了空间中每个点图像的强度。它们可以生成分辨率非常高的图像,这使得它们非常吸引人,因为您可以调度该函数来获取每个像素的颜色或强度(给定该像素的位置)。

f(x,y)只是一个可以被许多数学运算所构建的函数。它也可以用一个神经网络来表示,一组连接激活门的权重在绘制图像时会保持不变,所以整个图像可以被定义为f(w,x,y)

我们TensorFlow的实现将与之前CPPN-NEAT所完成的工作有所不同。像以前的工作一样的是,我们的功能f(x,y)将返回0到1之间的单个实数,以定义该点的图像强度(结果将是一个灰度图像)或三维向量,用介于(0,1)之间的每个值来表示颜色强度(红,绿,蓝)。此外,与CPPN-NEAT类似的,为了使图像更有趣,我们还将每个点的半径项作为输入(定义为 r= \sqrt{x^2+y^2}),所以CPPN函数将是 f(w,x,y,r)。神经网络的权重wwww将会被初始化为单位高斯分布的随机值。

与CPPN-NEAT不同的是,我们将在这个实现中使用的神经网络将只不过是一个由用户定义的多层前馈网络。例如,用户可以修改TensorFlow代码使f(w,x,y,r)为一个由双曲线正切函数、relu’s、softplus、正弦曲线等定义的前馈神经网络。每一层还将有一个额外的偏置输入值,为了清楚起见,将在图中省略。

我们还将在CPPN函数中添加一个称为潜在向量的额外输入,z,这是一个n个实数的向量(其中,n 通常远小于网络中加权连接的总数),所以我们的生成网络被定义为 f(w,z,x,y,r)。通过修改z的值,我们的网络会产生不同的图像,而且,通过逐一改变z,图片的整个空间,也就是潜在空间有可能会被网络生成。在某种意义上,z可以解释为对最终图像的压缩描述,总结为n个实数。如果我们仅少量地修改z,因为网络是一个连续函数,输出图像改变的幅度也不会大,所以我们也可以想像,通过一个潜在的矢量z逐渐从z1移动到z2,如何在相同的潜在空间里将一幅图像慢慢演变成另一幅图像。

请注意,在进入第一个隐藏层之前,图中的空白层只是一个缩放因子,它将应用于的预处理层 x,y,r和z。

当我们可以逐渐调整权重来获得不同的输出图像时,为什么我们需要一个潜在向量作为一个输入,成为了一个争论点。实际上,在一个复杂的生成网络中,可能有成百上千的权重。而且在许多生成应用中,我们希望保持潜在载体的数量不那么多。当我们最终在大型数据集上训练时时,潜在矢量不仅可以控制绘制的对象,还可以控制图像的特定风格。使用概率论的一些方法,我们甚至可以强制使z具有良好的属性,比如独立性和单位高斯性。例如,z的一个子集可以表示人的性别,另一个子集则可以表示由生成算法绘制的人脸的情绪。在后面的博文中,我们会试图培训这种类型的CPPN来写数字,也许可以画一些面部、动物、汽车、厕所等等。

探索神经网络的潜在空间

生成.png和.gif图像所需的CPPN模型和代码现在可在github上找到了。我将使用该代码作为一个参考,用IPython会话来生成图像。

在回购里面,model.py包含CPPN使用TensorFlow库例程生成图像的类。该generator(生成器)的方法将是最有趣的实验,它围绕CPPN的神经网络架构进行修改,并尝试一些替代架构,现今已被注释出可供练习。我们将使用Sampler(采样器)内部的类sampler.py来让我们可以在IPython会话中交互查询图像。

在IPython会话中,我们首先进入repo的目录并运行

%run -i sampler.py
sampler = Sampler(z_dim = 8, scale = 10.0, net_size = 32)

采样器将创建一个CPPN模型,该模型使用由8个实数组成的潜在向量,缩放比例为10倍,并且在每个神经网络层包含有32个激活。请注意,这些都是默认值,所以我们也可以下次调用sampler = Sampler()

为了生成图像,我们需要生成一个包含8个数字的随机的潜在向量 zzzz

z1 = sampler.generate_z()

之后,我们可以通过 z 到生成器函数中来查看输出图像。 img_data只是一个包含图像数据的numpy数组。

img_data = sampler.generate(z1)
sampler.show_image(img_data)

输出

 

如果我们想要重置和随机化神经网络的权重,我们可以调用reinit()的方法

sampler.reinit()
sampler.show_image(sampler.generate(z1))

产量

 

面具(2016)

请注意,由于我们已经改变了网络的权重,所以潜在空间已经改变,我们将看到不同的输出,尽管在相同的z矢量中通过。

我们可以使用sampler.save_png(img_data, 'output.png')来保存文件。

穿越潜在空间

让我们在相同的潜在空间中生成另一个随机图像

2z2 = sampler.generate_z()
sampler.show_image(sampler.generate(z2))

输出

 

束缚(2016)

两个图像属于相同的潜在空间,并且能简洁地以矢量格式来表示。如果我们小步小步地从 z1移动到z2,并且在每一步都能生成图像,我们可以看到被 z1 定义的图像是如何逐渐演变成 z2图像的。我创建了一个方法,通过创建一个.gif文件来做到这一点。这种方法有一些可调整的设置来调整帧的时间和大小。默认设置生成.gif文件大约占5兆字节。

1sampler.save_anim_gif(z1, z2, 'output.gif')

输出

 

彩色图像

如果我们重置IPython会话,并使用此设置运行设置c_dim到3,我们也可以生成彩色图像

%run -i sampler.py
sampler = Sampler(z_dim = 8, c_dim = 3, scale = 10.0, net_size = 32)

我们可以得到CPPN为每个像素位置输出的3个值,为每个像素定义了RGB的值。不过,我个人觉得,使用随机权重的生成的艺术品,黑白版看起来更有艺术感。

修改网络体系结构

我们也可以修改generate里面的方法model.py来改变CPPN网络的体系结构。

默认架构是多层双曲正切函数

4H = tf.nn.tanh(U)
for i in range(3):
  H = tf.nn.tanh(fully_connected(H, net_size, 'g_tanh_'+str(i)))
output = tf.sigmoid(fully_connected(H, self.c_dim, 'g_final'))

例子

或者,我们可以在tanh层中混合softplus层

5H = tf.nn.tanh(U)
H = tf.nn.softplus(fully_connected(H, net_size, 'g_softplus_1'))
H = tf.nn.tanh(fully_connected(H, net_size, 'g_tanh_2'))
H = tf.nn.softplus(fully_connected(H, net_size, 'g_softplus_2'))
output = tf.sigmoid(fully_connected(H, self.c_dim, 'g_final'))

例子

 

 

 

我们甚至可以混合正弦激活函数

5H = tf.nn.tanh(U)
H = tf.nn.softplus(fully_connected(H, net_size, 'g_softplus_1'))
H = tf.nn.tanh(fully_connected(H, net_size, 'g_tanh_2'))
H = tf.nn.softplus(fully_connected(H, net_size, 'g_softplus_2'))
output = 0.5 * tf.sin(fully_connected(H, self.c_dim, 'g_final')) + 0.5

例子

 

 

我也尝试过绝对函数、平方根、高斯激活(自我实现)和残差/梯型等结构。

这个CPPN类是相当通用的,并且能够同时生成大量的图像。当我们想要CPPN为我们生成一些有趣的东西时,批处理对于将来基于GPU的神经网络训练是非常有用的。

结论

TensorFlow库提供了执行向量计算的有效方法,可用于一些不需要梯度计算的任务。但是这也意味着未来可以利用深度学习功能扩展任何工作。就像这个例子一样,我们可以使用这个代码,并尝试训练我们的CPPN来做一些有趣的事情:比如绘制确定类型的图像、字体或不同风格的数字。我将在即将发布的博客文章中撰写这方面的内容,敬请关注。


联系我们


微信订阅号
官方微博
慧都科技有限公司 版权所有 Copyright 2003-2018 渝ICP备12000582号 | 渝公网安备 50010702500608号