「末は博士か大臣か?」、顔から判別してみる
【イントロ】
「末は博士か大臣か」。
優秀な子供は周りの大人から、そんな期待を背負って成長していきます。小さな頃の期待を背にぐんぐん頭角を表し、時代を代表する寵児となっていきます。
しかし、「十で神童、十五で才子、二十歳過ぎれば只の人」。
歳を重ね、より大きな集団の中で比較されることで、かつての輝きは失われていく人も、一方では存在します。。。
一体何が、その差を生み出すのか。やはり、顔か・・・
でも、博士と大臣って顔つきは違うんだろうか(´・ω・`)?
そこで本記事では、「顔面から博士か大臣か判別できるか?」、調べてみました。
【方法】
方法はかっこよく、Deep Learningを使用することにしました。
@kakinaguru_zoさんのPythonとKerasを使った猫犬分類プログラムを参考に、分類を実施しました。実際は基本的にそのままです。(コメント含めて)。参考書として、こちらのKeras本を使用していますが、丁寧に書かれていると思いました(こなみ
とはいえ、学習用の博士と大臣画像は自分で集めないといけません。
そこで、Wikipediaから科学系のノーベル賞(物理学、化学、医学・生理学)受賞者と各国の首相・大統領の顔画像を収集するプログラムをPythonで作成しました。
その結果、博士画像488枚、大臣画像625枚を収集することができました。
(ほかのサイトからも集めてみましたが、画像サイズは同じくらいのほうが収束が良かったので、すべてWiki画像で揃えました。)
これらの画像を2:1:1くらいの割合に分割し、それぞれ「学習」・「評価」・「テスト」データとしました。この辺の割合はフィーリングです。
次に、この画像を使って学習を実施しました。学習にはGoogle Colabを使用しました。これは、GPU使用可能なクラウド上でIpython notebook形式のPythonを実行できるアプリケーションです。すごい時代になったもんだ。。。
学習用データをアップロードして学習した結果が下図のとおりです。
図1からわかるように、学習データで85%、評価データで70%程度の正確さで分類できる結果となりました。当てずっぽうだと50%なので、少しは分類ができているようです。
やはり、博士と大臣は顔つきが違う・・・?
とはいえ、図2のようにLoss関数の収束は十分ではないので改善の余地はありそうです。過学習してるのかな?
それでは、テストデータに対してどういう結果になるか調べたのが下図です。図3のように、写真下の左側の数字が博士っぽさ、右側が大臣っぽさの確率を示しています。
・・・ナニが決め手なんだ?何かしらの分類がなされていますが、根拠がわからないところが不思議なところです。Deep Learningはすごいがむずかしい(語彙)
【まとめ】
なかなか、100%の分類は難しいです。いろいろ調整しがいはありますが、Kerasがそれぞれナニをしているか、ちゃんと勉強しないといけないなぁと思います。
次の記事では、もう少しいろんな人を分類してみたいと思います。
画像収集スクリプト
画像分類用プログラム
「末は博士か大臣か」。
優秀な子供は周りの大人から、そんな期待を背負って成長していきます。小さな頃の期待を背にぐんぐん頭角を表し、時代を代表する寵児となっていきます。
しかし、「十で神童、十五で才子、二十歳過ぎれば只の人」。
歳を重ね、より大きな集団の中で比較されることで、かつての輝きは失われていく人も、一方では存在します。。。
一体何が、その差を生み出すのか。やはり、顔か・・・
でも、博士と大臣って顔つきは違うんだろうか(´・ω・`)?
そこで本記事では、「顔面から博士か大臣か判別できるか?」、調べてみました。
【方法】
方法はかっこよく、Deep Learningを使用することにしました。
@kakinaguru_zoさんのPythonとKerasを使った猫犬分類プログラムを参考に、分類を実施しました。実際は基本的にそのままです。(コメント含めて)。参考書として、こちらのKeras本を使用していますが、丁寧に書かれていると思いました(こなみ
とはいえ、学習用の博士と大臣画像は自分で集めないといけません。
そこで、Wikipediaから科学系のノーベル賞(物理学、化学、医学・生理学)受賞者と各国の首相・大統領の顔画像を収集するプログラムをPythonで作成しました。
その結果、博士画像488枚、大臣画像625枚を収集することができました。
(ほかのサイトからも集めてみましたが、画像サイズは同じくらいのほうが収束が良かったので、すべてWiki画像で揃えました。)
これらの画像を2:1:1くらいの割合に分割し、それぞれ「学習」・「評価」・「テスト」データとしました。この辺の割合はフィーリングです。
次に、この画像を使って学習を実施しました。学習にはGoogle Colabを使用しました。これは、GPU使用可能なクラウド上でIpython notebook形式のPythonを実行できるアプリケーションです。すごい時代になったもんだ。。。
学習用データをアップロードして学習した結果が下図のとおりです。
図1,学習・評価データのAccuracy |
図2、学習・評価データのLoss |
図1からわかるように、学習データで85%、評価データで70%程度の正確さで分類できる結果となりました。当てずっぽうだと50%なので、少しは分類ができているようです。
やはり、博士と大臣は顔つきが違う・・・?
とはいえ、図2のようにLoss関数の収束は十分ではないので改善の余地はありそうです。過学習してるのかな?
それでは、テストデータに対してどういう結果になるか調べたのが下図です。図3のように、写真下の左側の数字が博士っぽさ、右側が大臣っぽさの確率を示しています。
図3、博士(左)・大臣(右)である確率 |
図4、大臣ぽい大臣(日本、鳩山氏) |
図5、博士っぽい大臣(ベルギー、ロンパウ氏) |
図6、博士っぽい博士(日本、南部氏) |
図7、大臣っぽい博士(アメリカ、バーディーン) |
・・・ナニが決め手なんだ?何かしらの分類がなされていますが、根拠がわからないところが不思議なところです。Deep Learningはすごいがむずかしい(語彙)
【まとめ】
なかなか、100%の分類は難しいです。いろいろ調整しがいはありますが、Kerasがそれぞれナニをしているか、ちゃんと勉強しないといけないなぁと思います。
次の記事では、もう少しいろんな人を分類してみたいと思います。
画像収集スクリプト
#For Wikipedia # -*- coding: utf-8 -*- import requests from bs4 import BeautifulSoup import os import sys import time def download_site_imgs(url, path): img_urls = [] # パス(保存先)が存在しなければ新規作成 if not os.path.exists(path): os.makedirs(path) # htmlのパース soup = BeautifulSoup(requests.get(url).content,'lxml') # 画像リンクなら(拡張子がjpgなど)リストに追加 for img_url in soup.find_all("img"): # imgタグのsrc要素を抽出 src = img_url.get("src") src="https:"+src print(src) #src要素に画像の拡張子が含まれていたらリストに追加 if 'jpg' in src: img_urls.append(src) elif 'png' in src: img_urls.append(src) elif 'gif' in src: img_urls.append(src) # 画像リンク先のデータをダウンロード for img_url in img_urls: #temp=img_url.split("?") #img_url=temp[0] re = requests.get(img_url) print('Download:', img_url) with open(path + img_url.split('/')[-1], 'wb') as f: # imgフォルダに格納 f.write(re.content) time.sleep(1.0) if __name__ == '__main__': site=input("Input URL; ") download_site_imgs(site, 'img/') print("ok")
画像分類用プログラム
# coding: utf-8 # Google Colab にファイルをアップロードする # 説明は https://colab.research.google.com/notebooks/io.ipynb などを参照 # Google Colab 自体の説明は # https://news.mynavi.jp/article/zeropython-27/ などを参照 # ファイルアップロードはChromeブラウザでないと失敗するかも知れない # Chromeで失敗した場合「設定」→「詳細」→「コンテンツの設定」→「Cookie」と辿って現れる # 「サードパーティの Cookie をブロックする」をOFFにする import google.colab.files uploaded = google.colab.files.upload() for filename in uploaded.keys(): with open(filename, "wb") as f: f.write(uploaded[filename]) # 変数uploadedを削除してメモリーの消費を節約する del uploaded # アップロードを確認する get_ipython().system('ls -l') # zipファイルを展開する get_ipython().system('unzip doctor-and-politician.zip') import keras # kerasの関数を使えるように読み込む # 個々の関数の説明は http://keras.io/ja を見て下さい import numpy import matplotlib.pyplot # 画像の画素は0〜255の値であるが、ニューラルネットに大きい値を入れるとうまく動かないので1/255を掛けて0〜1の値にする train_datagen = keras.preprocessing.image.ImageDataGenerator( rescale=1/255.0, # 以下は、学習する画像を回転させたり上下左右反転させたりして、枚数の水増しをさせる指示である rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True, vertical_flip=True, fill_mode='nearest') validation_datagen = keras.preprocessing.image.ImageDataGenerator(rescale=1/255.0) train_generator = train_datagen.flow_from_directory( "train", # 最初に渡す引数は学習用データがあるフォルダー。trainの中にdogとcatというフォルダーがありそれぞれ犬と猫の画像が入っている。種類をフォルダー名にしてその中に画像を入れる target_size=(224, 224), # ニューラルネットに渡すときの画像の大きさ batch_size=20, interpolation="lanczos") validation_generator = validation_datagen.flow_from_directory( "validation", # 最初に渡す引数は「検証」用データがあるフォルダー # 検証とは、学習に用いなかったデータに対してニューラルネットがどの程度うまく動作するか確認することである target_size=(224, 224), # ニューラルネットに渡すときの画像の大きさ shuffle=False, # 同じ順番で画像を出す batch_size=20, interpolation="lanczos") # MobileNetと呼ばれるニューラルネットを、入力画像サイズ224×224で、最後の分類のための変換を除いて、 # ImageNetデータベースで学習済みのウェイトとバイアスを持ったものとして読み込む # MobileNetは高速に動作するので選んだ。もっと識別性能が高いものもあるのでそれを使ってもよい mobilenet = keras.applications.mobilenet.MobileNet(input_shape=(224,224,3), weights="imagenet", include_top=False) # MobileNetのウェイトとバイアスを更新しないように指示する mobilenet.trainable=False model = keras.models.Sequential() # model.add() にニューラルネットの層を処理する順番に渡す。 # ここで「層」とはウェイトとバイアスと活性化関数による変換のほうを指す # 各画素の値の範囲を0〜1から-1〜+1に変更する。MobileNetがそういう入力形式について # 学習を行っているからである。以下の行を削除しても学習ならびに識別は出来る model.add(keras.layers.Lambda(lambda x: x*2-1, input_shape=(224,224,3))) model.add(mobilenet) # Flattenは2次元以上のデータを平ら(1次元)にする変換である。学習で更新されるウェイトを持たない model.add(keras.layers.Flatten()) # Denseは入力の実数と出力の実数がすべて結合されている変換である。256は出力する実数の数 model.add(keras.layers.Dense(256, activation="relu")) # 分類の場合最後の活性化関数をsoftmaxにするとうまくいくことが多い model.add(keras.layers.Dense(2, activation="softmax")) # ネットワークの構造と学習可能・学習不可能な変数の数を表示する model.summary() # 学習させる前にcompileを呼ぶ必要がある。このときに損失関数(loss)などを指定する。 # 識別するときには交差エントロピー(categorical crossentropy)を用いるのがよい。 # optimizerはウェイトの更新に用いる手順で、SGDよりはRMSpropのほうが上手くいくことが多い model.compile(optimizer=keras.optimizers.RMSprop(lr=2e-5), loss='categorical_crossentropy', metrics=['acc']) # 上記で、optimizer="rmsprop" として標準の設定をそのまま使うと全く学習が進まない # エポックとは、おおよそすべての訓練データを最低1度は読んで学習する単位である # バッチサイズ×steps per epochが2000で画像の枚数は1000である # steps per epochは1エポック内に何個のバッチを読ませるか指定している # epochsは何エポック学習させるか指定している # 1エポックの学習が行われるごとに検証データによる識別率の確認を行うが、 # validation_stepsはそのときに何個のバッチを読ませるか指定している # メモリが足りない場合はworkersやmax_queue_sizeを小さくして下さい # この例では学習は30エポック程度行うことが望ましい history = model.fit_generator( train_generator, steps_per_epoch=100, epochs=10, validation_data=validation_generator, validation_steps=50, verbose=1, max_queue_size=200, use_multiprocessing=False, workers=10) #学習結果を保存する。 model.save("doctor_and_politician1.h5") # load_modelを用いて学習結果を読み込めるがMobileNetを用いた場合は # model = keras.models.load_model('ファイル名', custom_objects={ # 'relu6': keras.applications.mobilenet.relu6}) # のようにcustom_objectsを用いないとエラーになる # 以下の部分は、損失関数の値と正答率を学習データと検証データそれぞれについてグラフ化している acc = history.history['acc'] val_acc = history.history['val_acc'] loss = history.history['loss'] val_loss = history.history['val_loss'] epochs = range(1, len(acc) + 1) matplotlib.pyplot.figure(1) matplotlib.pyplot.plot(epochs, acc, 'bo', label='Training acc') matplotlib.pyplot.plot(epochs, val_acc, 'b', label='Validation acc') matplotlib.pyplot.title('Training and validation accuracy') matplotlib.pyplot.legend() matplotlib.pyplot.figure(2) matplotlib.pyplot.plot(epochs, loss, 'bo', label='Training loss') matplotlib.pyplot.plot(epochs, val_loss, 'b', label='Validation loss') matplotlib.pyplot.title('Training and validation loss') matplotlib.pyplot.legend() matplotlib.pyplot.show() # 画像を表示する doctor_image1=keras.preprocessing.image.load_img("test/doctor/Doctor (78).jpg", target_size=(224,224), interpolation="lanczos") politician_image1=keras.preprocessing.image.load_img("test/politician/Politician (352).jpg", target_size=(224,224), interpolation="lanczos") matplotlib.pyplot.figure() matplotlib.pyplot.imshow(doctor_image1) matplotlib.pyplot.figure() matplotlib.pyplot.imshow(politician_image1) matplotlib.pyplot.show() # 政治家を分類する politician_tensor1=keras.preprocessing.image.img_to_array(politician_image1) politician_tensor1=numpy.expand_dims(politician_tensor1, axis=0) politician_tensor1 /= 255.0 print(model.predict(politician_tensor1)) # 結果は博士と政治家である確率が2つ並んだベクトルである # 番号と識別結果の関係を表示する print(train_generator.class_indices) # 博士を分類する doctor_tensor1=keras.preprocessing.image.img_to_array(doctor_image1) doctor_tensor1=numpy.expand_dims(doctor_tensor1, axis=0) doctor_tensor1 /= 255.0 print(model.predict(doctor_tensor1)) # 以降の操作を行うと識別率がより向上する # MobileNetの中にある層の名前を表示する mobilenet.summary() # 最終的な出力に近い層だけ学習可能と以下で設定する mobilenet.trainable=True for layer in mobilenet.layers: if layer.name in ["conv_pw_13_bn", "conv_pw_13", "conv_dw_13_bn", "conv_dw_13"]: layer.trainable=True else: layer.trainable=False # trainableに何かを代入した場合必ずcompileをもう一度行う必要がある model.compile(optimizer=keras.optimizers.RMSprop(lr=2e-5), loss='categorical_crossentropy', metrics=['acc']) # 学習可能パラメータの数が前と異なることを確認する model.summary() history = model.fit_generator( train_generator, steps_per_epoch=100, epochs=10, validation_data=validation_generator, validation_steps=50, verbose=1, max_queue_size=200, use_multiprocessing=False, workers=10) model.save("doctor_and_politician2.h5") # 以下の部分は、損失関数の値と正答率を学習データと検証データそれぞれについてグラフ化している acc += history.history['acc'] val_acc += history.history['val_acc'] loss += history.history['loss'] val_loss += history.history['val_loss'] epochs = range(1, len(acc) + 1) matplotlib.pyplot.figure(1) matplotlib.pyplot.plot(epochs, acc, 'bo', label='Training acc') matplotlib.pyplot.plot(epochs, val_acc, 'b', label='Validation acc') matplotlib.pyplot.title('Training and validation accuracy') matplotlib.pyplot.legend() matplotlib.pyplot.figure(2) matplotlib.pyplot.plot(epochs, loss, 'bo', label='Training loss') matplotlib.pyplot.plot(epochs, val_loss, 'b', label='Validation loss') matplotlib.pyplot.title('Training and validation loss') matplotlib.pyplot.legend() matplotlib.pyplot.show() # 政治家を分類する #画像を表示する politician_image1=keras.preprocessing.image.load_img("test/politician/Politician (212).jpg", target_size=(224,224), interpolation="lanczos") matplotlib.pyplot.figure() matplotlib.pyplot.imshow(politician_image1) matplotlib.pyplot.show() #分類結果を表示する politician_tensor1=keras.preprocessing.image.img_to_array(politician_image1) politician_tensor1=numpy.expand_dims(politician_tensor1, axis=0) politician_tensor1 /= 255.0 print("[Doctor, Politician]="+str(model.predict(politician_tensor1))) # 結果は政治家と博士である確率が2つ並んだベクトルである # 博士を分類する # 画像を表示する doctor_image1=keras.preprocessing.image.load_img("test/doctor/Doctor (45).jpg", target_size=(224,224), interpolation="lanczos") matplotlib.pyplot.figure() matplotlib.pyplot.imshow(doctor_image1) matplotlib.pyplot.show() #分類結果を表示する doctor_tensor1=keras.preprocessing.image.img_to_array(doctor_image1) doctor_tensor1=numpy.expand_dims(doctor_tensor1, axis=0) doctor_tensor1 /= 255.0 print("[Doctor, Politician]="+str(model.predict(doctor_tensor1)))
コメント
コメントを投稿