你女朋友生你氣是因為你還沒學深度學習

你女朋友生你氣是因為你還沒學深度學習

來自專欄 AI Butterfly151 人贊了文章

又惹女朋友不開心了?最近的幾個節日光顧打遊戲擼代碼陪基友,沒有顧上妹子心情,也沒送禮物。你發現你越來越摸不清她的情緒,上一秒說你不用來的,下一秒就為你的缺席而大發脾氣;明明嘴上說隨便看什麼,卻埋怨你選的電影無聊透了。作為理科技術宅,你意識到和女朋友的感情出現了很大的問題。這時候,你決定:

? 給女朋友買買買

? 學習 tensorflowjs 預測女朋友心情


首先介紹 Tensorflow.js。我個人平時用的是 Python 版的 Tensorflow 來編寫人工智慧的演算法,Python 的語法靈活,相關工具也非常齊全,雖然說類型系統糟糕,但又不是不能用!Google 公司在 TensorFlow Dev Summit 2018 中推出了基於瀏覽器的 Tensorflow.js 深度學習框架,現在你不再需要配置 Python 環境與安裝各種依賴庫,還可以直接使用 gpu 進行加速計算,而且還可以用上 Typescript !超幸福。

今天用 Tensorflow.js 做的是情緒識別器(暫定),本質是一個基於卷積神經網路的圖片分類器,不過這個圖片來源是你的電腦攝像頭,你需要做就是盡情展示你的顏藝了。今天有 @顧兮 小姐姐來演示 tensorflowjs 的使用哦,小姐姐是一個超好看的 coser ,大家記得關注她。

https://www.zhihu.com/video/992520664619786240

這個教程中,我使用 Angluar 製作展示界面,當然你不會 Angular 也沒關係,這裡我只展現最重要的邏輯代碼,至於界面來說,丑一點倒是沒什麼大不了嘛!反正小姐姐好看呀!


教程大綱

  • 原理
  • 攝像頭採集
  • 卷積神經網路設計
  • 訓練與預測

我將按以上的順序寫這個教程,注意,我的文章不可能寫很長,所以我預設你已經有了相應的神經網路知識,你不需要知道所有的數學原理,但是至少要對卷積操作,池化層,全連接網路有一定了解。


原理

以往我們的神經網路代碼都是使用 python 等語言進行編寫,運行在伺服器上,如今智能終端越來越多,對人工智慧技術的應用也越來越普遍,如果人工智慧演算法能直接在用戶的手機或者瀏覽器中進行計算,則可以大幅提高計算延遲,減輕伺服器壓力。但是直接在用戶的終端上運行演算法總有或多或少的問題,比如性能的限制,運行環境的限制,這方面 js 具有極大的優勢。一方面 HTML5 標準使 js 具有了訪問設備高級介面的能力,比如獲取麥克風、攝像頭、重力感測器等介面,另一方面當前 js 已經可以利用 GPU 進行計算加速,這也大大提高了運行效率。

情緒識別器(暫定)的工作原理是這樣,通過瀏覽器提供的介面獲取用戶攝像頭圖片,並由用戶錄製多張表情圖片,包含『高興』、『難過』、『嫉妒』等類別,然後使用卷積神經網路進行分類訓練。在訓練完畢後,進行預測,預測的方法是每隔一小段時間從攝像頭捕捉圖片,然後輸入到模型中輸出該幀圖片分類可能的概率分布,選取概率最高的一項作為預測結果。

攝像頭採集

我們首先需要獲取攝像頭許可權,開啟攝像頭的代碼被寫到對應的 component 類中,下面截取了主要部分:

const n = navigator as any;n.getUserMedia = n.getUserMedia ||n.webkitGetUserMedia || n.mozGetUserMedia ||n.msGetUserMedia || n.oGetUserMedia;if (navigator.getUserMedia) { navigator.getUserMedia( {video: true}, this.handleVideo.bind(this), this.handleVideoError.bind(this));}

運行這段代碼後,瀏覽器會彈出請求攝像頭的提示,如果用戶同意,即可得到攝像頭數據:我們的回調函數 handleVideo 就會被執行,函數長這樣:

handleVideo(stream: MediaStream) { (this.videoElement.nativeElement as HTMLVideoElement).srcObject = stream;}

其中 videoElement 是一個 <video></video> 對象,要注意的是一定要設置 autoplay=1 ,不然無法顯示實時視頻。

<video autoplay=1 id="videoElement" [class.active]=video_on></video>

接著就是獲取圖片信息,首先創建一個 canvas 元素用來暫存圖像:

const hiddenCanvas = document.createElement(canvas);hiddenCanvas.getContext(2d).drawImage(this.videoElement.nativeElement, 0, 0, hiddenCanvas.width, hiddenCanvas.height);const img = hiddenCanvas.getContext(2d).getImageData(0, 0, hiddenCanvas.width, hiddenCanvas.height);

最後得到的 img 是一個長度為width * height * 4 的數組,其中 width 和 height 都是 32,所以最後得到數據是一個 4096 長度的數組。

卷積神經網路設計

有了圖片數據後,就可以設計我們的分類網路了。在圖像處理中卷積神經網路非常有效,使用十分廣泛。我們首先設計兩個卷積層,在卷積層後都有池化和激活層,在經過卷積網路後,將提取到的特徵輸入到多層全連接神經網路。最終的輸出層的節點數和分類數是對應的,在應用後 softmax 後,得到每一個分類的概率值,概率在 [0, 1] 之間,所有分類相加的概率和為 1,在訓練時,使用交叉熵函數計算損失值。

對應代碼如下:

import * as tf from @tensorflow/tfjs;const model = new tf.Sequential();model.add(tf.layers.conv2d({ kernelSize: 3, inputShape: [image_width, image_height, 3], filters: 32, strides: 1, activation: relu,}));model.add(tf.layers.maxPooling2d({ strides: [2, 2], poolSize: 2,}));model.add(tf.layers.conv2d({ kernelSize: 3, filters: 32, strides: 1, activation: relu,}));model.add(tf.layers.maxPooling2d({ strides: [2, 2], poolSize: 2,}));model.add(tf.layers.flatten());model.add(tf.layers.dense({ units: 128, activation: relu,}));model.add(tf.layers.dense({ units: 32, activation: relu,}));model.add(tf.layers.dense({ units: num_of_class, activation: softmax,}));const optimizer = tf.train.adam(learning_rate);model.compile({ optimizer: optimizer, loss: categoricalCrossentropy, metrics: [accuracy],});

細心的同學會發現,網路第一層的 inputShape 是 [image_width, image_height, 3] ,而不是之前採集的 width * height * 4,因為輸入的圖片第四個顏色通道是 alpha ,這個通道在我們從 canvas 中獲取過來的時候始終是 1,所以沒有意義,我們要去除。tensorflow.js 提供了 fromPixels 方法讓我們可以選擇前三個顏色通道,與此同時我們還要對輸入數據歸一化到 [-1, 1] 的區間上,以便網路訓練:

const image_xs = xs.map((d) => { const pixels = tf.fromPixels(d, 3); return pixels.toFloat().div(tf.scalar(127)).sub(tf.scalar(1));});

其中 xs 是一個類型為 ImageData[] 的攝像頭圖片數組。

輸入的標籤信息當前設置為了四類,分別是「開心」、「難過」、「吃醋」和「星星眼」,輸入的標籤使用 one-hot 格式進行編碼:

  • 開心: [1, 0, 0, 0]
  • 難過: [0, 1, 0, 0]
  • 吃醋: [0, 0, 1, 0]
  • 星星眼:[0, 0, 0, 1]

訓練與預測

最終訓練時使用 fit 方法,定義 batch size 為 16,計算 20 輪。這裡我們只管傳入所有數據就可以了,tensorflow 會自動將數據打亂次序。

model.fit(x_data, y_data, { batchSize: 16, epochs: 20,}});

在預測時,同樣採集一幀攝像頭數據,reshape 為 [1, this.image_height, this.image_width, 3],其中第一項是 batch size,我們預測的時候一次只預測一張圖片,所以這裡我們直接設為 1。

let pixels = tf.fromPixels(image, 3).toFloat().div(tf.scalar(127)).sub(tf.scalar(1));pixels = pixels.reshape([1, this.image_height, this.image_width, 3]);const result = this.model.predict(pixels) as any;probs = Array.from(result.dataSync());

到這一步,我們就得到 probs 了,這個變數存儲著輸入圖片所對應的概率分布。

?接著只要遍歷一遍就可以知道哪一個類的概率最高,對應就可以知道當前你女朋友的心情啦!是不是超簡單超方便?會深度學習真是太棒了!

體驗地址:情緒識別器(第一次載入會比較慢,你的電腦還需要有攝像頭哦)

源代碼:moevis/what-is-my-girlfriend-thinking


如果對編程感興趣,歡迎加入我們的討論群:745478917,一起來學習魔法吧。也可以關注公眾號『代碼律動codewaving』第一時間獲得文章推送。


推薦閱讀:

深度學習框架TensorFlow學習筆記(1)
劉光聰 | TensorFlow:揭示多語言編程的奧秘
用TensorFlow寫個簡單的神經網路
Install TensorFlow with Anaconda
如何用40行代碼寫出一個神經網路?

TAG:TensorFlow | 深度學習DeepLearning | Python |