背景
在上一篇文章《詳解阿里99大促活動頁內容識別技術實現》裏,我們介紹了在淘寶99大促中,我們使用了怎樣的算法模型去識別並完成自動化測試的。
樣本問題的困難點
淘寶大促有近百個模塊、上千個頁面,模塊間具有相似性,並且模塊內部具備多種狀態,如果想要準確識別每個模塊類型,單模塊的樣本數量至少要達到萬級,而人工標註成本高、效率低下、數據量少,純靠人力是無法滿足模型訴求的。基於此,今天,我來介紹下,模型識別背後的大批量數據樣本生成的技術方案。
思路
總體技術方案如下,後面會分別詳細講:
模型的樣本要求
算法模型識別的輸入是99大促的各個會場截圖,輸出是目標模塊名稱及其在截圖中的座標位置。
模型訓練時,就是把模塊渲染圖、相應座標位置與模塊類型輸入給模型,交給模型去進行監督學習。而模型需要的,就是各個模塊大批量的圖片樣本。
一個模塊,是由View和ViewModel組合而成,而View是固定的,ViewModel跟隨會場場景不同,是動態變化的。
那麼,如果我們能拿到描述模塊的View的這一層DSL,輔助以動態的ViewModel數據,再把View和ViewModel渲染成圖片,那我們就可以生成無窮無盡的樣本數據了。
DSL描述View
仔細梳理之後,View拆分爲原子級元素(Text、Image、Shape)和原子級元素的組合關係(Group),即與HTML DOM樹狀結構中的各層級容器嵌套與葉子節點類型是同樣的邏輯。
基於節點類型和節點樣式的DSL,我們就能描述一個完整的View了。
{
"layers": [{
"frame": {
"y": 354,
"x": 44,
"height": 32,
"width": 312
},
"id": 2,
"type": "text",
"value": "Adidas Stan Smith",
"textStyles": {
"fontFamily": "Helvetica, sans-serif",
"fontSize": 24
}
}, {
"frame": {
"y": 0,
"x": 384,
"height": 342,
"width": 342
},
"id": 3,
"type": "image",
"value": "//img.alicdn.com/bao/uploaded/i1/TB1.mcuNpXXXXctXFXXSutbFXXX.jpg_350x350Q50s50.jpg_.webp",
"styles": {
"height": 342,
"width": 342
}
}, {
"frame": {
"y": 0,
"x": 384,
"height": 342,
"width": 342
},
"id": 4,
"type": "shape",
"styles": {
"height": 342,
"width": 342,
"backgroundColor": "rgba(0, 0, 0, 0.1)"
}
}],
"frame": {
"y": 0,
"x": 0,
"height": 4920,
"width": 750
},
"id": 1,
"type": "group",
"moduleName": "pmod-zebra-recommand-item"
}
其中,除了節點類型和節點樣式之外,最外層的moduleName代表模塊名稱,id是爲了標記每一個子元素,frame是每個子元素的座標位置、輔助算法模型識別模塊內部子元素,value值只有text和image纔有,對應相應的文本值還有圖片鏈接。
獲取模塊View的DSL
有3種方案可以獲取到模塊View的DSL,分別是:
- 從代碼倉庫中獲取;
- 從sketch視覺稿中生成;
- 從瀏覽器渲染好的頁面中獲取。
我最後選擇了第三種方案,放棄第一個方案是因爲代碼寫法千差萬別,很多展現邏輯還包含在js代碼中,並且還要處理各種for循環子View、style的映射關係等等,複雜度太高。第二個方案目前集團內已有技術方案imgcook,這一塊的準確率聽說還不錯,並且一直在持續優化,而最終選擇第三個方案的原因是,能100%準確地還原模塊DSL,並且只需要關注模塊最終展現給用戶時候的形態,不需要理會過程中開發者做得各種複雜業務邏輯,複雜度相對低很多。
技術方案
在開發流程上,每個模塊在開發完成後,都會有對應的模塊預覽頁面。我使用了puppeteer模擬真實瀏覽器,對模塊的節點信息進行提取,並保存爲規範的DSL。
清洗window.getComputedStyle
通過window.getComputedStyle獲取DOM節點的樣式,會返回包含280個樣式屬性的對象,如果把每個DOM節點的所有280個樣式屬性都存儲到DSL中,會造成兩個問題:
- DSL文件冗餘,且文件大小過大,解析耗時。
- 增加算法同學對DSL的理解和調整成本。
第一步,隱藏默認屬性值;
大部分的樣式屬性都是默認值,我們首先把默認的樣式屬性剔除出去。
css
{
alignSelf: 'auto',
...
}
第二步,剔除無效屬性;
開發者常用的樣式屬性在20個左右,有很多的樣式是不具備實際效用的,把無效用的樣式屬性剔除掉,比如說:
{
zoom: '1',
writingMode: 'horizontal-tb',
...
}
第三步,transform動態計算
通過getComputedStyle拿到的transform屬性值是一個矩陣方法 matrix()
,感興趣的同學可以去學習理解下2D轉換矩陣。我們使用puppeteer模擬瀏覽器設置的屏幕寬度是750,也就是說,得到的transform值中translateX和translateY兩個值是以750爲基準換算得到的一個數字,假如想要在下面描述到的將DSL渲染成圖時(算法同學期望能模擬各種各樣的屏幕尺寸去生成樣本),就必須將獲取到的transform值換算成相應屏幕設備時的值。
# 爲了方便算法同學更好使用DSL渲染成圖的工具,這裏使用python來實現
# screenshotShape是一個數組,代表屏幕寬高 [width, height]
if 'transform' in style and 'matrix' in style['transform']:
matrix = style['transform'][7:-1].split(',')
translate = list(map(float, matrix[-2:]))
translateResult = list(map(str, [distance*(screenshotShape[0]/750) for distance in translate]))
matrix[-2:] = translateResult
通過以上3個步驟,最終得到的DOM節點樣式屬性個數一般維持在20個以內,能使輸出的DSL精簡非常多。
DSL渲染成圖片
同樣的,我們能基於puppeteer去對頁面做操作,也能使用它去把DSL渲染成目標模塊頁面,並截圖。
首先,建立DSL與HTML標籤的映射關係
其次,如果是DSL類型爲Group,就遞歸遍歷裏面的所有子元素,以此類推。完整的渲染流程圖如下:
ViewModel動態數據
一個模塊,應用到99大促、雙十一等各種會場,背後樣式都是一致的,只有對應的數據不同,動態的數據一般是商品圖片和商品信息。
閒魚有一億多的商品數據,如果把這商品數據拿過來與View一起渲染成模塊,每個模塊就有了成千上萬種展現形態,且貼合算法模型實際識別過程中的輸入,既能滿足樣本數量的要求,也能符合模型實際識別的場景,使模型準確率獲得更大地提升。
效果
通過這樣一條生成樣本的通道,每個模塊都能夠提供給算法同學幾萬張質量很高的樣本截圖,使模型的準確率達到98%以上。
展望
上述文章描述瞭如何批量生成樣本來幫助解決算法模型對99大促和雙十一會場中各個模塊的識別。
目前,對模塊DSL的動態調整依賴算法同學對模塊的理解,eg.改變圓角borderRadius生成更多正向樣本,或者增加噪聲,eg.刪除商品內容節點等生成負向樣本,這些操作都需要算法同學對DSL進行定製化配置。在未來,我們希望嘗試把這部分的工作也交給模型去處理,讓模型對樣本生成做決策,調整DSL的局部,並生成樣式更加豐富和可靠的樣本。
本文轉載自公衆號閒魚技術(ID:XYtech_Alibaba)。
原文鏈接: