導讀:智能生成代碼平台 imgcook 以 Sketch、PSD、靜态圖檔等形式的視覺稿作為輸入,可以一鍵生成可維護的前端代碼,但從設計稿中擷取的都是 div、img、span 等元件,而前端大多是元件化開發,我們希望能從設計稿直接生成元件化的代碼,這就需要能夠将設計稿中的元件化元素,例如 Searchbar、Button、Tab 等識别出來。識别網頁中的 UI 元素在人工智能領域是一個典型的的目标檢測問題,我們可以嘗試使用深度學習目标檢測手段來自動化解決。
本文介紹了使用機器學習的方式來識别 UI 界面元素的完整流程,包括:現狀問題分析、算法選型、樣本準備、模型訓練、模型評估、模型服務開發與部署、模型應用等。
應用背景
[imgcook](
https://www.imgcook.com/) 以 Sketch、PSD、靜态圖檔等形式的視覺稿作為輸入,通過智能化技術一鍵生成可維護的前端代碼,Sketch/Photoshop 設計稿的代碼生成需要
安裝插件,在設計稿中通過 imgcook 插件導出視覺稿的 JSON 描述資訊(
D2C Schema)粘貼到 imgcook 可視化編輯器,在編輯器中可以進行視圖編輯、邏輯編輯等來改變 JSON 描述資訊。
我們可以選擇 DSL 規範來生成對應的代碼。例如生成 React 規範的代碼,需要實作從 JSON 樹轉換成 React 代碼 (
自定義 DSL)。

如下圖,左側為 Sketch 中的視覺稿, 右側為使用
React 開發規範生成的按鈕部分的代碼。
從 Sketch 視覺稿「導出資料」生成「React 開發規範」的代碼,圖為按鈕部分代碼片段。
生成的代碼都是由 div、img、span 這些标簽組成,但實際應用開發有這樣的問題:
-
web頁面開發為提升可複用性,頁面元件化,例如:Searchbar、Button、Tab、Switch、Stepper
一些原生元件不需要生成代碼,例如狀态欄 Statusbar、Navbar、Keyboard
我們的需求是,如果想要使用元件庫,例如 Ant Design,我們希望生成的代碼能像這樣:
// Antd Mobile React 規範
import { Button } from "antd-mobile";
<div style={styles.ft}>
<Button style={styles.col1}>進店搶紅包</Button>
<Button style={styles.col2}>加購物車</Button>
</div>
"smart": {
"layerProtocol": {
"component": {
"type": "Button"
}
}
}
為此我們在 JSON 描述中添加了 smart 字段, 用來描述節點的類型。
我們需要做的,就是找到視覺稿中需要元件化的元素,用這樣的 JSON 資訊來描述它, 以便在 DSL 轉換代碼時, 通過擷取 JSON 資訊中的 smart 字段來生成元件化代碼。
現在問題轉化為:如何找到視覺稿中需要元件化的元素,它是什麼元件,它在 DOM 樹中的位置或者在設計稿中的位置。
解決方案
▐ 約定生成規則
通過指定設計稿規範來幹預生成的 JSON 描述,進而控制生成的代碼結構。例如在我們的
設計稿進階幹預規範中關于元件的圖層命名規範:将圖層中的元件、元件屬性等顯性标記出來。
#component:元件名?屬性=值#
#component:Button?id=btn#
在使用 imgcook 的插件導出 JSON 描述資料時就通過規範解析拿到圖層中的約定資訊。
▐ 學習識别元件
人工約定規則的方式需要按照我們制定的協定規範來修改設計稿,一個頁面上的元件可能會有很多,這種人工約定方式讓開發者多了很多額外工作,不符合使用 imgcook 提高開發效率的宗旨,我們期望通過智能化手段自動識别視覺稿中的可元件化元素,識别的結果最終會轉換并填充在 smart 字段中,與手動約定元件協定所生成的 json 中的 smart 字段内容相同。
這裡需要完成兩件事情:
- 找到元件資訊:類别、位置、尺寸等資訊。
- 找到元件中的屬性, 例如 button 中的文字為“送出”
第二個事情我們可以根據 json 樹來解析元件的子元素。第一個事情我們可以通過智能化來自動化的完成,這是一個在人工智能領域典型的的目标檢測問題,我們可以嘗試使用深度學習目标檢測手段來自動化解決這個手動約定的流程。
學習識别 UI 元件
▐ 業界現狀
目前業界也有一些使用深度學習來識别網頁中的 UI 元素的研究和應用,對此有一些讨論:
- Use R-CNN to detect UI elements in a webpage?
- Is machine learning suitable for detecting screen elements?
- Any thoughts on a good way to detect UI elements in a webpage?
- How can I detect elements of GUI using opencv?
- How to recognize UI elements in image?
讨論中的訴求主要有兩種:
- 期望通過識别 UI 界面元素來做 Web 頁面自動化測試的應用場景。
- 期望通過識别 UI 界面元素來自動生成代碼。
既然是使用深度學習來解決 UI 界面元素識别的問題, 帶有元素資訊的 UI 界面資料集則是必須的。目前業界開放且使用較多的資料集有 Rico 和 ReDraw。
ReDraw
一組 Android 螢幕截圖,GUI 中繼資料和标注了 GUI 元件圖像,包含 RadioButton、ProgressBar、Switch、Button、CheckBox 等 15 個分類,14,382 個 UI 界面圖檔和 191,300 個帶有标簽的GUI元件,該資料集經過處理之後使每個元件的數量達到 5000 個。關于該資料集的詳細介紹可檢視
The ReDraw Dataset。
這是用于訓練和評估
ReDraw 論文中提到的 CNN 和 KNN 機器學習技術的資料集,
該論文于 2018 年在IEEE Transactions on Software Engineering上釋出。該論文提出了一種通過三個步驟來實作從 UI 轉換為代碼自動化完成的方法:
1、檢測 Detection
先從設計稿中提取或使用 CV 技術提取 UI 界面元資訊,例如邊界框(位置、尺寸)。
2、分類 Classification
再使用大型軟體倉庫挖掘、自動動态分析得到 UI 界面中出現的元件,并用此資料作為 CNN 技術的資料集學習将提取出的元素分類為特定類型,例如 Radio、Progress Bar、Button 等。
3、組裝 Assembly
最後使用 KNN 推導 UI 層次結構,例如縱向清單、橫向 Slider。
在 ReDraw 系統中使用這種方法生成了 Android 代碼。評估表明,ReDraw 的 GUI 元件分類平均精度達到 91%,并組裝了原型應用程式,這些應用程式在視覺親和力上緊密地反映了目标模型,同時展現了合理的代碼結構。
Rico
迄今為止最大的移動 UI 資料集,建立目的是支援五類資料驅動的應用程式:設計搜尋,UI布局生成,UI代碼生成,使用者互動模組化和使用者感覺預測。
Rico 資料集包含 27 個類别、1 萬多個應用程式和大約 7 萬個螢幕截圖。
該資料集在 2017 年第30屆ACM年度使用者界面軟體和技術研讨會上對外開放
(RICO: A Mobile App Dataset for Building Data-Driven Design Applications)此後有一些基于 Rico 資料集的研究和應用。例如: Learning Design Semantics for Mobile Apps, 該論文介紹了一種基于代碼和視覺的方法給移動 UI 元素添加語義注釋。根據 UI 螢幕截圖和視圖層次結構可自動識别出 25
UI元件類别,197 個文本按鈕概念和 99 個圖示類。
▐ 應用場景
這裡列舉一些基于以上資料集的研究和應用場景。
智能生成代碼
Machine Learning-Based Prototyping of Graphical User Interfaces for Mobile Apps | ReDraw Dataset智能生成布局
Neural Design Network: Graphic Layout Generation with Constraints | Rico Dataset使用者感覺預測
Modeling Mobile Interface Tappability Using Crowdsourcing and Deep Learning | Rico DatasetUI 自動化測試
A Deep Learning based Approach to Automated Android App Testing | Rico Dataset▐ 問題定義
在上述介紹的基于 Redraw 資料集生成 Android 代碼的應用中,我們了解了它的實作方案, 對于第 2 步需要使用大型軟體倉庫挖掘和自動動态分析技術來擷取大量元件樣本作為 CNN 算法的訓練樣本,以此來得到 UI 界面中存在的特定類型元件,例如 Progress Bar、Switch 等。
對于我們 imgcook 的應用場景,其本質問題也是需要找到 UI 界面中這種特定類型的元件資訊:類别和邊界框,我們可以把這個問題定義為一個目标檢測的問題,使用深度學習對 UI 界面進行目标檢測。那麼我們的目标是什麼?
檢測目标就是 Progress Bar、Switch、Tab Bar 這些可在代碼中元件化的頁面元素。
UI 界面目标檢測
▐ 基礎知識
機器學習
人類是怎麼學習的?通過給大腦輸入一定的資料,經過學習總結得到知識和經驗,有當類似的任務時可以根據已有的經驗做出決定或行動。
機器學習(Machine Learning)的過程與人類學習的過程是很相似的。機器學習算法本質上就是獲得一個 f(x) 函數表示的模型,如果輸入一個樣本 x 給 f(x) 得到的結果是一個類别,解決的就是一個分類問題,如果得到的是一個具體的數值那麼解決的就是回歸問題。
機器學習與人類學習的整體機制是一緻的,有一點差別是人類的大腦隻需要非常少的一些資料就可以歸納總結出适用性非常強的知識或者經驗,例如我們隻要見過幾隻貓或幾隻狗就能正确的分辨出貓和狗,但對于機器來說我們需要大量的學習資料,但機器能做到的是智能化不需要人類參與。
深度學習
深度學習(Deep Learning)是機器學習的分支,是一種試圖使用包含複雜結構或由多重非線性變換構成的多個處理層對資料進行高層抽象的算法。
深度學習與傳統機器學習的差別可以看這篇
Deep Learning vs. Machine Learning,有資料依賴性、硬體依賴、特征處理、問題解決方式、執行時間和可解釋性這幾個方面。
深度學習對資料量和硬體的要求很高且執行時間很長,深度學習與傳統機器學習算法的主要不同在于對特征處理的方式。在傳統機器學習用于現實任務時,描述樣本的特征通常需要由人類專家來設計,這稱為“特征工程”(Feature Engineering),而特征的好壞對泛化性能有至關重要的影響,要設計出好特征并非易事。深度學習可以通過特征學習技術分析資料自動産生好特征。
目标檢測
機器學習有很多
應用,例如:
- 計算機視覺(Computer Vision,CV) 用于車牌識别和面部識别等的應用。
- 資訊檢索 用于諸如搜尋引擎的應用 - 包括文本搜尋和圖像搜尋。
- 市場營銷 針對自動電子郵件營銷和目标群體識别等的應用。
- 醫療診斷 諸如癌症識别和異常檢測等的應用。
- 自然語言處理(Natural Language Processing, NLP) 如情緒分析和照片标記等的應用。
目标檢測(
Object Detection)就是一種與計算機視覺和圖像處理有關的計算機技術,用于檢測數字圖像和視訊中特定類别的語義對象(例如人,動物物或汽車)。
而我們在 UI 界面上的目标是一些設計元素, 可以是原子粒度的 Icon、Image、Text、或是可元件化的 Searchbar、Tabbar 等。
▐ 算法選型
用于目标檢測的方法通常分為基于機器學習的方法(傳統目标檢測方法)或基于深度學習的方法(深度學習目标檢測方法),目标檢測方法經曆了從傳統目标檢測方法到深度學習目标檢測方法的變遷:
傳統目标檢測方法
對于基于機器學習的方法 ,需要先使用以下方法之一來定義特征,然後使用諸如支援向量機(SVM)的技術進行分類。
- 基于 Haar 功能的 Viola–Jones 目标檢測架構
- 尺度不變特征變換(SIFT)
- 定向梯度直方圖(HOG)特征
深度學習目标檢測方法
對于基于深度學習的方法,無需定義特征即可進行端到端目标檢測,通常基于卷積神經網絡(CNN)。基于深度學習的目标檢測方法可分為 One-stage 和 Two-stage 兩類,還有繼承這兩種類方法優點的 RefineDet 算法。
✎ One-stage
基于 One-stage 的目标檢測算法直接通過主幹網絡給出類别和位置資訊,沒有使用RPN網路。這樣的算法速度更快,但是精度相對Two-stage目标檢測網絡了略低。典型算法有:
- SSD(Single Shot MultiBox Detector)系列
- YOLO (You Only Look Once)系列(YOLOv1
- YOLOv2、YOLOv3)
- RetinaNet
✎ Two-stage
基于 Two-stage 的目标檢測算法主要通過一個卷積神經網絡來完成目标檢測過程,其提取的是 CNN 卷積特征,在訓練網絡時,其主要訓練兩個部分,第一步是訓練 RPN 網絡,第二步是訓練目标區域檢測的網絡。
即先由算法生成一系列作為樣本的候選框,再通過卷積神經網絡進行樣本分類。網絡的準确度高、速度相對 One-stage 慢。典型算法有:
- R-CNN,Fast R-CNN,Faster R-CNN
✎ 其他(RefineDet)
RefineDet (Single-Shot Refinement Neural Network for Object Detection) 是基于SSD算法的改進。繼承了兩種方法(例如,單一階段設計方法,兩階段設計方法)的優點,并克服了它們的缺點。
目标檢測方法對比
✎ 傳統方法 VS 深度學習
基于機器學習的方法和基于深度學習的方法的算法流程如圖所示,傳統目标檢測方法需要手動設計特征,通過滑動視窗擷取候選框,再使用傳統分類器進行目标區域判定,整個訓練過程分成多個步驟。而深度學習目标檢測方法則通過機器學習特征,通過更高效的 Proposal 或直接回歸的方式擷取候選目标,它的準确度和實時性更好。
關于目标檢測算法的研究現在基本都是基于深度學習的,傳統的目标檢測算法已經很少用到了,深度學習目标檢測方法更适合工程化, 具體對比如下:
✎ One-stage VS Two-stage
✎ 算法優缺點
這裡就不寫各個算法的原理了,直接看下優缺點。
總結
由于對 UI 界面元素檢測的精度要求比較高, 是以最終選擇 Faster RCNN 算法。
▐ 架構選擇
機器學習架構
這裡簡要列舉下幾個機器學習架構:Scikit Learn、TensorFlow、Pytorch、Keras。
Scikit Learn 是通用的機器學習架構,内部實作了各種分類,回歸和聚類算法(包括支援向量機,随機森林,梯度增強,k-means 等); 還包括資料降維、模型選擇和資料預處理等工具庫,容易安裝和使用,樣例豐富,而且教程和文檔也非常詳細。
TensorFlow、Keras 和 Pytorch 是目前深度學習的主要架構, 提供各種深度學習算法調用。這裡推薦個學習資源:
強烈推薦的TensorFlow、Pytorch和Keras的樣例資源,特别同意這篇作者說的: 隻要把以上資源運作一次,不懂的地方查官方文檔,很快就能了解和運用這三大架構。
在後面的模型訓練代碼中可以看到實際任務中是怎麼使用這些架構的。
目标檢測架構
目标檢測架構可以了解成是把目标檢測算法整合在一起的一個庫,例如深度學習算法架構 TensorFlow 不是一個目标檢測架構,但它提供目标檢測的 API:Object Detection API。
目标檢測架構主要有:
Detecn-benchmark、
mmdetection Detectron2。目前使用較廣的是
Facebook AI 研究院于 2019 年 10 月 10 日開源的 Detectron2 目标檢測架構。我們做 UI 界面元件識别也是用的 Detectron2, 後面會有使用示例代碼。
tron、maskrcn可參考:如何評價FAIR于2019年10月10日開源的Detectron2目标檢測架構?
前端機器學習架構 Pipcook
作為一個前端開發者,我們還可以選擇[ Pipcook](
https://github.com/alibaba/pipcook),這是由阿裡巴巴前端委員會智能化小組開源的一個幫助前端工程師使用機器學習的前端算法工程架構。
Pipcook 使用對前端友好的 JS 環境,基于
Tensorflow.js架構作為底層算法能力并且針對前端業務場景包裝了相應算法,進而讓前端工程師可以快速簡單的運用起機器學習的能力。
Pipcook 是基于管道的架構,為前端開發者封裝了資料收集、資料接入、資料處理、模型配置、模型訓練、模型服務部署、線上訓練七個部分的機器學習工程鍊路。
關于 Pipcook 的原理和使用可檢視:
▐ 樣本準備
環境和模型準備好了,機器學習的大頭還是資料集的收集和處理。我們的樣本來源有兩種:
阿裡系應用的 UI 界面圖檔。目前移動端 UI 界面 25647 張圖檔,人工标注了 10 個分類共計 49120 個元件。
代碼自動生成的圖檔。支援 10 個分類的樣本生成,生成圖檔時自動标注。
元件類型定義
目前圈定的元件類型有 Statusbar、Navbar、Searchbar、Tabbar 等,不管是人工标注還是自動标注目标元件,都需要有明确的元件類型定義。
人工标注時需要根據明确定義的特征來标注元件
自動生成時需要根據明确定義的特征來編寫樣式代碼。
例如,對手機狀态欄 Status Bar 的定義:
例如,對頁籤 Tab Bar 的定義:
阿裡系應用 UI 界面樣本
阿裡系 APP 和産品業務有很多,這個業務的視覺稿都有平台集中管理,我們可以拿到這些視覺稿作為樣本來源。目前隻選取了 Sketch 視覺稿, 因為 PSD 檔案難以導出以頁面為次元的圖檔。
收集樣本
這裡講的比較細,因為我覺得有些地方可以還是給人啟示的。
✎ 下載下傳 Sketch 檔案
從阿裡内部平台下載下傳 Sketch 檔案是第一步,後面的每個步驟腳本都以序号開頭,例如 1-download-sketch.ts。因為樣本處理腳本很多,友好的命名更易了解。
/**
* 【用途】下載下傳 Sketch 檔案
* 【指令】ts-node 1-download-sketch.ts
*/
✎ 使用 sketchtool 批量導出為圖檔
Sketch 自帶一個指令行工具 sketchtool, 我們可以用 sketchtool 來批量導出為圖檔。點此檢視
更多 sketchtool 的用法【用途】使用 sketchtool 導出 Sketch 中的 Artboards 儲存為 1x 的 png 圖檔
【指令】sh 2-export-image-from-sketch.sh $inputDir $outputDir
for file in $1"/*"
do
sketchtool export artboards $file --output=$2 --formats='png' --scales=1.0
done
樣本預處理
設計師出的設計稿你懂得,淘寶直播4.0_v1、淘寶直播4.0_v2、淘寶直播4.0_v3..., 每個小版本改動可能不太大,每個 Sketch 檔案中的頁面可能還有:詳情頁初版、詳情頁2版、詳情頁終版,這在導出圖檔之後會有大量重複的圖檔。
另外還有一些不規範的設計稿,一個畫闆畫一個 ICON 這種的,還有互動稿、PC 端視覺稿等都不是我們需要的。是以需要做一些處理。
✎ 按尺寸分類過濾
将圖檔分為移動端 和 PC 端, 按尺寸去除無效圖檔。
# 【用途】将圖檔按尺寸分類剔除
# 【指令】python3 3-classify-by-size.py $inputDir $outputDir
# 删除尺寸不規範的圖檔,width_list 中是數量大于 100 的尺寸
if width not in width_list:
print('move {}'.format(img_name))
move_file(img_dir, other_img_dir, img_name)
# 删除 高度小于 30 的圖檔
elif height < 30:
print('move {}'.format(img_name))
move_file(img_dir, other_img_dir, img_name)
# 按尺寸歸檔
else:
width_dir = os.path.join(img_dir, str(width))
if not os.path.exists(width_dir):
print('mkdir:{}'.format(width))
os.mkdir(width_dir)
print('move {}'.format(img_name))
move_file(img_dir, width_dir, img_name)
✎ 圖檔去重
如果自己寫圖檔去重的邏輯也可以,做一個圖檔相似度比較。這裡偷個懶,直接用了現成的圖檔相似度檢測工具
duplicate Photos Fixer Pro。這裡簡單說一下使用方法, 如圖紅框提示,支援調整檢測條件和相似程度。
每張圖檔的 Hash 值計算完成之後,還可以再調整相似度來篩選。
✎ 圖檔重命名
這裡主要是想說一下怎麼做樣本管理,因為資料集是會逐漸豐富的,可能會存在很多個版本的資料集。友好的命名便于管理,比如 generator-mobile-sample-10cate-20200101-1.png, 表明這是 2020.01.01 自動生成的第 1 個移動端樣本,這一批資料集包含 10 個分類。
樣本标注
✎ 半自動标注
有一些元件是可以自動标注的,比如 statusbar 和 navbar, 因為幾乎每張圖檔都會有且位置和尺寸基本相同,是以可以為每張圖檔自動生成一個 VOC 格式的 xml, 包含兩個目标元件分類。然後在人工标注其他元件時隻有少部分需要調整,可以節省很多人力。
目前也在探索更多的半自動化标注方式以減少人工标注的成本。
✎ 人工标注
使用
labelImg工具進行人工标注,按照連結中提供的安裝步驟安裝,這裡簡單介紹下用法。
// 下載下傳 labelImg
git clone https://github.com/tzutalin/labelImg.git
// 進入 labelImg
cd labelImg-master
// 之後按照 github 中的提示安裝環境
// 執行一下指令就會打開可視化界面
python3 labelImg.py
可視化界面如下, 支援 Pascal VOC 和 Yolo 兩種儲存标注的格式。
界面怎麼用就不說了,這裡推薦一些提高标注效率的快捷鍵, 另外 選中 View > Auto Save mode 可以自動儲存。
w 建立立一個矩形框
d 下個圖檔
a 上個圖檔
del/fn + del 删除選中的矩形框,我的電腦需要 fn + del
Ctrl/Command++ 放大
Ctrl/Command-- 縮小
↑→↓← 移動矩形框
▐ Puppeteer 自動生成樣本
根據元件類型定義随機生成元件,這個元件的樣式也是随機的。示例如下, 每個正樣本都有一個 class 為 element-開頭的選擇器,例如 element-button 便于後續擷取元件的類别資訊。
随機生成頁面
編寫一個頁面,随機選取一些元件展示。本地啟動服務,打開頁面,例如
http://127.0.0.1:3333/#/generator,示例頁面如下:
但是這個的頁面與實際的 UI 界面相差太大,隻有正樣本,背景太簡單。這裡通過裁剪出真實的 UI 界面中的片段與自動生成的目标元件組合的方式,來提高生成樣本的品質, 示例樣本如下,應該能看出來頁面中自動生成的元件吧?
Puppeter 截圖生成樣本
确定随機頁面(
)可通路後,使用 Puppeteer 編寫腳本自動打開頁面、截圖儲存、并擷取元件的類别和邊界框。主要邏輯如下:
const pptr = require('puppeteer')
// 存放 COCO 格式的樣本資料
const mdObj = {};
const browser = await pptr.launch();
const page = await browser.newPage();
await page.goto(`http://127.0.0.1:3333/#/generator/${Date.now()}`)
await page.evaluate(() => {
const container: HTMLElement | null = document.querySelector('.container');
const elements = document.querySelectorAll('.element');
const msg: any = {bbox: []};
// 擷取頁面中所有帶 .element 選擇器的元素
elements.forEach((element) => {
const classList = Array.from(element.classList).join(',')
if (classList.match('element-')) {
// 擷取類别
const type = classList.split('element-')[1].split(',')[0];
// 計算邊界框并儲存至 msg
pushBbox(element, type);
}
});
});
// 儲存 COCO 格式樣本資料
logToFile(mdObj);
// 儲存 UI截圖
await page.screenshot({path: 'xxx.png'});
// 關閉浏覽器
await browser.close();
▐ 樣本評估
阿裡系應用 UI 界面樣本中,元件數量和豐富度不平衡,可通過自動生成樣本來平衡每種元件的數量。對于自動生成的樣本,如何來評估樣本品質?如何自動生成目标元件的邏輯可枚舉為 1 萬種,自動生成 2 萬個此類元件就沒有意義。
如何評估自動生成的樣本的豐富度和數量是否合理?這是目前我們在探索的問題。
▐ 模型訓練
Detectron 2
使用 Facebook 開源的目标檢測架構 Detectron 2, 通過 merge_from_file 指定使用 Faster R-CNN。
from detectron2.data import MetadataCatalog
from detectron2.evaluation import PascalVOCDetectionEvaluator
from detectron2.engine import DefaultTrainer,hooks
from detectron2.config import get_cfg
cfg = get_cfg()
cfg.merge_from_file("./lib/detectron2/configs/COCO-Detection/faster_rcnn_R_50_C4_3x.yaml")
cfg.DATASETS.TRAIN = ("train_dataset",)
cfg.DATASETS.TEST = ('val_dataset',) # no metrics implemented for this dataset
cfg.DATALOADER.NUM_WORKERS = 4 # 多開幾個worker 同時給GPU喂資料防止GPU閑置
cfg.MODEL.WEIGHTS = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" # initialize from model zoo
cfg.SOLVER.IMS_PER_BATCH = 4
cfg.SOLVER.BASE_LR = 0.000025
cfg.SOLVER.NUM_GPUS = 2
cfg.SOLVER.MAX_ITER = 100000 # 300 iterations seems good enough, but you can certainly train longer
cfg.MODEL.ROI_HEADS.BATCH_SIZE_PER_IMAGE = 128 # faster, and good enough for this toy dataset
cfg.MODEL.ROI_HEADS.NUM_CLASSES = 29 # only has one class (ballon)
# 訓練集
register_coco_instances("train_dataset", {}, "data/train.json", "data/img")
# 測試集
register_coco_instances("val_dataset", {}, "data/val.json", "data/img")
import os
os.makedirs(cfg.OUTPUT_DIR, exist_ok=True)
class Trainer(DefaultTrainer):
@classmethod
def build_evaluator(cls, cfg, dataset_name, output_folder=None):
### 按需求重寫
@classmethod
def test_with_TTA(cls, cfg, model):
### 按需求重寫
trainer = Trainer(cfg)
trainer.resume_or_load(resume=True)
trainer.train()
使用 Detectron 2 模型訓練的産物是一個 .pth 格式的模型檔案。這個 .pth 格式的模型檔案長啥樣可以看下這篇
Pytorch 中儲存的模型檔案.pth 深入解析Pipcook
Pipcook 已經幫我們封裝好了從資料收集、資料接入、模型訓練、模型評估的代碼,我們無需寫這些工程鍊路的 Python 腳本。目前 Pipcook 中的目标檢測鍊路使用的是 Detectron 2 中的 Faster RCNN 算法,大家可以去
Pipcook Plugins看下實作就明白了。
以下是使用 Pipcook 進行目标檢測的示例代碼。
const {DataCollect, DataAccess, ModelLoad, ModelTrain, ModelEvaluate, PipcookRunner} = require('@pipcook/pipcook-core');
const imageCocoDataCollect = require('@pipcook/pipcook-plugins-image-coco-data-collect').default;
const imageDetectronAccess = require('@pipcook/pipcook-plugins-detection-detectron-data-access').default;
const detectronModelLoad = require('@pipcook/pipcook-plugins-detection-detectron-model-load').default;
const detectronModelTrain = require('@pipcook/pipcook-plugins-detection-detectron-model-train').default;
const detectronModelEvaluate = require('@pipcook/pipcook-plugins-detection-detectron-model-evaluate').default;
async function startPipeline() {
// collect detection data
const dataCollect = DataCollect(imageCocoDataCollect, {
url: 'http://ai-sample.oss-cn-hangzhou.aliyuncs.com/image_classification/datasets/autoLayoutGroupRecognition.zip',
testSplit: 0.1,
annotationFileName: 'annotation.json'
});
const dataAccess = DataAccess(imageDetectronAccess);
const modelLoad = ModelLoad(detectronModelLoad, {
device: 'cpu'
});
const modelTrain = ModelTrain(detectronModelTrain);
const modelEvaluate = ModelEvaluate(detectronModelEvaluate);
const runner = new PipcookRunner( {
predictServer: true
});
runner.run([dataCollect, dataAccess, modelLoad, modelTrain, modelEvaluate])
}
startPipeline();
▐ 模型評估
評估名額
發現文章太長了,這裡扔個學習資源吧, 強烈推薦這個視訊課程[ Python3入門機器學習經典算法與應用[慕課網] ](
https://coding.imooc.com/class/chapter/169.html),沒有時間看的話直接看人家寫的筆記吧,第 10 章詳細講了使用精準率和召回率評估分類結果。
啊, 還是簡單解釋下吧。
精準率可以了解成查準率,比如預測了 100 個是 Button, 其中 80 個是預測正确的, 精準率是 80 / 100。
召回率可以了解成查全率,比如實際有 60 個是 Button, 成功預測了 40 個, 召回率是 40 / 60。
在目标檢測的性能評價名額是 mAP 和 FPS,mAP 計算的是所有類别的平均準确率, 但由于目标檢測結果除了類别還有個邊界框,如何評價這個邊界框的預測準确性,又涉及到 IoU (Intersection over Union)交并比的概念,用來表示預測的邊界框與真實的邊界框的交并比。
在後面的評估結果中可以看到這樣的結果,當 IoU=0.50:0.95 即預測的邊界框與真實的邊界框的交并比在 0.5 到 0.95 之間時都算邊界框預測正确,此時的精準率 AP 為 0.772。
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.772
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.951
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.915
評估代碼
評估代碼如下:
from pycocotools.cocoeval import COCOeval
from pycocotools.coco import COCO
annType = 'bbox'
# 測試集 ground truth
gt_path = '/Users/chang/coco-test-sample/data.json'
# 測試集 預測結果
dt_path = '/Users/chang/coco-test-sample/predict.json'
gt = COCO(gt_path)
gt.loadCats(gt.getCatIds())
dt = COCO(dt_path)
imgIds=sorted(gt.getImgIds())
cocoEval = COCOeval(gt,dt,annType)
for cat in gt.loadCats(gt.getCatIds()):
cocoEval.params.imgIds = imgIds
cocoEval.params.catIds = [cat['id']]
print '------------------------------ ' cat['name'] ' ---------------------------------'
cocoEval.evaluate()
cocoEval.accumulate()
cocoEval.summarize()
評估結果
目前用阿裡系 UI 界面與自動生成的樣本組合訓練, mAP 基本在 75% 左右。
------------------------------ searchbar ---------------------------------
Running per image evaluation...
Evaluate annotation type *bbox*
DONE (t=2.60s).
Accumulating evaluation results...
DONE (t=0.89s).
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.772
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.951
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.915
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = -1.000
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.795
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.756
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.816
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.830
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.830
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = -1.000
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.838
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.823
我們用非阿裡系的 UI 界面看下預測結果, 可以看出一些容易被誤識别的情況。
▐ 模型服務部署
我們期望的是當輸入一張圖檔時能傳回給模型預測的結果。是以拿到模型檔案之後,需要寫一個模型服務,接收一個樣本,并傳回模型預測的結果。
from detectron2.config import get_cfg
from detectron2.engine.defaults import DefaultPredictor
with open('label.json') as f:
mp = json.load(f)
cfg = get_cfg()
cfg.merge_from_file("./config/faster_rcnn_R_50_C4_3x.yaml")
cfg.MODEL.WEIGHTS = "./output/model_final.pth" # initialize from model zoo
cfg.MODEL.ROI_HEADS.NUM_CLASSES = len(mp) # only has one class (ballon)
cfg.MODEL.DEVICE='cpu'
model = DefaultPredictor(cfg)
def predict(image):
im2 = cv2.imread(image)
out = model(x)
data = {'status': 200}
data['content'] = trans_data(out)
# EAS python sdk
import spark
num_io_threads = 8
endpoint = '' # '127.0.0.1:8080'
spark.default_properties().put('rpc.keepalive', '60000')
context = spark.Context(num_io_threads)
queued = context.queued_service(endpoint)
while True:
receive_data = srv.read()
try:
msg = json.loads(receive_data.decode())
ret = predict(msg["image"])
srv.write(json.dumps(ret).encode())
except Exception as e:
srv.error(500, str(e))
模型部署後拿到通路連結即可直接調用例如
http://example.com/api/predict/detect來預測。
▐ 模型應用
模型部署之後就可以在我們的應用中調用擷取預測結果(視覺稿中的元件類别和邊界框),與從視覺稿中導出的 JSON 樹比對,進而獲得一個帶有元件資訊(D2C Schema 中的 smart.layerProtocol.component 字段)的 JSON 描述(處理後的最終 JSON 作為
DSL的輸入生成代碼)。
const detectUrl = 'http://example.com/api/predict/detect';
const res = await request(detectUrl, {
method: 'post',
dataType: 'json',
timeout: 1000 * 10,
content: JSON.stringify({
image: image,
}),
});
const json = res.content;
關于模型服務的部署和調用可以去
PAI 的阿裡雲文檔檢視。
未來展望
由于選擇的是深度學習算法, 需要大量的訓練集樣本,是以樣本數量和品質是亟需解決的問題。
目前我們已經擁有 2.5萬+ 的 UI 界面樣本,包含 10 個分類,自動生成的樣本支援 10 個分類。但人工标注的 UI 界面樣本均為阿裡系産品,雖然樣本圖檔不同但設計風格會有相似且設計規範較統一,使得元件樣式的豐富度不夠,對阿裡系之外的設計稿泛化能力較阿裡系的要差。另外遵循一定的随機化規則自動生成的樣本還存在版式和樣式與實際樣本有差異的地方,自動生成的樣本品質無法評估。
未來在資料集方面,會考慮加入業界樣本數量較大的資料集,并優化樣本自動生成邏輯,同時探索評估自動生成的樣本品質的方法。
One More Thing
我們是阿裡巴巴-淘系技術部-頻道與D2C智能團隊,緻力于前端智能化領域的探索和實踐,賦能淘寶、天貓、聚劃算等日常與大促(如雙 11 )業務,是淘系前端智能化實踐的領路人,也是阿裡經濟體前端委員會智能化方向的核心團隊。
目前團隊有較多高校和海外背景的技術小二,專業領域涉及前端、算法、全棧等。我們在 D2C(Design to Code) 領域開放了 Imgcook(
https://www.imgcook.com) 平台,在逐漸釋放阿裡生态的前端生産力;我們也與 Google 的 tensorflow 團隊保持長線合作,基于 tfjs-node 之上,開源了我們的前端算法工程架構 Pipcook(
https://github.com/alibaba/pipcook),在引領前端行業向智能化時代邁進。
履歷投遞至📮:[email protected]
或加微信:onlychang92, 備注:淘系技術公衆号-招聘
歡迎加入我們的社群群,釘釘群号:32918052
關注「淘系技術」微信公衆号,一個有溫度有内容的技術社群~