在 UI2CODE 项目中,咱们大量使用了深度学习方法来作一些物体检测。而深度学习模型的训练,避免不了须要大量的样本,所以如何制造大量样本,来知足模型训练须要是咱们必需要解决的一个问题。在这篇文章中,咱们将介绍咱们如何利用工具,批量泛化出大量样本,为模型训练提供数据保障。前端
咱们的模型要解决的问题是在一个设计稿图片上识别出基础控件等信息,包括位置和类别。而它所须要的样本,主要存在两个问题:node
获取样本,主要有几种途径。web
对于真实样本,这类质量是最高的,要想训练出效果很好的模型,这类样本基本是必不可少的,可是因为这类样本数量少,成本高,所以还须要其余方法来补充样本量。浏览器
对于数据增广,这种方法简单快速,可是效果也有限,特别是对于咱们 UI2CODE 里识别控件这个任务来讲,作旋转等操做基本是无效的。weex
所以,咱们须要利用样本Mock,来扩充咱们的数据量,尽可能模拟出质量又多,量又大的样本。这里咱们选择的是利用Weex页面来进行样本的Mock泛化。(固然还有一些其它方法,好比利用 Android 的特性,在运行时的APP页面,抓取页面数据,通过过滤和清洗,获得带标注的样本,这里不作展开)less
在这里,咱们介绍如何利用 Weex 页面,来批量泛化样本,而且获得样本标注的方法。dom
之因此选择使用前端页面来生成样本,是由于前端页面更多的是作一些数据展现,而且其拥有完整的 DOM 树,只要咱们拿着DOM树就能够解析出里面的各个元素。工具
对于节点内容,只要咱们改变元素内容便可。这样咱们就能够由一个前端页面很方便地泛化出不一样文字、不一样图片的多个样本。布局
固然,咱们的闲鱼APP上有大量的Weex活动页,这也是咱们选择作Weex页面泛化的缘由之一。学习
咱们须要的基础控件的分类有“文本”、“图片”、“Shape”这三类,对于一个页面来讲,咱们的文本和图片内容基本都是可替换的,所以咱们解析出全部节点之后,对里面的文本和图片进行替换,再进行渲染就能够获得新的样本。
要想获得Weex页面,须要有一个渲染容器,而且咱们能够很方便地修改其内容。这里,咱们选择了Google的Puppeteer,它是Google推出的能够运行 Chrome Headless 环境以及对其进行操控的js接口套装。经过它,咱们能够模拟一个Chrome运行环境,而且进行操控。官方简介在这里.
首先启动一个不带界面的浏览器:
const browser = await puppeteer.launch({ headless: true });
启动一个页面,而后打开一个网站:
const page = await browser.newPage(); await page.goto(nowUrls, {waitUntil: ['load','domcontentloaded','networkidle0']});
模拟IPhone6环境:
await page.emulate({ 'name': 'iPhone 6', 'userAgent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1', 'viewport': { 'width': 750, 'height': 1334, 'deviceScaleFactor': 1, 'isMobile': true, 'hasTouch': true, 'isLandscape': false } });
搜索所需控件:
let d_root = document.querySelectorAll('.weex-root'); let nodes_root = []; collectChildren(d_root, nodes_root); /** * 遍历节点,搜集全部须要的控件 */ function collectChildren(d, _nodes) { for(var i = 0,l = d.length;i < l;i++){ let hasPushed = false; //nodeType === 1 时 push if (d[i].nodeType !== 1 && d[i].nodeType !== 3) { continue; } if(d[i].style){ let backgrounColorValue = d[i].style['background-color']; if(backgrounColorValue && backgrounColorValue !== 'rgb(255, 255, 255)' && backgrounColorValue !== 'rgb(0, 0, 0)' && backgrounColorValue !== 'transparent'){ _nodes.push(d[i]); hasPushed = true; } } if(d[i].hasChildNodes()){ collectChildren(d[i].childNodes, _nodes); }else{ let _node = d[i]; let _className = _node.className; if(!_className && _node.nodeName === '#text'){ _className = _node.parentNode.className; } if(_className && !hasPushed){ if(_className.indexOf('weex-text') > -1 || _className.indexOf('weex-image') > -1){ _nodes.push(d[i]); } } } } return _nodes; }
获取控件信息:
/** * 获取 基础视图元素的属性 */ function getRealyStyle(node,attrKey){ let wvStyle = window.getComputedStyle(node); if(node[attrKey] && node[attrKey] !== ''){ return node[attrKey]; }else{ return wvStyle[attrKey] } } /** * 获取 基础视图元素的位置 */ function getViewPosition(node){ const {top, left, bottom, right} = node.getBoundingClientRect(); return { "y": top, "x": left, "height": bottom-top, "width": right-left } }
获取页面图片:
await page.screenshot({ path: pngName, fullPage : true });
清理数据:
部分页面会存在弹窗的状况(mask图层),而咱们的标注规则是但愿只标注上面的图层,所以还须要根据mask图层的位置和大小,过滤掉底下图层里的控件。
经过上述方法,咱们就能获得各个文本、图片、Shape以及他们的位置和属性等。基于位置和控件类别信息,咱们就可以获得带有位置和类别标注的样本。
经过上面的方法,只要提供一个Weex页面的url,就能够获取到一个带有标注的真实样本,后面咱们只要修改里面文本和图片节点的内容,就能够批量泛化出多个样本。这些样本基于真实的页面布局,质量相对较高,而且能够随意控制泛化比例,好比设置 1:10,就能够有100分样本生成出10000份,大大提升了样本量。
经过Weex泛化样本的方法,咱们由100多个Weex活动页,泛化出10000+个样本,而且无需手动打标,节省了大量的打标成本。且因为样本质量相对较高,模型的准确率获得了很大的提高。固然,咱们也探索了不少其它方法,包括抓取Android运行时的页面数据来生成自动打标的数据,以及利用已训练模型自动预打标来节省手动打标的人力成本等,将来咱们还会继续探索更多的样本生成及自动打标方法,为模型训练提供更多有用数据。
原文连接 本文为云栖社区原创内容,未经容许不得转载。