文/阿里淘系 苏川
淘宝天猫业务产品很多,每年还会有很多大促活动,产品页面心智不同导致 UI 视觉多样、业务性质不同导致业务逻辑不同,大量的 UI 难以复用,需要投入大量的前端人力来开发,前端同学的业务压力非常大。为了解决这种困境,我们研发了自动生成代码平台 imgcook,帮助提升前端研发效率。
在 imgcook 中我们逐步引入了 CV、NLP 等 AI 技术来辅助识别设计稿信息智能生成可读性和可维护性较高的代码,并在双 11 大促中自动生成了 79.34% 的前端代码,助力前端研发效率提升 68%。智能生成代码平台 imgcook.com 在 2019 年已经正式对外开放,服务于 25000+ 用户。
本次分享将为大家介绍设计稿生成代码平台 imgcook 的核心思想以及AI技术在 imgcook 中的应用场景和落地实践。
分享的内容主要是我们用机器学习技术可以解决哪些设计稿生成代码的问题,以及怎么用机器学习技术来解决这些问题。期望能站在前端的视角下,讲清楚用机器学习解决代码生成问题的整个实践过程。
设计稿生成代码平台 imgcook 介绍
imgcook 是一个设计稿智能生成代码平台,可以将 Sketch、PSD、Figma、图片等类型的设计稿一键生成可维护的前端代码,例如 React、Vue、小程序等。
imgcook 到目前为止已经经历了3个阶段,在 2019 年 1 月我们正式对外开放了 imgcook 1.0 版本,后来经过 1 年多的发展,也就是 2.0 阶段,逐步引入了计算机视觉、机器学习技术来帮助我们解决设计稿生成代码中遇到的一些的问题,产品链路也已经基本完善。到了 3.0 阶段机器学习技术在 imgcook 中已经落地应用于多个场景了。
在这些去年双11活动页面的模块中,有 90.4% 的新增模块使用了 imgcook 3.0 来生成代码,生成代码的可用率达 79%,帮助前端编码效率提升了 68%。
那 imgcook 是怎么生成代码的呢?
我们可以从设计稿文件中提取到描述这个设计稿文件的 JSON Schema,如果大家手上有 Sketch 文件的话,可以用 unzip 命令解压这个 Sketch 文件,就能看到这个JSON Schema。
拿到这个 JSON Schema 之后呢,我们可以使用设计稿协议、程序算法和模型算法来计算、分析和识别,将这个JSON Schema 转换成一个具有合理的嵌套布局结构和代码语义的 D2C JSON Schema。
如果大家用过 imgcook 的话,可以在 imgcook 编辑器中看到这个 D2C schema,然后我们再通过各种不同的DSL 转换函数将 D2C Schema 转换成不同类型前端代码,比如 React、Vue 等等
(imgcook 3.0 技术原理)
这是 imgcook 生成代码的一个大致流程,其中程序算法是指通过有限的规则计算来自动的识别设计稿,例如我们可以根据每个图层的坐标、宽高等信息来计算出它是不是一个循环结构,可以根据字体的大小和字数来判断这个文本是个标题还是提示信息。
但是这种判断的规则是有限的,没办法去解决千千万万的设计稿中的问题,而通过机器学习模型来智能识别能够解决一部分程序算法无法覆盖的问题,对于程序算法和模型算法目前都还不能解决的问题,我们也提供了人工标记设计稿图层的方式来解决。
机器学习技术在 D2C 领域的应用场景
那用机器学习模型来识别设计稿能解决哪些问题呢?在介绍机器学习能解决哪些代码生成的问题之前,我们先了解下机器学习能做什么,它能解决哪些类型的问题。
机器学习解决的问题类型
我们今天分享的主题是 AI 在 imgcook 中的应用实践,AI 就是人工智能的缩写,人工智能可以为机器提供适应能力和推理能力,让机器代替人来思考和工作,可以帮助我们降低人力成本。
而机器学习是实现人工智能的一种技术,可以给机器学习的算法输入大量的样本,算法学习到这些样本特征之后,可以去预测相似样本有什么特征。
机器学习的过程与人类学习的过程是很相似的,经过学习总结得到知识和经验,当有类似的任务时可以根据已有的经验做出决定或行动。
有一点区别是人类的大脑只需要非常少的一些资料就可以归纳总结出适用性非常强的知识或者经验,例如我们只要见过几只猫或几只狗就能正确的分辨出猫和狗,但对于机器来说它需要大量的学习资料。
深度学习是机器学习的分支,深度学习与传统机器学习算法的主要不同在于对特征处理的方式。这里就不细讲了。在 imgcook 中用到的多数是深度学习算法,也有传统的机器学习算法,后面我们在讲的时候就统一叫机器学习算法了。
了解了这些概念之后,我们再看下机器学习能够解决的什么类型的问题。这里列举了在计算机视觉领域处理图像的 4 大基本任务,和 NLP 领域处理文字的 4 大基本任务。
对于图像的识别,给定一张图像,如果只是想识别这个物体是什么,就是一个图像分类任务,如果是要识别图像中的目标物体,还要知道这个物体的位置,就是一个目标检测的任务,对图像更细粒度的识别还有语义分割、实体分割。
文本识别也是一样的,比如这里的知乎问题,如果想要识别这段文本是一个问题标题还是问题描述,这是一个文本分类的问题,还有像从一段文本中识别出哪些是地名哪些是人名的序列标注任务、智能客服问答类的这种句子关系判断的任务,还有像机器翻译这种文本生成式任务。
那在 imgcook 中对页面中 UI 信息的识别问题,一般都会定义成图像分类、目标检测和文本分类任务。
imgcook 中 UI 语义识别
设计稿中是有很多不同维度的 UI 信息的,比如这份设计稿中有这些图标,我们可以训练一个图标识别模型来识别页面中有哪些图标,比如这里的页面返回图标、三个 … 表示更多的图标
页面中还有图片,可以通过图片识别来获取图片的语义,还有文本识别、组件识别、模块识别等等,这些都可以通过训练一个机器学习模型来识别
那这些识别的结果可以用来做什么呢?大家可以想一下,我们手写代码时会写什么?
模型识别结果的应用场景
这些模型识别的结果可以用于给类名一个易于理解的名称、或者自动生成字段绑定代码,如果识别到是一个按钮组件,可以生成无障碍属性代码,或者在生成代码时自动引入外部组件,如果识别出是一个模块,可以生成模块化代码等等。
前端视角下,D2C 领域机器学习实践落地
要用机器学习模型来识别这些UI信息并应用到代码生成的链路,具体要怎么做呢?
使用机器学习技术解决问题的一般步骤
一般用机器学习解决问题的步骤是这样的。
先要确定我们要解决的业务问题是什么,然后要确定这个问题适不适合使用机器学习来解决,并不是所有的问题都适合使用机器学习来解决的,比如如果用程序算法就可以解决 90% 以上的问题,并且准确度很高,那就没必要使用机器学习。
如果确定了使用机器学习,我们还要确定这个问题是属于什么类型的机器学习任务,比如是一个图像分类的任务还是目标检测的任务,确定了任务类型之后,再开始准备样本,因为不同的任务类型,模型训练的数据集要求是不一样的。
训练数据准备好之后,还要再确定用哪一种机器学习算法,训练数据有了、算法也确定了,就可以给算法输入大量的训练数据来学习样本特征,也就是训练模型,得到模型之后,再部署模型服务,最后在工程项目中调用模型服务来识别UI信息。
这就是我今天要重点要分享的,用机器学习来解决业务问题实践过程。
这里我不会去讲怎么去写机器学习算法、也不会详细的去讲机器学习算法的原理,还有训练模型时调参怎么调这些。我是一个前端工程师哈,我比较关注的是怎么用机器学习技术解决我的问题,我要给大家分享的也是这个用机器学习技术解决代码生成问题的实践过程,以及实践过程中的一些经验。
这里以代码生成过程中需要解决引入外部组件的问题为例来详细的介绍一下机器学习实践落地的过程。
组件识别的问题定义
比如这样一个页面,里面有 input 输入框、button 按钮,现在想要用 Ant Design 组件来实现,如果人工手写,需要先引入 Ant Design 组件库,并使用 Antd 中的 Input 组件和 Button 组件。
那现在要自动生成的话,我们从设计稿中提取到的都是矩形框、文本、图片这些信息,并不知道哪些是组件,所以需要识别出页面中的组件并替换成引入的 Antd 组件。
所以要做两件事,组件识别和代码表达。
我们现在要识别出这个页面中有哪些组件,如果机器学习模型的输入是这一张页面,模型需要识别出页面中有哪些组件,组件位置的位置在哪里,是什么类别的组件,那这在机器学习中就是一个目标检测的任务。
但我们是可以得到这张页面的 D2C JSON 描述信息的,每个节点的位置信息都是知道的,比如这个按钮的根节点,在D2C JSON描述中有这样一个描述位置尺寸的 rect 属性,那既然每个节点的位置都是知道的,我只需要知道这个节点它是不是组件,是什么类型的组件就可以了。
那这个在机器学习中,就是一个图像分类的任务,我们给模型输入这些节点的截图,模型识别后告诉我这是什么类型的组件就好了。
样本准备
确定了是一个图像分类任务之后,就可以准备模型训练的样本了。样本数据的准备是整个阶段最费时费力的事情,即需要收集大量的样本,还需要给样本标注类型。
因为我们让机器学习算法学习样本特征时,除了要给算法一张样本的图片,还需要告诉算法,这张图片是什么,这样算法就能学习到,哦,这种类型的样本都具有这些特征,下次再有相似特征的样本,模型就能识别这个相似的样本是什么类型了。
在收集样本之前,需要确定模型需要识别哪些类别的组件,如果你给算法学习的样本只有 button、searchbar、stepper、input、switch、tabbar 这几个类别,那训练出来的模型也只能识别这几个类型的组件,如果你模型给一个进度条的组件截图让它识别,这个模型肯定不能识别出来这是个进度条的,它只会告诉这个进度条属于这几个类别的概率是多少。
这里我们确定了模型需要识别的 6 种类型,然后我们就可以开始收集样本了,每一个样本包括一张组件的图像以及这个组件图像的类别。
收集页面中的组件图像
收集这些基础组件的图像,最直接的方式就是从线上页面或者设计稿中提取组件的截图。比如这里先收集了这些页面,然后通过一些工具或者脚本辅助把这些页面中的组件截图保存下来。
对这些组件的截图,还需要做组件的类型标注,然后以这样的文件格式存储。同一种类型的组件图像放在同一个文件夹下,文件夹的名称就是组件的类型。然后压缩成 zip 文件,这就是我们需要的组件图像分类的数据集了。
我是从我们阿里内部 2 万多份设计稿中提取的页面,然后人工把这些组件从页面中框选提取出来,当时记录了一下,从 1 万 4 千多张页面中提取 2 个类别的组件截图花了完整的 11 个小时,这只是 2 个类别组件的提取,前面还包括从 2 万多份 Sketch 文件中提取页面图片、过滤、去重的时间,整个收集样本的过程很艰辛。
而且,页面中一般按钮 button 比较多,但其他类型的组件就很少,比如开关 switch、步进器 stepper , 这样就会导致样本不均衡。我们输入给算法学习的每个分类的样本是需要差不多的数量的。
样本标注耗时:14000页面 * 2个类别 == 11 个小时 样本不均衡: 25647张页面 == 7757个button + 1177个 switch + …
使用 puppeteer 自动生成组件样本
对于这样的问题,我们可以用我们前端比较熟悉的技能来解决,就是使用无头浏览器 puppeteer 来自动生成。
具体是这样的,我们可以写一个页面,每刷新一次这个页面就会渲染出不同类型不同样式的组件。组件是随机生成的,在实现组件样式时做了 padding、margin、文字内容文字大小等的随机生成,唯一确定的是要给每个组件加一个类型标记,比如如果是 stepper 组件,在这按钮的节点上加一个 class 是 element-stepper 的类名。
这样我们再用无头浏览器 puppeteer 自动访问这个页面,每访问一次,从这个页面中根据我们设置好的类名获取所有组件节点类型,并截图保存。最后就得到了我们需要的数据集了。
有同学可能会说,那我直接这样自动生成不就好了,要多少就可以生成多少。
也不是的,这种方式虽然可以节省很多精力,但是毕竟还是基于有限的规则生成的,样本特征不够发散,与真实的样本视觉特征还是有一定差异的。
如果输入给算法学习的样本多数是造出来的跟真实样本有一定差异的图像,那模型学习到的都是人造样本的特征,在用模型识别真实样本时准确度也会比较差。
所以一般自动生成的样本只是用作真实样本比较少的一个补充。
算法选型
现在数据集已经准备好了,下一步需要确定选择哪一种机器学习算法来学习这些样本的特征。
因为这是一个图像分类的任务,我们可以搜集图像分类算法的发展历史,看下有哪些经典的算法。
(图像分类经典算法)
然后从这些算法中选取业界使用比较多的几个算法从模型精确度和预测耗时来那个方面做个比较。对于组件识别,组件图像的复杂度相对较高,对识别的准确度要求也比较高,在组件识别中最后选择 Resnet50。
像图标识别模型,因为图标相对比较简单,识别的结果基本都是用于给 classname 命名,所以图标分类算法选择的是 MobileNetV2。
(几种图像分类算法的模型效果比较)
模型训练
现在我们已经确定了使用的算法,下一步就是训练模型了。
模型训练的工作流一般是这样的,我们需要先下载数据集,然后可能需要做数据集格式转换,然后做数据集处理,比如将准备好的数据集按一定比例拆分成训练集和测试集,然后下一步引入算法定义模型,然后开始用训练集训练模型,模型训练完成后,用测试集做模型准确度评估。这样模型训练的流程就结束了,最后将训练输出的模型部署到远程机器。
如果是机器学习工程师,对机器学习和 python 比较熟悉,会自己手写这个模型训练工作流的脚本来训练模型。
对于前端同学,我们可以使用前端算法工程框架 PipCook 来训练模型,我们可以这样来理解 PipCook,Nodejs 可以让前端工程师做服务端的事情,PipCook 也可以让前端工程师做机器学习任务。
PipCook 将这些过程做了封装和集成,用一个 JSON 文件来表示,训练工作流中的每个阶段都用一个插件来定义,支持配置每个阶段的参数。我们只需要将数据收集这个插件的 url 参数的输入值替换成自己的数据集链接即可。
然后执行 pipcook run pipline.json 就会开始训练模型了。
训练流程结束后会生成一个 model 文件,PipCook 将文件封装成了一个 npm 包,我们可以直接在本地写一个 js 脚本测试一下,预测的结果是这张测试样本属于每一个分类的概率。我们可以设置一个阈值,当概率大于这个阈值时,则认为识别的结果是可靠的。
模型服务部署
如果要在工程项目中应用模型识别服务,还需要将模型部署到远程机器上。
部署的方式有多种,例如我们可以将模型部署到阿里云 EAS 上,阿里云控制台选择模型在线服务,上传模型文件部署,部署完成后,就可以用虚拟机所在地区和填写的模型名称构造出访问模型服务的 API。
模型应用
也就是说模型部署之后,可以得到这样一个可访问的 RESTful API。现在我们就可以在 D2C 的工程链路中调用这个 API 来识别组件了,也就是最后的模型应用阶段。
模型的应用链路在不同的应用场景都会不一样,在 D2C 组件识别这个场景,我们是在经过布局算法生成了具有比较合理的嵌套布局结构的 D2C Schema 之后,根据 div 容器节点的位置将这些页面内部截图裁剪出来,然后调用组件识别模型服务来识别,识别的结果再更新到 D2C Schema 中对应的节点的 smart 字段上。
那这样模型应用是不是结束了?还没有啊,这里只是完成了组件识别的过程,前面我们讲过,要做两件事,一个是识别组件,一个是表达成代码。我们还要将识别的结果表达成代码。
imgcook 支持用户自定义 DSL 转换函数,将 D2C Schema 转换成不同类型的前端代码。大致的逻辑是递归遍历 D2C schema 中的节点,判断节点类型,转换成对应的标签。
如果在转换过程中还需要将组件识别的结果转换成引入外部组件的话,还需要给DSL函数输入用户录入的组件,根据组件的类型和模型识别的结果,在生成代码时引入用户录入的外部组件。
实践过程回顾
到这里,用机器学习来解决引入外部组件的问题实践过程就介绍完了。
我们可以回顾下,为了解决在生成代码时对于引入外部组件的场景也能自动生成的问题,我们选择训练一个机器学习图像分类模型来识别页面中的组件。
首先需要准备样本,我们可以收集真实的页面或设计稿,从中提取组件并进行样本标注,生成数据集,也可以用pupeteer 自动生成,准备好数据集后,根据对模型识别准确率和识别速度的权衡,选择了 Resnet 算法。
然后用前端机器学习算法框架 PipCook 来训练模型,PipCook 为我们封装好了载入数据集,数据处理、定义模型、训练模型、模型评估一整条 pipline,我们只要执行一条 pipcook run 就可以开始训练了。
训练完成后会得到一个模型文件,把这个模型文件部署到阿里云 EAS,可以得到一个可访问的模型识别服务 Restful API,然后在代码生成链路中调用此 API 识别 UI 界面中的组件,组件识别结果最后用于生成代码。
这里提供了几个 step by step 的实践案例,第 1 个是用 pipcook 训练,在 colab 中执行,不用考虑环境问题,直接点点点就好了。 第 2 个是用 pipcook 训练在你的电脑上训练,需要在你的电脑上安装环境。第 3 个是一个用 python 实现的机器学习任务,你也可以看下用 python 是怎么实现数据集载入、模型定义和模型训练这个流程的。
这几个实践任务,都提供了代码和数据集。如果大家感兴趣的话,可以根据这个案例体验一下今天介绍的这个机器学习实践过程。
D2C 双 11 零研发工程实践
刚才我们以组件识别为例,介绍了整个机器学习实践和应用的过程。除了组件识别,还有文本识别、图标识别、等,实践方法基本都是这个步骤。
这些模型识别结果不仅可以应用于 UI 代码的生成,还可以应用于业务逻辑代码的生成,在双 11 页面中有一些业务逻辑代码不是很复杂的模块,生成代码可用率可以达到 90% 以上,基本上能做到 0 研发投入。
比如这样一个双 11 活动页面中的模块,如果我们人工手写代码的话,会涉及哪些业务逻辑。
比如这里有个循环,我们只需要实现一个商品的UI然后用 map 循环,比如这里的商品标题、价格、商品图等需要绑定动态数据,还有点击商品要跳转、在用户点击时还要发送埋点日志。除了这些,好像也没有其他的业务逻辑了。
那这些业务逻辑我们是可以根据模型识别的结果来自动生成的,这里是一个 1 排 2 的循环,可以识别到这是一个商品标题,可以识别出这是一个商品图,这些识别的结果我们可以应用于字段绑定、页面跳转等业务逻辑代码的生成。
前面也介绍过,识别的结果都是挂在在每个节点的 smart 字段上,比如这里识别到这个文本节点内容是一个商品标题 itemTitle,如果要将这个识别的结果用于字段绑定的话,我们可以把有这个识别结果的 text 节点的内容替换成字段绑定的表达式,
那这个替换的操作在哪做呢?imgcook 支持用户录入自己的业务逻辑,每一条业务逻辑都有一个识别器和一个表达器。
D2C Schema 中的每个节点都会经过这些逻辑识别器看有没有业务逻辑点,如果有的话,根据对应的表达器中的逻辑表达。
例如商品标题字段绑定这个业务逻辑,设置的识别器就是判断文本节点的 smart 字段上 fieldbind 字段是不是itemTitle,如果是的话,就执行表达器中的逻辑,将此节点自动绑定上 itemTitle 字段。
这样我们就能根据用户自定义的业务逻辑,更新 D2C Schema,最后再将这个带有业务逻辑的 D2C Schema 用 DSL 转换函数转换成代码。
这就是模型识别的结果是应用于业务逻辑代码生成的过程,其他业务逻辑生成也是用业务逻辑库来承接模型识别结果然后应用于代码生成。
未来展望
未来我们一方面会用智能化技术继续提升生成代码的数量和质量,另一方面,在多状态UI、微动效等复杂 UI 等代码生成问题上会有一些新的探索,比如这里的发布试用报告按钮,它有多种状态,根据服务端返回的数据不同,展示不同的 UI,比如这个页面中点击进入按钮,有这种微动效。
另外,设计稿生成代码 D2C 的本质是对 “产品设计产物(高保真设计稿)”进行智能理解而高保真设计稿是经过产品设计周期,从产品经理基于一些方法和理论确定业务目标和页面结构,到视觉设计师结合产品特性、功能理解与视觉设计规范产出的。
我们期望能够构建的一套这样的 D2C 底层认知的理论体系,将产品设计和视觉设计方法融入 D2C 实践中,基于产品设计方法和视觉设计原则智能理解设计产物,可以帮助我们对设计稿的理解更全面深入,有助于生成数量更多质量更高的代码。
(D2C 实践理论体系)
结语
imgcook 是一个对外开放的平台,欢迎体验设计稿生成代码 imgcook。另外,如果大家对这次分享有所收获,也可以想一下,用机器学习能解决前端领域的哪些问题,比如智能设计、代码推荐、智能 UI、UI 自动化测试等等,如果大家感兴趣,也可以用今天介绍的这个实践流程,自己去写一个 Demo 项目体验一下。
未来,也希望有越来越多的同学能参与到前端智能化的建设中,用智能化技术解决更多前端领域的问题。