Back to Home

What does the convolutional neural network look at when it sees nudity

NSFW · nudity · nudity · naked · erotic · convolutional neural network · CNN · deployment network · deconvolutional neural network · machine learning

What does the convolutional neural network look at when it sees nudity

Original author: Ryan Compton
  • Transfer


Last week at Clarifai, we formally announced our obscene content recognition model (NSFW, Not Safe for Work) .

Warning and disclaimer. This article contains images of naked bodies for scientific purposes. We ask you not to read further those who are under 18 years old or who are offended by nudity.



Automatically detecting nude photographs has been a central issue in computer vision for over two decades, and because of its rich history and clearly defined task, it has become a great example of how the technology has evolved. I use the obscenity detection problem to explain how training modern convolution networks differs from past studies.

Back in 1996 ...




One of the first works in this area had a simple and understandable title: “Search for Naked People,” by Margaret Fleck et al. It was published in the mid-90s and is a good example of what computer vision specialists did before the mass distribution of convolutional networks. In part 2 of the scientific article, they provide a generalized description of the technique:

Algorithm:
  • First find images with large areas of flesh-colored pixels.
  • Then, find elongated areas in these areas and group them into possible human limbs or joint groups of limbs using specialized grouping modules that contain a significant amount of information about the structure of the object.

The skin was detected by filtering the color space, and the skin regions were grouped by modeling the human figure as “a set of almost cylindrical parts, where the individual outlines of the parts and the connections between the parts are limited by the skeleton geometry (Section 2). The methods for developing such an algorithm become more understandable if we study Figure 1 in a scientific article, where the authors showed some manual grouping rules.



The scientific article says "60% recognition accuracy and 52% recall on an uncontrolled sample of 138 nudity images." The authors also show examples of correctly recognized images and false positives with visualization of those areas that the algorithm processed.





The main problem when making rules manually is that the complexity of the model is limited by the patience and imagination of the researchers. In the next part, we will see how a convolutional neural network trained to perform the same task demonstrates a much more complex representation of the same data.

Now in 2014 ...


Instead of inventing formal rules for describing how input data should be presented, deep learning researchers come up with network architectures and data sets that will allow the AI ​​system to master these representations directly from data. However, due to the fact that the researchers do not indicate exactly how the network should respond to the given input data, a new problem arises: how to understand what exactly the neural network responds to?



To understand the actions of the convolutional neural network, one needs to interpret the activity of the attribute at various levels. In the rest of this article, we will examine an early version of our NSFW model, highlighting activity from the top level down to the pixel space level at the input. This will allow you to see which specific input patterns caused a certain activity on the feature map (that is, why, in fact, the image is marked as "NSFW").

Sensitivity to screening The
illustration below shows photos of Lena Söderberg after applying 64x64 sliding windows with step 3 of our NSFW model to cropped / obscured versions of the original image.



To build a heatmap on the left, we sent each window to our convolutional neural network and averaged the "NSFW" score for each pixel. When a neural network is found with a fragment filled with skin, it is prone to evaluate it as "NSFW", which leads to the appearance of large red areas on the body of Lena. To create a heatmap on the right, we systematically obscured portions of the original image and marked -1 as “NSFW” (that is, “SFW”). When most NSFW regions are closed, the "SFW" rating increases, and we see higher values ​​on the heatmap. For clarity, here are examples of images that we gave to the convolutional neural network for each of the two experiments at the top.



One of the remarkable features of these experiments is that they can be carried out even if the classifier is an absolute “black box”. Here is a code snippet that reproduces these results through our APIs:

# NSFW occlusion experiment
from StringIO import StringIO
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image, ImageDraw
import requests
import scipy.sparse as sp
from clarifai.client import ClarifaiApi
CLARIFAI_APP_ID = '...'
CLARIFAI_APP_SECRET = '...'
clarifai = ClarifaiApi(app_id=CLARIFAI_APP_ID,
                       app_secret=CLARIFAI_APP_SECRET,
                       base_url='https://api.clarifai.com')
def batch_request(imgs, bboxes):
  """use the API to tag a batch of occulded images"""
  assert len(bboxes) < 128
  #convert to image bytes
  stringios = []
  for img in imgs:
    stringio = StringIO()
    img.save(stringio, format='JPEG')
    stringios.append(stringio)
  #call api and parse response
  output = []
  response = clarifai.tag_images(stringios, model='nsfw-v1.0')
  for result,bbox in zip(response['results'], bboxes):
    nsfw_idx = result['result']['tag']['classes'].index("sfw")
    nsfw_score = result['result']['tag']['probs'][nsfw_idx]
    output.append((nsfw_score, bbox))
  return output
def build_bboxes(img, boxsize=72, stride=25):
  """Generate all the bboxes used in the experiment"""
  width = boxsize
  height = boxsize
  bboxes = []
  for top in range(0, img.size[1], stride):
    for left in range(0, img.size[0], stride):
      bboxes.append((left, top, left+width, top+height))
  return bboxes
def draw_occulsions(img, bboxes):
  """Overlay bboxes on the test image"""
  images = []
  for bbox in bboxes:
    img2 = img.copy()
    draw = ImageDraw.Draw(img2)
    draw.rectangle(bbox, fill=True)
    images.append(img2)
  return images
def alpha_composite(img, heatmap):
  """Blend a PIL image and a numpy array corresponding to a heatmap in a nice way"""
  if img.mode == 'RBG':
    img.putalpha(100)
  cmap = plt.get_cmap('jet')
  rgba_img = cmap(heatmap)
  rgba_img[:,:,:][:] = 0.7 #alpha overlay
  rgba_img = Image.fromarray(np.uint8(cmap(heatmap)*255))
  return Image.blend(img, rgba_img, 0.8)
def get_nsfw_occlude_mask(img, boxsize=64, stride=25):
  """generate bboxes and occluded images, call the API, blend the results together"""
  bboxes = build_bboxes(img, boxsize=boxsize, stride=stride)
  print 'api calls needed:{}'.format(len(bboxes))
  scored_bboxes = []
  batch_size = 125
  for i in range(0, len(bboxes), batch_size):
    bbox_batch = bboxes[i:i + batch_size]
    occluded_images = draw_occulsions(img, bbox_batch)
    results = batch_request(occluded_images, bbox_batch)
    scored_bboxes.extend(results)
  heatmap = np.zeros(img.size)
  sparse_masks = []
  for idx, (nsfw_score, bbox) in enumerate(scored_bboxes):
    mask = np.zeros(img.size)
    mask[bbox[0]:bbox[2], bbox[1]:bbox[3]] = nsfw_score 
    Asp = sp.csr_matrix(mask)
    sparse_masks.append(Asp)
    heatmap = heatmap + (mask - heatmap)/(idx+1)    
  return alpha_composite(img, 80*np.transpose(heatmap)), np.stack(sparse_masks)
#Download full Lena image
r = requests.get('https://clarifai-img.s3.amazonaws.com/blog/len_full.jpeg')
stringio = StringIO(r.content)
img = Image.open(stringio, 'r')
img.putalpha(1000)
#set boxsize and stride (warning! a low stride will lead to thousands of API calls)
boxsize= 64
stride= 48
blended, masks = get_nsfw_occlude_mask(img, boxsize=boxsize, stride=stride)
#viz
blended.show()

Although such experiments make it easy to see the result of the classifier, they have a drawback: the generated visualizations are often quite vague. This makes it difficult to truly understand what the neural network actually does and to understand what might go wrong during her training.

Deconvolutional Networks
After training the network on a given dataset, we would like to take an image and a class, and ask the neural network something like “How can we change this image to better fit a given class?”. For this, we use a deployable neural network, as described in section 2 of the 2014 scientific article of Sailer and Fergus:

A developing neural network can be represented as a convolutional neural network that uses the same components (filtering, pooling), but vice versa, so instead of displaying pixels for signs, it does the opposite. To study the specific activation of the convolutional neural network, we set all other activations in this layer to zero and skip the attribute cards as input parameters to the attached layer of the developing neural network. Then we successfully perform 1) anspooling; 2) correction and 3) filtering to restore activity in the lower layer that generated the selected activation. Then the procedure is repeated until we reach the original pixel layer.

[...]

The procedure is similar to the back propagation of one strong activation (in contrast to ordinary gradients), for example, the calculation, where is the element of the feature map with strong activation, and is the original image.

Here is the result obtained from the developmental neural network, which was given the task of showing the necessary changes in Lena’s photo so that it looks more like pornography (note: the developmental neural network used here works only with square images, so we supplemented Lena’s photo to the square):



Barbara - more decent version of Lena. According to the neural network, this can be corrected by adding red to the lips.



The next frame with Ursula Andress in the role of Hani Ryder from the film “Doctor Know” with James Bond, took first place in a 2003 poll for “The Sexiest Moment in Cinema History”.



The outstanding result of the above experiments is that the neural network was able to understand that red lips and navels are indicators of NSFW. Most likely, this means that we did not include enough red lip and navel images in our SFW training dataset. If we evaluated our model only by studying the accuracy / completeness and ROC curves (shown below, set of test images: 428,271), we would never have discovered this fact, because our test sample has the same drawback. This shows the fundamental difference between rule-based classifiers and modern AI research. Instead of manually processing the characteristics, we redraw the data set until the characteristic improves.



In the end, to test for reliability, we launched a scan neural network on hardcore pornography to make sure that the acquired attributes really correspond to objects that obviously belong to NSFW.



Here we clearly see that the convolutional neural network has correctly acquired the objects “penis”, “anus”, “vagina”, “nipple” and “buttocks” - those objects that our model should recognize. Moreover, the detected signs are much more detailed and complex than the researchers can manually describe, and this explains the significant success that we achieved when using convolutional neural networks to recognize obscene photos.

Read Next