如何在瀏覽器使用synaptic.js訓練簡單的神經(jīng)網(wǎng)絡(luò)推薦系統(tǒng)
本文利用 synaptic 庫構(gòu)建簡單的神經(jīng)網(wǎng)絡(luò),并在瀏覽器中實現(xiàn)訓練過程。該神經(jīng)網(wǎng)絡(luò)可以和其他框架共同打造一款簡單的推薦系統(tǒng)應(yīng)用。這種在瀏覽器上訓練的神經(jīng)網(wǎng)絡(luò)因為將計算任務(wù)分配到各個終端設(shè)備,所以服務(wù)器的壓力大大降低。此外,在終端上訓練的神經(jīng)網(wǎng)絡(luò)也大大保護了用戶的隱私。機器之心對本文做了簡要介紹,全部代碼請查看 Github 項目地址。
項目地址:https://github.com/markselby9/ml-in-browser
用 JS 進行機器學習?為什么不呢!
一、項目概覽
我們將構(gòu)建一個基于人工神經(jīng)網(wǎng)絡(luò)的簡單 Web 應(yīng)用程序推薦系統(tǒng)。該應(yīng)用程序包含兩頁,***頁顯示書籍,第二頁顯示電影。用戶可以在***頁中選擇他或她感興趣的書籍,當他點擊下一頁按鈕時,我們實際上可以在后臺預測他可能會感興趣的電影。在用戶選擇他喜歡的電影之后,我們會給用戶顯示他所選擇的結(jié)果,以及我們對他的選擇的預測。一些顯示截屏如下所示:
在***頁中選擇感興趣的書籍
在第二頁中選擇感興趣的電影
將選擇與預測結(jié)果進行比較
這個應(yīng)用程序使用 Vue.js 和 ElementUI(Vue.js 的 UI 框架)構(gòu)建,并使用 synaptic 庫構(gòu)建神經(jīng)網(wǎng)絡(luò)。
有什么優(yōu)點?
該項目的優(yōu)勢可以歸結(jié)如下:
- 我們將模型訓練工作轉(zhuǎn)移到前端而不是后端,這不僅降低了服務(wù)器的壓力,同時還將一些計算任務(wù)分配給許多客戶端。由于 npm 社區(qū)提供許多神經(jīng)網(wǎng)絡(luò)相關(guān)的 Javascript 庫,這種方法是可行的。
- 我們保護用戶的隱私。用戶的數(shù)據(jù)并沒有實際被上傳到服務(wù)器,當服務(wù)器更新訓練模型時,他們的數(shù)據(jù)對于服務(wù)器是匿名的。
二、神經(jīng)網(wǎng)絡(luò)簡要介紹
首先,這里使用的神經(jīng)網(wǎng)絡(luò)是最基礎(chǔ)的人工神經(jīng)網(wǎng)絡(luò),我們決定僅使用用戶的選擇作為輸入和輸出集。本節(jié)使用的圖像來自一個介紹神經(jīng)網(wǎng)絡(luò)的博客:
https://ujjwalkarn.me/2016/08/09/quick-intro-neural-networks/。
本項目涉及的概念包括:人工神經(jīng)網(wǎng)絡(luò)、神經(jīng)元、層次和訓練(反向傳播)。如果你已經(jīng)熟悉這些概念,你可以跳過此部分。人工神經(jīng)網(wǎng)絡(luò)(ANN)是一種由人腦啟發(fā)的計算模型。它由神經(jīng)元組成,神經(jīng)元是神經(jīng)網(wǎng)絡(luò)的基本單元。神經(jīng)元從其它來源接收輸入,每個輸入分配一個權(quán)重,權(quán)重根據(jù)輸入的重要程度賦予。神經(jīng)元使用激活函數(shù)作用于所有輸入的加權(quán)之和,然后給出輸出。
神經(jīng)元
單層神經(jīng)網(wǎng)絡(luò)由幾個神經(jīng)元組成,如下圖所示。神經(jīng)網(wǎng)絡(luò)可以包含或可以不包含多個隱藏層,每對相鄰層之間具有連接,這種連接通常由前面提到的權(quán)重表示。
簡單全連接神經(jīng)網(wǎng)絡(luò)的層級結(jié)構(gòu)
但是如何通過正確衡量這些權(quán)重來架構(gòu)神經(jīng)網(wǎng)絡(luò)呢?這些權(quán)重需要訓練,來達到使神經(jīng)網(wǎng)絡(luò)正常工作的要求。假設(shè)我們有一個數(shù)據(jù)表,其中包含 1000 對輸入和相應(yīng)的輸出。我們首先產(chǎn)生 0 和 1 之間的隨機數(shù)給出所有權(quán)重,然后遍歷所有數(shù)據(jù)對。在每對輸入和輸出中,我們使用神經(jīng)網(wǎng)絡(luò)的激活函數(shù)給出計算結(jié)果,并將其與實際輸出進行比較。然后我們使用反向傳播算法重新訓練網(wǎng)絡(luò)并調(diào)整權(quán)重。更新反向傳播的權(quán)重可以通過隨機梯度下降來完成,這是一種獲得***權(quán)重值的方法。
這里只是簡要介紹神經(jīng)網(wǎng)絡(luò)的原理。更詳細的內(nèi)容,請參考以下鏈接:
- https://ujjwalkarn.me/2016/08/09/quick-intro-neural-networks/
- https://en.wikipedia.org/wiki/Artificial_neural_network
- https://github.com/cazala/synaptic/wiki/Neural-Networks-101
神經(jīng)網(wǎng)絡(luò)在瀏覽器中的實現(xiàn)
最近有一些在瀏覽器中實現(xiàn)神經(jīng)網(wǎng)絡(luò)的相關(guān)研究,如:
- Deeplearnjs:https://github.com/PAIR-code/deeplearnjs
- ConvnetJS:http://cs.stanford.edu/people/karpathy/convnetjs/
- synaptic.js:http://caza.la/synaptic/
我們在這里使用 synaptic.js,因為不管對于 node.js 還是瀏覽器,它都是一個無架構(gòu)的神經(jīng)網(wǎng)絡(luò)庫。我們可以通過 github 庫中的 wiki 來檢查文檔:
https://github.com/cazala/synaptic/wiki/Architect。我們計劃在瀏覽器中實現(xiàn)所有的神經(jīng)網(wǎng)絡(luò)訓練和部分激活函數(shù),服務(wù)器(使用簡單的 node.js 和 express 搭建服務(wù)器框架)只保留包含網(wǎng)絡(luò)參數(shù)的 JSON 文件。synaptic.js 有一個方便的 API 來將神經(jīng)網(wǎng)絡(luò)解析成 JSON,并將 JSON 解析為神經(jīng)網(wǎng)絡(luò)實例。
應(yīng)用程序由 Vue.js 和 ElementUI 構(gòu)建。在創(chuàng)建主應(yīng)用程序組件的生命周期中,應(yīng)用程序從服務(wù)器獲取模型的 JSON 文件,并基于 JSON 文件構(gòu)建神經(jīng)網(wǎng)絡(luò)實例。然后該模型展示 20 張含有電影信息的卡片,讓用戶選擇他/她感興趣的項目,用戶完成選擇并單擊下一步后,網(wǎng)絡(luò)實例將調(diào)用激活函數(shù),并給出該用戶可能想要的書籍的預測值(基于 20 種書籍選項)。同時,應(yīng)用程序還展示另外 20 張包含書籍信息的卡片,讓用戶選擇。用戶點擊提交按鈕后,應(yīng)用程序會將預測的書籍列表和實際的書籍列表呈現(xiàn)給用戶,并在后臺使用新的訓練數(shù)據(jù)來反向傳播并重新訓練模型。再次訓練后,新的神經(jīng)網(wǎng)絡(luò)將被解析為 JSON 對象并發(fā)送回服務(wù)器。
下面是代碼。服務(wù)器利用簡單的 node.js 中的 I/O API 和 Express 構(gòu)建。
- app.post('/getNetwork', (req, res) => {
- if (req.body) {
- console.log(req.body);
- readJSONFromFile((network) => { // read local JSON file
- res.send({
- code: 200,
- network,
- });
- }, (err) => {
- console.log(err.toString());
- });
- }
- });
- app.post('/setNetwork', (req, res) => {
- if (req.body && req.body.networkJSON) {
- console.log(req.body);
- const { networkJSON } = req.body;
- saveJSONToFile(networkJSON, (err) => { // write to local JSON file
- if (err) {
- res.send({
- code: 500,
- err
- });
- } else {
- res.send({
- code: 200,
- });
- }
- });
- } else {
- res.send({
- code: 406,
- })
- }
- });
- app.listen(3000, () => {
- console.log('server started');
- });
以及在客戶端。在本文中我們不會介紹 DOM 的細節(jié),我們將僅關(guān)注組件 app.vue 下的腳本部分。
- created() {
- // fetch the train model from server
- thisthis.content_data = this.shuffle(book_data);
- this.loading = true;
- axios.post('http://localhost:3000/getNetwork')
- .then((response) => {
- console.log(response);
- this.loading = false;
- const networkJSON = response.data.network;
- if (networkJSON && Object.keys(networkJSON).length > 0) {
- this.$message('Received neural network from server.');
- localNetworkInstance = Network.fromJSON(networkJSON);
- } else {
- this.$message('Created a new network instance.');
- // create a new network instance
- const inputLayer = new Layer(20);
- const hiddenLayer = new Layer(20);
- const outputLayer = new Layer(20);
- inputLayer.project(hiddenLayer);
- hiddenLayer.project(outputLayer);
- localNetworkInstance = new Network({
- input: inputLayer,
- hidden: [hiddenLayer],
- output: outputLayer
- });
- }
- })
- .catch(function (error) {
- this.loading = false;
- console.log(error);
- });
- },
以上是我們在應(yīng)用程序中創(chuàng)建的生命周期(lifecycle)。它嘗試從「getNetwork」API 獲取 JSON 對象:如果它是網(wǎng)絡(luò)的可用 JSON 設(shè)置,則它將通過 synaptic 的 fromJSON 方法創(chuàng)建本地網(wǎng)絡(luò)實例;否則,它將創(chuàng)建一個新的網(wǎng)絡(luò)實例并保存到「localNetworkInstance」變量中。
當用戶在***頁單擊「下一頁」按鈕后,我們在「onClick」功能中調(diào)用激活函數(shù),并將其作為預測結(jié)果保存在 Vue 組件的數(shù)據(jù)中。然后在用戶選擇他/她感興趣的電影之后,調(diào)用重新訓練函數(shù)。
- reTrainByThisUserData() {
- // retrain the model by this user's data
- if (localNetworkInstance) {
- localNetworkInstance.propagate(learningRate, this.trainingSet.output); // propagate the network
- this.$message('Neural Network retrained!');
- const successFunc = () => {
- console.log('success');
- this.$message('Successfully sent the new Neural Network!');
- };
- const errorFunc = (error) => {
- console.log('error', error);
- this.$message(error);
- };
- this.loading = true;
- axios.post('http://localhost:3000/setNetwork', {
- networkJSON: localNetworkInstance.toJSON()
- })
- .then((response) => {
- this.loading = false;
- if (response.data && response.data.code === 200) {
- successFunc();
- } else {
- errorFunc(response.data);
- }
- })
- .catch(function (error) {
- errorFunc(error)
- });
- } else {
- this.loading = false;
- console.log('network is undefined!');
- }
- }
重新訓練過程是一個反向傳播過程,利用當前用戶的選擇作為輸入和輸出數(shù)據(jù)對。用戶對電影的選擇將成為反向傳播的數(shù)據(jù)。在反向傳播后,神經(jīng)網(wǎng)絡(luò)的權(quán)重將被調(diào)整,神經(jīng)網(wǎng)絡(luò)的新數(shù)據(jù)將被上傳到服務(wù)器并被保存。理想情況下,新網(wǎng)絡(luò)應(yīng)該更強大:)
進一步探索
正如很多讀者可能已經(jīng)意識到的那樣,我們可以利用這個方法做比簡單推薦系統(tǒng)更多的事情。改進此項目的一些可能方法包括:
- 更多關(guān)于瀏覽器的信息可以當作輸入,例如用戶在每張卡上花費的時間、用戶的點擊事件和滾動事件等。這些信息可以從前端得到。
- 神經(jīng)網(wǎng)絡(luò)可以具有更復雜的架構(gòu),但注意不能過擬合。
- 前端項目應(yīng)該考慮大小。目前,構(gòu)建的文件大小約為 1Mb,這在 PC 上是可以接受的,但對于移動端網(wǎng)站來說可能太大了。如果要在移動設(shè)備上使用該項目,應(yīng)采取優(yōu)化措施。
有關(guān)此項目的完整代碼,請查閱參考:
https://github.com/markselby9/ml-in-browser/tree/feature/Recommendation_system_in_browser_demo。
原文:https://medium.com/@markselby9/neural-network-in-your-browser-3e6fc91709ca
【本文是51CTO專欄機構(gòu)“機器之心”的原創(chuàng)譯文,微信公眾號“機器之心( id: almosthuman2014)”】