バレンタインデーにもらったチョコが義理か本命か判定するAIを実際に作って公開した
07
- 3月
2018
Posted By : boomin
バレンタインデーにもらったチョコが義理か本命か判定するAIを実際に作って公開した
Advertisements

0. 概要

AIが義理チョコを判別するシステムを開発してみました。
その挙動について、ここで解説をしていきたいと思います。

1. Introduction

世界中にあるイベントの一つに、2/14のバレンタインデー(Valentine’s Day)があります。
国や地域によって、その日の過ごし方などは異なるようですが、ここ日本では、女性から男性へチョコレートを贈る日、という お菓子会社の戦略によって定着した文化 があります(例えば[1]参照。諸説あり)。

[1]によれば、1970年代後半頃に本命チョコを贈る習慣が定着したようです。ここから、さらにお菓子業界の商機ととらえた全国飴菓子工業協同組合は、義理チョコとホワイトデーという文化を創り出し、1984年をホワイトデー定着の年としました。

しかし、このような文化は悲劇を起こしました。それは、女性から贈られたチョコが、本命なのか義理なのかがはっきりしないケースが登場したためです。はっきり口にせず、以心伝心や忖度を是とする日本の文化が、多くの迷える男性を生み出して しまうことになりました。

このような背景から、贈られたチョコレートが、本命なのか義理なのかをはっきりさせる社会的ニーズが産まれました。図1に、この状況証拠を示します。世の中のどれだけの人が検索をすれば、Googleの検索サジェスチョンにこのような結果をもたらすのでしょうか。これは、もはや国民的課題といえるでしょう。
以降、これを バレンタイン問題 と呼ぶことにします。


図1 バレンタイン問題 – バレンタインのチョコレートが本命か義理かを判断する社会的ニーズ

一方、最近の機械学習手法の発達は目覚ましい物があります。AIすなわち、人工知能がこの バレンタイン問題 を解決する手段になりえることが示されました([2])。これは画期的な観点であると思われましたが、開発したエンジニアのみが実行、評価できるものでした。これでは、迷える男子中高生、理系男子大学生、そしてIT系男性社会人を救う ことはできません(筆者の独断と偏見による)。

また、男性の立場からすると、ホワイトデーにお返しを行う義務が、暗黙のうちに発生します。ここで不適切な対応をとると、その後の長期間にわたり、生きづらい人生を歩むことになりかねません。
そのため、贈られたチョコレートに対して、どのくらいのお返しを行えばよいのか、その指針を示すことができれば、悩める男性に対して有効なサービスとなり得ると思われます。図2に、悩める男性像の一例を示します。


図2 バレンタインのお返しに苦悩する男性の一端
以上のことから、本記事では[2]をさらに発展させ、バレンタイン問題の一助となることを目指すものです。

  • 義理チョコと本命チョコを判別するAIを開発する
    • 二値分類と呼ばれる、機械学習における代表的な手法を用いたアプローチをとる
    • これを本命・義理チョコ分類問題(Honmei・Obligation Chocholate Classification Problem)と呼ぶ[2]
  • 開発した、構築した学習モデルをサービス化して公開する
    • PC、スマホのどちらからでも使えるように、Webサービス化を行う
  • 義理チョコと本命チョコを分類する際の確率を算出する
    • 定量的に判断指標を示すことで、悩める男性自身の判断を促す
    • 確率を用いることで、ホワイトデーのお返し予算の提案も併せて行う

なお、筆者が人生でチョコレートを貰った回数(個数)は、年齢のおよそ半分以下である。
チョコ欲しい。

2. 構築した学習モデル

本章では、本命・義理チョコ分類問題 を解くための学習モデル構築手順について示します。
ここでの目的は、本命チョコと義理チョコの違いの特徴量を抽出、獲得することです。
特徴量とはベクトルのようなもの だと思ってください。
この特徴量を抽出、獲得することを、機械学習における学習 と呼びます。

概念的には、以下のようなイメージとなります。

図3 本命チョコと義理チョコの特徴量の概念図([2]より引用)

ここでは簡単なイメージを持っていただくために、2次元平面上にチョコ画像をマッピングしてあります。
この2次元平面上において、以下の図4に示すように、境界線を求めることができれば、
次にどんな画像が来たとしても、その画像が本命なのか義理なのかを判別することができるようになります。

図4 本命チョコと義理チョコの特徴量の分離境界線([2]より引用)

2.1 学習データ

学習モデルを作るためには、その特徴量を見出すためのデータを準備する必要があります。

  • 教師データ
    • 学習データ
    • 正解データ(正解ラベルともいう:ここでは本命/義理のどちらか)
  • 評価データ
    • 検証データ
    • 正解データ(正解ラベルともいう:ここでは本命/義理のどちらか)

しかしここで問題があります。
自分の人生において、年齢の半分程度の個数のチョコしかもらったことのない筆者は、学習のための教師データや評価データを持っていません。そこで、インターネット上で画像検索を行い、それを収集することで、教師データと評価データを集めることにします。

インターネットからの画像データ収集を人手で数百枚も集めるのは大変です。そこで、画像データ収取を自動化しました。

2.1.1 学習データの収集

googleをはじめ、世の中にはいくつかの検索サービスがあります。しかし、そのうち画商データを大量に無料で 収集する為に調査を行いました。その結果、googleやyahoo!などの検索サービスがあることがわかりました[3]

しかし、こちら[4]によると、googleなどは使い物にならないという報告がされています。そこで、これらの情報を踏まえて、BingのREST APIを使用して画像の収集を行いました。

取得できるだろう、検索結果の例を図5と図6に示します。

図5 本命チョコの検索結果(の一部)図6 義理チョコの検索結果(の一部)

チョコではない、なんかおかしな画像も混ざっていますが、気にしないことにします。
何故なら、それが本命チョコ義理チョコ というラベルが付けられた、まごうことなき検索結果だからです。

そして、実際に学習データと評価データとして収集してきた画像が、以下の図7、図8となります。

図7 学習データ、検証データとして使用する本命チョコの画像(の一部)図8 学習データ、検証データとして使用する義理チョコの画像(の一部)

みなさん、本命チョコと義理チョコの 雰囲気の違いを感じ取れますか ・・・・・・?
強いて言うなら、こんな感じでしょうか。

  • 本命
    • 華やかな色使い
    • ハート形
  • 義理
    • さっぱり
    • 直線

これまでの人生で、女心が読めたためしがない筆者の感性では、これ以上の違いは読み取れませんでした。では、この画像たちを使用して、機械学習を進めていきます。

2.1.2 収集した画像データ

収集した画像データは、以下のような結果となりました。

データ検索クエリ検索で得た枚数重複除外などした後の枚数
本命チョコバレンタイン チョコ 本命473枚404枚
義理チョコバレンタイン チョコ 義理614枚545枚

義理チョコのほうが枚数が多いですね。
検索の結果、pagingを進めていっても、同じ画像が繰り返し登場したりして、これ以上の枚数を収集してもあまり意味をなさなかったのです。

そこで、さらに以下の処理を行いました。

  • jpg, jpeg, png形式の画像ファイルのみ使用する
    • gifやその他の形式のファイルは、お菓子販売店のバナーであったりするため、不適切と判断
  • 義理と本命のどちらにも存在する画像は対象外とする
    • 本命・義理チョコ分類問題 は二値問題のため、どちらにも属するような第3の属性は排除
    • 具体的には、本命と義理の両方に登場する画像は除外

2.1.3 画像データの水増し

前節で得られた合計945枚(404+545)の画像に対して、6倍の水増しを行い、学習データを増幅させます。6倍の理由は、筆者の予算の都合上、準備できたGPUがもつGPUメモリが処理できる限界容量だからです。

といっても、まったく同じ画像を使っては意味がありません。そこで、画像をランダムに回転、移動、引き延ばしを行うことで、オリジナルとは異なる画像を生成します。

以下に、実際に水増しした画像を示します。


図8 水増しされた画像たち
最終的に得られた教師データの画像が、以下の通りとなりました。

データ検索クエリ検索で得た枚数重複除外などした後の枚数水増し後の枚数
本命チョコバレンタイン チョコ 本命473枚404枚2,424枚
義理チョコバレンタイン チョコ 義理614枚545枚3,270枚

これらの画像を使用して、学習を行うことにします。

Advertisements

2.2 学習モデルと学習パラメータ

2.2.1 学習モデル

機械学習にはDeep Learningを適用させることにします。Deep Learningのなかでも、画像認識に強みがあることで知られるCNN (Convolution Neural Network) を使います。なお、このCNN実装として開発言語はpython、ライブラリはkerasを使用しました。

CNNについての詳細は適当にググっていただければと思いますが、その一例はこんな感じです。


収集した画像を、すべて32pixel x 32pixelの画像へ縮小変換します。その上で、32x32x3(RGBの3チャンネル)のデータとして入力とします。

以下が、実際に使用したCNNのネットワークモデルです。

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 32, 32, 64)        832       
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 32, 32, 64)        16448     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 16, 16, 64)        0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 16, 16, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 16, 16, 128)       32896     
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 16, 16, 128)       65664     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 8, 8, 128)         0         
_________________________________________________________________
dropout_2 (Dropout)          (None, 8, 8, 128)         0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 8, 8, 256)         131328    
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 8, 8, 256)         262400    
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 4, 4, 256)         0         
_________________________________________________________________
dropout_3 (Dropout)          (None, 4, 4, 256)         0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 4096)              0         
_________________________________________________________________
dense_1 (Dense)              (None, 3092)              12667924  
_________________________________________________________________
dropout_4 (Dropout)          (None, 3092)              0         
_________________________________________________________________
dense_2 (Dense)              (None, 1024)              3167232   
_________________________________________________________________
dropout_5 (Dropout)          (None, 1024)              0         
_________________________________________________________________
dense_3 (Dense)              (None, 256)               262400    
_________________________________________________________________
dropout_6 (Dropout)          (None, 256)               0         
_________________________________________________________________
dense_4 (Dense)              (None, 64)                16448     
_________________________________________________________________
dropout_7 (Dropout)          (None, 64)                0         
_________________________________________________________________
dense_5 (Dense)              (None, 16)                1040      
_________________________________________________________________
dropout_8 (Dropout)          (None, 16)                0         
_________________________________________________________________
dense_6 (Dense)              (None, 2)                 34        
=================================================================

全結合層の最後の活性化関数にsoftmaxを使用して、分類問題とすることにします。
この時、本命・義理チョコ分類問題 は二値分類ですが、分類の確率を得ることができます。

2.2.2 学習パラメータ

  • batch size
    bsizeは128としました。これ以上大きいと、GPUメモリが根を上げてしまうためです。。。orz

その他、kerasならではの機能として、学習時のcallbackに以下を指定しています。

  • EarlyStopping
    • 学習精度などに変化がなくなったと判断した場合に、そこで学習を停止する
    • 過学習の防止に有効
es = EarlyStopping(monitor='loss', min_delta=0.0001, patience=10, verbose=0, mode='auto')
  • ReduceLROnPlateau
    • 学習精度などに変化がなくなったと判断した場合に、学習率を下げる
    • 計算の前半は高い学習率で効率よく学習を進め、後半は段階的に学習率を落とし、さらに高い精度を狙う
rl = ReduceLROnPlateau(monitor='acc', factor=0.5, patience=5, min_lr=0.0, verbose=1)
  • TensorBoard
    • 学習経過を可視化するためのログを出力
    • 計算中に、リアルタイムで精度や誤差の推移を確認することができる
    • おかしなパラメタを指定してしまい、うまく学習できなかった場合に早期に気が付くことができる
tb = TensorBoard(log_dir="logs/", histogram_freq=1, write_graph=True)
  • ModelCheckpoint
    • 学習精度などが改善した場合に、その時点の学習モデルを保存する
cp = ModelCheckpoint("model.h5", monitor='acc', verbose=1, save_best_only=True)

2.3 学習と学習精度

実際に学習させた際の、学習精度と学習誤差について示します。
二値分類問題なので、精度はおよそ0.5から開始し、学習のepoch数が進むにつれて、精度が上がっていくことが確認できます。最終的な 精度を0.991495549 まで高めることができました。


図9 学習精度の推移
機械学習において気にしなければならない一つの観点に、過学習 があります。
過学習 とは、学習データに特化しすぎて、学習データ以外のデータに対してきちんとした判断ができなくなることです。そこで、過学習の程度を見るために、val_lossを確認してみましょう。


図10 学習誤差の推移
val_lossの値があまり減少していませんね。。。。また、その後若干の上昇を見せています。いろいろと調べてみると、val_lossが極小となるところがbestとは限らず、その後に若干学習を進めてval_lossが少し大きくなっても、結果的に良いモデルが得られることも多いようです。

そもそも、ここで厳密に本命と義理をきっちり分離することなんて不可能です。むしろ、この程度の揺らぎがあったほうが、結果的にエンターテイメントとして面白いと考え (意訳:何も考えず)、このモデルを採用することにします。

簡単に言うと、

  • 学習モデルとして採用されている画像は、イイ感じに判別できる
  • でもあまり汎化性能は高いとはいえない
  • でも割きって、これでよいとすることにする

3/10 ここまで

以降、順次追記していきます。
3/14前までに、全部書ききりたい。。。。

3. お返し予算の算出

3.1 本命チョコと義理チョコの購入予算

3.2 本命チョコと義理チョコ、それぞれに対して女性が期待するお返し

3.3 義理と本命の確率と予算の推定

4. Webサービス化

4.1 クライアントサイド

4.2 サーバサイド

5. Discuttion

6. 結論

7. Future Work

参考文献

Advertisements

コメントを残す