BETA

fastai動かしてみたLesson3②

投稿日:2019-04-27
最終更新:2019-04-27

今回はLesson3 https://course.fast.ai/videos/?lesson=3

notebookはこちら https://github.com/fastai/course-v3/tree/master/nbs/dl1

U-Net


セグメンテーションではただのCNNを作るわけではなく、U-Netというものを使う
U-Netは上記のような構造になっている

左上から入力されたデータはどんどん小さい形になっていき、そこからどんどん大きい形に戻っていく。
全結合層がないため入力データのサイズは関係ない
Encoderの部分(左半分)で出力される特徴量マップをDecoder(右半分)の対応する層の特徴マップに直接連結することでpixelのディティールを補っているらしい

Encoderではpadding=0の畳み込みを行って特徴マップが少しずつ小さくなっているためので、Decoderで2倍に逆畳み込みしてもEncoderの特徴マップとサイズが合わない
そのため、Encoderの特徴マップの中央部分を切り出してDecoderの特徴マップとサイズを一致させて連結している

自分でもわかってるかよくわからないので、調べてください
参考:https://blog.negativemind.com/2019/03/15/semantic-segmentation-by-u-net/

learn.recorderについてもう少し詳しく

learn.recorder.plot_losses()  

これでtrainとvalidのlossを見ることができる

learn.recorder.plot_lr()  

これで学習率の変化を見ることができる

lossを見ると、lossが減る前にわずかに増えていることがわかる
これは学習率が増加した後に減少しているためである

learn.fit_one_cycle()  

これはone cycleの原則でfitさせているためで、はじめは低い学習率から増加させ、そのあと減少させるという方法である

なぜone cycleがよい方法なのかというと、$$y = ax + b $$つまり
$$y = w_0x + w_1$$で2つの重みを扱っていたことを思い出してほしい
これらを改善していき最適な重みを見つける

(gifを張りたいけど張れない!こんな感じ: https://github.com/hiromis/notes/blob/master/lesson3/jose2.gif)


縦軸がLoss、横軸がWeight
学習率が大きすぎると赤線のようにlossを最小にする黄色い点を超えてしまい、学習率が小さすぎると青線のように別の局所解が求まってしまう
そこで、one cycleの原則で学習率を決めるとこのような動きになる

はじめは低い学習率から増加させ、そのあと減少させることで、うまくlossを最小にする点を求めることができた

Mixed precision training

もしメモリ不足になったりした場合にこのテクニックを使う
(日本語にうまく訳せない:https://qiita.com/arutema47/items/d9e097f00b0b4934d07a)
半精度とか言われるものらしい
要は少し計算が早くなってメモリ不足を解消できるとかなんとか

learn = Learner.create_unet(data, models.resnet34, metrics=metrics).to_fp16()  

のように後ろに.to_fp16()を加えればそれでヨシ

BIWI head pose データセットで回帰モデルを作る

BIWI head pose データセットという顔のどこが中心であるかのデータセットを使って回帰を行ってみる。

データを見てみよう
データとか必要なものを以下で導入

%reload_ext autoreload  

%autoreload 2  
%matplotlib inline  
from fastai import *  
from fastai.vision import *  

path = untar_data(URLs.BIWI_HEAD_POSE)  

calibration numbers(較正データ?)がいくつか存在する

cal = np.genfromtxt(path/'01'/'rgb.cal', skip_footer=6); cal  

array([[517.679, 0. , 320. ],
[ 0. , 517.679, 240.5 ],
[ 0. , 0. , 1. ]])

画像を見てみる

fname = '09/frame_00667_rgb.jpg'  

def img2txt_name(f): return path/f'{str(f)[:-7]}pose.txt'  
img = open_image(path/fname)  
img.show()  

ctr = np.genfromtxt(img2txt_name(fname), skip_header=3); ctr  

array([187.332 , 40.3892, 893.135 ])

ここで、データセットには実際の座標を得るためにこのdepth sensor calibration(深さセンサー較正?)から座標を変更するための関数が提供されている

def convert_biwi(coords):  
    c1 = coords[0] * cal[0][0]/coords[2] + cal[0][2]  
    c2 = coords[1] * cal[1][1]/coords[2] + cal[1][2]  
    return tensor([c2,c1])  

def get_ctr(f):  
    ctr = np.genfromtxt(img2txt_name(f), skip_header=3)  
    return convert_biwi(ctr)  

def get_ip(img,pts): return ImagePoints(FlowField(img.size, pts), scale=True)

ここで画像上に顔のどこが中心であるかのデータを得ることができるようになったので表示してみると

get_ctr(fname)  

tensor([263.9104, 428.5814])

ctr = get_ctr(fname)  
img.show(y=get_ip(img, ctr), figsize=(6, 6))  

画像上に赤い点(鼻のあたり)が追加されたのがわかる

顔の中心点のデータ(coordinates)はxとyのデータである
したがって、もし顔の中心を求めるモデルを作ろうとしているなら、2つの値を得ることができるニューラルネットワークが必要である

しかし、これは今まで学んできた分類問題ではない
ここで初めて回帰モデルを作ってみる
多くの人が回帰というのは線形回帰であると考えるが、それは違う
回帰というのは出力が連続する数あるいは一連の数であるモデルということを意味する
そこで、image regression modelを作ってみる

data = (ImageItemList.from_folder(path)  
        .split_by_valid_func(lambda o: o.parent.name=='13')  
        .label_from_func(get_ctr, label_cls=PointsItemList)  
        .transform(get_transforms(), tfm_y=True, size=(120,160))  
        .databunch().normalize(imagenet_stats)  
       )  

.split_by_valid_func(lambda o: o.parent.name=='13')

今回のデータは動画から取り出した画像である
検証用データは訓練に使用したことのないものを使う必要があるため、今回は訓練に使用していない特定の人の画像を使った

.label_from_func(get_ctr, label_cls=PointsItemList)

上で定義したget_ctrを使ってラベル付けを行う
またテキストファイルからラベル名を取得する

data.show_batch(3, figsize=(9, 6))  

データを作ることができたので、今度はモデルを作っていく
そこで損失関数(Loss function)について今後の講義でも説明されるが、基本的に損失関数はどれくらいモデルがいいものであるかを示す数である

画像分類では、cross entropy(クロスエントロピー)を使ったが、これは正解のクラスを予測することができたか?予測に自信があるかを示すものである

今回は回帰であり、クロスエントロピーを使うことができないので、Mean squared error(平均二乗誤差)を使う

learn = create_cnn(data, models.resnet34)  
learn.loss_func = MSELossFlat()  

最適な学習率を探す

learn.lr_find()  
learn.recorder.plot()  


学習させる

lr = 2e-2  
learn.fit_one_cycle(5, slice(lr))  

Total time: 07:28
epoch train_loss valid_loss
1 0.043327 0.010848 (01:34)
2 0.015479 0.001792 (01:27)
3 0.006021 0.001171 (01:28)
4 0.003105 0.000521 (01:27)
5 0.002425 0.000381 (01:29)

valid loss が小さいのでいい感じ

learn.show_results()  

IMDB

次の講義は自然言語処理なので、最後に少し自然言語処理を行ってみる

必要なものを持ってくる

%reload_ext autoreload  
%autoreload 2  
%matplotlib inline  
from fastai import *  
from fastai.text import *  

path = untar_data(URLs.IMDB_SAMPLE)  
path.ls()  

[PosixPath('/home/jhoward/.fastai/data/imdb_sample/texts.csv'),
PosixPath('/home/jhoward/.fastai/data/imdb_sample/models')]

今回使うデータは.csvファイルであり、中身は映画のレビューでラベルに高評価か低評価か、そしてレビュー本文が含まれている

df = pd.read_csv(path/'texts.csv')  
df.head()  

今回はテキストデータであるため、fastaiのData block APIよりTextDataBunchを使う

data_lm = TextDataBunch.from_csv(path, 'texts.csv')  

自然言語処理をするので、まず初めにTokenization(形態素解析)を行う

形態素解析とは

自然言語処理の手法の一つで、ある文章・フレーズを「意味を持つ最小限の単位(=単語)」に分解し、文章やフレーズの内容を判断するために用いられる

例えば下記のような文章があったとする

SEOは、検索エンジンで上位表示させるための手法です。

これを形態素解析で単語分解すると下記のようになります

SEO / は / 、 / 検索 / エンジン / で / 上位 / 表示 / さ / せる / ため / の / 手法 / です / 。

このように、文章を一つずつ品詞分解(名詞・動詞・助詞・etc.)して文章がどのような単語で構成されていて、どのような意味を持つかを判断することを形態素解析という
(参考:https://www.seohacks.net/basic/terms/morphological-analysis/)

Numericalization(日本語でなんていうかわかりません)

単語が文章のどこに現れたかを示す?

自然言語処理は次回の講義でやるらしいので、とりあえずここまでにしておく

参考:
https://github.com/hiromis/notes/blob/master/Lesson3.md

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

大学生の書きなぐりブログ 間違ってる事も書いてるので自己責任で勉強しましょう

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!