UI2CODE系列文章|如何批量製造高質量樣本

在 UI2CODE 項目中,我們大量使用了深度學習方法來做一些物體檢測。而深度學習模型的訓練,避免不了需要大量的樣本,因此如何製造大量樣本,來滿足模型訓練需要是我們必須要解決的一個問題。在這篇文章中,我們將介紹我們如何利用工具,批量泛化出大量樣本,爲模型訓練提供數據保障。

1.樣本現狀

我們的模型要解決的問題是在一個設計稿圖片上識別出基礎控件等信息,包括位置和類別。而它所需要的樣本,主要存在兩個問題:

  1. 數據量少:一個APP的頁面是有限的,特別是針對單個APP做優化適配的時候,頁面的數量是相對較少的,可能在幾十到上百個。而模型的對樣本數量的需求是巨大的,特別像較爲複雜的模型,對數據量的要求至少是萬級別的,單靠真實樣本,是遠遠達不到要求的。
  2. 標註成本高:物體檢測的樣本標註,不僅需要標註物體的類別,更需要標註出物體的具體位置,而一個樣本上會存在多個物體標註。因此,這類樣本打標成本非常大。

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 時 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份,大大提高了樣本量。

5. 總結

通過Weex泛化樣本的方法,我們由100多個Weex活動頁,泛化出10000+個樣本,並且無需手動打標,節省了大量的打標成本。且由於樣本質量相對較高,模型的準確率得到了很大的提升。當然,我們也探索了很多其它方法,包括抓取Android運行時的頁面數據來生成自動打標的數據,以及利用已訓練模型自動預打標來節省手動打標的人力成本等,未來我們還會繼續探索更多的樣本生成及自動打標方法,爲模型訓練提供更多有用數據。



本文作者:閒魚技術-楚豐

閱讀原文

本文爲雲棲社區原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章