2020年12月25日金曜日

fastai Ver2でImage Classification(2)

 Image Classification(1)では、シンプルに行いましたが精度としては今の時代では全く話にならない結果となりました。

その理由としてRsizeやNormalizeをしていない事を指摘しました。最もとNormalizeは初期値でTrue=`実施`となっているようなのでNormalizeは影響しないかな(?)

そこで

1.前回にRsizeを加えて精度の変化を確認する。

2.DataBlock APIを使う。DataBlockを使うことで細やかなカスタム設定が出来るのでしょうが、勉強不足なので見様見真似で行ってみます。


▼Rsizeを加えたtraining

変更点は1つだけ

dls = ImageDataLoaders.from_folder(path, valid_pct=0.2, bs=16)  

dls = ImageDataLoaders.from_folder(path, valid_pct=0.2, item_tfms=Resize(224), bs=16)

にするだけです。  その結果、精度は格段に良くなりました。

epochtrain_lossvalid_losserror_ratetime
00.4768590.2966810.10266704:15
epochtrain_lossvalid_losserror_ratetime
00.3518890.2451880.08225005:51
10.2548300.1966160.06466705:52
20.1573530.1538420.05191705:53
30.0565290.1385280.04058305:50
40.0245740.1305480.03750005:49


▼DataBlock APIを使ったtraining

おそらく、DataBlock APIを使ってのプログラミングで中級者、上級者への要望にも答えられるようになったのが今回のVer2の大きな変更点の1つだと思います。

ただ、fast.aiの「deep learningをごく一部の研究者ではなく、広くみんなが使えるように」の基本精神は変わることなく敷居の低いプラットフォームであってほしいと思います。

DataBlock APIを使った全プログラムを載せておきます。細かく説明できるほど理解していないので説明は改めてさせてください。


#fast.aiのインストール
! [ -e /content ] && pip install -Uqq fastai  # upgrade fastai on colab

from fastai.vision.all import *

#データセットのダウンロードと解凍
path = untar_data(URLs.CIFAR)

#DataBlockAPIの設定
cifar10 = DataBlock(
    blocks=(ImageBlock, CategoryBlock), 
    get_items=get_image_files, 
    splitter=RandomSplitter(valid_pct=0.2, seed=42),
    batch_tfms=aug_transforms(),
    get_y=parent_label,
    item_tfms=RandomResizedCrop(224, min_scale=0.5))

#DataLoadeの作成
dls = cifar10.dataloaders(path)

#Learnerの作成
learn = cnn_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(5)
コアとなる部分はたったこれだけです。 そしてそのtrainingの結果は、
epochtrain_lossvalid_losserror_ratetime
00.8004150.4606560.15491703:53
epochtrain_lossvalid_losserror_ratetime
00.4022770.2327080.08016705:11
10.3423220.1866190.06300005:11
20.2483060.1480170.04891705:11
30.1743680.1142040.03883305:11
40.1165830.1048990.03633305:12

1のResizeを行った場合と同じようなまずまずの結果が得られました。
これから少しづづfast.ai ver2も勉強していきたいと思います。


2020年12月14日月曜日

fastai Ver2でImage Classification(1)

しばらくお休みしている間にfast.aiもバージョン2となり、今まで通り初心者はバカチョンで、中級者や上級者はそれなりにキメ細かく出来るようになったみたいです。

Ver2になると今まで使っていたプログラムもおそらく動かなくなるでしょう。また、プログラムの実行環境はGoogle Colaboratoryがイチ推しのようです。そこでチュートリアルを参考にCIFAR10を使ってImage Classificationを行ってみます。


まずは、fast.aiをインストール

! [ -e /content ] && pip install -Uqq fastai  # upgrade fastai on colab
from fastai.vision.all import *

▼データセットのダウンロード・解凍

fast.aiに組込まれているCIRA10のデータセットをダウンロード・解凍する。
path = untar_data(URLs.CIFAR)
内容の確認
path.ls()
(#3) [Path('/root/.fastai/data/cifar10/train'),Path('/root/.fastai/data/cifar10/test'),Path('/root/.fastai/data/cifar10/labels.txt')]
 ダウンロードしたdatasetにはtest,trainのフォルダーとladel.txtIがあるようです。 

 trainの中は、truck、airplain、cat・・・とクラスごとに分かれているようです。
(path/"train").ls()
(#10) [Path('/root/.fastai/data/cifar10/train/truck'),Path('/root/.fastai/data/cifar10/train/airplane'),Path('/root/.fastai/data/cifar10/train/cat'),Path('/root/.fastai/data/cifar10/train/deer'),Path('/root/.fastai/data/cifar10/train/horse'),Path('/root/.fastai/data/cifar10/train/automobile'),Path('/root/.fastai/data/cifar10/train/dog'),Path('/root/.fastai/data/cifar10/train/frog'),Path('/root/.fastai/data/cifar10/train/ship'),Path('/root/.fastai/data/cifar10/train/bird')]

全部で幾つに分かれているのでしょうか?(当然10クラスということは判っていますが(^^))
len((path/"train").ls())
10

クラスのクラス名を取得します。
for i in range(len((path/"train").ls())):
  fname = (path/"train").ls()[i]
  print(fname.name)
truck 
airplane 
cat 
deer 
horse 
automobile 
dog 
frog 
ship 
bird

モデル用にデータを準備するには、データをDataLoadersオブジェクトに配置する必要があります。 ここにフォルダー名を使用してラベルを付ける関数があるので、ImageDataLoaders.from_folderを使用します。

問題に適したImageDataLoaderのfactory methodsは他にもあるので、vision.dataでそれらすべてを確認してください。
dls = ImageDataLoaders.from_folder(path, valid_pct=0.2)
pathを使っているので、trainとtestフォルダーの合計6000個のimageを使うことになる。 もしtrain内のimageのみで行うのであればpath_d = path/"train" とする必要がある。 幾つかを確認する。
dls.valid_ds.items[:4]
[Path('/root/.fastai/data/cifar10/train/ship/40435_ship.png'), Path('/root/.fastai/data/cifar10/train/horse/40005_horse.png'), Path('/root/.fastai/data/cifar10/train/frog/31706_frog.png'), Path('/root/.fastai/data/cifar10/train/ship/38138_ship.png')]

show_batchメソッドを呼び出すことにより、これらの画像を確認できます。
dls.valid.show_batch(max_n=4, nrows=1)


モデルトレーニングに適した形式でデータを組み立てたので、次はそれを使用して画像分類器をトレーニングしましょう。


▼Learnerの作成

転移学習を使用して、事前にトレーニングされたモデルをわずか2行のコードでfine tuneします。
learn = cnn_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(5)
epochtrain_lossvalid_losserror_ratetime
01.7176181.4982600.51858303:09
epochtrain_lossvalid_losserror_ratetime
01.0801850.8644270.29033303:40
10.8489600.6946560.22525003:40
20.6398850.5929370.19783303:41
30.4509170.5140660.17791703:38
40.3787650.5004580.17216703:34

▼training結果の分析

traningの結果を分析するのに、ClassificationInterpretationクラスを使用します。from_learnerメソッドを使って、次のように作成します。
interp = ClassificationInterpretation.from_learner(learn)

confusion_matrix

confusion_matrixを使ってtrainingの全体像を俯瞰する事ができます。
interp.plot_confusion_matrix(figsize=(12,12), dpi=60)
縦軸のクラス名の画像を予測すると、横軸のクラス名の通りになったということです。 例えばairplaneの1184枚の画像を予測すると、1047枚は正解のairplaneに9枚はautomobile、36枚はbirdと判別されたということです。 

損失関数でみた損失上位の画像をplotすることが出来ます。
interp.plot_top_losses(16, figsize=(15,11))
画像の上の4つの項目はそれぞれ、予測、実際、損失、実際のクラスの確率を示しています。

▼検証データのクラス毎のaccuracy

import numpy as np

res_corr=interp.confusion_matrix()

#classの数
class_num=dls.c

#class毎のデータ総数
class_sum=np.sum(res_corr, axis=1)

for i in range(class_num):
    #print(data.classes[i],':',res_corr[i,i]/class_sum[i])
    print(interp.vocab[i],':','{:.4f}'.format(res_corr[i,i]/class_sum[i]))
airplane : 0.8843 automobile : 0.8847 bird : 0.7886 cat : 0.6706 deer : 0.7851 dog : 0.6906 frog : 0.8956 horse : 0.8709 ship : 0.9161 truck : 0.8863

▼おわりに

一番シンプルな型で行いました。DataLoadersオブジェクトを作るとき、前回2020/5/9の投稿時のように、RsizeNormalizeなどを全く行っていません。
そのため、精度も期待したほどではなく、悪い結果となっています。
DataLoadersオブジェクトを作るとき、trainとtestの両方のデータ60000個を使ってしまったので、trainデータだけにすればよかった。

次はRsizeNormalizeを加えて、精度を上げたいと思います。


2020年12月3日木曜日

Google Code-Prettifyでシンタックスハイライトを実装する(Bloggerにソースコードを見栄えよく載せる)

プライベートで色々とあり、投稿が滞ってしまいました。これからもボチボチと投稿します。 

私もBloggerにソースコードを載せの機会が多いのですが、今までコードをベタ書きしていて、他の方のように見栄えが良くありませんでした。

そこで軽量で簡単に導入できるらしいGoogle Code-Prettifyを使ってみました。Google Code-Prettifyの解説・設定方法は既に多くの方がブログで紹介されているので、私が苦労したところをメモしておきます。

Google Code-Prettifyの設定は簡単で、BloggerのHTMLの<head>部分に

<script src='https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?skin=sons-of-obsidian'/>

を追加するだけですが、そのHTMLの編集が何処で行えるのか探すのに苦労したのでメモしておきます。

【手順】

1.ブログメニューのテーマを開く


2.カスタマイズ右の▼プルダウンメニューを開きHTMLを編集を開くと、HTMLが表示されるので<head>の下に、上記スクリプトを追加する。











3.後は投稿編集画面でソースコードを載せたい場面で、HTMLモードで

<pre class="prettyprint">

</pre>

の間にソースコードを書き加えればOK











2020年6月6日土曜日

fast.aiでCOVID-19 Xray Dataset (Train & Test Sets)をClassification

コロナウイルスで自粛ムードの真っ只中、Kaggleで『COVID-19 Xray Dataset (Train & Test Sets)』というデータセットを見つけました。
前回に続いて、このデータを分析してみます。

元データでは、Train用とTest用に分かれていましたが、1つにまとめてdataフォルダー下に'NORMAL'と'PNEUMONIA'のフォルダーとして保存しました。


%reload_ext autoreload
%autoreload 2
%matplotlib inline


from fastai.vision import *
from fastai.metrics import accuracy


Data Preparation

tfms = get_transforms(do_flip=False)


path = './data'; path


data=ImageDataBunch.from_folder(path,valid_pct=0.2,ds_tfms=tfms,size=224,bs=16)
data.normalize(imagenet_stats)


サンプルデータの表示

data.show_batch(rows=3,figsize=(5,5))



クラス名とクラス数の確認

print(data.classes)

  ['NORMAL', 'PNEUMONIA']


len(data.classes)

  2

modelの作成

import torchvision.models as models


learn1 = cnn_learner(data, models.resnet101, metrics=accuracy,callback_fns=ShowGraph)


Learning rate finder

earn1 = cnn_learner(data, models.resnet101, metrics=accuracy, callback_fns=ShowGraph)
learn1.unfreeze()
learn1.lr_find()
learn1.recorder.plot()
learn1.recorder.plot_lr()



Training

learn1 = cnn_learner(data, models.resnet101, metrics=accuracy, callback_fns=ShowGraph)
learn1.unfreeze()
learn1.fit_one_cycle(20, slice(1e-5,1e-4), pct_start=0.10)
learn1.recorder.plot_lr()

epochtrain_lossvalid_lossaccuracytime
01.1046080.6815580.59459500:06
10.9099590.1502440.91891900:06
20.6608420.0831280.94594600:06
30.5137640.1265560.94594600:06
40.3914560.1370530.94594600:07
50.3249140.1268450.97297300:06
60.2684650.0965470.97297300:06
70.2235950.0171471.00000000:06
80.1880710.1054460.97297300:07
90.1606100.0206551.00000000:06
100.1396890.0214691.00000000:07
110.1163900.0148321.00000000:06
120.1108000.0177431.00000000:06
130.1118020.1564240.94594600:06
140.0989270.1569560.94594600:06
150.0910040.0470440.97297300:06
160.0854180.0247390.97297300:06
170.0742400.0079741.00000000:06
180.0754800.0044381.00000000:06
190.0732070.0028891.00000000:06



あまり良い学習は出来てないようです。


training結果の分析

Load pre-trained weights

learn = learn.load("covid19-res101")


trainingの結果

interp = ClassificationInterpretation.from_learner(learn)

losses,idxs = interp.top_losses()

len(data.valid_ds)==len(losses)==len(idxs)

  True

interp.plot_top_losses(16, figsize=(15,11))



Confusion matrix
interp.plot_confusion_matrix(figsize=(12,12), dpi=60)

分類は100%出来ているようです。


検証データのクラス毎のaccuracy(取り敢えず・・・)

import numpy as np

res_corr=interp.confusion_matrix()

#classの数
class_num=data.c

#class毎のデータ総数
class_sum=np.sum(res_corr, axis=1)

for i in range(class_num):
    print(data.classes[i],':','{:.4f}'.format(res_corr[i,i]/class_sum[i]))

  NORMAL : 1.0000
  PNEUMONIA : 1.0000



2020年5月23日土曜日

fast.aiでtransfer learningした後のデータ分析について

前回・前々回とtransfer learningを行い、結果の分析まで進みました。

そのときConfusion matrixとかあまり説明をしていなかったので、まとめてここでしておきます。


fast.aiライブラリーを使って、fit()またはfit_one_cycle()でtarningした後、その結果を色々と分析できます。しかし、その度に時間を掛けてtrainingし直していたのでは時間の無駄です。

そこでtraining後に、


learn.save('learn-model')


として、training結果を保存しておくと、次回からはそれを読み込んで作業を続けることが出来ます。

learn.save('learn-model')のlearnは、training時に作成したlearnerで、

learn = cnn_learner(data, models.resnet34, metrics=error_rate)の’=’の左側の名前です。

'learn-model'は保存する任意のファイル名で拡張子「.pth」のファイルが出来ます。

もう一つ、拡張子「.pkl」のファイルもありますが、それはまたpredictの時に・・・


保存したファイルを読み込むには、次のようにします。


learn.load('learn-model')


Interpretation(学習結果の分析)

traningの結果を分析するのに、ClassificationInterpretationクラスを使用します。from_learnerメソッドを使って、次のように作成します。


interp = ClassificationInterpretation.from_learner(learn)


Confusion matrix

confusion_matrixを使ってtrainingの全体像を俯瞰する事ができます。


interp.plot_confusion_matrix(figsize=(12,12), dpi=60)


縦軸のクラス名の画像を予測すると、横軸のクラス名の通りになったということです。

例えばairplaneの1000枚の画像を予測すると、960枚は正解のairplaneに、2枚はautomobile、4枚はbirdと判別されたということです。



Most confused categories

most_confusedは、最も頻繁に間違った予測と実際の特定の組み合わせを混同マトリックスから単純に取り出します。 

interp.most_confused(min_val=10)

min_valで間違った組み合わせの最小数を指定できます。


[('cat', 'dog', 69),

 ('dog', 'cat', 30),

 ('automobile', 'truck', 26),

 ('airplane', 'ship', 18),

 ('truck', 'automobile', 18),

 ('bird', 'frog', 16),

 ('ship', 'airplane', 15),

 ('deer', 'dog', 14),

 ('horse', 'dog', 13),

 ('bird', 'dog', 12),

 ('deer', 'horse', 12),

 ('ship', 'truck', 12),

 ('bird', 'airplane', 11),

 ('cat', 'deer', 11),

 ('frog', 'cat', 11),

 ('cat', 'frog', 10),

 ('deer', 'bird', 10),

 ('deer', 'cat', 10)]

 

Plot top losses

損失関数でみた損失上位の画像をplotすることが出来ます。

 

interp.plot_top_losses(9, figsize=(15,11))

 

画像の上の4つの項目はそれぞれ、予測、実際、損失、実際のクラスの確率を示しています。

上段真ん中の「flog/cat/11.19/0.00」の画像は、cifar10の検証クラス(val)のcatクラスにある、ファイナル名236_catの画像です。カエルの画像が間違ってネコのクラスとして分類されてしまったのですネ。


2020年5月9日土曜日

fast.aiのtransfer learningでcifar10の画像分類

ここまで、fast.aiでtransfer learningで訓練するときの、fit_one_cycleのパラメータの設定について見てきたので、改めてcifar10の分類を行いたいと思います。

cifar10の分類は、以前にもPytorchでプログラムするよりfast.aiを使ったらいかに簡単かを比較するために行っています。
その時はバカチョンで行いましたが、今回は少しパラメータを調整しています。
精度は上がるでしょうか?。

また細かいことですが、前回はcifar10のダウンロードデータを画像ファイルに変換したものをデータとして使用しました。
今回はcifar10のダウンロードデータをそのまま使いました。



%reload_ext autoreload
%autoreload 2
%matplotlib inline

いつもの通りおまじないですか。



from fastai.vision import *
from fastai.metrics import accuracy


Data Preparation

path = untar_data(URLs.CIFAR); path; path

PosixPath('/home/◇◇◇◇/.fastai/data/cifar10')
usr'◇◇◇◇'のルートディレクトリ下の.fastaiにデータファイルが保存されるようです。


tfms = get_transforms(do_flip=False)



data=ImageDataBunch.from_folder(path,train='train',valid='test',
                 ds_tfms=tfms,size=224,bs=16)
data.normalize(imagenet_stats)


お決まりでサンプルデータを見てみます。


data.show_batch(rows=3,figsize=(5,5))



クラス名とクラス数を確認します。

print(data.classes)

['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']


len(data.classes)

10


modelの作成


import torchvision.models as models

learn1 = cnn_learner(data, models.resnet34,metrics=accuracy,callback_fns=ShowGraph)


Learning rate finder

learn1 = cnn_learner(data, models.resnet34, metrics=accuracy,   
                     callback_fns=ShowGraph)
learn1.unfreeze()
learn1.lr_find()
learn1.recorder.plot()
learn1.recorder.plot_lr()




training



learn1 = cnn_learner(data, models.resnet34, metrics=accuracy, callback_fns=ShowGraph)
learn1.unfreeze()
learn1.fit_one_cycle(20, slice(1e-5,1e-4), pct_start=0.10)
learn1.recorder.plot_lr()

epochtrain_lossvalid_lossaccuracytime
00.7447030.3545370.88540003:53
10.4695120.2016250.93060003:46
20.2702120.1669020.94400003:55
30.2221460.1478380.95060003:55
40.1590850.1423350.95350003:56
50.1002090.1386060.95770003:57
60.0843130.1377530.95790003:55
70.0745600.1395010.95870003:58
80.0459000.1314650.96380003:56
90.0416300.1349780.96170003:57
100.0367390.1335090.96390003:58
110.0422980.1280950.96630003:58
120.0186160.1288200.96710003:57
130.0126730.1294480.96590003:54
140.0180200.1327600.96720003:54
150.0116240.1252110.96900003:40
160.0045080.1204620.97000003:54
170.0090190.1189490.97030003:58
180.0069940.1191830.97070003:54
190.0081990.1162950.97100003:55




Load pre-trained weights



learn = learn.load("cifar-res34")


training結果の分析



interp = ClassificationInterpretation.from_learner(learn)

losses,idxs = interp.top_losses()

len(data.valid_ds)==len(losses)==len(idxs)



interp.plot_top_losses(16, figsize=(15,11))



Confusion matrix


interp.plot_top_losses(16, figsize=(15,11))



interp.confusion_matrix()

array([[973,   2,   6,   2, ...,   1,   0,  14,   0],
       [  0, 989,   0,   0, ...,   0,   0,   1,  10],
       [  7,   1, 962,   8, ...,   6,   0,   2,   0],
       [  0,   1,   6, 928, ...,   8,   4,   3,   0],
       ...,
       [  1,   0,   3,   6, ..., 983,   0,   0,   1],
       [  0,   0,   1,   4, ...,   1, 975,   0,   0],
       [  5,   0,   1,   0, ...,   0,   0, 991,   3],
       [  3,  21,   0,   1, ...,   0,   0,   6, 968]])


interp.most_confused(min_val=1)

[('cat', 'dog', 44),
 ('dog', 'cat', 37),
 ('truck', 'automobile', 21),
 ('airplane', 'ship', 14),
 ('horse', 'deer', 13),
 ('deer', 'cat', 11),
 ('automobile', 'truck', 10),
 ('dog', 'deer', 10),
 ('bird', 'deer', 9),
 ('bird', 'cat', 8),
 ('cat', 'frog', 8),
 ('bird', 'airplane', 7),
 ('airplane', 'bird', 6),
 ('bird', 'frog', 6),
 ('cat', 'bird', 6),
 ('cat', 'deer', 6),
 ('frog', 'cat', 6),
 ('horse', 'dog', 6),
 ('truck', 'ship', 6),
 ('bird', 'dog', 5),
 ('dog', 'bird', 5),
 ('frog', 'deer', 5),
 ('ship', 'airplane', 5),
 ('cat', 'horse', 4),
 ('deer', 'dog', 4),
 ('horse', 'cat', 4),
 ('cat', 'ship', 3),
 ('deer', 'horse', 3),
 ('dog', 'horse', 3),
 ('frog', 'bird', 3),
 ('ship', 'truck', 3),
 ('truck', 'airplane', 3),
 ('airplane', 'automobile', 2),
 ('airplane', 'cat', 2),
 ('bird', 'ship', 2),
 ('deer', 'frog', 2),
 ('airplane', 'deer', 1),
 ('airplane', 'dog', 1),
 ('airplane', 'frog', 1),
 ('automobile', 'ship', 1),
 ('bird', 'automobile', 1),
 ('cat', 'automobile', 1),
 ('dog', 'frog', 1),
 ('frog', 'airplane', 1),
 ('frog', 'dog', 1),
 ('frog', 'truck', 1),
 ('horse', 'bird', 1),
 ('horse', 'frog', 1),
 ('ship', 'bird', 1),
 ('truck', 'cat', 1),
 ('truck', 'deer', 1)]


検証データのクラス毎のaccuracy

import numpy as np

res_corr=interp.confusion_matrix()

#classの数
class_num=data.c

#class毎のデータ総数
class_sum=np.sum(res_corr, axis=1)

for i in range(class_num):
    print(data.classes[i],':','{:.4f}'.format(res_corr[i,i]/class_sum[i]))

airplane : 0.9730
automobile : 0.9890
bird : 0.9620
cat : 0.9280
deer : 0.9800
dog : 0.9440
frog : 0.9830
horse : 0.9750
ship : 0.9910
truck : 0.9680