文 / Zaid Alyafeai
我們將創建一個簡單的工具來識別圖紙并輸出當前圖紙的名稱, 此應用程式將直接在瀏覽器上運行,無需任何安裝,我們會使用 Google Colab 來訓練模型,并使用 TensorFlow.js 在瀏覽器上部署它,

【想獲取 TensorFlow js. 視頻教程,請前往 Bilibili,TensorFlow 渠道查看:https://www.bilibili.com/video/BV1D54y1p7PQ】
代碼和演示
在 GitHub 上找到現場演示和代碼, 另外,請務必在此處測驗 Google Colab 上的 notebook,
注:此處鏈接
https://colab.research.google.com/github/zaidalyafeai/zaidalyafeai.github.io/blob/master/sketcher/Sketcher.ipynb
資料集
我們將使用 CNN 識別不同型別的圖樣, CNN 將在 Quick Draw 資料集上進行訓練, 該資料集包含大約 345 個類別 5000 萬個圖樣,

類的子集
傳遞途徑
我們將使用 Keras 在 Google Colab 的 GPU 上免費訓練模型,然后使用 TensorFlow.js(tfjs)直接在瀏覽器上運行, 我在 TensorFlow.js 上制作了一個教程,煩請閱讀之后再繼續, 這是該專案的傳遞途徑:

在 Colab 上培訓
Google 在 GPU 上提供免費處理能力, 您可以在本教程中看到如何創建筆記本和激活 GPU 編程,
輸入
我們將基于 tensorflow 使用 keras
1 import os
2 import glob
3 import numpy as np
4 from tensorflow.keras import layers
5 from tensorflow import keras
6 import tensorflow as tf
加載資料
由于記憶體有限,我們不會對所有類別進行訓練, 我們只使用 100 個資料集, 每個類別的資料在 Google Cloud 上可用作形狀為 [N,784] 的 numpy 陣列,其中 N 是該特定類的影像數, 我們首先下載資料集
1 import urllib.request
2 def download():
3
4 base = 'https://storage.googleapis.com/quickdraw_dataset/full/numpy_bitmap/'
5 for c in classes:
6 cls_url = c.replace('_', '%20')
7 path = base+cls_url+'.npy'
8 print(path)
9 urllib.request.urlretrieve(path, 'data/'+c+'.npy')
由于記憶體有限,我們只會將每個類別中的 5000 張影像加載到記憶體, 還保留 20% 的未經測驗的資料
1 def load_data(root, vfold_ratio=0.2, max_items_per_class= 5000 ):
2 all_files = glob.glob(os.path.join(root, '*.npy'))
3
4 #initialize variables
5 x = np.empty([0, 784])
6 y = np.empty([0])
7 class_names = []
8
9 #load a subset of the data to memory
10 for idx, file in enumerate(all_files):
11 data = np.load(file)
12 data = data[0: max_items_per_class, :]
13 labels = np.full(data.shape[0], idx)
14
15 x = np.concatenate((x, data), axis=0)
16 y = np.append(y, labels)
17
18 class_name, ext = os.path.splitext(os.path.basename(file))
19 class_names.append(class_name)
20
21 data = None
22 labels = None
23
24 #separate into training and testing
25 permutation = np.random.permutation(y.shape[0])
26 x = x[permutation, :]
27 y = y[permutation]
28
29 vfold_size = int(x.shape[0]/100*(vfold_ratio*100))
30
31 x_test = x[0:vfold_size, :]
32 y_test = y[0:vfold_size]
33
34 x_train = x[vfold_size:x.shape[0], :]
35 y_train = y[vfold_size:y.shape[0]]
return x_train, y_train, x_test, y_test, class_names
預處理資料
我們預處理資料準備開始訓練,
1 # Reshape and normalize
2 x_train = x_train.reshape(x_train.shape[0], image_size, image_size, 1).astype('float32')
3 x_test = x_test.reshape(x_test.shape[0], image_size, image_size, 1).astype('float32')
4
5 x_train /= 255.0
6 x_test /= 255.0
7
8 # Convert class vectors to class matrices
9 y_train = keras.utils.to_categorical(y_train, num_classes)
10 y_test = keras.utils.to_categorical(y_test, num_classes)
創建模型
我們將創建一個簡單的 CNN, 請注意,引數數量越少,模型越簡單越好, 實際上,我們將在瀏覽器轉換后運行模型,并且我們希望讓模型快速運行并進行預測, 以下模型包含 3 個轉換層和 2 個密集層,
1 # Define model
2 model = keras.Sequential()
3 model.add(layers.Convolution2D(16, (3, 3),
4 padding='same',
5 input_shape=x_train.shape[1:], activation='relu'))
6 model.add(layers.MaxPooling2D(pool_size=(2, 2)))
7 model.add(layers.Convolution2D(32, (3, 3), padding='same', activation= 'relu'))
8 model.add(layers.MaxPooling2D(pool_size=(2, 2)))
9 model.add(layers.Convolution2D(64, (3, 3), padding='same', activation= 'relu'))
10 model.add(layers.MaxPooling2D(pool_size =(2,2)))
11 model.add(layers.Flatten())
12 model.add(layers.Dense(128, activation='relu'))
13 model.add(layers.Dense(100, activation='softmax'))
14 # Train model
15 adam = tf.train.AdamOptimizer()
16 model.compile(loss='categorical_crossentropy',
17 optimizer=adam,
18 metrics=['top_k_categorical_accuracy'])
19 print(model.summary())
適配,驗證和測驗
之后,我們基于 5 個 epochs 和 256 個 batch 訓練模型,
1 #fit the model
2 model.fit(x = x_train, y = y_train, validation_split=0.1, batch_size = 256, verbose=2, epochs=5)
3
4 #evaluate on unseen data
5 score = model.evaluate(x_test, y_test, verbose=0)
6 print('Test accuarcy: {:0.2f}%'.format(score[1] * 100))
以下是訓練的結果

測驗精度為 92.20%,
準備 Web 格式的模型
在我們對模型的準確性感到滿意之后,我們將其保存以便進行轉換
1 model.save('keras.h5')
我們安裝了 tfjs 包進行轉換
1 !pip install tensorflowjs
之后我們轉換該模型
1 !mkdir model
2 !tensorflowjs_converter --input_format keras keras.h5 model/
這將創建一些權重檔案以及包含模型體系結構的 json 檔案,
壓縮模型,準備將其下載到本地計算機
1 !zip -r model.zip model
最后下載模型
1 from google.colab import files
2 files.download('model.zip')
瀏覽器推斷
在本節中,我們將展示如何加載模型并進行推理, 假設我們有一個尺寸為 300 x 300 的畫布, 關于界面和 TensorFlow.js 部分我就不一一詳細展開了,
加載模型
為了使用 TensorFlow.js 首先我們使用以下的腳本
1 <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest"> </script>
你需要在本地計算機上運行服務器來承載權重檔案, 可以像我一樣在專案上創建一個 apache 服務器或在 GitHub 上托管頁面,
之后,將模型加載到瀏覽器
1 model = await tf.loadModel('model/model.json')
使用 await 等待瀏覽器加載模型,
預處理
我們需要在進行預測之前預處理資料, 首先從畫布中獲取影像資料
1 //the minimum boudning box around the current drawing
2 const mbb = getMinBox()
3 //cacluate the dpi of the current window
4 const dpi = window.devicePixelRatio
5 //extract the image data
6 const imgData = canvas.contextContainer.getImageData(mbb.min.x * dpi, mbb.min.y * dpi,
7 (mbb.max.x - mbb.min.x) * dpi, (mbb.max.y - mbb.min.y) * dpi);
我們以后再解釋 getMinBox(), 變數 dpi 用于根據螢屏像素的密度拉伸畫布,
我們將畫布的當前影像資料轉換為張量,調整大小并進行標準化,
1 function preprocess(imgData)
2 {
3 return tf.tidy(()=>{
4 //convert the image data to a tensor
5 let tensor = tf.fromPixels(imgData, numChannels= 1)
6 //resize to 28 x 28
7 const resized = tf.image.resizeBilinear(tensor, [28, 28]).toFloat()
8 // Normalize the image
9 const offset = tf.scalar(255.0);
10 const normalized = tf.scalar(1.0).sub(resized.div(offset));
11 //We add a dimension to get a batch shape
12 const batched = normalized.expandDims(0)
13 return batched
14 })
15 }
對于預測,我們使用 model.predict 這將回傳形狀為 [N,100]的概率,
1 const pred = model.predict(preprocess(imgData)).dataSync()
然后我們可以使用簡單的函式來找到前 5 個概率,
提高準確率
請記住,我們的模型接受形狀為 [N,28,28,1] 的張量, 我們的繪圖畫布尺寸為 300 x 300,對于繪圖來說可能是太大了,或者說,用戶可能想繪制一個小圖, 最好裁剪到僅包含當前圖形大小的框, 為此,我們通過查找左上角和右下角來提取圖形周圍的最小邊界框
1 //record the current drawing coordinates
2 function recordCoor(event)
3 {
4 //get current mouse coordinate
5 var pointer = canvas.getPointer(event.e);
6 var posX = pointer.x;
7 var posY = pointer.y;
8
9 //record the point if withing the canvas and the 10 mouse is pressed
if(posX >=0 && posY >= 0 && mousePressed)
11 {
12 coords.push(pointer)
13 }
14 }
15
16 //get the best bounding box by finding the top left and bottom right cornders
17 function getMinBox(){
18
19 var coorX = coords.map(function(p) {return p.x});
20 var coorY = coords.map(function(p) {return p.y});
21 //find top left corner
22 var min_coords = {
23 x : Math.min.apply(null, coorX),
24 y : Math.min.apply(null, coorY)
25 }
26 //find right bottom corner
27 var max_coords = {
28 x : Math.max.apply(null, coorX),
29 y : Math.max.apply(null, coorY)
30 }
31 return {
32 min : min_coords,
33 max : max_coords
34 }
35 }
測驗繪圖
下面是大家初次繪圖出現的最頻繁的圖樣, 我用滑鼠畫了所有的圖樣, 如果使用筆,準確性會更高,

想了解 TensorFlow js. 組件更多實操案例,請前往 Bilibili Google 中國—— TensorFlow 頻道查看 Made With TensorFlow js. 中文系列視頻,
https://www.bilibili.com/video/BV1D54y1p7PQ
有關 TensorFlow 更多資訊,可前往 TensorFlow 中國官網(tensorflow.google.cn)查看,或掃描下方二維碼,關注 TensorFlow 官方公眾號!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/289822.html
標籤:AI
上一篇:【史詩級干貨長文】集成學習演算法
