Blog

ProjectBlog: How to make a Machine Learning Recommendation System from scratch

Blog: How to make a Machine Learning Recommendation System from scratch


Learn to make a machine learning recommendation system from scratch.

Are you interested in machine learning and it’s applications?
re you looking forward to learning something new and great?

How about a machine learning recommendation system?

Well, don’t be just scared or blown away with those massive terms and words, they are just to tell someone what exactly a code does.

Machine learning has always been in the news, whether for taking away the jobs or for making a robot understand and reply to a human. But what exactly can we do with machine learning?

The answer is, almost everything. Here is the statement that most people shy away to say, because it makes things a bit more uncomfortable.

Whatever a human can do, machine learning model can do better.

Now before all of you guys who have seen such models failing drastically before a human or those who don’t particularly like ML to be overtaking humans, let me rephrase my statement a bit.

Whatever a human can do, machine learning model can do better; given time and efforts.

Well enough of ML vs human, let’s get into what we’re making here. So here we would be building a machine learning recommendation system.

What is it?

Recommendation system is a machine learning model that recommends you new options to choose from, and learns from your previous actions for the topic.

What does this mean? Is it used often?

This means that the model would understand your choices and recommend you choices that you “might” like. It is very often used, for example you would have seen Spotify, Netflix or Amazon recommended items.

This is exactly that but maybe a bit less efficient.

What exactly are we going to recommend?

After giving a bit thought, movies and songs require lot of other code and data that would make the learning harder, so I came up with the code/text editor theme.

Everybody (read almost everybody) loves customisable themes for their IDE or text editors and spend a lot of time looking for themes they would like to use.

This problem is what we would be targeting in this example. Why, you ask?

Well because this makes learning the concept of machine learning recommendation system easy to understand, and makes up an example that would be usable in real world.

How would it look?

Open this link to know how it looks. (For best results open this in private/incognito window) You don’t want to visit a new link? You might miss something nice, but here is the screenshot:

Screenshot is not enough for you? Scroll down to the bottom to find the GIF.

Enough with flexing, let’s get into some code now. We would be using JavaScript, CSS and HTML along with ‘brain.js’ machine learning library to make this model.

The whole code for the webpage can be found in this GitHub repository.

Now let’s discuss the JavaScript code that we are using to make this model work.

First let’s write code for instantiating some variable, libraries and code that will make up the UI for our webpage.

const editorWrapper = document.querySelector(".editor-wrapper")
const colorOne = document.querySelectorAll(".color-1")
const colorTwo = document.querySelectorAll(".color-2")
const colorThree = document.querySelectorAll(".color-3")
const stars = document.querySelectorAll(".star")
const themes = document.getElementById("themes")

window.localStorage.trainingData = window.localStorage.trainingData || JSON.stringify([])


// our current voting combination
const currentColors = {
back: {},
one: {},
two: {},
three: {},
}

These are some of those variables that we would be using to change the text colour and the theme of the text in our webpage.

We would now generate a random theme on the start of the webpage. Here we would also be giving user the choice to vote the themes and use that data to recommend more themes that would appeal to the user.

// kick it off by creating a random theme for you to vote on
/*
as you vote, saveTrainingData
- saves to localstorage
- runs predictThemeCombinations
*/
/*
predictThemeCombinations
- trains a neural network from your vote history
- creates 100,000 random themes
- runs the themes through the network, getting a score for each
- sorts and returns the top 20 scored themes
*/
generateRandomTheme()
predictThemeCombinations()

We would be writing code for generateRandomTheme() and predictThemeCombinations() later.

Also we need to write code for stars that user can use to rate the theme. We would be making it a bit interactive by make the star golden when user hovers over the star and when user clicks on the star, the theme is rated and that data is used by our model to predict further choices for the user.

stars.forEach((star, i) => {
const score = i / 4
star.addEventListener("mouseenter", setStars.bind(setStars, i))
star.addEventListener("mouseleave", clearStars)
star.addEventListener("click", saveTrainingData.bind(saveTrainingData, score))
})

We would also be writing code for setStars() and clearStars() functions.

Now let’s write the code for the function saveTrainingData() for the score that the user generates when he rates the theme. This method saves training data according to the score and use it to train our model.

function saveTrainingData(score) {
const data = JSON.parse(window.localStorage.trainingData)

data.push({
input: [
Math.round(currentColors.back.r/2.55) / 100, // divide by 255 and round to 2 decimal places
Math.round(currentColors.back.g/2.55) / 100,
Math.round(currentColors.back.b/2.55) / 100,
Math.round(currentColors.one.r/2.55) / 100,
Math.round(currentColors.one.g/2.55) / 100,
Math.round(currentColors.one.b/2.55) / 100,
Math.round(currentColors.two.r/2.55) / 100,
Math.round(currentColors.two.g/2.55) / 100,
Math.round(currentColors.two.b/2.55) / 100,
Math.round(currentColors.three.r/2.55) / 100,
Math.round(currentColors.three.g/2.55) / 100,
Math.round(currentColors.three.b/2.55) / 100,
],
output: [score]
})

window.localStorage.trainingData = JSON.stringify(data)


predictThemeCombinations()
clearStars()
generateRandomTheme()
}

Here we also have used the method clearStars() which basically removes the star rating from the page if user has not clicked on it and remove the cursor.

setStars() method does exactly what we discussed earlier.

function setStars(whichStar) {
for (let i = 0; i < stars.length; i++) {
stars[i].classList.add("gold")
if (i >= whichStar) {
break;
}
}
}

function clearStars() {
for (const star of stars) {
star.classList.remove("gold")
}
}

Now let’s write code for generateRandomTheme() that we use at the very beginning of the webpage. This method just gets a random background colour and 3 random other colours to set the theme for the first time.

function generateRandomTheme() {
currentColors.back = getRandomBackgroundRgb()
currentColors.one = getRandomRgb()
currentColors.two = getRandomRgb()
currentColors.three = getRandomRgb()

editorWrapper.style.background = `rgb(${currentColors.back.r},${currentColors.back.g},${currentColors.back.b})`
for (let color of colorOne) {
color.style.color = `rgb(${currentColors.one.r},${currentColors.one.g},${currentColors.one.b})`
}
for (let color of colorTwo) {
color.style.color = `rgb(${currentColors.two.r},${currentColors.two.g},${currentColors.two.b})`
}
for (let color of colorThree) {
color.style.color = `rgb(${currentColors.three.r},${currentColors.three.g},${currentColors.three.b})`
}
}

And the supplement methods (functions if you call them) that we used above.

function getRandomRgb() {
return {
r: Math.round(Math.random()*205 + 50), // number between 50 and 255
g: Math.round(Math.random()*205 + 50),
b: Math.round(Math.random()*205 + 50),
}
}

function getRandomBackgroundRgb() {
return {
r: Math.round(Math.random()*50), // number between 0 and 50
g: Math.round(Math.random()*50),
b: Math.round(Math.random()*50),
}
}

And the method that does it all. predictThemeCombinations() function uses the user liked history trained network to get colours and themes that would probably be liked by the user. Take some time to understand the working of this method. Simple but clever.

function predictThemeCombinations() {
const data = JSON.parse(window.localStorage.trainingData)
if (!data.length) {
return;
}

themes.innerHTML = ""
const net = new brain.NeuralNetwork({activation: "leaky-relu"});
const results = []

net.train(data)

for (let i = 0; i < 100000; i++) {
const back = getRandomBackgroundRgb()
const one = getRandomRgb()
const two = getRandomRgb()
const three = getRandomRgb()
const colors = [
Math.round(back.r/2.55) / 100, // divide by 255 and round to 2 decimal places
Math.round(back.g/2.55) / 100,
Math.round(back.b/2.55) / 100,
Math.round(one.r/2.55) / 100,
Math.round(one.g/2.55) / 100,
Math.round(one.b/2.55) / 100,
Math.round(two.r/2.55) / 100,
Math.round(two.g/2.55) / 100,
Math.round(two.b/2.55) / 100,
Math.round(three.r/2.55) / 100,
Math.round(three.g/2.55) / 100,
Math.round(three.b/2.55) / 100,
]

const [ score ] = net.run(colors)
results.push({ back, one, two, three, score})
}

Also we are having 100,000 results and we don’t just want to show user the first one. Top 20 recommendations would be better. So we just sort the results and show Top 20 results from them.

// sort results
const sortedResults = results.sort(function(a, b) {
var a = a.score
var b = b.score

return b - a
})

// keep the top 20 results after sorting
for (let i = 0; i < 20; i++) {
addNewTheme(sortedResults[i])
}
}

addNewTheme() ? Well, this method would also contain a lot of HTML part so would be a bit ugly. But in essence, we are just using the colour themes in the HTML components, using this.

function addNewTheme({back, one, two, three, score}) {
const newTheme = document.createElement("div")
newTheme.classList.add("predicted-theme")
newTheme.innerHTML = `
<div class="editor-wrapper" style="background:rgb(${back.r}, ${back.g}, ${back.b})">
<span style="color:rgb(${one.r}, ${one.g}, ${one.b})">import</span> React <span style="color:rgb(${one.r}, ${one.g}, ${one.b})">from</span> <span style="color: rgb(${two.r}, ${two.g}, ${two.b})">"react"</span><br/>
<span style="color:rgb(${one.r}, ${one.g}, ${one.b})">import</span> ReactDOM <span style="color:rgb(${one.r}, ${one.g}, ${one.b})">from</span> <span style="color: rgb(${two.r}, ${two.g}, ${two.b})">"react-dom"</span><br/>
<span style="color:rgb(${one.r}, ${one.g}, ${one.b})">import {</span> Provider <span style="color:rgb(${one.r}, ${one.g}, ${one.b})">} from</span> <span style="color: rgb(${two.r}, ${two.g}, ${two.b})">"react-redux"</span><br/>
<br/>
<span style="color:rgb(${one.r}, ${one.g}, ${one.b})">import</span> Layout <span style="color:rgb(${one.r}, ${one.g}, ${one.b})">from</span> <span style="color: rgb(${two.r}, ${two.g}, ${two.b})">"./components/Layout"</span><br/>
<span style="color:rgb(${one.r}, ${one.g}, ${one.b})">import</span> store <span style="color:rgb(${one.r}, ${one.g}, ${one.b})">from</span> <span style="color: rgb(${two.r}, ${two.g}, ${two.b})">"./store"</span><br/>
<br/>
<span style="color:rgb(${one.r}, ${one.g}, ${one.b})">const</span> app<span style="color:rgb(${one.r}, ${one.g}, ${one.b})"> = </span>document.<span style="color: rgb(${three.r}, ${three.g}, ${three.b})">getElementById</span><span style="color:rgb(${one.r}, ${one.g}, ${one.b})">(</span><span style="color: rgb(${two.r}, ${two.g}, ${two.b})">'app'</span><span style="color:rgb(${one.r}, ${one.g}, ${one.b})">)</span><br/>
<br/>
ReactDOM.render<span style="color:rgb(${one.r}, ${one.g}, ${one.b})">(</span>&lt;<span style="color: rgb(${three.r}, ${three.g}, ${three.b})">Provider store={</span>store<span style="color: rgb(${three.r}, ${three.g}, ${three.b})">}</span>&gt;<br/>
&nbsp;&nbsp;&lt;<span style="color: rgb(${three.r}, ${three.g}, ${three.b})">Layout </span>/&gt;<br/>
&lt;/<span style="color: rgb(${three.r}, ${three.g}, ${three.b})">Provider</span>&gt;, app<span style="color:rgb(${one.r}, ${one.g}, ${one.b})"">)</span>
</div>
<li>Score ${score}</li>
<li>Background rgb(${back.r}, ${back.g}, ${back.b})</li>
<li>Color 1 rgb(${one.r}, ${one.g}, ${one.b})</li>
<li>Color 2 rgb(${two.r}, ${two.g}, ${two.b})</li>
<li>Color 3 rgb(${three.r}, ${three.g}, ${three.b})</li>
`
themes.appendChild(newTheme)
}

Did I say a bit ugly?

Well, Shit!

And believe me this method does nothing but sets colours that we get in our Top 20 list and sets them up in the components.

So in nutshell what did we do?

We gave user the choices to choose background colour, text colour with it’s variants. Then the user rated the combination of that choice shown as a theme. We took that choice to train our model to present themes that align with user’s choices more.

The model simply makes more combinations of the colours the users chooses or the colours that are similar, and is trained with that. This lets it make a list of other possible choices for the user. We get the top choices using sorting and display the top 20.

And the result?


In case we’re meeting for the first time here, I am Pradyuman Dixit and I mostly write about Machine learning, Android Development and sometimes about Web Development.

You can read my other Machine Learning posts here:

How to make a simple Machine Learning Website from Scratch

How to understand machine learning with simple code examples

Source: Artificial Intelligence on Medium

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top
a

Display your work in a bold & confident manner. Sometimes it’s easy for your creativity to stand out from the crowd.

Social