在 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運行時的頁面數據來生成自動打標的數據,以及利用已訓練模型自動預打標來節省手動打標的人力成本等,將來咱們還會繼續探索更多的樣本生成及自動打標方法,爲模型訓練提供更多有用數據。
本文爲雲棲社區原創內容,未經容許不得轉載。