Building a simple text classification neural network in TensorFlow.js

George Perry
7 min readJul 12, 2019

--

As a developer at an influencer marketing company, I work intimately with Facebook’s Instagram Graph API and attempt to gain as much understanding as possible from the data we receive. Our client’s continually ask us for more and more insight into their campaign performance, however, it is notoriously difficult to attribute spend through Instagram, and finding metrics such as Cost Per Acquisition on influencer-generated content is impossible.

All hope is not lost though, as the development of machine learning libraries such as TensorFlow has opened up the door for mass data analysis, allowing people to answer questions that were previously unanswerable. For me, it was: “Can user comments on a specific Instagram post show intent to buy?” Essentially, we wanted to discover whether the content our clients were paying for would create intent to buy a product. Below I outline my process of building a relatively simple neural network with Google’s TensorFlow.js API and how it produced valuable results for our business.

To begin, I set up my repo following these steps:

  1. Created a folder named comment-analysis
  2. In my terminal, ran yarn init to create a package.json file
  3. Install the TensorFlow.js libraries, Nodemon and Parcel like this:
yarn add @tensorflow/tfjs @tensorflow/tfjs-node @tensorflow-models/universal-sentence-encoder nodemon parcel

4. Add my run script to my package.json file:

"scripts": {
"start": "parcel --target=node index.js & nodemon dist/index.js"
}

5. Create an index.js file

6. Import packages into index.js file like this:

Step One: Collecting & Labelling Data

This is the best starting point when building a neural network. Most people who are looking to utilise such tools typically already have a large dataset they are looking to analyse, and in my case this was no different. We had access to every comment on every post that was generated through out platform, but no way to analyse all the data. We could have read comments and evaluated whether a user showed intent to buy; for example, if someone said “where did you buy that dress?” or “what store sells that bracelet?”, we could reasonably assume the person was showing positive intent but on scale it was impossible.

Each influencer campaign usually produces 8 to 10 posts, from influencers between 50k and 500k followers, receiving 2,500 comments per post on average — that’s a lot of data! I would only need to take a sample of this data to build my model, so it meant sifting through comments to find words and phrases that positively reinforced intent to buy. On top of this, I wanted to train the model to recognise and filter out positive sentiment without intent; for example, “I love that dress on you” or “you look beautiful in those shorts.” These phrases do not show intent to buy but show positive attitude towards the post or product and although this data is valuable, it would distort the result of the model.

To do this, I created two JSON files: one for training and one for testing. This was made up of a large array of objects that had two keys: text and intent. It looked a little like this:

and…

This is just a tiny sample, as I used around 200 comments for each category of intent to give the model plenty of examples. As you can see, some of the comments that showed no intent were similar to the buying intent comments but for the exception of a few words. It was important to include this to get accurate results.

For the testing data, I repeated the JSON but with just four examples, two for each intent:

These files were then imported into my index.js file:

Step Two: Encoding Text Data

If you are familiar with Tensors, you will know that they are great at dealing with numbers but not words. That meant that I needed to somehow convert my words into numbers that the model could compute. This has been a significant problem for data scientists and engineers for a long time, and the recent advent of Natural Language Processing has produced some amazing tools powered by the likes of IMB’s Watson and Microsoft’s Cognitive Services. Without going into too much detail, it involves mapping specific alphabetic characters to numbers and forming words with different weights — that’s about as far as my expertise goes!

Fortunately, TensorFlow.js provides a Universal Sentence Encoder package that we installed and imported earlier, which solves the exact problem I mentioned above by encoding sentences into arrays of numbers that can be easily processed into Tensors. Using the package is super simple; it takes an array of string, uses a process called tokenisation and returns what looks like an array, but is actually a Tensor. Because the process is asynchronous, I created a function for encoding each dataset that could be called as a promise later. It looked like this:

During training, the model also requires output data that the input data can be matched to. In this case, it is the intent string on each data object within the JSON file. Like the training data that I encoded above the same is required for the output, as the model cannot spit out a word. However, because we only want a simple boolean answer, we can tell the model to output an array of numbers like this:

It means that when the neural network predicts that there is intent to buy within a comment, it will return an array with the first number as close to 1 as possible and the second as close to 0. The inverse is true for when no intent to buy is predicted. As models will rarely predict with 100% certainty, it is common to receive something like this: [0.9853, 0.0147].

Step Three: Building the Model

The next step is creating and configuring the TensorFlow model to fit our input data and output the correct format. I used a very standard setup that is used in many tutorials you can find on the TensorFlow website and around the web. However, as you become more familiar with machine learning concepts and the API, you can customise and refine your configuration.

To begin, I used TensorFlow’s sequential model because the input for each layer of the network would be the output of the previous layer and no other extra data was needed between layers. Next, I added layers to the model, which is what the data flows through in order to compute a result. Without going into detail, a good rule of thumb is that the more layers there are, the higher the complexity of problem the model can deal with. In my case, three layers was enough as the neural network was performing a relatively simple task. I used dense layers in my model, which you can read more about here.

For each layer I had to define an inputShape, activation and units. Simply put, the inputShape is just the length of each Tensor that was being fed into the layer. If we were to log the data returned by the Universal Sentence Encoder, we would see that the Tensor had a shape of [n, 512], as it goes through a process known as 512-dimensional embedding. This just means the data is represented as a 512-dimensional array and our comments are now a matrix made up of arrays with a length of 512. That’s why the first inputShape is [512].

The activation is the function used for the output of a layer. I used sigmoid as it is great for probability values and providing a number between 0 and 1, which as you will remember from our output data array above is exactly what I wanted. You can read about other activation functions in this really great cheatsheet. If you hadn’t of guessed, the units is the essentially the output shape of the layer. Because I wanted an array of two numbers, this was set to 2 and repeated over three layers.

Finally, we use the compile function to create the model and get it ready for data. As you can see this function takes a loss and optimizer option, which helps the neural network know how the improve the model each time it runs. My setup was fairly standard, but if you want to learn more about loss functions checkout this article.

Step Four: Training & Testing the Model

The final step is to train and test the model. For this, I created a simple function that resolved the encodeData function using the comments and comments_testing data. Using the fit function, the training data and output data from above was run through the model 200 times (epochs is the name for this type of iteration). Once complete, the model can then be used to predict the intent of comments using the predict function. Because the output of this function is a Tensor, we can use print to display the results. It looked something like this:

Running yarn start in your terminal would begin the process of training and testing the model, then printing a result. The output was this:

If you remember the testing data from earlier, the first two comments displayed intent to buy whilst the second two did not. As you can see, the model correctly predicted this was the case with a very high degree of certainty, and if you try it with phrases of your own you will get similar results.

Conclusion:

This is a very simple use-case for TensorFlow but has helped provide us with valuable campaign insights when choosing influencers and briefing them for posts. It helps us see how specific posts performed on a deeper level whilst providing benchmarks for us to measure against in the future. Hopefully this gives you a useable model for setting up your own TensorFlow.js project and if you’re new to machine learning or searching for a way to get setup, can help you get started.

--

--

George Perry

I build stuff, write about the world, and code. Currently in Barcelona.