Introduction
Fast AIの第2講のレッスンを受けたので、そこまでの知識をもって、ドラえもんのキャラクタ認識を実装してみます。
なおFast AIではTop-Downアプローチで、機械学習を学ぶことを推奨しています。
つまり理論を先に学ぶより、まずは機械学習を動かしてみる。
その体験をしてから、必要なものを学ぶスタイルです。
実践こそ一番学べると私自身共感しているので、この方針で学習を進めています。
前提
実行環境はColabで行っています。
画像のダウンロードに、BingSearchのv7を利用しています。
BingSearchは、Azureの無料アカウントが必要となります。
https://www.microsoft.com/en-us/bing/apis/bing-web-search-api
結果
learn_inf.predict(path/'doraemon.png')
実装
早速実装していきます。
初期設定
マウント
Google DriveをColabにマウントします。
from google.colab import drive
drive.mount('/content/gdrive')
インストール
fastbookモジュールをインストールします。
ただしColabにインストールしてしまうと、再度接続したときにモジュールが消えてしまいます。
そのため、マウントしたGoogle Drive上に、保存します。
!pip install --target "/content/gdrive/MyDrive/Colab Notebooks/modules" -Uqq fastbook
pythonでインポートする際、Google Driveからもモジュールを探し出せるように、パスに追加します。
import sys, os
nb_path = '/content/modules'
os.symlink('/content/gdrive/MyDrive/Colab Notebooks/modules', nb_path)
sys.path.insert(0, nb_path)
インポート
必要なライブラリをインポートします。
import fastbook
fastbook.setup_book()
from fastbook import *
from fastai.vision.widgets import *
画像ダウンロード
BingSearchを利用して、画像をダウンロードします。
Azureキーの設定
# azureのキー
key = os.environ.get('AZURE_SEARCH_KEY', '{{ Azureのキーを入れてください }}')
検索する関数
検索したい用語をパラメータとして、検索結果を返す関数を作成します。
def my_search_images_bing(key, search_term):
search_url = "https://api.bing.microsoft.com/v7.0/images/search"
headers = {"Ocp-Apim-Subscription-Key" : key}
min_size = 128
params = {"q": search_term,
"imageType": "photo",
"minHeight": min_size,
"minWidth": min_size,
"count": 150}
response = requests.get(search_url, headers=headers, params=params)
response.raise_for_status()
search_results = response.json()
return L(search_results['value'])
search_images_bing = my_search_images_bing
ダウンロード
検索する用語と、ダウンロード先の定義します。
image_types = ['doraemon','nobita', 'suneo', 'jaian']
path = Path('/content/gdrive/My Drive/Colab Notebooks/doraemon/images')
先ほど作成した、画像検索用の関数my_search_images_bing
を利用して画像をダウンロードします。
またfastaiでは、URLから画像をダウンロードする関数download_images
が用意されています。
if not path.exists():
path.mkdir()
for o in image_types:
dest = (path/o)
dest.mkdir(exist_ok=True)
results = search_images_bing(key, f'{o}')
download_images(dest, urls=results.attrgot('contentUrl'))
ダウンロードした画像のパスを、取得します。
fastaiでget_image_files
を利用することで、サブディレクトリを含め、画像ファイルのパスをすべて取得できます。
fns = get_image_files(path)
fns
=== 結果
[Path('/content/gdrive/My Drive/Colab Notebooks/doraemon/doraemon/00000000.jpg'),Path('/content/gdrive/My Drive/Colab Notebooks/doraemon/doraemon/00000009.jpg')....
===
破損ファイルの削除
残念ながらダウンロードした画像には、破損している画像がありえます。
fastaiで破損ファイルを特定し、削除する機能があります。
まずは破損ファイルを特定します。
failed = verify_images(fns)
=== 結果
[Path('/content/gdrive/My Drive/Colab Notebooks/doraemon/doraemon/00000012.jpg'),Path('/content/gdrive/My Drive/Colab Notebooks/doraemon/doraemon/00000052.jpg')....
===
特定したファイルを削除していきます。
failed.map(Path.unlink)
画像データからモデルを生成
画像データから、fastaiのモデルを作成します。
fastaiのモデルを作成するには、まずDataBlockクラスでモデルの作成方法を定義します。
次にDataBlockクラスを通して、実際にモデル作成するDataLoaderクラスを生成します。
最後DataLoaderクラスに、ファイルのパスを指定してあげて、モデル作成します。
DataBlockの定義
DataBlockに、以下の情報を定義します。
- どんな種類のデータを扱うか
- どうやってリストを取得するのか
- ラベリングのラベリングの方法
- validationセットの作成方法
characterBlock = DataBlock(
blocks=(ImageBlock, CategoryBlock),
get_items=get_image_files,
splitter=RandomSplitter(valid_pct=0.2, seed=42),
get_y=parent_label)
細かく見ていきます。
独立変数(予測の元となる変数)と、従属変数(予測する変数)を定義します。
ここでは独立変数はイメージデータ、従属変数はカテゴリーデータになります。
blocks=(ImageBlock, CategoryBlock),
イメージファイルの取得方法を定義します。
get_items=get_image_files,
ランダムにtrainingデータと、validationデータに分割します。
ここでは、2割をvalidationデータとします。
なお毎度実行するたびに、利用するvalidationデータが変わってしまうのを防ぐため、seedを指定しています。
splitter=RandomSplitter(valid_pct=0.2, seed=42),
どのようにラベルを決定づけるかを定義します。
parent_label
は、fastaiで用意されている、親ディレクトリ名をカテゴリ名とする関数です。
get_y=parent_label,
DataLoaderの生成
DataBlockからDataLoaderを生成します。
characters = characterBlock.new(
item_tfms=RandomResizedCrop(224, min_scale=0.5),
batch_tfms=aug_transforms())
dls = characters.dataloaders(path)
ここでも各パラメータを見ていきます。
各画像を解析するためには、画像を同じサイズにする必要があります。
ここでは224×224のサイズを作成するようにします。
また画像のリサイズ方法は、いくつかfastaiで用意されています。
ここでは画像をランダムな拡大率で拡大し、正方形切り取るRandomResizedCrop
を使用していきます。
item_tfms=RandomResizedCrop(224, min_scale=0.5),
すべての画像が同一サイズになると、GPU上で複数の画像に対して、一気に加工できます。
すべての画像に対して、データ拡張します。
この処理は、画像のアングルを変えたり、コントラストや明るさなどを変更します。
これは、学習データと少しアングル等が違うだけで、全く予測できないような状況を防ぐことが目的です。
batch_tfms=aug_transforms()
最後、実際にパスを指定して、画像からモデルを生成します。
dls = characters.dataloaders(path)
学習
いよいよモデルに学習をさせていきます。
アーキテクチャとして、画像のニューラルネットワークの代表的なCNNを利用します。
learn = cnn_learner(dls, resnet18, metrics=error_rate)
cnn_learner
は、fastaiで用意されているCNNのライブラリです。
resnet18
は、ここではresnetを深堀しませんが、resnetという画像解析の手法を18層使用することを指定しています。
metrics
は、学習結果として失敗の割合を出せたり(error_rate)、正解率を出させたり(accuracy)と指定できます。
学習の方法が定義できたら、実際に学習をさせます。
今回は10epoch、つまり10周trainingとvalidationを行います。
learn.fine_tune(10)
学習結果
学習した結果は、fastaiのライブラリを利用することで、わかりやすく表示させることができます。
interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix()
このマトリックスは、行が正解を表し、列が予測を表します。
つまり今回は、実際はのび太なのに、ジャイアンと予測したケースが21ケースありました。
データクリーニング
画像データのクリーニング作業をします。
fastaiでは、実際にうまく予測できなかったイメージを確認し、
削除やラベルの変更するためのツールが用意されています。
cleaner = ImageClassifierCleaner(learn)
cleaner
今回は、複数キャラクタが写っている画像や、手書きの似顔絵などは削除していきます。
またスネ夫なのに、ドラえもんの訓練データに入っているものなどは、ラベルの変更します。
※プルダウンで選択したら、削除や移動がされるわけではありません。
以下のコードを実行して初めて、削除や移動されます。
import os
for idx in cleaner.delete(): cleaner.fns[idx].unlink()
for idx,cat in cleaner.change():
filepath = str(cleaner.fns[idx])
ext = os.path.splitext(filepath)[-1]
dest = path/cat/os.path.basename(filepath)
i = 1
while True:
if os.path.exists(dest):
filename = os.path.splitext(os.path.basename(dest))[0] + "_" + str(i)
dest = path/cat/(filename+ext)
i += 1
else:
break
shutil.move(str(cleaner.fns[idx]), dest)
再学習
データクリーニングが終わったので、再度学習したら、こうなりました。
多少のび太の認識精度が悪いですが、どらえもんの予測には問題なさそうなので、このままいきます。
予測
ドラえもんの画像を読み込ませて、実際に予測させます。
learn_inf.predict(path/'doraemon.png')
予測した結果は、
- ラベル名
- ラベルのインデックス
- 各ラベルの確率
が表示されています。
おわりに
機械学習全くの未経験の私ですが、理論からではなく実際にどんなことをしているかが理解できて、非常に楽しく学べました。
また初めて記事投稿しましたが、記事を書くうちに改めて調べなおしたりして、良い学習ができました。
今後もfast aiに準じて学習していくので、各チャプターごとに自分で何かを作って、記事投稿につなげていきたいです。
参考
[fastai] https://docs.fast.ai/
[bing search] https://www.microsoft.com/en-us/bing/apis/bing-web-search-api
[Github] https://github.com/iris-net/fastbook/blob/main/02_doraemon_identifier.ipynb