NudeNet: An ensemble of Neural Nets for Nudity Detection and Censoring
Note: This post can also be read from here
Part 1: Nudity detection with image classification
With the advent of excellent DL libraries and plethora of open-source implementations and papers, Image Classification is very easy to implement. That is, if you have the dataset. There are plenty of open datasets available to test and refine classification models. But obtaining a task specific dataset is tough. Nudity/ NSFW detection is one such use-case where there are no practically useful open datasets available.
In the first part of this two part project, I collect data for and implement nudity detection using Image Classification. My goal is to build a good open source dataset for this use-case and provide a pre-trained model for the same. In the second part I aim to implement and open-source private/ exposed part detection using Object Detection.
The Why: I sincerely believe in not re-inventing the wheel. At the time of starting this project, Yahoo’s open_nsfw is the only decently working nudity detection module available. It is quiet outdated and the data isn’t available to public. There is no (AFAIK) open-source project available for censoring of exposed parts.
Collecting Nude images: For getting these images, I started with Reddit. I made a list of NSFW sub-reddits mostly collected from the website scrolller (thanks to the hard work of /u/faroix). After using the excellent RipMe application to download ~1000 images from each of these sub-reddits and going through them (not recommended for the lighthearted), I found a major problem with this data.
Almost all the images collected from these sub-reddits are of very high quality. In fact, combined these images came out to ~260 GB. This isn’t ideal, since a lot of porn is of potato quality. To balance this out, I crawled thumbnails of videos from PornHub.
While I was doing this, another open-source enthusiast GantMan open-sourced a model trained on the data collected by alexkimxyz. So, I contacted GantMan and obtained the dataset he used.
# Resizing and removing duplicates
mogrify -geometry x320 *
fdupes -rdN ./
After using the above two commands to reduce normalize the images and remove duplicates, I ended up with 1,78,601 from PornHub, 1,21,644 from Reddit and 1,30,266 from GantMan’s dataset.
Collecting Safe images: After I was done with collection of nude images, I though that the tough part was over and I couldn’t be more wrong. Collecting some random images would be very easy, but doing so will result in the classifier learning to classify all humans into nude category. Since, neural networks try to minimize loss, not having a lot of human images in the “safe” category will result in our model being a “Human detector” (which is an easy task) rather than being a “nudity detector”.
For building a good classifier, I needed lot of non-nude images that had people in them. After going through GantMan’s sfw data, I observed that though the total number of images is decent, the number of images with people in them was very less.
After some thinking, I realized that Facebook is a great place to collect images with people in them. So, I crawled Facebook profile pictures using their Graph API. I also made a list of safe sub-reddits and crawled ~1000 images from each one using ripme. After resizing and cleaning, I ended up with 68,948 from Facebook, 98,359 from GantMan’s dataset and 55,137 from Reddit.
Processing Pipeline: I used the excellent image augmentation library Augmentor with some added fail-safes for on the fly image augmentation to use with Keras’s fit_generator.
The following snippet is the augmentation used for training data.
# Random rotation, flips, zoom, distortion, contrast, skew and brightness
pipeline.rotate(probability=0.2, max_left_rotation=20, max_right_rotation=20)
pipeline.zoom(probability=0.2, min_factor=1.1, max_factor=1.5)
pipeline.random_distortion(probability=0.2, grid_width=4, grid_height=4, magnitude=8)pipeline.random_brightness(probability=0.2, min_factor=0.5, max_factor=3)pipeline.random_color(probability=0.2, min_factor=0.5, max_factor=3)
pipeline.random_contrast(probability=0.2, min_factor=0.5, max_factor=3)
The Training: For the training I rented a machine with GTX 1080Ti, with 12GB of vram and 64GB system memory from vast.ai for 0.11$ per hour. I was able to to use a batch size of 32 with Xception and 256x256 input image size.
Note: The implementations of image classification models provided in Keras’s applications, do not use any type of regularization. For adding regularization (dropout or l2) loop over each layer and add regularization.
# For l2
for layer in model.layers: layer.W_regularizer = l2(..)
# Or for dropout add dropout between the fully connected layers and redefine the model using functional API.
Using SGD with momentum, the model converges at 0.9347 accuracy on GantMan’s data.
Evaluation: Finding the right data for evaluating a nudtiy detection module is very tough. Nudity/ Porn has thousands of variations (See Rule 34) and building a comprehensive test data is next to impossible. But, just to get a simple idea of how this is working, I use the data collected by Aditya at https://towardsdatascience.com/comparison-of-the-best-nsfw-image-moderation-apis-2018-84be8da65303. Note that, even this is not a good test data. The best way to test and improve something like this is by community help. Since, his test data is in different categories, I map the categories “nsfw_porn”, “nsfw_explicit_nudity”, “nsfw_simulated_porn” to “nude” category and the category “sfw” to “safe”.
For testing with GantMan’s test data, I map the the classes “hentai”, “porn” to “nude” category and “drawings”, “neutral”, “sexy” to safe category. This is because, according to his definition of classes nudes of people will be labelled as “porn” and cartoon nudes will be labelled as “hentai”.
Funnily enough, All the three projects get similar scores, although they fail at different images. For example, GantMan’s model funnily fails with Jeff Goldblum’s images where as NudeNet doesn’t. Similarly, NudeNet fails with some images, for which GantMan’s model doesn’t. In the couple of months of working on this project, I came across a lot of images for which Google’s cloud vision api fails miserably. Because most of these images are NSFW (duh!), I am unable to add them here. If you want to take a look at some of these examples, please leave a comment.
Update: I was able to find another manually curated dataset for testing. This dataset is available at https://dataturks.com/projects/Mohan/NSFW(Nudity%20Detection)%20Image%20Moderation%20Datatset .
The project can be found at https://github.com/bedapudi6788/NudeNet
The pre-trained models at https://github.com/bedapudi6788/NudeNet-models/
To install and use NudeNet, take a look at the following snippet.
# installing the project
pip install git+https://github.com/bedapudi6788/NudeNet# Using the classifier
from NudeNet import NudeClassifier
classifier = NudeClassifier('classifier_checkpoint_path')
In the second part of this post, I implement Exposed Part Detection and Censoring using Object Detection. Please find the second part of this post at https://medium.com/@praneethbedapudi/nudenet-an-ensemble-of-neural-nets-for-nudity-detection-and-censoring-c8fcefa6cc92?source=friends_link&sk=f0a4786bf005cd4b7e89cf625f109af0
Edit: The data is available