根據(jù) GitHub Octoverse 2017 報(bào)告,JavaScript 是過去一年中 GitHub 最流行的編程語言。根據(jù) pull requests 的數(shù)量,JavaScript 的體量與 Python、Java 以及 Go 語言的總和相當(dāng)。
JavaScript 已經(jīng)征服了 Web,并在服務(wù)器、移動(dòng)電話、桌面和其他平臺(tái)上取得了進(jìn)展。
與此同時(shí),GPU 加速的使用已經(jīng)遠(yuǎn)遠(yuǎn)超出了計(jì)算機(jī)圖形學(xué)的范圍,它現(xiàn)在已經(jīng)成為機(jī)器學(xué)習(xí)的一個(gè)組成部分。
訓(xùn)練深層神經(jīng)網(wǎng)絡(luò)是一個(gè)計(jì)算密集型過程,深度神經(jīng)網(wǎng)絡(luò)在機(jī)器智能的許多重要領(lǐng)域得到了當(dāng)前最優(yōu)結(jié)果。
本文著眼于這些趨勢(shì)的持續(xù)融合,并概述了將 GPU 加速的神經(jīng)網(wǎng)絡(luò)引入 JavaScript 的一些項(xiàng)目。
概述
本文列出的所有項(xiàng)目都是正被社區(qū)積極維護(hù)的,它們?cè)?GitHub 上有著數(shù)千 stars,并且通過 NPM 或 CDN 進(jìn)行分發(fā)。
它們都是通過 WebGL 在瀏覽器中實(shí)現(xiàn) GPU 加速的,如果沒有合適的顯卡,則返回到 CPU 模式。
本概述不包含旨在運(yùn)行現(xiàn)有模型(尤其是使用 Python 訓(xùn)練的模型)的庫(kù)。
最后,有 4 個(gè)項(xiàng)目被列入清單。
盡管 deeplearn.js 的特征集是面向神經(jīng)網(wǎng)絡(luò)的,但是它也可被看作是一個(gè)通用的機(jī)器學(xué)習(xí)框架。Propel 是一個(gè)用于科學(xué)計(jì)算的庫(kù),提供自動(dòng)微分功能。gpu.js 提供了在 GPU 上運(yùn)行 JavaScript 函數(shù)的便捷方式。Brain.js 是一個(gè)較老的神經(jīng)網(wǎng)絡(luò)庫(kù)的延續(xù),它使用 gpu.js 來完成硬件加速。
Deeplearn.js
Deeplearn.js 是以上四個(gè)項(xiàng)目中最流行的,被描述為「用于機(jī)器智能的硬件加速 JavaScript 庫(kù)」。它由 Google Brain 團(tuán)隊(duì)和一個(gè)超過 50 位貢獻(xiàn)者的社區(qū)共同支持。兩位主要作者是 Daniel Smilkov 和 Nikhil Thorat.
import * as dl from 'deeplearn'
const xs = inputXs.as4D(-1, IMAGE_HEIGHT, IMAGE_WIDTH, 1)
const conv1Weights = dl.variable(
dl.randomNormal([FILTER_HEIGHT, FILTER_WIDTH, 1, NUMBER_FILTERS], 0, 0.1) as dl.Tensor4D)
const layer1 = dl.tidy(() => {
return xs.conv2d(conv1Weights, 1, 'same')
.relu()
.maxPool([2, 2], STRIDES, PADDING)
})
deeplearn.js 中卷積層的定義
deeplearn.js 是仿照 TensorFlow 用 TypeScript 寫成的。deeplearn.js 支持由 Google Brain 主要開源項(xiàng)目提供的一個(gè)功能子集。API 基本上擁有 3 個(gè)部分(API http://www.deeplearnjs.org/docs/api/index.html)。
第一部分包括用來創(chuàng)建、初始化以及變換張量的函數(shù)(http://www.deeplearnjs.org/docs/api/index.html#Tensors-Creation),用類似數(shù)組的結(jié)構(gòu)來保存數(shù)據(jù)。
第二部分提供了在張量上執(zhí)行的操作(http://www.deeplearnjs.org/docs/api/index.html#Operations-Arithmetic),包括基本的數(shù)學(xué)運(yùn)算、規(guī)約(reduction)、正則化以及卷積。對(duì)循環(huán)神經(jīng)網(wǎng)絡(luò)的支持目前還處于初級(jí)階段,但是已包括 LSTM 單元的堆疊(http://www.deeplearnjs.org/docs/api/index.html#dl.multiRNNCell)。
API 的第三部分圍繞模型訓(xùn)練展開。所有流行優(yōu)化器,從隨機(jī)梯度下降到 Adam 都包含在其中。不過,目前 reference 中提及的損失函數(shù)只有交叉熵?fù)p失函數(shù)。
API 其他部分用來進(jìn)行環(huán)境設(shè)置和資源管理。
可以通過 headless-gl(https://github.com/stackgl/headless-gl0)在 node.js 中實(shí)現(xiàn) GPU 加速的實(shí)驗(yàn)(參見 issue #49,https://github.com/PAIR-code/deeplearnjs/issues/49)。
項(xiàng)目網(wǎng)站有很多優(yōu)秀的 demo(http://www.deeplearnjs.org/index.html#demos),包括使用循環(huán)神經(jīng)網(wǎng)絡(luò)進(jìn)行鋼琴演奏、用來構(gòu)建模型的可視化界面,以及基于 SqueezeNet(一個(gè)使用較少參數(shù)的圖像分類器)的 webcam 應(yīng)用。
PropelJS
PropelJS 被描述為「可微分編程的 JavaScript」。這份工作由主要作者 Ryan Dahl 和 Bert Belder 以及其他 11 位貢獻(xiàn)者完成。
import * as pr from "propel"
export async function train(maxSteps = 0) {
const ds = pr.dataset("mnist/train").batch(128).repeat(100)
const exp = await pr.experiment("exp001")
for (const batchPromise of ds) {
const { images, labels } = await batchPromise
exp.sgd({ lr: 0.01 }, (params) =>
images.rescale([0, 255], [-1, 1])
.linear("L1", params, 200).relu()
.linear("L2", params, 100).relu()
.linear("L3", params, 10)
.softmaxLoss(labels))
if (maxSteps && exp.step >= maxSteps) break
}
}
在 MNIST 數(shù)據(jù)集上使用 Propel 訓(xùn)練一個(gè)三層的前饋神經(jīng)網(wǎng)絡(luò)。
自動(dòng)微分(AD)是這個(gè)項(xiàng)目的核心,它使得我們無需手動(dòng)指定導(dǎo)數(shù)。給定一個(gè)由支持的張量運(yùn)算定義的函數(shù) f(x),它的梯度函數(shù)可以使用 grad(http://propelml.org/docs/#grad)得到。多變量的情況可以使用 multigrad 完成(http://propelml.org/docs/#multigrad)。
除了自動(dòng)微分之外,目前尚不清楚該項(xiàng)目的方向。雖然網(wǎng)站上提到其目標(biāo)是成為「類似 numpy 的基礎(chǔ)架構(gòu)」,但該項(xiàng)目目前仍在開發(fā)中,并且包含與神經(jīng)網(wǎng)絡(luò)(http://propelml.org/docs/#conv2d)和計(jì)算機(jī)視覺(http://propelml.org/docs/#imread)相關(guān)的功能。npy 文件的內(nèi)容可以通過 load 函數(shù)(http://propelml.org/docs/#load)進(jìn)行解析,并作為張量使用。
在瀏覽器環(huán)境中,PropelJS 利用了 deeplearn.js 中的 WebGL 功能。對(duì)于節(jié)點(diǎn)中的 GPU 加速,該項(xiàng)目則使用了 TensorFlow 的 C API。
gpu.js
雖然我的大部分經(jīng)驗(yàn)是使用 CUDA 而不是 WebGL,但我可以證明 GPU 編程的耗時(shí)性。因此,當(dāng)我遇到 gpu.js 時(shí),我感到非常意外。該項(xiàng)目在 GitHub 上擁有約 5700 個(gè) stars,在知名度方面與 deeplearn .js 相當(dāng),共有 18 位貢獻(xiàn)者。Robert Plummer 是主要作者。
import GPU from 'gpu.js'
const gpu = new GPU()
const multiplyMatrix = gpu.createKernel(function(a, b) {
var sum = 0;
for (var i = 0; i < 512; i++) {
sum += a[this.thread.y][i] * b[i][this.thread.x];
}
return sum;
}).setOutput([512, 512])
使用 gpu.js 進(jìn)行矩陣乘法運(yùn)算,相當(dāng)于 GPU 編程中的 Hello World!
在當(dāng)前語境中,內(nèi)核是在 GPU 而不是 CPU 上執(zhí)行的函數(shù)。使用 gpu.js,內(nèi)核可以用 JavaScript 的子集(https://github.com/gpujs/gpu.js#creating-and-running-functions)編寫。然后編譯代碼并在 GPU 上運(yùn)行。幾周前,gpu.js 支持基于 OpenCL 的 Node.JS(https://github.com/mikeseven/node-opencl/issues/55)。
數(shù)字和最多具有三維的數(shù)組被用作輸入和輸出。除了基本的數(shù)學(xué)運(yùn)算之外,gpu.js 還支持局部變量、循環(huán)和 if/else 語句。
為了實(shí)現(xiàn)代碼重用并允許更多模塊化設(shè)計(jì),你們可以注冊(cè)自定義函數(shù) ( https://github.com/gpujs/gpu.js#adding-custom-functions #),然后從內(nèi)核代碼中使用。
在內(nèi)核的 JavaScript 定義中,this 對(duì)象提供線程標(biāo)識(shí)符,并存儲(chǔ)在實(shí)際內(nèi)核里是常量、在外部是動(dòng)態(tài)變量的值。
該項(xiàng)目專門研究加速 JavaScript 函數(shù),并不試圖提供神經(jīng)網(wǎng)絡(luò)框架。為此,我們可以求助一個(gè)依賴 gpu.js 的庫(kù)。
Brain.js
Brain.js 繼承自 harthur/brain(https://github.com/harthur/brain),一個(gè)可以回溯至 2010 年的 repo。
import brain from 'brain.js'
const network = new brain.recurrent.RNN()
const data = [
{input: [0, 0], output: [0]},
{input: [0, 1], output: [1]},
{input: [1, 0], output: [1]},
{input: [1, 1], output: [0]}
]
network.train(data)
共有近 30 人對(duì)這兩個(gè) repo 做出了貢獻(xiàn)。
對(duì) GPU 加速神經(jīng)網(wǎng)絡(luò)的支持基于 GPU.js,這可以算得上該項(xiàng)目近期最重要的進(jìn)展了。
除了前饋網(wǎng)絡(luò)之外,Brain.js 還包括三種重要 RNN 類型的實(shí)現(xiàn)(https://github.com/BrainJS/brain.js#neural-network-types):經(jīng)典 Elman 網(wǎng)絡(luò)、LSTM,以及具備門控循環(huán)單元的近期網(wǎng)絡(luò)。
該 repo 包含的 demo 處于早期階段。源代碼中還有另外兩個(gè)演示 ( https://github.com/BrainJS/brain.js/tree/develop/examples),其中一個(gè) demo 涉及檢測(cè)用 ASCII 碼繪制的字符。
針對(duì)機(jī)器學(xué)習(xí)的加速 JavaScript 庫(kù)有很多有趣的應(yīng)用。
在線課程可以將與機(jī)器學(xué)習(xí)或 GPU 計(jì)算相關(guān)的練習(xí)直接集成到 web 應(yīng)用程序中。學(xué)生不必跨不同的操作系統(tǒng)和軟件版本去設(shè)置單獨(dú)的開發(fā)環(huán)境。
許多基于神經(jīng)網(wǎng)絡(luò)的 demo 可以更容易地部署,并且不再需要服務(wù)器端 API。
對(duì)機(jī)器學(xué)習(xí)感興趣的 JavaScript 開發(fā)者可以充分利用他們的專業(yè)技能,在集成問題上花費(fèi)更少的時(shí)間。
此外,客戶端上的可用計(jì)算資源應(yīng)該被更好地利用。畢竟,并非所有的顯卡都一直用于虛擬現(xiàn)實(shí)和挖礦。
需要說清楚,我現(xiàn)在并不主張將本文中提到的庫(kù)用于任務(wù)關(guān)鍵型神經(jīng)網(wǎng)絡(luò)。Python 生態(tài)系統(tǒng)仍然是大多數(shù)應(yīng)用程序的首選。
然而,過去 12 個(gè)月取得的進(jìn)展確實(shí)令人鼓舞。一年前既沒有 deeplearn.js,也沒有 Propel。彼時(shí) gpu.js repo 中的活動(dòng)水平相對(duì)較低,Brain.js 也不支持 GPU 加速。
隨著時(shí)間的推移,這些項(xiàng)目將在某些方面與已建立的框架發(fā)生競(jìng)爭(zhēng),并催生出 JavaScript 完美適合的全新應(yīng)用。
https://towardsdatascience.com/gpu-accelerated-neural-networks-in-javascript-195d6f8e69ef