Python
DeepLearning
Chainer

ディープラーニングを用いたエロ画像の収集と自動化

More than 1 year has passed since last update.


はじめに

 エロ画像は最高です。

 エロ画像さえあれば、誰でも自由にエッチなことができます。たとえ彼女がいないブサメンであっても、一人で性的に興奮することができます。そこに確かな満足を覚えて、幸せに浸ることができます。どんな嗜好も思いのままです。

 故に我々人類にとって、エロ画像を集めることは、フンコロガシが糞を転がすのと同じように、種としての習性と称しても過言ではない行いなのではないかと思います。

 しかしながら、我々は霊長類の長たる生き物です。かれこれ数万年に渡って同じように糞を転がし続けているフンコロガシと同じではいけません。より効率的に、より意欲的にエロ画像を収集してこその人類です。

 ただ、そうは言ってもエロ画像の収集は非常に大変です。様々なサイトを巡り、十分に吟味した上で、フィッティングのあった品々を、確たるスキームに合わせて収集、構造化してゆく必要があるのです。日によって必要な一枚が異なる人も多いことでしょう。

 ところで最近、ディープラーニングというものが流行っていますね。

 猫も杓子もディープラーニングです。

 難易度の高い案件に当たると、そこはディープラーニングで何とかならない? みたいな相談が増えました。受注確度の低い案件に当たると、とりあえずディープラーニングでお客の気を引けない? 流行ってるんでしょ? みたいな振りをされる営業さんも増えました。

 これは社外でエンジニアの方々とお会いしても同様でありまして、それならディープラーニングで楽勝だぜ? とか、そこはディープラーニングで解決でしょう、みたいなお話をする機会が増えました。誰も彼もディープラーニングに首ったけでございます。

 ということで、そこまで流行っているというのであれば、挑戦せざるを得ません。

 エロ画像収集をディープラーニングでやってみました。


実装

 実装にはchainerを用いました。

 安心安全の国産です。

 モデルは以下のような形となります。

class NIN(chainer.Chain):

def __init__(self, output_dim, gpu=-1):
w = math.sqrt(2)
super(NIN, self).__init__(
mlpconv1=L.MLPConvolution2D(3, (16, 16, 16), 11, stride=4, wscale=w),
mlpconv2=L.MLPConvolution2D(16, (32, 32, 32), 5, pad=2, wscale=w),
mlpconv3=L.MLPConvolution2D(32, (64, 64, 64), 3, pad=1, wscale=w),
mlpconv4=L.MLPConvolution2D(64, (32, 32, output_dim), 3, pad=1, wscale=w),
)
self.output_dim = output_dim
self.train = True
self.gpu = gpu

def __call__(self, x, t):
h = F.max_pooling_2d(F.relu(self.mlpconv1(x)), 3, stride=2)
h = F.max_pooling_2d(F.relu(self.mlpconv2(h)), 3, stride=2)
h = F.max_pooling_2d(F.relu(self.mlpconv3(h)), 3, stride=2)
h = self.mlpconv4(F.dropout(h, train=self.train))
h = F.average_pooling_2d(h, 10)

h = F.reshape(h, (x.data.shape[0], self.output_dim))

loss = F.softmax_cross_entropy(h, t)
print(loss.data)
return loss

 ご覧の通りchainerに付属しているNINのサンプルを少し弄っただけとなります。しかも弄った理由はと言えば、開発用のVPSのメモリが少なかった為、これ以上複雑なモデルを学習させられなかったからです。こちらのモデルでも学習時には2GBほどを要します。

 学習用のデータに関しては、インターネット上で公開されているイラストを利用させて頂きました。合計2000枚の画像のうち、息子が反応を示す画像をレア画像として1000枚、それ意外の画像をコモン画像として1000枚、二種類間でのクラス分けとなります。


確認

 結果確認の為に利用した画像は、以下の三枚です。

91vw8GTN37L.jpg

無題.png

aaa.jpg

 最初の一枚は、自身の著作「田中 ~年齢イコール彼女いない歴の魔法使い」の表紙です。二枚目は3Dカスタム少女から出張願った私の嫁です。そして、三枚目はそんな彼女と電車内で痴漢プレイを楽しんだ際の一枚です。

 こちらの三枚の画像に対して、上記のモデルを利用したスコアリングを行いました。

 結果、以下の通りスコアが得られました。


一枚目

{

"result": [
[
0.9290218353271484,
"1,rare"
],
[
0.07097823172807693,
"0,common"
]
]
}


二枚目

{

"result": [
[
0.6085503101348877,
"0,common"
],
[
0.3914496898651123,
"1,rare"
]
]
}


三枚目

{

"result": [
[
0.5935600399971008,
"1,rare"
],
[
0.40644001960754395,
"0,common"
]
]
}

 chainerに付属するimagenetのサンプルと同様、対応するラベルに対する値が高いほど、そちらに近しいという判断になります。つまり、rareの値が高ければ高いほど、息子が喜ぶといった寸法です。

 ここまでやったところで、多少の手応えを感じました。

 そこでより現実的にエロ画像収集を行うため、上記のモデルへのアクセスをAPI化し、これに対してtwitterのリアルタイムクローリングから取得した画像を読み込ませ、結果をデータベースに格納、ランキング化するウェブアプリを作成しました。

 構成は以下の通りです。

version: '2'

volumes:
db-data:
driver: local
object-data:
driver: local

services:
db:
container_name: db
image: mysql
volumes:
- db-data:/var/lib/mysql
# - ./dockerfiles/staging/mysql:/docker-entrypoint-initdb.d
environment:
MYSQL_ROOT_PASSWORD: $APP_DATABASE_PASSWORD
MYSQL_DATABASE: app_staging
MYSQL_USER: xxxx
MYSQL_PASSWORD: $APP_DATABASE_PASSWORD
expose:
- "3306"
restart: always

app:
build:
context: ../../
dockerfile: dockerfiles/staging/app/Dockerfile
environment:
RAILS_LOG_TO_STDOUT: 'true'
RAILS_SERVE_STATIC_FILES: 'true'
RAILS_ENV: 'staging'
DISABLE_DATABASE_ENVIRONMENT_CHECK: $DISABLE_DATABASE_ENVIRONMENT_CHECK
APP_SECRET_KEY_BASE: $APP_SECRET_KEY_BASE
APP_DATABASE_USERNAME: xxxx
APP_DATABASE_PASSWORD: $APP_DATABASE_PASSWORD
APP_DATABASE_HOST: db
TW_CONSUMER_KEY: $TW_CONSUMER_KEY
TW_CONSUMER_SECRET: $TW_CONSUMER_SECRET
TW_ACCESS_TOKEN: $TW_ACCESS_TOKEN
TW_ACCESS_TOKEN_SECRET: $TW_ACCESS_TOKEN_SECRET

minio:
build:
context: ../../
dockerfile: dockerfiles/staging/minio/Dockerfile
volumes:
- object-data:/var/lib/minio
environment:
MINIO_ACCESS_KEY: 'xxx'
MINIO_SECRET_KEY: 'xxx'
ports:
- '0.0.0.0:9000:9000'
command: [server, /export]

calc:
container_name: calc
build:
context: ../../
dockerfile: dockerfiles/staging/calc/Dockerfile
command: python web.py
expose:
- "5000"

web:
container_name: web
extends:
service: app
command: bin/wait_for_it db:3306 -- pumactl -F config/puma.rb start
expose:
- "3000"
depends_on:
- db

crawler:
container_name: crawler
extends:
service: app
command: bin/wait_for_it db:3306 -- rails runner bin/crawler.rb
depends_on:
- calc
- db

nginx:
container_name: nginx
build:
context: ../../
dockerfile: dockerfiles/staging/nginx/Dockerfile
ports:
- '80:80'
- '443:443'
depends_on:
- web

 こちらを用いてしばらく、twitter上でおかずの収集を試みました。

 結果は以下の通りです。

ranking.png

 5,60枚ほど集まった時点でのランキングから、幾枚か抜き出したものとなります。

 画像の右側に記載されている値が、rareの値となります。

 取り立てて何をすることもなく、甲乙に分けたイラストをモデルに与えただけで、ここまでの成果が出たことに驚きです。もう少し厳密にラベリングを行えば、より息子好みの画像を集めることが可能なのではないかと、強く感じました。また、複数のモデルを組み合わせて使用すれば、更に精度は向上しそうです。

※ 記載の画像に対する各種値については、私の息子の好みを学習させたAIによる判断となります。その値は特定の趣味や主義を否定するものではございません。


結論

 一人が一人、己の為に最適化されたエロ画像ソムリエを抱える日が、すぐ近くまで来ているのかもしれません。