目前,自动化前端开发的最大阻碍是计算能力。但我们已经可以使用目前的深度学习算法,以及合成训练数据来探索人工智能自动构建前端的方法。在本文中,作者将教神经网络学习基于一张图片和一个设计模板来编写一个HTML和CSS网站。以下是该过程的简要概述:
1)向训练过的神经网络输入一个设计图
2)神经网络将图片转化为HTML标记语言
3)渲染输出
我们将分三步从易到难构建三个不同的模型,首先,我们构建最简单地版本来掌握移动部件。第二个版本HTML专注于自动化所有步骤,并简要解释神经网络层。在最后一个版本Bootstrap中,我们将创建一个模型来思考和探索LSTM层。
代码地址:
所有FloydHubnotebook都在floydhub目录中,本地notebook在local目录中。
本文中的模型构建基于Beltramelli的论文《pix2code:GeneratingCodefromaGraphicalUserInterfaceScreenshot》和JasonBrownlee的图像描述生成教程,并使用Python和Keras完成。
核心逻辑
我们的目标是构建一个神经网络,能够生成与截图对应的HTML/CSS标记语言。
训练神经网络时,你先提供几个截图和对应的HTML代码。网络通过逐个预测所有匹配的HTML标记语言来学习。预测下一个标记语言的标签时,网络接收到截图和之前所有正确的标记。
创建逐词预测的模型是现在最常用的方法,也是本教程使用的方法。
注意:每次预测时,神经网络接收的是同样的截图。也就是说如果网络需要预测20个单词,它就会得到20次同样的设计截图。现在,不用管神经网络的工作原理,只需要专注于神经网络的输入和输出。
我们先来看前面的标记(markup)。假如我们训练神经网络的目的是预测句子「Icancode」。当网络接收「I」时,预测「can」。下一次时,网络接收「Ican」,预测「code」。它接收所有之前单词,但只预测下一个单词。
神经网络根据数据创建特征。神经网络构建特征以连接输入数据和输出数据。它必须创建表征来理解每个截图的内容和它所需要预测的HTML语法,这些都是为预测下一个标记构建知识。把训练好的模型应用到真实世界中和模型训练过程差不多。
我们无需输入正确的HTML标记,网络会接收它目前生成的标记,然后预测下一个标记。预测从「起始标签」(starttag)开始,到「结束标签」(endtag)终止,或者达到最大限制时终止。
HelloWorld版
现在让我们构建HelloWorld版实现。我们将馈送一张带有「HelloWorld!」字样的截屏到神经网络中,并训练它生成对应的标记语言。
首先,神经网络将原型设计转换为一组像素值。且每一个像素点有RGB三个通道,每个通道的值都在0-255之间。
为了以神经网络能理解的方式表征这些标记,我使用了one-hot编码。因此句子「Icancode」可以映射为以下形式。
在上图中,我们的编码包含了开始和结束的标签。这些标签能为神经网络提供开始预测和结束预测的位置信息。以下是这些标签的各种组合以及对应one-hot编码的情况。
我们会使每个单词在每一轮训练中改变位置,因此这允许模型学习序列而不是记忆词的位置。在下图中有四个预测,每一行是一个预测。且左边代表RGB三色通道和之前的词,右边代表预测结果和红色的结束标签。
在HelloWorld版本中,我们使用三个符号「start」、「HelloWorld」和「end」。字符级的模型要求更小的词汇表和受限的神经网络,而单词级的符号在这里可能有更好的性能。
以下是执行预测的代码:
输出
我走过的坑:
在FloydHub上运行代码
FloydHub是一个深度学习训练平台,我自从开始学习深度学习时就对它有所了解,我也常用它训练和管理深度学习试验。我们能安装它并在10分钟内运行第一个模型,它是在云GPU上训练模型最好的选择。若果读者没用过FloydHub,可以花10分钟左右安装并了解。
复制Repo:
在FloydHub云GPU机器上运行Jupyternotebook:
所有的notebook都放在floydbub目录下。一旦我们开始运行模型,那么在floydhub/Helloworld/helloworld.ipynb下可以找到第一个Notebook。更多详情请查看本项目早期的flags。
HTML版本
概览
如果我们将前面的架构扩展为以下右图展示的结构,那么它就能更高效地处理识别与转换过程。
该架构主要有两个部,即编码器与解码器。编码器是我们创建图像特征和前面标记特征(markupfeatures)的部分。特征是网络创建原型设计和标记语言之间联系的构建块。在编码器的末尾,我们将图像特征传递给前面标记的每一个单词。随后解码器将结合原型设计特征和标记特征以创建下一个标签的特征,这一个特征可以通过全连接层预测下一个标签。
设计原型的特征
因为我们需要为每个单词插入一个截屏,这将会成为训练神经网络的瓶颈。因此我们抽取生成标记语言所需要的信息来替代直接使用图像。这些抽取的信息将通过预训练的CNN编码到图像特征中,且我们将使用分类层之前的层级输出以抽取特征。
我们最终得到1536个8*8的特征图,虽然我们很难直观地理解它,但神经网络能够从这些特征中抽取元素的对象和位置。
标记特征
在HelloWorld版本中,我们使用one-hot编码以表征标记。而在该版本中,我们将使用词嵌入表征输入并使用one-hot编码表示输出。我们构建每个句子的方式保持不变,但我们映射每个符号的方式将会变化。one-hot编码将每一个词视为独立的单元,而词嵌入会将输入数据表征为一个实数列表,这些实数表示标记标签之间的关系。
上面词嵌入的维度为8,但一般词嵌入的维度会根据词汇表的大小在50到500间变动。以上每个单词的八个数值就类似于神经网络中的权重,它们倾向于刻画单词之间的联系(Mikolovaltel.,2013)。这就是我们开始部署标记特征(markupfeatures)的方式,而这些神经网络训练的特征会将输入数据和输出数据联系起来。
编码器
我们现在将词嵌入馈送到LSTM中,并期望能返回一系列的标记特征。这些标记特征随后会馈送到一个TimeDistributed密集层,该层级可以视为有多个输入和输出的全连接层。
和嵌入与LSTM层相平行的还有另外一个处理过程,其中图像特征首先会展开成一个向量,然后再馈送到一个全连接层而抽取出高级特征。这些图像特征随后会与标记特征相级联而作为编码器的输出。
如下图所示,现在我们将词嵌入投入到LSTM层中,所有的语句都会用零填充以获得相同的向量长度。
为了混合信号并寻找高级模式,我们运用了一个TimeDistributed密集层以抽取标记特征。TimeDistributed密集层和一般的全连接层非常相似,且它有多个输入与输出。
图像特征
对于另一个平行的过程,我们需要将图像的所有像素值展开成一个向量,因此信息不会被改变,它们只会用来识别。
如上,我们会通过全连接层混合信号并抽取更高级的概念。因为我们并不只是处理一个输入值,因此使用一般的全连接层就行了。
级联图像特征和标记特征
所有的语句都被填充以创建三个标记特征。因为我们已经预处理了图像特征,所以我们能为每一个标记特征添加图像特征。
如上,在复制图像特征到对应的标记特征后,我们得到了新的图像-标记特征(image-markupfeatures),这就是我们馈送到解码器的输入值。
解码器
现在,我们使用图像-标记特征来预测下一个标签。
在下面的案例中,我们使用三个图像-标签特征对来输出下一个标签特征。注意LSTM层不应该返回一个长度等于输入序列的向量,而只需要预测预测一个特征。在我们的案例中,这个特征将预测下一个标签,它包含了最后预测的信息。
最后的预测
密集层会像传统前馈网络那样工作,它将下一个标签特征中的512个值与最后的四个预测连接起来,即我们在词汇表所拥有的四个单词:start、hello、world和end。密集层最后采用的softmax函数会为四个类别产生一个概率分布,例如[0.1,0.1,0.1,0.7]将预测第四个词为下一个标签。
训练不同轮数所生成网站的地址:
Bootstrap版本
我们将使用这一版本为之前未见过的截图生成标记。我们还深入研究它如何构建截图和标记的先验知识。
我们稍微修改一下pix2code论文中的模型,使之预测网络组件的准确率达到97%。
端到端方法
从预训练模型中提取特征在图像描述生成模型中效果很好。但是几次实验后,我发现pix2code的端到端方法效果更好。在我们的模型中,我们用轻量级卷积神经网络替换预训练图像特征。我们不使用最大池化来增加信息密度,而是增加步幅。这可以保持前端元素的位置和颜色。
LSTM适合时序数据的输入,它是一种适合顺序信息的神经网络。模型展开图示如下,对于每个循环步,你需要保持同样的权重。
理解LSTM层级中的单元
每一层LSTM单元的总数决定了它记忆的能力,同样也对应于每一个输出特征的维度大小。LSTM层级中的每一个单元将学习如何追踪句法的不同方面。以下是一个LSTM单元追踪标签行信息的可视化,它是我们用来训练bootstrap模型的简单标记语言。
测试准确率
找到一种测量准确率的优秀方法非常棘手。比如一个词一个词地对比,如果你的预测中有一个词不对照,准确率可能就是0。如果你把百分百对照的单词移除一个,最终的准确率可能是99/100。
我使用的是BLEU分值,它在机器翻译和图像描述模型实践上都是最好的。它把句子分解成4个n-gram,从1-4个单词的序列。在下面的预测中,「cat」应该是「code」。
为了得到最终的分值,每个的分值需要乘以25%,(4/5)×0.25+(2/4)×0.25+(1/3)×0.25+(0/2)×0.25=0.2+0.125+0.083+0=0.408。然后用总和乘以句子长度的惩罚函数。因为在我们的示例中,长度是正确的,所以它就直接是我们的最终得分。
你可以增加n-gram的数量,4个n-gram的模型是最为对应人类翻译的。我建议你阅读下面的代码:
样本输出的链接:
下一步
前端开发是深度学习应用的理想空间。数据容易生成,并且当前深度学习算法可以映射绝大部分逻辑。一个最让人激动的领域是注意力机制在LSTM上的应用。这不仅会提升精确度,还可以使我们可视化CNN在生成标记时所聚焦的地方。注意力同样是标记、可定义模板、脚本和最终端之间通信的关键。注意力层要追踪变量,使网络可以在编程语言之间保持通信。