天天看點

手把手教你打造前端智能圖示識别工具背景術語簡介開始行動吧總結

來源:Alibaba F2E

作者:業楓

手把手教你打造前端智能圖示識别工具背景術語簡介開始行動吧總結

Hi~ 我是前端學徒業楓,今天為大家帶來一篇硬核前端智能化教程,真·手把手教你用機器學習打造一個純前端運作的圖示智能識别工具。并附上完整代碼,一起來體驗前端智能化的魅力吧~

背景

目前的前端元件庫都使用 Iconfont 來管理圖示,随着時間推移,圖示越來越多,圖示的命名也五花八門,很難限制。開發者還原設計稿時,經常要人肉從幾百個圖示中尋找對應的圖示。有時候連設計師都找不到,導緻重複添加圖示。

最近發現在 AntDesign 官網有以圖搜圖示的功能,使用者對設計稿或任意圖檔中的圖示截圖,點選/拖拽/粘貼上傳,就可以搜尋到比對度最高的幾個圖示:

手把手教你打造前端智能圖示識别工具背景術語簡介開始行動吧總結

這個功能很好的解決了上面提到的問題,但還有些不足:

  • 截圖最好是正方形的,否則拉伸後識别率會下降(後面會解釋)。
  • 隻能識别 AntDesign 的圖示。

為了解決這些問題,我們決定自己打造一個前端圖示識别工具。下面将以我們團隊的開源元件庫 Cloud Design為例,手把手教你打造純前端的專屬圖示識别工具。(完整代碼放在文末)

術語簡介

簡單介紹幾個術語,了解的同學可以直接跳過。

機器學習

機器學習研究和建構的是一種特殊算法(而非某一個特定的算法),能夠讓計算機自己在資料中學習進而進行預測。

是以,機器學習不是某種具體的算法,而是很多算法的統稱。

機器學習包含:線性回歸、貝葉斯、聚類、決策樹、深度學習等等。前面 AntDesign 的模型是通過深度學習的代表算法 CNN 訓練得到的。

CNN 卷積神經網絡

卷積神經網絡(Convolutional Neural Networks, CNN)是一類包含卷積計算且具有深度結構的前饋神經網絡(Feedforward Neural Networks),最常用于分析視覺圖像。

CNN 能有效的将大資料量圖檔降維到小資料量,且保留圖像特征,非常适合處理圖像資料。即使圖像翻轉、旋轉或變換位置也能有效識别,常用來解決:圖像分類檢索、目标定位監測、人臉識别等等。

開始行動吧

我們要對圖示進行識别,屬于機器學習中經典的“圖像分類”問題。CNN(卷積神經網絡) 可以有效的識别圖示,但是無法适應拉伸變形的場景。因為模型輸入時要先把圖像變換為正方形尺寸,截圖尺寸不對會導緻圖像拉伸變形,降低識别率,甚至識别錯誤。

手把手教你打造前端智能圖示識别工具背景術語簡介開始行動吧總結

常用的解法有兩種:

1、純機器學習:通過增加不同拉伸狀态的樣本,讓模型适應變形的圖像。

2、機器學習 + 圖像處理:用圖像處理算法對資料進行裁剪,保證圖像接近正方形。

第一種方法需要生成大量的訓練資料,訓練速度變慢,而且拉伸變形的情況很難周遊。第二種方法隻需要進行簡單的圖像處理就可以有效提高識别率,是以我選擇了它。那最終工作流應該是這樣的:

手把手教你打造前端智能圖示識别工具背景術語簡介開始行動吧總結

接下來我會從 樣本生成、模型訓練、模型使用 三部分來介紹完整的過程。

樣本生成

圖像分類的訓練樣本都是圖檔,我們的圖示則是 iconfont 渲染在頁面中的。可以自然想到用 樣本頁面 + Puppeteer 截圖來生成樣本。但截圖速度很慢,我也不想用 Faas 服務,于是想了個本地生成的方法:

首先人工把圖示庫的css部分轉為js:

手把手教你打造前端智能圖示識别工具背景術語簡介開始行動吧總結

這樣就能把圖示當作文本繪制在 canvas 上,并用圖像算法裁剪四周的空白區域:

// 用離屏 canvas 繪制圖示
offscreenCtx.font = `20px NextIcon`;
offscreenCtx.fillText(labelMap[labelName]);


// 用 getImageData 擷取圖檔資料,計算需裁剪的坐标
const { x, y, width: w, height: h } = getCutPosition(canvasSize, canvasSize, offscreenCtx.getImageData(0, 0, canvasSize, canvasSize).data);


// 計算需裁剪的坐标
function getCutPosition(width, height, imgData) {
  let lOffset = width; let rOffset = 0; let tOffset = height; let bOffset = 0;
  // 周遊像素,擷取最小的非空白矩形區域
  for (let i = 0; i < width; i++) {
    for (let j = 0; j < height; j++) {
      const pos = (i + width * j) * 4;
      if (notEmpty(imgData[pos], imgData[pos + 1], imgData[pos + 2], imgData[pos + 3])) {
        // 調整 lOffset、rOffset、tOffset、bOffset
        // 略
      }
    }
  }

  // 如果形狀不是正方形,将其擴充為正方形
  const r = (rOffset - lOffset) / (bOffset - tOffset);
  if (r !== 1) {
    // 略
  }


  return { x: lOffset, y: tOffset, width: rOffset - lOffset, height: bOffset - tOffset };
}


// 門檻值 0 - 255
const d = 5;
// 判斷是否非空白像素
function notEmpty(r, g, b, a) {
  return r < 255 - d && g < 255 - d && b < 255 - d;
}


// 用 canvas 裁剪 & 縮放圖像,導出為 base64
ctx.drawImage(offscreenCanvas, x, y, w, h, 0, 0, 96, 96);
canvas.toDataURL('image/jpeg');           
手把手教你打造前端智能圖示識别工具背景術語簡介開始行動吧總結

生成一張圖檔的邏輯就寫完了。改造一下,周遊不同圖示、不同字号,可以得到全量的樣本:

const fontStep = 1;
const fontSize = [20, 96];


labels.map((labelName) => {
  // 周遊不同的字号繪制圖示
  for (let i = fontSize[0]; i <= fontSize[1]; i += fontStep) {
    // ...before
    offscreenCtx.font = `${i}px NextIcon`;
    // 其它邏輯
  }
});           

通過 Blob 将資料作為一個 json 下載下傳:

const resultData = /* 生成全量資料 */;
const aLink = document.createElement('a');
const blob = new Blob([JSON.stringify(resultData, null, 2)], { type : 'application/json' });

aLink.download = 'icon.json';
aLink.href = URL.createObjectURL(blob);
aLink.click();           

這樣就得到了包含幾萬張(350個圖示,每個分類約70張圖)樣本圖檔的大 json,大概長這樣:

[
  {
    "name": "smile",
    "data": [
      {
        "url": "data:image/jpeg;base64,/9j/4AA...IkB//9k=",
        "size": 20
      },
      {
        "url": "data:image/jpeg;base64,/9j/4AA...JAf//Z",
        "size": 21
      },
      ...
]
  },
]           

最後寫一個簡單的 node 程式,把每個分類的樣本按照訓練集70%,驗證集20%,測試集10%的比例拆分打散并存儲為圖檔檔案。

--- train
  |-- smile
    |-- smile_3.jpg
    |-- smile_7.jpg
  |-- cry
    |-- cry_2.jpg
    |-- cry_8.jpg
  ...
--- validation
  |-- smile
  |-- cry
  ...
--- test
  |-- smile
  |-- cry
  ...           

這樣我們就得到了完整的訓練樣本,而且生成速度很快,運作一遍隻要1分鐘左右。然後把三個目錄一起打包成一個 zip 檔案即可,因為下一步訓練隻支援 zip 格式。

模型訓練

機器學習工具有很多種,作為一個前端,我最終選擇使用 Pipcook 來訓練。

Pipcook 項目是一個開源工具集,它能讓 Web 開發者更好地使用機器學習,進而開啟和加速前端智能化時代!(

https://alibaba.github.io/pipcook/#/zh-cn/)

Pipcook 的安裝和教程看官網即可,要注意目前隻支援 Mac & Linux,Windows 暫時無法使用(Windows 可以使用 Tensorflow.js 訓練)。

寫一份 pipcook 的配置項:

{
  "plugins": {
    "dataCollect": {
      "package": "@pipcook/plugins-image-classification-data-collect",
      "params": {
        "url": "file://絕對路徑,指向上一步打包的檔案.zip"
      }
    },
    "dataAccess": {
      "package": "@pipcook/plugins-pascalvoc-data-access"
    },
    "dataProcess": {
      "package": "@pipcook/plugins-tfjs-image-classification-process",
      "params": {
        "resize": [224, 224]
      }
    },
    "modelDefine": {
      "package": "@pipcook/plugins-tfjs-mobilenet-model-define",
      "params": {}
    },
    "modelTrain": {
      "package": "@pipcook/plugins-image-classification-tfjs-model-train",
      "params": {
        "batchSize": 64,
        "epochs": 12
      }
    },
    "modelEvaluate": {
      "package": "@pipcook/plugins-image-classification-tfjs-model-evaluate"
    }
  }
}           

使用 Pipcook 配套的 Cli 工具開始訓練:

$ pipcook run 上面寫的配置項.json           

看到出現 Epochs 和 Iteration 字樣說明訓練成功開始了。

...
i [job] running modelTrain start
i start loading plugin @pipcook/plugins-image-classification-tfjs-model-train
i @pipcook/plugins-image-classification-tfjs-model-train plugin is loaded
i Epoch 0/12 start
i Iteration 0/303 result --- loss: 5.969481468200684 accuracy: 0
i Iteration 30/303 result --- loss: 5.65574312210083 accuracy: 0.015625
i Iteration 60/303 result --- loss: 5.293442726135254 accuracy: 0.0625
i Iteration 90/303 result --- loss: 4.970404624938965 accuracy: 0.03125
...           

兩萬多張樣本以上面的參數在我的 Mac 上訓練大約需要兩個小時,期間電腦的 cpu 資源都會被占用,是以要找好空閑的時間訓練。如果中途要停下來,用 control + c 是沒用的,需要先用

pipcook job list

檢視任務清單,再用

pipcook job stop <jobId>

來停止訓練。

訓練的時長與:樣本的資料量、epochs 和 batchSize 有關。

/ ======= 兩個小時後... ======= /

訓練完成,能看到最終的損失率(越低越好)和準确率(越高越好):

...
i [job] running modelEvaluate start
i start loading plugin @pipcook/plugins-image-classification-tfjs-model-evaluate
i @pipcook/plugins-image-classification-tfjs-model-evaluate plugin is loaded
i Evaluate Result: loss: 0.05339580587460659 accuracy: 0.9850694444444444
...           

如果損失率大于 0.2,準确率低于 0.8,那訓練的效果就不太好了,需要調整參數或樣本,然後重新訓練。

同時 pipcook 會在配置項 json 同目錄下建立一個 output 檔案夾,裡面包含了我們需要的模型:

output
  |-- logs    # 訓練日志檔案夾
  |-- model   # 模型檔案夾,裡面兩個檔案就是最終需要的産物
      |-- weights.bin
      |-- model.json
  |-- metadata.json    # 元資訊
  |-- package.json     # 項目資訊
  |-- index.js         # 預設入口檔案
  |-- boapkg.js        # 輔助檔案           

模型使用

因為用的 Pipcook 插件底層調用 Tensorflow.js 進行訓練,是以模型可以直接在前端頁面運作。

我們先把生成的

model.json

weights.bin

放在同一目錄下存好。然後找到

metadata.json

中的

output.dataset

字段,是個 Json 字元串,反序列化後找到的

labelArray

屬性的值并且存下來:

// 目前這個順序是随機生成的,和樣本生成時的順序不一樣,不要混淆了
const labelArray = ["col-before","h1","solidDown","add-test",...];           

準備就緒,隻要再寫一些 Tensorflow.js 代碼就可以進行識别了。

import * as tf from '@tensorflow/tfjs';


const modelUrl = 'model.json 的通路位址';
// 加載模型
model = await tf.loadLayersModel(modelUrl);


// 對輸入圖像裁剪
const { x, y, width: w, height: h } = getCutPosition(imgW, imgH, offscreenCtx.getImageData(0, 0, imgW, imgH).data, 'white');
ctx.drawImage(offscreenCanvas, x, y, w, h, 0, 0, cutSize, cutSize);


// 圖像轉化為 tensor
const imgTensor = tf.image
  .resizeBilinear(tf.browser.fromPixels(canvas), [224, 224])
  .reshape([1, 224, 224, 3]);

// 模型識别
const pred = model.predict(imgTensor).arraySync()[0];


// 找出相似度最高的 5 項
const result = pred.map((score, i) => ({ score, label: labelArray[i] }))
  .sort((a, b) => b.score - a.score)
  .slice(0, 5);           

大功告成

手把手教你打造前端智能圖示識别工具背景術語簡介開始行動吧總結

現在可以開始體驗圖示識别的能力,享受機器學習帶來的便利了。這是一個純前端工具,無需額外後端服務,可以在靜态網站上部署,非常适合在元件庫網站中查找圖示的場景。團隊有自己的圖示庫也完全沒問題,隻要按照步驟走,就能訓練出專屬的模型。

完整代碼見:

https://github.com/maplor/iconcook

總結

從開始寫代碼到模型能用花了一個周末加兩個晚上,而搭建環境和訓練模型的時間占了很大比例。Pipcook 雖然使用簡單,省去了很多工作,但入門也有不少坑:文檔稀少,插件的參數隻有看源碼才明白,運作過程有一些潛規則需要不斷試錯。希望 Pipcook 的文檔能及時更新和維護。

如果有什麼疑問可以在評論指出,歡迎大家體驗交流~

常見問題

圖示庫如果有 新增/修改 圖示怎麼辦?答:需要重新訓練模型。

參考資料

關于我們

本文作者來自于阿裡雲TxD團隊,是由技術開發、體驗設計、品牌創意構成的綜合體驗團隊,我們一直緻力于雲産品基礎建設的探索與發展。坐标:杭州/北京。聯系我們:[email protected]

手把手教你打造前端智能圖示識别工具背景術語簡介開始行動吧總結

繼續閱讀