UI2CODE系列文章|如何批量制造高质量样本
在 UI2CODE 項目中,我們大量使用了深度學習方法來做一些物體檢測。而深度學習模型的訓練,避免不了需要大量的樣本,因此如何制造大量樣本,來滿足模型訓練需要是我們必須要解決的一個問題。在這篇文章中,我們將介紹我們如何利用工具,批量泛化出大量樣本,為模型訓練提供數據保障。
1.樣本現狀
我們的模型要解決的問題是在一個設計稿圖片上識別出基礎控件等信息,包括位置和類別。而它所需要的樣本,主要存在兩個問題:
2.樣本獲取途徑
獲取樣本,主要有幾種途徑。
對于真實樣本,這類質量是最高的,要想訓練出效果很好的模型,這類樣本基本是必不可少的,但是由于這類樣本數量少,成本高,因此還需要其他方法來補充樣本量。
對于數據增廣,這種方法簡單快速,但是效果也有限,特別是對于我們 UI2CODE 里識別控件這個任務來說,做旋轉等操作基本是無效的。
因此,我們需要利用樣本Mock,來擴充我們的數據量,盡量模擬出質量又多,量又大的樣本。這里我們選擇的是利用Weex頁面來進行樣本的Mock泛化。(當然還有一些其它方法,比如利用 Android 的特性,在運行時的APP頁面,抓取頁面數據,經過過濾和清洗,得到帶標注的樣本,這里不做展開)
3.WEEX頁面樣本泛化
在這里,我們介紹如何利用 Weex 頁面,來批量泛化樣本,并且得到樣本標注的方法。
前端頁面特點
之所以選擇使用前端頁面來生成樣本,是因為前端頁面更多的是做一些數據展示,并且其擁有完整的 DOM 樹,只要我們拿著DOM樹就可以解析出里面的各個元素。
對于節點內容,只要我們改變元素內容即可。這樣我們就可以由一個前端頁面很方便地泛化出不同文字、不同圖片的多個樣本。
當然,我們的閑魚APP上有大量的Weex活動頁,這也是我們選擇做Weex頁面泛化的原因之一。
泛化思路
我們需要的基礎控件的分類有“文本”、“圖片”、“Shape”這三類,對于一個頁面來說,我們的文本和圖片內容基本都是可替換的,因此我們解析出所有節點以后,對里面的文本和圖片進行替換,再進行渲染就可以得到新的樣本。
利用 Puppeteer 實現泛化
要想得到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 時 pushif (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份,大大提高了樣本量。
5. 總結
通過Weex泛化樣本的方法,我們由100多個Weex活動頁,泛化出10000+個樣本,并且無需手動打標,節省了大量的打標成本。且由于樣本質量相對較高,模型的準確率得到了很大的提升。當然,我們也探索了很多其它方法,包括抓取Android運行時的頁面數據來生成自動打標的數據,以及利用已訓練模型自動預打標來節省手動打標的人力成本等,未來我們還會繼續探索更多的樣本生成及自動打標方法,為模型訓練提供更多有用數據。
原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。
總結
以上是生活随笔為你收集整理的UI2CODE系列文章|如何批量制造高质量样本的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 6万人同时离场,竟然一点都不挤?原来用了
- 下一篇: 编码规范 | Java函数优雅之道(下)