天天看點

利用人工智能檢測色情圖檔

色情内容在中國一直處于嚴格的監管,即使這樣,網際網路上還是很容易就能通路到色情内容。還記得曾經的“綠壩-花季護航”軟體麼?由于其識别效果差、軟體不穩定,最後不了了之,浪費了大量的人力和金錢。

随着計算機視覺和深度學習的發展,算法已經成熟,利用人工智能,我們能夠更加精确的識别色情内容。現在有很多雲服務商提供鑒黃服務,通過內建鑒黃API到産品中,就可以給産品增加色情過濾功能。這種模式對于大多數網際網路産品非常有效,可以極大的降低營運風險(對于部落格、圖床、直播等服務提供商而言,過濾色情、暴力等内容是重中之重)。但對于一部分産品而言,這種模式存在一些不足:

  1. 鑒别圖檔、視訊内容等必須通過網絡服務進行,響應速度難以保證;
  2. 通常鑒黃服務按次或者按照流量計費,對于個人開發者而言,有成本負擔;

open_nsfw

現在有個好消息,雅虎開源了其深度學習色情圖檔檢測模型open_nsfw,項目位址: http://github.com/yahoo/open_nsfw,這裡的NSFW代表Not Suitable for Work。項目提供了基于caffe的深度神經網絡模型和一個python腳本,可以供測試:

python ./classify_nsfw.py \
--model_def nsfw_model/deploy.prototxt \
--pretrained_model nsfw_model/resnet_50_1by2_nsfw.caffemodel \
test_image.jpg           

複制

定義NSFW内容是非常主觀的,在某個國家或地區會引起反感的内容可能在另一個地方完全沒問題。是以,open_nsfw模型隻關注一種類型的NSFW内容:色情圖檔。但需要注意的是,該模型不能解決草圖、漫畫、文本等内容的識别。

色情圖檔的判别也是非常主觀的,是以該模型并不會直接給出某個圖檔是否色情的結果,而是給出一個機率(0-1之間的分數)。一般而言,得分小于0.2表示圖像很可能是安全的,評分大于0.8則基本可判定圖檔屬于色情圖檔。如果得分介于這兩個值之間,則需要程式員根據需求來設定一個閥值。如果需要比較嚴格的過濾,設一個比較低的閥值,反之設一個比較高的閥值。

內建open_nsfw到C++程式

open_nsfw提供了一個python腳本,google了一圈,也沒有找到有人将open_nsfw內建到C++代碼中。好在classify_nsfw.py這個腳本比較簡單,而caffe提供了C++的例子cpp_classification,結合這兩部分的代碼,我編寫了一個C++的鑒黃程式,源碼參考:https://gitee.com/mogoweb/dpexamples。

當然看似簡單的代碼翻譯工作,遇到的坑也不少,下面就總結一下C++代碼中需要注意的地方。

圖檔預處理

在classify_nsfw.py中,編寫了一個resize_image函數來處理圖檔縮放,沒有采用caffe内置的圖檔縮放程式。代碼注釋中解釋是因為訓練時使用了這個縮放算法,是以為了達到最好的效果,測試中也需要采用該縮放算法。随後又對圖檔進行了一次裁剪,因為調用resize_image縮放為256x256大小,而模型接受的size為224x224。

img_data_rs = resize_image(pimg, sz=(256, 256))
image = caffe.io.load_image(StringIO(img_data_rs))H, W, _ = image.shape
_, _, h, w = caffe_net.blobs['data'].data.shape
h_off = max((H - h) / 2, 0)
w_off = max((W - w) / 2, 0)
crop = image[h_off:h_off + h, w_off:w_off + w, :]           

複制

在C++代碼中,我使用了caffe中提供的opencv方法處理這個步驟:

cv::Mat img = ReadImageToCVMat(file, 256, 256);
...
// crop image
cv::Size size = sample.size();
int H = size.height;
int W = size.width;
int h = input_geometry_.height;
int w = input_geometry_.width;int h_off = std::max((H - h) / 2, 0);
int w_off = std::max((W - w) / 2, 0);
sample(cv::Rect(w_off, h_off, w, h)).copyTo(sample_resized);           

複制

資料預處理

在classify_nsfw.py中,我們可以看到這樣一段代碼:

caffe_transformer = caffe.io.Transformer({'data': nsfw_net.blobs['data'].data.shape})
# move image channels to outermost
caffe_transformer.set_transpose('data', (2, 0, 1))
# subtract the dataset-mean value in each channel
caffe_transformer.set_mean('data', np.array([104, 117, 123]))
# rescale from [0, 1] to [0, 255]
caffe_transformer.set_raw_scale('data', 255)
# swap channels from RGB to BGR
caffe_transformer.set_channel_swap('data', (2, 1, 0))           

複制

這段代碼其實是對資料做某種變換,将讀入的資料轉換為caffe模型能夠接受的格式。

caffe_transformer.set_transpose('data', (2, 0, 1))           

複制

該語句困擾了我好長時間,不知道在C++程式中該如何處理。後查網絡資料,才了解到因為caffe.io讀取的圖像為HWC(H:高,W:寬,C:顔色)矩陣,而caffe模型需要(CHW)格式,是以需要對矩陣做一個變換,将顔色值次元提前到最前面。(2, 0, 1)的含義就是将原來資料的第2, 0, 1列按照新的順序重新排列。

caffe_transformer.set_raw_scale('data', 255)           

複制

這個處理是因為使用caffe.io讀取的顔色值歸一化為0~1之間的數,而caffe模型接受的是一個位元組的整數,範圍0~255,是以需要進行一個放大。

在C++代碼中,由于采用了opencv進行圖像處理,不需要上面兩步的變換處理。

caffe_transformer.set_mean('data', np.array([104, 117, 123]))           

複制

在很多示例中,均值通常從均值檔案中加載,這裡直接給了一個固定值。所謂均值,可以了解為資料的算術平均值。通常輸入資料減去均值,是為了減少奇異值(異常的值,比平均值大很多或小很多的值)的影響。這裡是一個三元組,分别代表RGB通道上的均值。

對應的C++代碼如下:

cv::Mat sample_normalized;
cv::subtract(sample_float, mean_, sample_normalized);           

複制

接下來的代碼是RGB轉BGR,這個比較容易了解。

caffe_transformer.set_channel_swap('data', (2, 1, 0))           

複制

檢視了一些caffe的C++例子,均沒有這個步驟,可能cv::Mat中已經能夠正确判斷出RGB和BGR,是以代碼中沒有增加這一步驟。

對比測試

這個程式是否能夠如願工作呢?讓我們找一些圖檔測試一下。考慮到内容審查,這裡進行測試的圖檔均不是嚴格意義上的色情圖檔,隻是裸露程度不同。下面使用C++程式和open_nsfw python腳本測試的結果進行對比。

利用人工智能檢測色情圖檔

C++ : 0.6122

python: 0.875480949879

利用人工智能檢測色情圖檔

C++ : 0.2536

python: 0.0773676931858

利用人工智能檢測色情圖檔

C++ : 0.6319

python: 0.782215833664

利用人工智能檢測色情圖檔

C++ : 0.0914

python: 0.0774072110653

利用人工智能檢測色情圖檔

C++ : 0.0073

python: 3.04092318402e-05

從結果可以看出,使用C++程式進行測試,結果基本符合預期,但是和python版本還是有一些差距,猜測問題可能在于對圖檔進行縮放采用的算法不同,如果要獲得好的結果,訓練和測試階段對資料的預處理需要一緻。另外一個可能是沒有RGB到BGR的轉換,這個還需要再驗證。

如果你有興趣,可以找一些尺度更大的圖檔測試,看看是不是能夠正确的識别出來。

繼續閱讀