Python
機械学習
DeepLearning
Keras
TensorFlow

ナスDがセネガルの監督に似ているか確かめるために,似ているワールドカップ出場者を検索するAIを作った

はじめに

 某SNSでナスDとセネガルの監督(シセさんという名前だそうです)が似ていると話題になったので,そのことを検証するため,入力した顔画像を基にその顔画像に似た選手と監督を検索するシステムをディープラーニングで作ってみました.フレームワークはKeras2,バックエンドにTensorFlowを使っています。
・・・確かに似てますね.
00-0.png

実装内容概略

画像特徴量による類似度検索

 今回は画像からCNNを通じて画像特徴量のベクトルを抽出し,その特徴量ベクトルの類似度を計算することで,その画像と画像が似ているかを判断します.

画像収集

 画像収集については「Aidemy Tech Blog 機械学習で乃木坂46を顏分類してみた」を参考にしました.画像はGoogle APIを用いて集めます.そのまえに選手の名前をスクレイピングで集めます.
 まず,Wikipediaの「2018 FIFAワールドカップのページ」に行って,下の方にある出場チームをコピペします.

00.png

それを基にスクレイピングで
「'ttps://ja.wikipedia.org/wiki/Template:2018_FIFAワールドカップ'+team_name +'代表'」
から選手名と監督名を持ってきます.スクレイピングには,Beautiful Soupを使いました.
01.PNG
名前のデータは以下のように保存しました.ちなみに768人います.

players.txt
ハルドーソン+アイスランド
サエバルソン+アイスランド
フリズヨンソン+アイスランド
A.グズムンドソン+アイスランド
・・・

あとはgoogle APIで「選手名+国名+サッカー」で画像検索し,選手画像を取得します.余談ですが,最初選手名だけで調べた時.他のもっと有名なものがひっかかるので面白かったです.

例)具志堅さんが出てきました.愛犬の名前だそうです.(グスマンはアルゼンチンの代表選手)
02.PNG

 次に画像から顔部分を取り出します.OpenCVにあるHaar Cascade識別器(分類器)で画像から顔を検出しました.また、この時画像をリサイズします.

検索方法

FaceNet

 画像検索では画像特徴量の類似度計算をします.各画像からCNNを通して,特徴抽出して得たベクトルについて関数(今回はユークリッド距離を使います.)で2つの画像の差を計算します.入力画像とデータベースの各画像の差を全て計算し,最も差が小さくなったデーダベースの人が最も似ている人となります.既存の重みを使用し,各人物の画像は一枚しかいらないので,one shot learningとも呼ばれています.詳しくはsiamese networkやtriplet lossでググれば出てくるかと思います.

差の出力例です.差は以下で計算できます.

def img2vec(image_path, model):
    img =cv2.imread(image_path)
    embedding = model.predict_on_batch(img)#特徴量ベクトル算出
    return embedding

def calc_dist(image_path1, image_path2, model):

        embedding1 = img2vec(image_path1,model)   
        embedding2 = img2vec(image_path1,model)  

        dist = np.linalg.norm(embedding1-embedding2)#類似度計算
        return dist

同じ人物の画像だと値が小さくなります.
03-01.PNG
違う人物だと値が大きくなります.
03-02.PNG
それでは、検索のためデータベースに画像の特徴量となるベクトルを格納していきます.

import numpy as np
import cv2
import tensorflow as tf
from keras.models import load_model

model = load_model('./facenet.h5')

with open("players.txt", "r") as file:
  data = file.read()

players=data.split('\n')

database = {}
for player in players:

    image_path = '.\\image_cut\\'+player+'.jpeg'

    database[player] = img2vec(image_path, model)

結果

サーチの結果を出力するために以下のような関数を定義します.

def who_resembles_it(image_path, database, model,top_N):

        embedding = img2vec(image_path, model)     

        rankings=[]       
        for (name, db_enc) in database.items():            
            dist = np.linalg.norm(embedding - database[name])
            rankings.append([dist,name])

        rankings.sort()
        tops = rankings[:top_N]

        return tops

出力は以下のようにします.画像を入力すると上位top_Nの似ている選手を出力します.

top_N = 1
tops = who_resembles_it("nasuD.jpg", database, model,top_N)
print('ranking')
for i in range(len(tops)):
        print ('rank {}:'.format(i+1),str(tops[i][1]) + ", the distance is " + str(tops[i][0]))

さて,期待通りナスDを入れるとセネガルの監督は出てきますかね・・・
e9982_1618_eb7f8cfd_21813e75-cm.jpg

結果は・・・こちらです!

ranking
rank 1: エルネニー+エジプト, the distance is 0.5117634

136.3.jpg.jpg
ナスDに一番似ているのはエジプト代表のエルネニー選手です!
・・・セネガルのシセ監督じゃないし.あんま似てない気がしますね・・・髭で判断されたのかな?

監督を探すため,top_N = 10にしてみます.

ranking
rank 1: エルネニー+エジプト, the distance is 0.5117634
rank 2: ルイス+コスタリカ, the distance is 0.5222345
rank 3: ピケ+スペイン, the distance is 0.5669997
rank 4: ロドリゲス+スイス, the distance is 0.58111894
rank 5: ケディラ+ドイツ, the distance is 0.5923222
rank 6: ヨルゲンセン+デンマーク, the distance is 0.63478035
rank 7: シセ+セネガル, the distance is 0.64416635
rank 8: デンベレ+ベルギー, the distance is 0.6506373
rank 9: ヴァラン+フランス, the distance is 0.65392536
rank 10: タグリアフィコ+アルゼンチン, the distance is 0.67588425

セネガルのシセ監督いました!7位です!選手監督総計800名弱いるので,なかなかの順位・・・なのでしょうか・・・.あとアジアの選手が全然でできませんね.
考察もどきとして,セネガルの監督が1位じゃない原因を考えてみました.
- 入力の画像サイズが小さい.
- 色あいの問題
- 使用した画像が悪い
- そもそも似てない
今考えられるのは,こんなところですかね.原因究明についてはおいおいしていきたいと思います.

参考