fastaiをfastに学ぶ　その１

# これは何？
fastaiをfast（早く）学ぶ、をコンセプトに記載した、しがないメモです。
その１は少し長いですが、これ以降は短くするように意識しています。

# なぜ、DLをやってみようと思ったのか
これまで、ＭＬ関連の勉強会に参加したり、講義とかを受けてきました。でも、「自分じゃ何もできない」日々が続いていることに気がつきました。
そして、「このままじゃあかん、、」と思っていた際に、偉い人の「まずは書け。そして発信しろ。」というありがたいお言葉を思い出し、実行に移したという次第です。


# DLの以外な事実
私もこれはずっと妄信していたのですが、

- ビックデータが前提
- ハイスペPCが必須

これは半分「嘘」であることがわかりました。
（とはいえ、あるならあった方がいいとは思いますが。）

つまり、少ないカラム数でもトップカンファレンス優勝レベルの記録は出せるし、無料GPU環境でも計算自体は全然いける、ということです。

# NNの歴史
「ニューラルネットワーク（NN）は、脳の神経網の挙動を模したもの」というところまでは、何となく知っていますよね。ちょっと意外だった（というか知らなかった）のは、以下の点でした。

- 神経活動は「全てか無か」という性質がある（CPUの原理と同じ）
- 神経生理学者と論理学者が共同で人工的なニューロンを作ったのがはじまり

NNの歴史は本当に深いので、短いですがこのくらいで。

# fastaiとは？
https://qiita.com/lilacs/items/abac45188daf8277489a#fastai%E3%81%A8%E3%81%AF

こちらの方が詳しく書いてくださっていたので、割愛。

# GPUの準備
正直、初学者が最もつまずく点はここだと思います。「Colab使えばいいじゃん」というコメントをよく見かけますが、無操作90分で初期化されてしまう仕様はいただけません。
そこで、私がおすすめしたいのが、[Gradient](https://gradient.run/)というサービスです。
とりあえず会員登録をしましょう、と言いたいところですが日本語リファレンスも少ないので簡単にTopicだけ挙げると以下のような感じです。

- 無操作で切断されることはない（求めていた環境）
- その代わり、作成したNotebookは公開される（ちょっと恥ずかしい+機密データはUpできない）
- 無料ストレージは5GB（少ない）

と、いったところでしょうか。この点に懸念が残らない方はぜひ使ってみて下さい。
※以降は無料登録が終わっているものとして話を続けます

登録が完了したら、Create Notebooks から、「paperspace + Fast AI」を選択して、、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710236/99cfe099-057c-e765-498a-a07020bcdb84.png)

マシンの設定を「Free-GPU」にしましょう。（おそらくデフォでそうなっている）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710236/fe33132a-20a7-c069-cec6-526718acb76c.png)

AdvancedOptionsを開くと、公開設定をできるのですが、無料版だとPublic以外にできない模様。
この辺は特にいじらずでいいと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710236/0cfbefc2-7600-9570-d021-d90375c2b085.png)

ここまできたら、「CreateNotebook」します。
（予期せぬ課金を防ぐため、どうしても必要な方以外は、クレカ登録しないことをお勧めします。）

すると、ご丁寧にNotebookが全て準備されているので、01_intro.ipynbを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710236/49dea7cd-6597-ee03-706b-33925023b008.png)

全部英語なので、原文のまま読み進められる方はこのnotebook上でそのまま勉強できます。
なお、最初の部分は著者の自己紹介とか、DLをどうやって学んでいくか？みたいな感じなので飛ばしてOKです。



# 01_intro.ipynb では何をしているのか
## モデルの作成

まず、スクロールしまくると全体の25%くらいめくった辺りに以下のようなCodeがあると思います。
これが今回のメインコードとなります。

```python:01_intro.ipynb
#id first_training
#caption Results from the first training
# CLICK ME

# fastai.visionのすべてをインポート
from fastai.vision.all import *

# fast.ai.datasetsからデータセットをDL、解凍し、場所のパスを返す
path = untar_data(URLs.PETS)/'images'

# fast.aiのデータセットのアノテーションルールに従って、「猫」かどうかのラベルを返す関数を定義
def is_cat(x): return x[0].isupper()

# fast.aiライブラリがデータを読み込む際の形式に従ってパラメータを設定する
dls = ImageDataLoaders.from_name_func(
    path, get_image_files(path), valid_pct=0.2, seed=42,
    label_func=is_cat, item_tfms=Resize(224))

learn = vision_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(1)

```

これが何をしているのか、というと、、

1. [fast.ai](https://www.fast.ai/)にあるデータセットから、7,349枚の画像で構成される「[Oxford-IIIT Petデータセット](https://www.robots.ox.ac.uk/~vgg/data/pets/)」をダウンロードし、展開する
2. 訓練済みのモデルをダウンロードする（コンペで優勝したすごいモデル）
3. 訓練済みモデルを、転移学習の技術を用いてファインチューニングする。これにより、犬と猫の識別に特化したモデルと化することができる

ざっくりこんな感じです。とりあえず実行してみましょう。
実行時間は数分かかるので、ジッと待ってもいいですがコーヒーブレイクしてもOKです。

さて、結果が表示されたらとりあえず「error_rate」を見てみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710236/84da6bb7-51ef-10aa-a951-6f297d23a3d6.png)
かなりの精度が出ていますね、、！

## モデルを使った未知データの分類
モデルを作成（ファインチューニング）しただけでは意味がないですから、未知データを分類させてみましょう。私はググって持ってきてますが、猫ちゃんワンちゃんがいる方は写真撮ってみてもいいと思いますよ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710236/a6ee4fdb-fd18-86b6-939d-fd009af10acc.png)

もう少しスクロールすると、GUIアップローダを表示させる以下のコードが見つかると思います。
```python:01_intro.ipynb
uploader = widgets.FileUpload()
uploader
```
出現したこのボタン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710236/b19d33f7-6703-403a-a6be-58e4f3e8d992.png)
から、好きな画像をUpしてみてください。
そして下記のコードを実行。

```python:01_intro.ipynb
img = PILImage.create(uploader.data[0])
is_cat,_,probs = learn.predict(img)
print(f"Is this a cat?: {is_cat}.")
print(f"Probability it's a cat: {probs[1].item():.6f}")
```
結果がこちら。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710236/431a10b8-01b4-a2e9-e27c-37602cb95ec0.png)
自信満々に「猫」を言い当てられました。これはすごいですね。。

※もしこの辺がうまく動かない場合、jupyterlabのモードで同じことをやってみて下さい。
恐らく解決すると思います。

# もう少しコードを詳しく
fastaiライブラリの挙動を理解するために、もう少し以下の部分をちゃんと見てみましょう。

```Python
from fastai.vision.all import *
path = untar_data(URLs.PETS)/'images'

def is_cat(x): return x[0].isupper()
dls = ImageDataLoaders.from_name_func(
    path, get_image_files(path), valid_pct=0.2, seed=42,
    label_func=is_cat, item_tfms=Resize(224))

learn = vision_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(1)
```
一行ずつ確認してみます。
***

```Python
from fastai.vision.all import *
```
これは単純にfastai.visionというライブラリのすべての内容をインポートしています。
これをすることでモデルを作成するために必要な関数やクラスを利用することができるようになります。

```python
path = untar_data(URLs.PETS)/'images'
```
これは、データセットを、[fast.aiデータセットコレクション](https://course.fast.ai/datasets)からダウンロードしています。かつuntarなので解凍されていなければ、自動的に解凍されます。さらにファイルの場所をPathオブジェクトで返すというもの。

```python
def is_cat(x): return x[0].isupper()
```
こちらは、is_cat関数という独自関数を作っています。今回用いるデータのラベル付けされている部分を返すように設定しています。

## 本題

```python
dls = ImageDataLoaders.from_name_func(
    path, get_image_files(path), valid_pct=0.2, seed=42,
    label_func=is_cat, item_tfms=Resize(224))
```
一番気になるのは、ここですよね。
fastaiライブラリは入力されるデータセットの特徴を引数として必要とします。それを明示的に示したのがこの部分となります。

ディープラーニングが扱うデータやタスクは様々です。なのでfastaiにも多くのクラスが準備されています。その中の一つ、```ImageDataLoaders```クラスを今回は用いている、というのが一行目の冒頭です。（```TextDataLoaders```など、データ型が頭につくので分かりやすい）

```from_name_func```というのは、今回のPetデータセットに対する処理を行うための関数です。
今回各画像のラベル情報は「ファイル名」で与えられています。

（例）ファイル名：「great_pyrenees_173.jpg」

- 先頭文字が小文字なので「犬」の画像だとわかる
    - 大文字だったら猫となります
- グレートピレネーという犬種だということも分かる（これはオマケ情報）

つまり、**ファイル名に何かしらの関数を適用すれば「猫」か「犬」か判断できる**ので、そういう関数（```is_cat```）を定義した、ということですね！　※```isupper()```に合致（猫）であればTrueを返す


あとは、「どうやってデータからラベル情報を取得するか」も明示的に教える必要があります。
画像認識に用いるデータのラベル情報は一般的に、

- ファイル名の一部
- パス情報

として得られるように構成されていて、状況に応じて指定するわけですね。
さらにユーザ定義に基づく指定も可能で、それを```label_func=is_cat```で指定しているのが今回のパターンです。

最後、最も重要なのが```Transform```を定義する部分です。ここで学習の際にどのコードを用いるか指定可能（ユーザ定義関数も使用できる）。

ざっくり```Transform```には二種類あり、


- ```item_tfms```
    - 個々のデータに適用されるルールを指定
    - 今回はResize関数を用いて224ピクセルにリサイズを行っている（224というのは慣習的なものらしい）
- ```batch_tfms```
    - データのバッチに対してGPUを用いて適用されるルールを指定
    - GPUを用いるのでこちらの方が高速に動作する（今後多用する）

これでこの部分が何をしているのか、何となくわかるようになりました。
疲れましたよね、ごめんなさい。まだ終わりません。

# 一番大事なとこ
```python
learn = vision_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(1)
```
すみません、ひとつ前で「本題」とか言っておきながら、こちらの方が大事かも知れません。
なぜかって、この部分で正に畳み込みニューラルネットワーク（CNN）の指示が行われているからです。

```python
learn = vision_learner(dls, resnet34, metrics=error_rate)
```

- dls
    - 訓練対象のデータセット
- resnet34
    - モデルの種類（アーキテクチャ）
- metrics
    - 使用するメトリック

という構成になっています。

### アーキテクチャについて
文章を読むの疲れたと思うので（ただ自分が疲れただけ）、箇条で簡潔に書きます。

- fastaiには多くのアーキテクチャが準備されている
- DLを実行する上では、実はアーキテクチャの選択は重要ではない（時間をかける部分ではない）
- とりあえず、```ResNet```は多くの場合において有効であることが分かっている
    - ```ResNet34```の「34」は層数である
    - 他にも、18,50,101,152などがある（数が大きくなると過学習しやすくなる傾向あり）

### メトリックについて
メトリックとは、「モデルの精度をテストデータで計測する際に用いる関数」である。
今回は```error_rate```を使っている。後日まとめますが、ほかにもいろいろあるみたいです。
（例えば、```Accuracy```など。言うて1から上を引いただけですが）
あと一行で終わりです！

##

```python
learn.fine_tune(1)
```
最後の行では、fastaiにどのようにモデルにfitさせるかを指示しています。
今回は```fine_tune```で、ファインチューニング（転移学習）をします。
※```fit```とすれば普通に学習が始まりますが、せっかくの構築済モデルの情報を捨てることになります。

既に学習済みのモデルに対して、さらに数エポックだけ訓練を行うことで訓練済みモデルのパラメータを更新することを「**ファインチューニング**」といいます。

```(1)```はエポック数です。個々のデータを学習に何回使うかを示した数です。
今回は学習に用いたデータを一回だけ使用して、パラメータを更新したわけですね。

# さいごに
とりあえずfastaiを「完全に理解」しました。
この後、「まったくわからん」状態に入っていきたいと思います。（笑）

その２に続きます！！

あとボリューム感が、、長くてすみません。。
誤植等ありましたらご指摘をお願い致します。

# 参考文献
[1] https://www.oreilly.co.jp/books/9784873119427/