不適切なワードや画像を手軽に検出できるSaaS WebPurifyをつかってみた

2021.08.03

みなさん大根はお好きですか?

今回は、チャットや掲示板などでよく見る

  • 攻撃的な言葉を送信したら非表示になる

  • 卑猥な画像をアップロードしても表示されない

といった、あの機能を手軽に実装できないか調べてみました。

いくつか候補があったのですが、Free Trialで利用できるWebPurifyというサービスを試してみました。

WebPurifyの特徴

WebPurifyは主に以下の3つの機能に分類されます。

それぞれ文字、画像、動画に不適切な情報が含まれていないか検知することが可能です。

  • Profanity Filter
  • Photo Moderation
  • Video Moderation

それぞれAPIが用意されており、HTTP経由で判定を行うことができます。

英語では、NGワードフィルタのことをProfanity(冒涜的、口汚い罵り) Filter、画像の検出をImage Moderationなんて読んだりするそうです。

Profanity Filter

  • 15言語対応した辞書を持っている
  • カスタムでBlock & Allow listsにワードを登録できる

Photo Moderation

  • AIでは、AIによる自動判定ができる(あくまでWebPurify側が用意したモデル)
  • Liveでは、訓練されたライブチームによる人力判定ができる
  • AILiveを組み合わせて、自動判定して怪しそうな画像から人力でフィルタするということもできる
  • 文字認識(OCR)もできて、前述のProfanity Filterと組み合わせて不適切なワードも検出できる

Video Moderation

  • こちらもAILiveの2つがある

Profanity Filterをつかってみる

Profanity Filterでは主に

  • 不適切なワードの検出
  • 不適切なワードの置換
  • カスタムBlock & Allow listsへのワード登録

という3つの機能があります。

これらをAPI Docsを参考に試していきます。

今回テストデータとして、

「NoSQLを思考停止で使うと死ねる」

という文章を判定してみます。

はたしてこの文章は不適切なのでしょうか?

不適切なワードが含まれているかチェック

不適切なワードが含まれていれば1が返ります。

リクエストはこんな感じの形式です。すべてクエリパラメータで表現していますね。

http://api1.webpurify.com/services/rest/?api_key=[your-api-key]&method=webpurify.live.check&text=NoSQL%E3%82%92%E6%80%9D%E8%80%83%E5%81%9C%E6%AD%A2%E3%81%A7%E4%BD%BF%E3%81%86%E3%81%A8%E6%AD%BB%E3%81%AD%E3%82%8B&lang=jp&format=json
  • Request Parameters
    • Text: NoSQLを思考停止で使うと死ねる
    • Language: Japanese
    • Method: webpurify.live.check
  • Response

{
  "rsp": {
    "@attributes": {
      "stat": "ok"
    },
    "method": "webpurify.live.check",
    "lang": "jp",
    "format": "rest",
    "found": "1",
    "api_key": "xxxxx"
  }
}

"found": "1"となり、検出されたのがわかります。

ちなみにシネ4ねタヒねタヒねあたりを試してみましたが、検出されませんでした。

不適切なワードを任意の文字列に置換

不適切なワードを任意の文字に置換します。

  • Request Parameters
    • Text: NoSQLを思考停止で使うと死ねる
    • Replace Symbol: *
    • Language: Japanese
    • Method: webpurify.live.replace
  • Response

{
  "rsp": {
    "@attributes": {
      "stat": "ok"
    },
    "method": "webpurify.live.replace",
    "lang": "jp",
    "format": "rest",
    "found": "1",
    "text": "*****************************************",
    "api_key": "xxxxx"
  }
}

想定だと一部が置換される予定でしたが、テキスト全体が置換されてしまいましたね。。。

英語のように単語の間にスペース区切りがある場合はうまく動作したのですが、日本語だと難しのかもしれません。

Allow Listへの追加

死ねる不適切なワードではないと判断し、Allow Listに追加します。

  • Request Parameters
    • Word: 死ねる
    • Language: Japanese
    • Deep Search: 1 ※部分文字列一致の場合でもフィルタする
    • Method: webpurify.live.addtoallowlist

Allow Listに追加した後、NoSQLを思考停止で使うと死ねるで再度チェックしてみます。

  • Request Parameters
    • Text: NoSQLを思考停止で使うと死ねる
    • Language: Japanese
    • Method: webpurify.live.check
  • Response

{
  "rsp": {
    "@attributes": {
      "stat": "ok"
    },
    "method": "webpurify.live.check",
    "lang": "jp",
    "format": "rest",
    "found": "0",
    "api_key": "xxxxx"
  }
}

"found": "0"となり検出されなくなりましたね。

Block Listへの追加

タヒね不適切なワードと判断し、BlockListへ追加します。

  • Request Parameters
    • Word: タヒね
    • Language: Japanese
    • Deep Search: 1
    • Method: webpurify.live.addtoblocklist

Allow Listに追加した後、今度はNoSQLを思考停止で使うとタヒねるという文章でチェックしてみます。

  • Request Parameters
    • Text: NoSQLを思考停止で使うとタヒねる
    • Language: Japanese
    • Method: webpurify.live.check
  • Response

{
  "rsp": {
    "@attributes": {
      "stat": "ok"
    },
    "method": "webpurify.live.check",
    "lang": "jp",
    "format": "rest",
    "found": "1",
    "api_key": "xxxxx"
  }
}

"found": "1"となっているので、検出できました。

置換も試してみます。

  • Request Parameters
    • Text: NoSQLを思考停止で使うとタヒねる
    • Replace Symbol: *
    • Language: Japanese
    • Method: webpurify.live.replace
  • Response

{
  "rsp": {
    "@attributes": {
      "stat": "ok"
    },
    "method": "webpurify.live.replace",
    "lang": "jp",
    "format": "rest",
    "found": "2",
    "text": "NoSQL\u3092\u601d\u8003\u505c\u6b62\u3067\u4f7f\u3046\u3068*********\u308b",
    "api_key": "xxxxx"
  }
}

今度は一部だけ置換されましたね。

ここは謎ですが、カスタム登録したワードだと挙動が変わるのかもしれません。

Photo Moderationをやってみる

Photo ModerationではLiveAIの2つの機能があります。

AIでは、最新の機械学習技術を用いて、ヌード、武器、アルコール、攻撃的なシンボル、ジェスチャー、有名人、テキストなどを検出してくれます。

一方、Liveではライブチームが人力で判断してくれます

またHybrid Approachを使うことで、AIで一定の閾値を超えた画像を、さらにLiveで人力で判定するというアプローチもできます。

これらをAPIドキュメントを参考にためしていきます。

今回テストデータとしてこちらのフリー素材を利用させて頂きました。

念のため断っておきますが、ただの大根ですよ。

AIでの自動判定

  • Request Parameters
    • Image URL: https://i1.wp.com/sozai-dc.com/wp-content/uploads/dcimage/food/hiwaidaikon.png
    • Categories: nudity,wad,offensive,celebrities,text,faces,ocr,scam
    • Method: webpurify.aim.imgcheck
  • Response

{
  "rsp": {
    "@attributes": {
      "stat": "ok"
    },
    "method": "webpurify.aim.imgcheck",
    "format": "rest",
    "categories": "8",
    "nudity": "88",
    "nuditypartial": "1",
    "nuditysafe": "11",
    "weapon": "1",
    "alcohol": "1",
    "drugs": "1",
    "offensive": "1",
    "artificialtext": "17",
    "naturaltext": "87",
    "scam": "1",
    "ocr_text": "",
    "ocr_profanity": "0",
    "api_key": "xxxxx"
  }
}

おや?おかしいですね。nudity(裸の状態)の値が88を超えましたね。

Hybrid Approachで人力判定も追加

先ほどの大根さんの画像がnudity(裸の状態)と判定されてしまったので、ライブチームへ確認してもらおうと思います。

閾値を設定して、閾値を超えたらLiveで判定してもらうようにします。

  • Request Parameters
    • Image URL: https://i1.wp.com/sozai-dc.com/wp-content/uploads/dcimage/food/hiwaidaikon.png
    • Categories: nudity
    • Threshold Nudity Greater Than: 80
    • Method: webpurify.hybrid.imgcheck
  • Response

{
  "rsp": {
    "@attributes": {
      "stat": "ok"
    },
    "method": "webpurify.hybrid.imgcheck",
    "format": "rest",
    "categories": "1",
    "nudity": "88",
    "nuditypartial": "1",
    "nuditysafe": "11",
    "imgid": "e3a191e3197c7b5f0d0e0980f532ec8b",
    "status": "pending",
    "api_key": "xxxxx"
  }
}

imgidが返ってきました。

ライブチームからの結果の取得方法はいくつかあるようですが、今回はAPIで結果を取得します。

  • Request Parameters
    • Image ID: e3a191e3197c7b5f0d0e0980f532ec8b
    • Method: webpurify.live.imgstatus
  • Response

{
  "rsp": {
    "@attributes": {
      "stat": "ok"
    },
    "method": "webpurify.live.imgstatus",
    "format": "rest",
    "imgid": "e3a191e3197c7b5f0d0e0980f532ec8b",
    "sdate": "2021-08-03 00:46:47",
    "mdate": "2021-08-03 00:47:15",
    "status": "approved",
    "api_key": "xxxxx"
  }
}

statusがapprovedとなっています。

どうやら大根と判断されたのでしょうか?

そりゃそうですよね。どう見ても大根ですよね?

まとめ

いかがだったでしょうか。

ワードのフィルターに関しては、辞書さえあればパターンマッチで自前実装できそうなので、SaaS使いつつ裏で辞書登録したりするのもアリかなと思いました。

画像のフィルターに関しては、もし自前実装するのであれば、Amazon Rekognitionに同等の機能があるのでこちらを使うのもアリかもしれません。

いずれにせよAPIで手軽に利用できるのはとても便利ですね!