fastbookのサンプルコード(02_production.ipynb)をGoogle Colabで試してみた Part 3

[前回](https://qiita.com/kumagorou/items/dbc63e9706f6729051be)までは準備系にちょっと回り道してきたので、今回からfast.aiのフリーコースlesson 2のマテリアル「02_production.ipynb」の中身に入りたいと思います。ここで紹介しているコードは、自分が理解し易いように書き換えてありますので、オリジナルと比較して見たいときは、ここ→[02_production.ipynb](https://github.com/fastai/fastbook/blob/master/02_production.ipynb)をクリックしてください。
## 環境設定とモジュールのインポート
一番初めに行うのは、環境の初期化と必要なモジュールのインポートです。

```python
!pip install -Uqq fastbook
import fastbook
fastbook.setup_book()

from fastbook import *
from fastai.vision.widgets import *
```
このコードを実行した時点のカレントディレクトリは`/content/gdrive`になっています。カレントディレクトリを確認するには`!pwd`とタイプしてください。OSのシステムコマンドを実行するときはコマンドの先頭に`!`を付けます。（※ OSが異なれば使うコマンドも違います！）
## サンプル画像のダウンロードとチェック
Being Search APIを使ってサンプル画像を検索するのですが、実行の都度検索すると[MS Azure](https://udemy.benesse.co.jp/development/system/azure.html)の無料枠を超える可能性もあるので、検索した画像はGoogleドライブの所定のディレクトリに格納することにします。

```python:
working_directory = Path('/content/gdrive/MyDrive/Colab Notebooks/FastAI')  # コース学習用のディレクトリ
images_directory  = working_directory/'images'                              # サンプル画像を格納するサブディレクトリ

# サンプル画像格納用のディレクトリがなければ新規に作成する
if not images_directory.exists():
  images_directory.mkdir()
```
02_production.ipynbで最初に呼び出される`fastbook.setup_book()`という関数は`/content/gdrive`にGoogleドライブをマウントするので、画像の格納先のルート・ディレクトリは`/content/gdrive/MyDrive/Colab Notebooks/FastAI/images`という設定にします。`images_directory`はそこを指す変数です。

`Path`は`pathlib.PosixPath`モジュールにあり、Pythonからファイル操作をする便利ツールです。例えば、`working_directory/'images' `とすれば、間に`/`を挟んでくれます。また、`images_directory.exists()`とすると、画像の格納先ディレクトリが存在するか否かを知ることができます。ちなみに、`??images_directory`とタイプすると、`images_directory`についてのヘルプが表示されます。それによれば、`pathlib.PosixPath`クラスのインスタンスですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407748/1dc1156e-8791-383d-fe0c-b3ef86cf0649.png)
実は、`?images_directory`でもいいみたいです。多少スペックが違ってもキー入力が少ない方がいい。
### Being Search APIを使った画像の検索とダウンロード
ここから、いよいよBeing Search APIによる検索とダウンロードです。検索にはAPIキーが必要です。APIキーの取得については、マイクロソフト社のサイトに「[クイックスタート: Azure portal を使用して Cognitive Services リソースを作成する](https://docs.microsoft.com/ja-jp/azure/cognitive-services/cognitive-services-apis-create-account?tabs=multiservice%2Cwindows)」という解説があります。
APIキー文字列を得るには、Microsoft Azureにログインし「`すべてのリソース`」から目的の`リソースの名前`をクリックして「`キーとエンドポイント`」を選択し、`キー1`か`キー2`の右側のアイコンをクリックして、キー文字列をコピーしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407748/77979fc0-4829-9d16-be75-02e4514abe08.png)
APIキーをコピーしたら、下記の`'XXX'`をコピーしたキーで置き換えてください。

```python:
# Being Search APIキーの設定
key = os.environ.get('AZURE_SEARCH_KEY', 'XXX')
```
検索する熊の種類は`grizzly`、`black`、`teddy`の3種で、画像サンプルの格納先ディレクトリは`/content/gdrive/MyDrive/Colab Notebooks/FastAI/images/bears`で、`bears_images_directory`はそのディレクトリを指すパス変数です。

```python:
bear_types = 'grizzly','black','teddy'            # 熊の種類
bears_images_directory = images_directory/'bears' # 熊の画像サンプルのホームディレクトリ
```
次のコードがBeing Search APIを使う部分で、変数名を変更してありますが、ロジックは提供されているままです。画像を格納するディレクトリが存在しない場合のみ実行するようになっています。そうしないと、セルを実行する都度検索が行われるので、あまり回数多く実行すると無料枠を超えてしまいます。

```python:
# サンプル画像保存用ディレクトリに検索した画像をダウンロードする
if not bears_images_directory.exists(): # 保存用ディレクトリが存在しないなら以下の処理を実行する
    bears_images_directory.mkdir()                                # 熊画像を格納する親ディレクトリを作成する
    for bear_type in bear_types:        # 熊の種類ごとに
        dest = bears_images_directory/bear_type                   # 指定の種類の熊の画像を格納するサブディレクトリ
        dest.mkdir(exist_ok=True)                                 # サブディレクトリを作成する
        results = search_images_bing(key, f'{bear_type} bear')    # 画像のURLを検索する
        download_images(dest, urls=results.attrgot('contentUrl')) # 検索したURLから画像をサブディレクトリにダウンロードする
```
このコードを実行した結果は次のとおりです。熊の種類ごとにサブディレクトが作成されたことが確認できますね。`dest.mkdir(exist_ok=True)`とすると、すでにディレクトリが存在してもエラーにならず何もしないので、事前に存在するか否かチェックせずに済みますが、この場合は、親ディレクトリが存在しないときのみ実行するので、`exist_ok=True`は不要な気がします。検索結果`results`の中身については、私の記事「[fastbookのサンプルコード(02_production.ipynb)をGoogle Colabで試してみた Part 1](https://qiita.com/kumagorou/items/6c9b92c1a9536cec888e)」を見てください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407748/b1cd53ac-e5bc-c138-7f64-317840bd2fa6.png)
ここでダウンロードした熊のサンプル画像は、各々読み込めるかどうかチェックして、読み込めない画像はすべて削除します。
### ダウンロードした画像のチェック
まず、[get_image_files](https://docs.fast.ai/data.transforms.html)関数を使って、全画像のURIを格納したリスト`fns`を取得します。

```python
# すべての熊画像のURIのリストを取得する
fns = get_image_files(bears_images_directory)
for i, uri in enumerate(fns):
  if i>5: break
  print(f'[{i}]:{uri}')
```
次に、リスト`fns`を[verify_images](https://docs.fast.ai/vision.utils.html)関数に渡して、オープンエラーになる画像のURIのリスト`failed`を得ます。

```python
# 読み込めない画像のURIのリストを取得する
failed = verify_images(fns)
for i, uri in enumerate(failed):
  if i>5: break
  print(f'[{i}]:{uri}')
```
動作確認のため、エラー画像のリスト`failed`のファイルを各々実際にオープンしてエラーになることを確認します。ちゃんと動作することを確認出来たら、このコードは不要です。`fastaiのGithub`を見た限り、同じことをやってます。

```python
# 実際読込エラーになるかどうかの確認
for im in failed:
  try:
    im = Image.open(im)
    im.to_thumb(128,128)
  except Exception as e:
    print(e)
```
次のイメージはエラーチェックまで実行した結果です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407748/5c520d70-95ca-70d3-c1c9-38fefea3a69a.png)
エラーになった画像を確認するためにGoogleドライブを開いてみると次のようになっていました。赤いボックスで囲んだファイルがエラー画像です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407748/33444758-9b94-8961-0171-16a203e60c7b.png)
これを見ると、エラー画像のサムネイルが他と異なっているのが確認できますね。
### エラー画像の削除
リスト`failed`の`mapメソッド`を使って、エラー画像ごとに`Path.unlink`関数をコールしてファイルを削除します。

```python
# 読み込めない画像を削除する
failed.map(Path.unlink);
```
エラー画像を削除した後のイメージは次のとおりです。確かに、エラー画像は無くなっていますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407748/6cc773c2-dd29-95ff-ce44-fc50001f9352.png)

これで学習に使うサンプル画像がオープンエラーの無いクリーンなデータになりました。次回は、このデータを使って、`転移学習(Transfer Learning)`という手法で短時間に非常に高い精度の予測モデルを作る方法と、間違ったラベリングの訂正について解説します。(ひょっとして`ファインチューニング`というべきかも？ モーちょっと勉強します！ トホホ:pensive:)
→「[転移学習とは | メリット・デメリット・ファインチューニングの意味](https://ledge.ai/transfer-learning/)」
## 関連記事
[fastbookのサンプルコード(02_production.ipynb)をGoogle Colabで試してみた Part 1](https://qiita.com/kumagorou/items/6c9b92c1a9536cec888e)
[fastbookのサンプルコード(02_production.ipynb)をGoogle Colabで試してみた Part 2](https://qiita.com/kumagorou/items/dbc63e9706f6729051be)
[Colabからfastbookのサンプルコード・リポジトリをGoogleドライブにclone/pullする](https://qiita.com/kumagorou/items/4a1bccd311eb1eb62cdb)