How to Build an NSFW Image Classification REST API with Node.JS and TensorFlow.JS
Tags: machinelearning, tutorial, beginners, javascript
Published: 2020-07-01 13:51:51

The Open-Source Content Moderation Service we have prepared for you is so simple and elegant to use, that even if you do not have any knowledge about algorithms, it won’t matter! You just need to know, it’s using Machine Learning … it’s trendy! ;)  

# Agenda:

[**1. The Problem**](#the-problem)
[**2. The Solution**](#the-solution) 
[**3. The Stack**](#the-stack)
[**4. Development Environment**](#development-environment)
[**5. API Usage Examples**](#api-usage-examples)
[**6. File Structure**](#file-structure)
[**7. NSFW Models**](#nsfw-models)
[**8. Deploying on Production**](#deploy-to-production)
[**9. What comes Next**](#what-comes-next)

# The Problem <a name="the-problem"></a>

Publishing user-generated content always comes with a risk and moderation is a must! Going through piles of disturbing content, however, is time-consuming and [seriously stressful](https://bit.ly/3eK37qX). Big companies like Google and Facebook have entire teams dedicated to removing offensive content, but that’s a luxury we can’t all share. Not to mention that automating this process can come **quite pricey & require a high level of expertise**. 

However, advances in computer processing power, storage, data tools, web, etc made machine learning technologies more and more affordable. Machine Learning has already matured to the point where it can be a vital part of various projects of all sizes. All this inspired our team to roll up our sleeves and create a fully-functional Content Moderation service built entirely with Open-Source tools and libraries, which is easily maintained and can be integrated into any Node.js project effortlessly.

# The Solution! <a name="the-solution"></a>

In a brief series of three tutorials, we will build with you a [**fully functional Open-Source Content Moderation service with ReactJS based Admin Panel**](https://bit.ly/3goH0Hh). It is designed to be easily integrated into every project even if this is your first encounter with Machine Learning. Check out [our demo](https://bit.ly/3goH0Hh) to have a sneak peek at the final result. ;)

Here’s what you’ll get by the end of this hattrick: 

 - **Restful APIs**, so you can flawlessly integrate content moderation logic in every project with user-generated content. 
 - **Content Moderation Automation Engine** - A straightforward and effortless way to remove all unseemly images. Set the params which decisions should be taken by the ML and for which manual moderation is needed, with no hassle. 
 - **Ready-to-use Mobile-first React JS Moderation application** - A beautiful admin panel, so your team can conveniently moderate and manage all images that require a manual assessment from an impeccable UI, no additional coding required.
 - **A cost-efficient solution** - Our mission is to bring Machine Learning closer to single developers and teams of all sizes by making it affordable. SashiDo managed to drop all costs to the bare minimum of $4.95 for 1 000 000 predictions.  
 - **No need for Machine Learning knowledge or skills to get this working!!!**
 <br />

### Examples and Demos 

| Image Source        | Image Source           | Image Source  |
| ------------- |:-------------:| -----:|
| <img src="https://mqfy379g6xncpc3va4epdzkfsxf0vs.files-sashido.cloud/41a516cc1141cdaf775a5cfa083dcb26_thumbnail.png" />      | <img src="https://nsfw-demo.sashido.io/sexy.png" /> | <img src="https://mqfy379g6xncpc3va4epdzkfsxf0vs.files-sashido.cloud/355a4b37b8a657dd09f3f50816481cca_thumbnail.png" /> |
| Classification Result      | Classification Result      |   Classification Result |
| [{<br>"className": "Neutral",<br>"probability": 0.93821<br>}, {<br>"className": "Drawing",<br>"probability": 0.05473<br>}, {<br>"className": "Sexy",<br>"probability": 0.00532<br>}, {<br>"className": "Hentai",<br>"probability": 0.00087<br>}, {<br>"className": "Porn",<br>"probability": 0.00085<br>}] | [{<br>"className": "Sexy",<br>"probability": 0.99394<br>}, {<br>"className": "Neutral",<br>"probability": 0.00432<br>}, {<br>"className": "Porn",<br>"probability": 0.00164<br>}, {<br>"className": "Drawing",<br>"probability": 0.00006<br>}, {<br>"className": "Hentai",<br>"probability": 0.00001<br>}]<br>      |    [{<br>"className": "Drawing",<br>"probability": 0.96063<br>}, {<br>"className": "Neutral",<br>"probability": 0.03902<br>}, {<br>"className": "Hentai",<br>"probability": 0.00032<br>}, {<br>"className": "Sexy",<br>"probability": 0.00001<br>}, {<br>"className": "Porn",<br>"probability": 0.00005<br>}]<br> |
| <a href="https://bit.ly/2NLFGBX" target="_blank">Neutral Demo<a/>      | <a href="https://bit.ly/3dQfssj" target="_blank">Sexy Demo<a/> | <a href="https://bit.ly/2NGC3wQ" target="_blank">Drawing Demo<a/> |

# The Stack <a name="the-stack"></a>

For the purpose of this article, we will implement image classification into a [SashiDo](https://bit.ly/2ZiAoDf) application, as such an approach will allow us to complete the task at hand masterly in no time. :) If you have your project elsewhere, no worries - our content moderation service is open-source and can be hosted on any other platform that supports fully featured **Node.js** + **MongoDB** or even cloud hosting providers as AWS VMs and Digital Ocean.

However, as we want to focus on the content moderation and not waste time on the infrastructure side, we decided to go serverless and use SashiDo. Have a look at how you can [register](https://bit.ly/2VmeWvY) and [create an application](https://bit.ly/3eKAQRh) in SashiDo with all Parse Server functionalities built-in.

When it comes to the tools and libraries, I’m sure you’ve asked yourself more than once - “Which one is the perfect tool to start off?”. Following the good old **‘Keep it simple’** rule we have preselected this awesome bundle for you. :) 

- [Node.js](https://bit.ly/38c37xv)
- [Mongo DB](https://bit.ly/2Aencqp) - a document database that’s used directly by the Parse Server.
- [Parse Server](https://bit.ly/2NKJGCp) - an open-source Backend-as-a-Service(BaaS) framework initially developed by Facebook. It comes with a bunch of built-in functionalities and can be deployed to any infrastructure that can run Node.js. To implement server-side logic, we'll use [Parse Server Cloud Code](https://bit.ly/2YFIdns). It is built directly into the Parse Server and we hope you’ll find it quite straightforward and easy to use.
-  [TensorFlow](https://bit.ly/3dI0kgN) - an open-source software library by Google, designed specifically for deep machine learning applications.
-   [nsfw.js](https://bit.ly/38cLwWi) - an amazing JavaScript library by [Gant Laborde](https://bit.ly/388P6kj) for identifying unseemly images. Check out their cool demo at https://nsfwjs.com.
-   [axios](https://bit.ly/38bQzpS) - our preferred HTTP client. Of course, you can easily replace it with your favorite lib.
-    [Parcel.js](https://bit.ly/2NDztb3)- Parcel deserved its place here, as it’s a blazing fast, zero-configuration web application bundler. 

Since **the goal is to have a ready-to-use product**, not only classification logic, for the upcoming tutorials, we will add [React](https://bit.ly/38bIZvn) to our stack. It will help us build the admin panel in Part 3 of the series. :)

Anyhow, today we’ll begin with building the REST API for image classification, so let’s kick it off by starting the project in a development environment!

# Development Environment <a name="development-environment"></a>

Implementing new features can always be challenging and has its risks, but setting up a local environment for testing makes life easier for you. :) So in case you want to apply some customizations or run a few tests before deploying in production, that’s the safe way to go. 

We have developed and tested with **Node.js 10.2.1 and MongoDB 3.4.X**, so these are the minimum requirements. 

You can find our plug-and-play service on [GitHub](https://bit.ly/2VlwS9S). Then simply open the terminal into the directory you’ll use for storing and take the plunge by cloning the project. 

Detailed instructions on how to proceed are available in the [README file](https://bit.ly/2ViSxiU), but let me summarize the setup here as well: 


  **1. Clone the project**. 
  
  ```bash
      git clone https://github.com/SashiDo/content-moderation-image-api.git
```
<br />

  **2. Copy the env.example file to a .env** and set your database URL.
  
 ```bash
    cd content-moderation-image-api
    cp env.example .env
 ```

Since we are testing with a SashiDo application, there is no need to run a local instance of MongoDB. Simply get your application's Database URL from the Dashboard -> App -> App Settings -> Security & Keys.

Once you set the DATABASE_URI variable into the newly created `.env` file, continue with the next step.

   **3. Install dependencies** The project holds a complete example, all you need to do to is execute:

```bash
    npm install
```
<br />

  **4. Start the local server**. 

```bash
    npm run dev
```

<br />

Et voila! Running on http://localhost:1337 displays on your terminal.

```bash
[nodemon] 2.0.4
...
[nodemon] starting `node index index.js`
✨  Built in 2.55s.
node-pre-gyp ...
...
Running on http://localhost:1337
⠙ Building index.js...The NSFW Model was loaded successfully!
✨  Built in 16.41s.
```

<br />

Now that we have the classification service running locally, let’s put the focus on the usage, file structure, and models. 


# API Usage Examples <a name="api-usage-examples"></a>

To make you feel as comfortable as possible we have added two approaches for classifying images - from an Express route, and for all Parse Server fans - from the Parse Cloud Code Functions. :)  

## cURL
The **Express Router** is a complete middleware and routing system. You can easily classify images in the browser or with a simple cURL request.


```bash
curl http://localhost:1337/api/image/classify?url=https://nsfw-demo.sashido.io/sexy.png
```

![express_crop](https://media-blog.sashido.io/content/images/2020/06/express_crop.gif)

This is for (future)Parse Server fans - **Simple Cloud Code Function**  - [Cloud Code](https://bit.ly/3dNj66d) runs in the Parse Server rather than running on the user’s device. You can get predictions calling the  `nsfwImageClassify` function from REST API or from your Android, iOS, etc app. Check the example below on how to invoke from the REST API:


```bash
curl -X POST \
  -H "X-Parse-Application-Id: myAppId" \
  -H "X-Parse-REST-API-Key: myRestKey" \
  -d '{ "url": "https://nsfw-demo.sashido.io/sexy.png" }' \
  http://localhost:1337/functions/nsfwImageClassify
```
<br />

## Parse SDKs

You can invoke the `nsfwImageClassify` function from the client-side or using the Parse SDKs as well:

### Android SDK

```Java
HashMap<String, String> params = new HashMap<String, String>();
params.put("url", "https://nsfw-demo.sashido.io/sexy.png");
ParseCloud.callFunctionInBackground("nsfwImageClassify", params, new FunctionCallback<Object>() {
  void done(Object predictions, ParseException e) {
    if (e == null) {
      // prediction
    }
  }
});
```

More information about how to work with the Android SDK can be found in the [official docs](https://bit.ly/3dE5aeN).

### iOS SDK

```c
PFCloud.callFunctionInBackground("nsfwImageClassify", withParameters: ["url":"https://nsfw-demo.sashido.io/sexy.png"]) {
  (predictions, error) in
  if !error {
    // prediction
  }
}
```

More information about how to work with the Parse iOS SDK can be found in the [official docs](https://bit.ly/3eHrwh2).

### API Console

SashiDo app owners can **test all Cloud Code functions from the Dashboard** with just a click. Check our super-friendly [API Console](https://bit.ly/2NDNJkf) that also gives you the option to export any request to a cURL.
<br />
![cloud_code_crop](https://media-blog.sashido.io/content/images/2020/06/cloud_code_crop.gif)
# File Structure

Being well informed about the file structure is of great importance, as it puts you on solid grounds. To avoid any misunderstanding, we will give the architecture a bit of special attention. 

Following the best practices, we keep all the source code in the `src` folder, with two subfolders - for the Cloud Code and visualization. Let’s focus on the `cloud` folder that holds all the image classification logic. 


```bash
src/cloud/
├── ml_models/
│   ├── nsfw_model.js
├── routes/
│   ├── api.js
│   └── index.js
├── app.js
├── config.js
├── functions.js
└── main.js
```

<br />

Our recommendation for a structure is decoupling the API, NSFW models, and image classification logic to different files. Let me go brief over the architecture:

* `nsfw_model.js` - contains logic for loading the NSFW model. The Classification method also belongs here.

* `config.js` - instead of loading the model and shape from process.env directly each time, we’ve saved them here.

* `api.js` - as you’ve already guessed, here’s the heart of this project - the API. To keep things simple and move one step at a time, we have implemented just one handler. Its only job is invoking the classify method for an image and returning a prediction.   

```js
import express from "express";
const router = express.Router();

router.get("/image/classify", async (req, res) => {
 try {
   const { url } = req.query;
   const result = await nsfwModel.classify(url);
   res.json(result)
 } catch (err) {
   console.error(err);
   res.status(500).json({
      error: "Ups... Something went wrong!"
    });
 }
});
```

<br />

That’s it! Our API is ready to go through a pile of photos in no time helping you protect your project’s users from inappropriate content. 

**NB!** `main.js`, `app.js`, and `functions.js` are part of SashiDo’s predefined cloud code and the structure is Parse Server specific. Let me give a piece of information about each file’s function as such insights could help you hand over effortlessly to your project if hosted elsewhere.

* `main.js` -  is the entry point for each SashiDo application. 

* `app.js` - holds your Express logic, as we mount it automatically to the Parse Server Deployment.

* `functions.js` - is where Simple Cloud Code lives. :) 

# NSFW Models <a name="nsfw-models"></a>

To keep it simple, in the context of this tutorial, you can think of the Machine Learning model as a file that has been trained to recognize certain types of patterns. For our specific case, we pass the model an image and it returns a JSON of predictions how likely this image falls into each of the following 5 classes:

* *Drawing* — Harmless art, or picture of art
* *Hentai* — Pornographic art, unsuitable for most work environments
* *Neutral* — General, inoffensive content
* *Porn* — Indecent content and actions, often involving genitalia
* *Sexy* — Unseemly provocative content, can include nipples


SashiDo stores three pre-trained ready for integration [nsfw models](https://bit.ly/3dI3MIm) that you can choose from. Each of these models has different accuracy, shape size, and resource consumption.

{% gist https://gist.github.com/mignev/f6c08ff0c73055deb1ea0c16edc30560.js %}

You've probably noticed that mobilenet models are far more lightweight than the inception one, which has a high **RAM and CPU consumption**. That's why for the development environment we have preset the Mobilenet 93. However, you can always choose a different model. To evaluate each model’s performance and check which matches your use case best, check the [NSFWJS Demo page](https://bit.ly/2YGKL4v).


For our examples and demos, we’re using Inception_v3, as that’s our favorite choice. If you also choose using Inception_v3, keep in mind that your project will need more computing power. For the occasion in which SashiDo is your mBaaS provider - the minimum [Engine](https://bit.ly/2NBPNZZ) that can handle this task is Production 2X. 

# Deploy to Production <a name="deploy-to-production"></a>

### 1. Configuration
<br />

The configs we need to set up in production are **TF_MODEL_URL** for the TensorFlow Model URL and **TF_MODEL_INPUT_SHAPE_SIZE** for the Input Shape Size. Check the [previous section](#nsfw-models) to make your pick.

If you are a proud [SashiDo](https://bit.ly/3dMcQM6) app owner or collaborator, jump to `Runtime -> Env Variables` section to set the values. Please, note that any change of environment variables from the Dashboard automatically triggers a [Deployment](https://bit.ly/3dDESJK) of your project. When you’re all set, the final result should be:

![Screenshot-2020-06-25-at-11.01.50](https://media-blog.sashido.io/content/images/2020/06/Screenshot-2020-06-25-at-11.01.50.png)

### 2. Push the project code to your SashiDo App

<br />

Having a local environment for experimenting surely pays off! You already went through the code and verified it’s behaving properly. Make sure that you've [connected your SashiDo account to GitHub](https://bit.ly/2BMtVIA) and safely push the changes to your production app’s master branch. 

An elegant approach to go with is adding a remote: 

```bash
git remote add production git@github.com:parsegroundapps/<your-pg-app-your-app-repo>.git
```
<br />

... and applying to the live app seamlessly.

```bash
git push -f production master
```

<br />

If you're keeping up with the good practice and have a testing/staging application apart from your live one, we would always advise pushing changes there first.

Naturally, pushing new code to GitHub automatically triggers a [Build and Deployment](https://bit.ly/3dDESJK) of your SashiDo App. 

## 3. Get Live Predictions

<br />

Same as with testing locally, try out calling the classify method to validate the smooth operation of the service applied. In case you forgot what are the commands, refresh your memory [here](#api-usage-examples).

![sexy](https://media-blog.sashido.io/content/images/2020/06/sexy.jpg)

**Well, that’s it! A piece of cake, right. :)**

# What comes Next

- [**Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 2: Moderation Automation**](https://bit.ly/2E1UjPL) - The next part describes in detail how to add an **Automation Engine** and implement the logic for the automatic deletion of most hazardous images. It will be quite useful, I promise!

- [**Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 3: Moderation Admin App**](https://bit.ly/2Bi3Lxs) - To put the cherry on top, we add a beautiful **Admin Panel** and prove that even most boring tasks may be completed with finesse.

And if you’re still wondering about where to host such a project, don’t forget that SashiDo offers an extended [45-day Free trial](https://bit.ly/2VmeWvY), as well as exclusive [Free consultation by SashiDo's experts](https://bit.ly/3eK5eLp) for projects involving Machine Learning. 

Our team will be happy to hear about your specific use case and what problems our moderation service helped you solve. You're more than welcome to drop us a line at hello@sashido.io! 

Happy Coding!