Neural network in your browser, for Front-end engineers
A simple recommendation system using synaptic.js, training in the browsers
Project overview
We’re going to build an artificial neural network based, simple recommendation system web application. The application contains two pages, with first page showing books, and the second page showing movies. The user can select the books he or she is interested in in the first page, and when he clicks the next page button, we would actually predict what movies he would likely be interested in in the background. After the user finished choosing the movies he like, we would give the user a result showing what he chose and what we predicted about his choice. Some screenshots are shown below:
We’re using Vue.js and ElementUI ( a Vue.js UI framework) to build the application, and synaptic library to build the neural network.
What’s the advantages?
The advantages of our project can be concluded as:
- We moved the model training work to front-end instead of back-end, which reduced the server’s pressure and apportioned some computational power to many clients. That should be feasible thanks to the many neural network related Javascript library provided by npm community.
- We protected the user’s privacy. No user’s data has actually been transferred to server, they kept their data anonymous to server, when the server has updated it’s training model.
Simple introduction to Neural Network
First of all, the neural network we used here is a most fundamental ANN, and we also decided to use only the user’s selection to be input and output set. The images used in this section come from a nice introduction blog about neural network: https://ujjwalkarn.me/2016/08/09/quick-intro-neural-networks/.
The concepts involved in the project includes: Artificial Neural Network, neurons, layers and training (back-propagation).You can skip this part if you’re already familiar with these concepts. An Artificial Neural Network (ANN) is a computational model that is inspired by the way human’s brain work. It’s made up by neurons, which is a basic unit of neural network. Neurons receives inputs from other source, each input has a weight, which is assigned according to each input’s importance. And the neuron apply an activation function to the weighted sum of all inputs, then gives an output.
A layer is composed by several neurons, which looks like the image below. A neural network may or may not contain several hidden layers, each pair of neighboring layers have connections between them, which usually means the weight mentioned before.
But how can we design the network by correctly measuring these weights? We need to train it to let the neural network work properly. Suppose we have a table of data, which contains 1000 pair of inputs and corresponding outputs. We firstly gives all the weights by a random number between 0 and 1, and then iterate over all the data pairs. In each pair of input and output, we activate the neural network to give a calculated result, and compare it to the actual output. Then we would use back-propagation algorithm to retrain the network and adjust the weights. The weight updates of back-propagation can be done via stochastic gradient descent, which is a method to get optimal values for the weights.
In this article I only give a very brief introduction. For better reference, please check these links:
Neural Network implementation in browser
Recently there have been some related works about implementing NN in browsers, like:
- Deeplearnjs, https://github.com/PAIR-code/deeplearnjs
- ConvnetJS, http://cs.stanford.edu/people/karpathy/convnetjs/
- synaptic.js, http://caza.la/synaptic/
We use synaptic.js here as it’s an architecture-free neural network library for both node.js and the browser, the documents can be checked through wiki in the github repository: https://github.com/cazala/synaptic/wiki/Architect. We plan to move all the neural network training and activation part in the browser, and the server (simple node.js server by express) only maintains a JSON file, which contains the network parameters. synaptic.js has a handy API to parse the neural network to JSON and parse JSON to a NN instance.
The application is built up by Vue.js and ElementUI. In the created lifecycle of the main app component, the application would fetch the model’s JSON file from server, and build a neural network instance based on the JSON file. Then it presents 20 cards containing movie information, and let the user select the items he/she is interested in. After the user finished selection and click next step, the network instance would call activate function, and give a predicted value about the books this user would possibly want (among the 20 options of books). Meanwhile, the application presents another 20 cards containing book information, and also let the user select. After click the submit button, the app would present the predicted book list and the actual book list to the user, and in the background use the new training data to back-propagate and retrain the model. After retraining, the new NN would be parsed as a JSON object and sent back to server.
Now let’s talk about the code. The server is a very simple express server with node’s file IO API.
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');
});
And in the client, we would not dive into the DOM detail here in the article, we will just focus on the script part of the particular app.vue file.
created() {
// fetch the train model from server
this.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);
});
},
Above is the created lifecycle hook in our application. It tries to fetch a JSON object from ‘getNetwork’ API, if it’s an available JSON configuration for network, it would create a local network instance through synaptic’s fromJSON method. Otherwise, it would create a new network instance and saves into ‘localNetworkInstance’ variable.
After the user clicks the ‘next page’ button from the first page, we call the activation function in the ‘onClick’ function, and save it as a predicted result in the Vue component’s data. Then after the user selects his/her interested films, a retrain function is called.
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!');
}
}
The retrain process is a back-propagation process, by the current user’s selection as a pair input and output data. The user’s selection about movies would become the data for back-propagation. After back-propagation, the neural network’s weight would be adjusted, and the new data of neural network would be transferred to server and saved. Ideally, the new network should be stronger :).
You can do more …
As our reader may have already considered, we can do far more things than a simple recommendation system, using this strategy. Some possible roadmap for improving this project contains:
- More factors from the browser can be taken into account as input set, like time spent on each cards, user’s click event and scroll event, etc. These factors are accessible for front-end’s perspective.
- The neural network can have more complicated architecture, but meanwhile it can’t be trained overfitted.
- Build size should be taken into consideration for front-end project. Currently the build file size is around 1Mb, which is acceptable on PC but maybe too large for mobile sites. Optimizations should be taken if you want to use the project’s approach on mobile.
For the complete code of this project, please check the reference: https://github.com/markselby9/ml-in-browser/tree/feature/Recommendation_system_in_browser_demo.