「末は博士か大臣か?」、顔から判別してみる
【イントロ】
「末は博士か大臣か」。
優秀な子供は周りの大人から、そんな期待を背負って成長していきます。小さな頃の期待を背にぐんぐん頭角を表し、時代を代表する寵児となっていきます。
しかし、「十で神童、十五で才子、二十歳過ぎれば只の人」。
歳を重ね、より大きな集団の中で比較されることで、かつての輝きは失われていく人も、一方では存在します。。。
一体何が、その差を生み出すのか。やはり、顔か・・・
でも、博士と大臣って顔つきは違うんだろうか(´・ω・`)?
そこで本記事では、「顔面から博士か大臣か判別できるか?」、調べてみました。
【方法】
方法はかっこよく、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がそれぞれナニをしているか、ちゃんと勉強しないといけないなぁと思います。
次の記事では、もう少しいろんな人を分類してみたいと思います。
画像収集スクリプト
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | #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" ) |
画像分類用プログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | # 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))) |
コメント
コメントを投稿