如何用深度学习做“前端”:基于设计模型图片生成HTML和CSS代码(上)

作者:Emil Wallner

编译:Bot

编者按:一个多月前,知名博主Emil Wallner授权论智编译了他的第三篇深度学习系列教程:如何用100行神经网络代码为黑白图片着色,受到了读者的广泛欢迎。这一次,我们又带来了他的新作:Turning Design Mockups Into Code With Deep Learning,即如何用深度学习模型将设计稿转化为代码,希望大家能喜欢。本文是连载文章,今天给大家带来的是上半部分,敬请关注。

我认为,在未来三年内,深度学习将被用于加快设计稿上线速度、降低软件开发门槛上,它会改变前端的发展轨迹。

从去年Tony Beltramelli在论文中介绍了pix2code——一种自动从单个输入图像(iOS、Android和Web)生成代码,准确率高达77%的模型,到后来Airbnb推出sketch2code,首次提出将人工智能辅助设计和开发用于下一代工具的愿景。自动化的前端设计已经成了一个可展望的趋势。

事实上,深度学习能依靠算法及训练数据实现前端开发的自动化,但目前它还有一个最大的障碍,就是算力。即便如此,为这个趋势进行一些初步探索也是不错的尝试。

在这篇文章中,我们将建立一个神经网络,它能基于设计稿的图片编写基本的HTML代码和CSS代码用于网站设计。以下是该过程的简要概述:

1.为训练好的神经网络提供设计稿

2.神经网络将图像转换成HTML标签

3.渲染输出

和“着色”一文类似,这里我们将具体介绍神经网络的三个迭代版本。

在第一个版本中,我们要做的是建立一个能编写可移动部件的低级版本;第二个版本是HTML版,它能自动执行代码编写的所有步骤,届时我会介绍神经网络的各个层次;第三个版本是Bootstrap,我们会建立一个模型来解析和探索LSTM层。

代码下载:

Jupyter Notebook:GitHub(https://github.com/emilwallner/Screenshot-to-code-in-Keras/blob/master/README.md)和FloydHub(https://www.floydhub.com/emilwallner/projects/picturetocode)

FloydHub notebook:FloydHub(https://www.floydhub.com/emilwallner/projects/picturetocode),floydhub目录

本地notebook:FloydHub(https://www.floydhub.com/emilwallner/projects/picturetocode),local。

参考阅读:

论文:pix2code:从用户界面图形截图生成代码(https://arxiv.org/abs/1705.07962)

博客:编码器-解码器模型的图片标题生成(https://machinelearningmastery.com/caption-generation-inject-merge-architectures-encoder-decoder-model/)

博客:用Python实现神经网络(https://blog.floydhub.com/my-first-weekend-of-deep-learning/)

博客:反向传播过程详解(https://blog.floydhub.com/coding-the-history-of-deep-learning/)

博客:卷积神经网络的应用——如何用100行神经网络代码为黑白图片着色(中文版)

以下实现基于Python和Keras。

核心逻辑

让我们回顾一下我们的初级目标——建立一个神经网络,它能基于屏幕截图生成对应的HTML/CSS标签。

整个训练思路十分清晰。在训练时,我们提供截图和截图对应的HTML标签作为训练数据,它要通过这两个输入学会准确预测代码中的标签。当它预测下一个标签时,它的输入是截图和下一个标签之前的所有正确标签。具体可看这个在线Excel中的演示。

根据描述可以发现,这其实就是一个常见的单词预测模型,我们通过给定一个单词序列,让模型预测哪一个单词是最有可能出现的下一个单词。考虑到过程中使用了截图,一些人也提出过结合计算机视觉的方法,但我们这里仍以单词预测为准。

请注意,对于每一次预测,模型得到的截图都是相同的,所以如果它要预测20个标签的话,它其实会得到20个相同的设计模型。现在,让我们把神经网络的工作机制放在一边,先来关注它的输入和输出。

我们先来看模型的“已有标签”(previous markup)。假设我们的模型需要预测“I can code”这个句子,当它接收输入“I”时,它要预测“can”;下一次它收到“I can”时,它要预测“code”。也就是说,输入之前的所有标签后,模型只需要返回下一个标签。

神经网络的作用机制是从数据中提取特征,然后利用特征构建输入数据和输出数据的连接。为了了解它预测的每张截图中的内容和HTML语法,它必须创建表征。这也是为预测做知识积累。

当你想用一个训练好的模型做实际预测时,它的流程其实和训练模型差不多。我们的模型每次都用相同的截图逐一生成文本,它只有迄今为止预测产生的标签,而无需手动输入正确标签。之后它再预测下一个标签。整个预测从“start tag”开始,并以“end tag”或达到最大层数告终。这里是另一个表现流程的在线Excel。

第一个版本:Hello World

让我们先从建立一个hello world版本开始。我们向神经网络输入一个只显示“Hello World!”的网页截图,教它怎么生成网页标签。

首先,神经网络需要将设计模型映射成像素值列表,它有3个调色通道:红色、蓝色、绿色。颜色数值区间为0—255。

为了让神经网络理解标签的含义,我在这里用了One Hot Encoding(独热编码),即把m个状态编码为m个二元特征,它们两两互斥,在任意时候只有一个有效值(激活)。因此,“I can code”可以被映射为:

上图中包含开始和结束标签:“start tag”“end tag”。它们是神经网络开始、终止预测的线索。对于输入,我们用了整个句子,并根据每轮预测逐个输入单词。神经网络的输出始终是下一个词。

这里句子的逻辑和单词一样,都有一个固定的输入长度。比起限制具体词汇,这种限制最大句长的方法更便捷,因为如果输出短于最大句长,我们可以用空字填充它,也就是0。

如上图所示,由于单词是从右到左打印的,这就迫使模型改变单词在每轮预测中的位置。因此它就能从最终所得的排序中习得每个单词的正确位置。

下图展示了一轮正确预测,每一行代表一次预测。函数的左边是由红、绿、蓝三种颜色表示的图像,右边是之前预测的单词。函数的输出是一个接一个的预测,以红色方块结束。

hello world版用了3个token:“start” “Hello World!”和“end”。一个token可以是一个字符、一个单词或一句话,它可以是任何东西。在这个模型中,字符token虽然对词汇量要求较小但是会限制神经网络,所以词汇级token是一个更合适的选择。

以下是我们的预测:

输出

10 epochs:;

100 epochs:;

300 epochs:。

我得到的经验

在收集数据前构建了第一个版本。在项目初期,我设法获得了Geocities网页寄存的旧版存档,它包含3800万个网页。事实证明我低估了它的“潜力”,我忽视了在实际操作中减少100k词汇所需的巨额工作量;

处理TB级的数据需要够好的硬盘,或足够的耐心。mac崩溃了几次后,我最后还是租了一个功能强大的远程服务器,它包含8个现代GPU和1Gpbs的高速网络;

在我真正理解输入和输出前,一切都毫无意义。输入X是屏幕截图和之前预测的标签,输出Y是下一个标签。当我掌握这两点后,理解中间过程就变得容易了。尝试不同的网页架构也有助于理解神经网络;

注意狡兔三窟。这个项目涉及深度学习的多个领域,所以我一路上经常迷失在“兔子洞”(未知领域)中。一次,因为我中途突然对向量空间embedding产生了兴趣,去学了一下,付出的代价是花一星期时间重新编写RNN;

把图像转成编码的神经网络其实就是Image Caption模型。即便我掌握了这一点,我还是忽视了很多关于自动生成图像描述的论文,因为它们太无聊了。但是一旦我有了一些灵感,我就能通过它们加深对问题的理解。

在FloydHub上运行代码

FloydHub是一个深度学习云平台。当我刚开始学习深度学习的时候,我接触到了它,并开始用它来训练和管理我的实验。你可以在10分钟内完成FloydHub安装并运行自己的第一个模型,它称得上是最好的云GPU主机之一。

如果你是FloydHub的新手,你可以看看这个2分钟安装教程和5分钟实际操作演示。

复制repository

登录并启动FloydHub命令行工具

在FloydHub云GPU主机上运行Jupyter notebook

所有notebook都放在floydhub目录中,本地存档在local下。模型一旦跑起来,你可以在floydhub/Helloworld/helloworld.ipynb里找到第一个notebook。

HTML版

这个版本将实现hello world版中多个步骤的自动执行,在这一节中,我们的关注点在于创建一个可扩展的神经网络,并让它能编写网页上的可移动部件。

需要注意的是,HTML版并不能预测随机网页的HTML标签,它的贡献在于探索动态问题。

模型概览

如果我们展开上一版的流程图,它看起来是这样的:

我们的神经网络主要由两部分构成。一是编码器,它是我们学习图像特征和之前预测的标签特征的地方,这些特征是连接设计稿和标签的重要桥梁。在编码器的最后,我们把学习到的图像特征一个个对应给之前预测的标签单词。二是解码器,当解码器接收到组合在一起的图像和标签特征后,它把这些特征导入全连接层,从而预测下一个标签。

设计稿特征

由于我们要为每一个预测单词插入一张截图,这会成为训练神经网络的瓶颈(示例)。为了解决这个问题,我们不直接使用图像,而是提取它生成标签所需的信息作为输入。

而为了把信息编码成图像特征,我们事先已经在imagenet上训练了一个的卷积神经网络(CNN),在进入最后的分类步骤前,它会完成图像的特征提取。

最终,我们得到了1536个8×8的图像,即特征。虽然它们在人眼中没有什么意义,但神经网络能理解它们,并能从中提取对象、坐标等信息。

标签特征

在hello world版本中,我们用one-hot encoding把标签编码成了二元特征,效果显著。这个版本仍将对输出使用one-hot encoding,但同时引入一种新方法——word embedding(词嵌入),来处理输入。

换句话说,就是我们仍将沿用原先句子的构造方式,但改变了映射token的方法。one-hot encoding会把每个单词视为一个独立的单元,相反地,word embedding会把输入的每个单词转换成一系列数字,它们反映的是和标签关系的远近。

在上图中,word embedding得到的矩阵是8维的,词嵌入向量空间的维度一般在50—500之间,具体取决于词汇量大小。每个单词对应的这8位数字有点类似单隐层神经网络中的权值w1、w2、w3……它们描述的是词语之间的相互关系。

以上就是我们生成标签特征的具体方法。神经网络的特点是能将输入数据和输出数据连接起来,具体的连接方法我们会在下一节中介绍。

编码器

如下图右侧所示,我们对输入的单词做word embedding后,把它们导入LSTM运行,并输出一系列标签特征。之后,这些标签特征被馈送进Time distributed全连接层(dense layer)——一个具有多个输入和输出的全连接层。

与此同时,另一侧,我们的图像特征首先通过Flatten层被“压平”。无论数据结构如何,它们都会被转换成一张巨大的数字列表。我们在这一层的基础上建立一个全连接层,形成高维特征,然后把图像特征对应给标签特征。

如果你理解不了,这里我们对它做一下分解。

标签特征

在编码器左侧,我们用LSTM运行word embedding。如下图所示,这个LSTM有256个单元,所以我们把矩阵从8维扩展到最大的256维(添加0)。

为了方便把两边特征放在一起,同时构建高维模式(连接特征),我们在标签特征上加了一个TimeDistributed全连接层,它和隔壁的全连接层是一模一样的,只不过有多个输入和多个输出(因为有多个单词输入)。

图像特征

当编码器在处理标签特征时,在另一个“平行空间”,它也在对图像特征做进一步处理。我们的Flatten层把之前得到的1536个8×8的小图像转换成了一个很长的列表。里面包含的信息没有变,只是被重组了。

同样的,为了方便把两边特征放在一起,同时构建高维模式(连接特征),我们在图像列表上加了一个全连接层。因为这边只有一张表,也就是只有一个输入,所以我们可以用正常的全连接层。最后,为了创建连接,我们根据标签特征的输出个数,复制致密层输出的图像特征。

在上图示例中,我们最终复制了3个图像特征,和之前输出的3个标签特征数量相同。

标签特征和图像特征的连接

两边特征被处理成同样数量后,由于已经有了图像特征,我们就可以为标签特征添加图像特征了。

如上图所示,我们把图像特征对应到标签特征,这时编码器结束了“使命”,输出这3个图像—标签特征。

解码器

解码器的“使命”是把编码器输出的图像—标签特征作为输入,然后预测下一个标签特征。

下图是编码器的3个输出进入解码器后的结果。值得注意的是,图中LSTM显示sequence=false,这是因为虽然它可以有多个输入,但它只能有一个输出,即预测下一个标签的特征。它只包含最终预测的信息。

最终预测

全连接层的工作机制和传统前馈神经网络一样,它把代表下一个标签特征的512个数字和4个最终预测联系在一起。这里我们假设这4个单词是:start、hello、world和end。

全连接层中的softmax激活函数的分布区间在0到1之间,所有预测的概率总和为1。这时,如果这4个单词的预测概率是[0.1,0.1,0.1,0.7],那么end就是我们的最终预测。利用one-hot encoding,我们把它转化为[0, 0, 0, 1],输出“end”。

输出

上图网站的链接:

250 epochs

350 epochs

450 epochs

550 epochs

如果打开网址看不到,可以右键查看网页源代码。这是原版参考。

我得到的经验

和CNN相比,LSTM要更难理解一点。在我展开LSTM的架构后,我才理解它的工作机制。这里不得不提一点,Fast.ai的RNN教程很好用。另外,在你尝试理解工作机制之前,注意先弄清输入和输出到底是什么;

重新创建词库比缩小大型词库要容易得多。这包含从字体、div大小、颜色到变量名称、普通单词等方方面面;

大多数库是被创建来解析文本的。在(英文)文档中,每个单词都用空格分离,但在代码中,你需要自定义分析;

你可以用在Imagenet上训练的模型提取特征。乍一看这可能不太科学,因为Imagenet上几乎没有web图像。当然,和从头训练的pix2code模型相比,Imagenet模型的loss还是高了30%。我个人比较倾向于用基于网页截图训练的inception-resnet模型。

未完待续……

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20180123G10T1J00?refer=cp_1026

同媒体快讯

相关快讯

扫码关注云+社区