LoginSignup
3
1

More than 3 years have passed since last update.

Fast AIでドラえもんの画像認識

Last updated at Posted at 2021-03-22

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

結果

ドラえもんの画像から、ドラえもんと予測できました。
image.png

learn_inf.predict(path/'doraemon.png')

image.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に、以下の情報を定義します。

  1. どんな種類のデータを扱うか
  2. どうやってリストを取得するのか
  3. ラベリングのラベリングの方法
  4. 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()

スクリーンショット 2021-03-18 072347.png

このマトリックスは、行が正解を表し、列が予測を表します。
つまり今回は、実際はのび太なのに、ジャイアンと予測したケースが21ケースありました。

データクリーニング

画像データのクリーニング作業をします。
fastaiでは、実際にうまく予測できなかったイメージを確認し、
削除やラベルの変更するためのツールが用意されています。

cleaner = ImageClassifierCleaner(learn)
cleaner

スクリーンショット 2021-03-18 072544.png

今回は、複数キャラクタが写っている画像や、手書きの似顔絵などは削除していきます。
またスネ夫なのに、ドラえもんの訓練データに入っているものなどは、ラベルの変更します。

※プルダウンで選択したら、削除や移動がされるわけではありません。
以下のコードを実行して初めて、削除や移動されます。

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)

再学習

データクリーニングが終わったので、再度学習したら、こうなりました。
スクリーンショット 2021-03-20 070426.png

多少のび太の認識精度が悪いですが、どらえもんの予測には問題なさそうなので、このままいきます。

予測

ドラえもんの画像を読み込ませて、実際に予測させます。

image.png

learn_inf.predict(path/'doraemon.png')

予測した結果は、
1. ラベル名
2. ラベルのインデックス
3. 各ラベルの確率
が表示されています。

image.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

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1