作者:Emil Wallner
编译:Bot
Bootstrap版
这一版我用了pix2code论文提供的Bootstrap web数据集。Bootstrap是twitter推出的基于HTML、CSS、JavaScript的一个热门前端框架,根据官网导引显示,我们可以用它把HTML和CSS代码结合起来,同时缩小词库。
我们这次的目标是让神经网络为之前没见过的网页截图生成标签,因此我会深入介绍如何构建标签和图像的连接。
不同于直接在bootstrap标签上训练,这里我们使用的方法是设置17个经简化的token,然后把它们转成HTML、CSS代码。pix2code数据集包含1500个测试截图和250个验证截图,每个截图平均有65个token,所以我们共有96925个训练样本。
把论文模型做进一步微调后,它的预测准确率提高到了97%(BLEU 4-ngram贪婪搜索,后文有介绍)。
一种端到端的方法
之前,我们在代码中曾提到过用一个预训练的VGG16模型从图像中提取特征,它在图像描述生成模型上跑得不错,但经过几次测试后,我发现pix2code的端到端方法可能会更好用一些。因为预训练的模型并没有在web截图上训练过,而且它的主要用处是分类。
所以在这个版本中,我们用一个轻量级CNN代替预训练图像特征步骤,同时不用最大化池层增加信息密度,而是增加步长来保持前端设计元素的位置和颜色。
要实现上图流程,我们要用到两种核心模型:CNN和RNN。关于CNN的具体内容,之前我已经在“如何着色”一文中有过详细介绍,此处不再详谈。而对于RNN,当前它最流行的一种形式就是LSTM,这也是我在下文中将介绍的重点。
LSTM中的时间步长
LSTM本身不难理解,如果说有什么难点,那么时间步长应该是其中之一。一般来说,我们认为单隐层神经网络共有2个时间步长,输入“Hello”,它输出预测“World”。如果一个网络有多个步长,那它的预测难度会增加。如下图所示,它的输入要经过4个时间步长,每一步生成一个单词。
LSTM就是这样一个允许输入带上时间步长的RNN,也就是说,它会按顺序提取特征信息。下图是我们模型的展开图,可以发现它的权值始终保持不变。LSTM的权值是共享、复用的,你对之前的输出用了这个权值,那么下一个新输入也会继续用这个数字。
加权的输入和输出进入LSTM后,经激活函数组合、过滤,最后成为该时间步长的输出。因为权值被重复使用了,所以它们能从多个输入中提取信息,并以此建立知识序列。
以下是LSTM每个时间步长的简要图示:
为了感受其中的逻辑,我在此建议有需要的读者跟着Andrew Trask的RNN教程试一试,自己构建一个神经网络。
LSTM层中的单元
此外,LSTM的每个单元都有一个被称为单元状态(cell state)的东西,如果把单元想象成神经细胞,那么单元状态就是储存其中的记忆。我们通过权值和激活函数以不同方式改变单元状态,让LSTM层“理解”什么信息是该保留的、什么是该丢弃的。
因此,除了根据输入预测输出,每一个单元的状态也会随时间步长发生改变。如果你想更详细地了解其中的作用机制,我推荐Colah的教程、Jayasiri的Numpy实现以及Karphay的讲座和文章。
测试模型准确率
训练完模型后,我们要找一个合适的方法测试它的准确率,但这其实不太容易。如果我们逐词对比,那么即便只有1个词不合语法,模型的准确率都可能会变成0%;如果模型每预测对一个词我们就把它去掉,那最后的结果可能是99/100。
我在这里引入了一种机器翻译自动评估算法——BLEU。BLEU是IBM于2002年提出的代替人工评分的工具,它能衡量机器翻译、图像字幕和参考文之间的差距,BLEU评分越高,模型性能越好。它的评分方式是比较输入文和参考文中共现的词的个数,以之前的“I can code”为例,它把这个句子分解成4元语法(4 n-gram),如下图所示,“cat”应该是“code”:
现在我们计算它的BLEU评分,已知n-gram概率之和为1,我们先给每个准确率都乘上0.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。理论上这个和要经过正则化才是正确的BLEU评分,但因为示例句中没有重复单词,且句长为3,用0.25没问题,所以0.408就是最终值。
你也可以把它分解成更多的元,但在这个例子中,4元语法模型是最贴近人类翻译的模型。你可以用下面这段代码多试几个句子,掌握其中的原理:
输出
上图网站的链接:
Generated website 1 - Original 1
(https://emilwallner.github.io/bootstrap/real_1/)
Generated website 2 - Original 2
(https://emilwallner.github.io/bootstrap/pred_2/)
Generated website 3 - Original 3
(https://emilwallner.github.io/bootstrap/pred_3/)
Generated website 4 - Original 4
(https://emilwallner.github.io/bootstrap/pred_4/)
Generated website 5 - Original 5
(https://emilwallner.github.io/bootstrap/pred_5/)
我得到的经验
了解模型的弱点,而不要测试随机模型。一开始我用了很随机的东西,比如批标准化(batch normalization)、双向网络(bidirectional networks),甚至尝试了注意力机制。但测试结果非常消极,模型预测的颜色和位置都不太准确。后来我意识到这是CNN有缺陷,所以把最大池化层换成了增加步长,之后模型的验证loss一下从0.12降到了0.02,准确率也从85%提高到了97%;
只有在相关的情况下才使用预先训练好的模型。因为数据集比较小,所以我觉得事先训练一个提取图像特征的模型可以提升整体性能。但从我的实验结果来看,端到端的模型虽然训练速度慢,占用内存高,但是精度会比预训练的高30%;
在远程主机上运行模型时,可能会出现意外。我之前用Mac做这个项目时,模型是按字母顺序读取文件的。但把它搬到远程主机上后,它开始随机读取文件了,截图和代码不匹配,虽然最后模型还是收敛的,但它的验证loss比修正后的值差了50%;
确保自己已经熟练掌握库的函数。词库里如果出现空白字符,规则要求是要打空格的,但是我忘了,它就没有token,也始终没能预测到“单一”的标签;
尽量用轻量级神经网络。用GRU代替LSTM可以减少30%的遍历周期,而且还不影响性能。
原文地址:blog.floydhub.com/Turning-design-mockups-into-code-with-deep-learning/
领取专属 10元无门槛券
私享最新 技术干货