20201208のTensorFlowに関する記事は3件です。

DNNにおけるClassificationの種類まとめ

概要

"AI"とかいうやつの課員用教育ネタの続き
深層学習の出口レイヤを変えると簡単に問題を切り替えられることを自習させようかと…
備忘録も兼ねて残すこととした

  • 実施期間: 2020年12月
  • 環境:Keras

ここで説明する各モデルの出口の全結合レイヤについて記述する
それより前のレイヤ構成はケースバイケース

Number of Nodes : ノード数 [1]
Activation : 活性関数 [2]
Loss : ロスの算出方法 [3]
Accuracy metrics: Fit中の精度評価指標 [4]
Output: 出力の型

Kerasだと、それぞれ下記の引数がそれらとなる
Optimizerはadamである必要はない
Dense, compileはそれぞれオフィシャルサイト参照のこと

model.add(Dense([1], activation='[2]'))
model.compile(loss='[3]', optimizer='adam‘, metrics=['[4]'])

Linear Regression

連続値である目的変数yを予測するモデルで線形回帰問題と呼ばれる

Linear regression

基本形がこれ
モデルにXを入力すると予測値のy_hat(ここでは0.539)が出力される
KerasではDenseの引数にActivationを指定しなければデフォルトでLinearとなる

image.png

Multi-output regression

入力Xに対して複数の連続値目的変数yがあるモデルがこれ
出口の全結合レイヤのNode数はそのyの数(ここでは3つ)でTrainingさせておく
image.png

Classification

Logistic regressionの一種がClassification
離散値である目的変数yを予測するモデルで分類問題と呼ばれる
モデル出口とyの型以外はRegressionと変わらず、ビビるに及ばない
なお、Logistic regressionとClassificationの違いはココで議論されているので興味があれば参照
ここでは便宜上、出口レイヤがTrueやFalseを出力しているが、実際には連続値が出力される
それをユーザがif文などでTrue/Falseに読み替えなければならない

Binary classification

モデルにXを入力すると予測値のy_hat(ここでは1(True))が出力される
yが連続値から離散値になり、出口の全結合レイヤの引数がやや変わっただけ
あとは上述のRegressionと同じ
不良品か否かを当てるようなモデル
image.png

Multilabel binary classification

Multi-output regressionとほぼ同じ
実際は各Nodeから出力されるのは連続値で、これを0(False)か1(True)に丸める
ただ小生はHeuristicに、例えば0.3以上ならTrueとか閾値は柔軟にしてよいと思っている
Training時のy(例えば[1, 1, 0])を各Nodeで予測するので、それぞれの出力は 0 <y_hat< 1 となる
image.png

Multiclass classification

これだけ少し毛色が違う
Activationがsoftmaxとなっており、全Nodeからの出力中Trueは1つである
実際は各Nodeから出力されるのは確率値となり、従い全Nodeの出力合計は1.0になる
最大のものを1(True)、それ以外を0(False)とする
犬、猫、人の画像からそのどれかを当てるようなモデルに使う
image.png

まとめ

モデル # of nodes Activation Loss Accuracy metrics Output
Regression 1 Linear MSE, etc. accuracy 連続値
Multi-output regression 複数 Linear MSE, etc. accuracy 連続値
Binary classification 1 sigmoid binary_crossentropy binary_accuracy 離散値(True/False)
Multilabel binary classification 複数 sigmoid binary_crossentropy binary_accuracy 離散値(複数True/False)
Multiclass classification 複数 softmax categorical_crossentropy categorical_accuracy 離散値(単数True/False)

“Class”と”Label”の違いについて

下図の場合、Classは3つありLabelは[犬, 猫, 人]
image.png

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Yoctoで生成した環境上で動かすことを目的としたTensorflowLite v2.4.0-rc4のスタンドアロン2MBインストーラとlibedgetpu.so.1のビルド

1. はじめに

超ライトな記事を書きます。 ことの発端は下記のやりとりでした。 YoctoEdgeTPU を動かしたいだって!? カオスさしか感じない問い合わせからスタートしたのでした。

Yoctoで作った環境ですので制限がとても厳しく、 apt コマンドすら使えないなど、とても苦労されているようでした。 で、やりとりを続けていくと、下記のコマンドで表示されるライブラリのバージョンと、EdgeTPUの公式が配布しているライブラリのバージョンがアンマッチであることが分かりました。

  • お使いの環境のライブラリのバージョン
環境のライブラリのバージョンチェック_サンプル
$ ldd --version

ldd (GNU libc) 2.27
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper
  • 公式サイトの手順に従ってインストールした場合の libedgetpu.so.1 が要求するライブラリのバージョン GLIBC 2.28

Eoov2UiVgAU15Fl.png

ということで、ライブラリのバージョンが一致しないなら自力でビルドして合わせてしまえばいいじゃん、とすぐに考えてしまうのがビルドジャンキーです。

2. 手順

利用中の環境のコンパイラのバージョンを使用して全ての依存ライブラリをビルドします。 今回は GLIBC 2.27 でしたので、 Ubuntu 18.04 が同じバージョンのコンパイラを保持していることを知っていました。 じゃあ、 Ubuntu 18.04 上でビルドしてしまえばいいじゃん。となりました。

ビルドに使用するのは下記のリポジトリです。

  1. TensorFlow v2.4.0-rc4
  2. libedgetpu 2020.12.07時点のmasterブランチ

2-1. TensorFlow Lite v2.4.0-rc4 のWheelインストーラのビルド

下記の通りの手順を踏むだけで10分弱でビルドできます。 Dockerを使用してクロスコンパイルでビルドしたい環境に使用するOSを BASE_IMAGE に指定するだけです。 簡単ですね。

TensorFlowLiteのインストーラのビルド
$ cd ~
$ git clone -b v2.4.0-rc4 https://github.com/tensorflow/tensorflow.git
$ cd tensorflow/tensorflow/lite/tools/pip_package
$ make BASE_IMAGE=ubuntu:18.04 PYTHON=python3 TENSORFLOW_TARGET=aarch64 BUILD_DEB=y docker-build

tensorflow/tensorflow/lite/tools/pip_package/gen/tflite_pip/python3/dist/ にインストーラが生成されます。
Screenshot 2020-12-08 00:29:44.png

2-2. libedgetpu.so.1 のビルド

特に難しいことはありませんが、公式のビルドシーケンスを一箇所だけ変更する必要があります。

$ cd ~
$ git clone https://github.com/google-coral/libedgetpu.git
$ cd libedgetpu
$ nano Makefile

### コレを
DOCKER_IMAGE ?= debian:stretch
↓
### このように修正します
DOCKER_IMAGE ?= ubuntu:18.04

レッツ、ビルド!!

$ DOCKER_CPUS="aarch64" DOCKER_TARGETS=libedgetpu make docker-build

5分ほどで終わりました。 libedgetpu/out/direct/aarch64/ あるいは libedgetpu/out/throttled/aarch64/ の配下に EdgeTPU をコントロールするために必要なデリゲート用.soファイルが生成されます。
Screenshot 2020-12-08 00:34:59.png

で、サポートをご依頼いただいた方へ上記の一式をご提供しました。

あ、私のお遊びモデルを動かすことが本来の目的だったのですね。。。 いずれにせよ、動作したようで何よりです。 それにしても 3ms/推論 ってめちゃくちゃ速いですね。


3. おわりに

ビルドジャンキーなんで、ビルドだけして楽しんで、作ったバイナリは一切使わないんですよね。 でも、アホみたいにビルドすればどんな環境でもだいたい動くんではないでしょうか?

対応開始序盤で @Nextremer_nb_o さん にヘルプを求めてしまいました。 そこから2人がかりで、あーでもないこーでもない、と議論し初めて、ちょっと楽しかったです。

Yocto で EdgeTPU、動くんすね。 ちょっと感動です。

Happy TensorFlow !! :laughing:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Tensorflow.kerasで学習済みモデルのレイヤーを差し替える

このエントリは TSG Advent Calendar 2020 の7日目の記事です。

現在のtensorflow(v2.3)ではtf.keras.applicationsに学習済みモデルが定義されており、転移学習などで活用することができます。しかしその学習モデルにDropoutを差し込む、レイヤーを差し替えるなどちょっとした変更を加えたくなることがたまにあると思います。こちらの記事ではVGG-likeな一本道のモデルに対してそのような変更を加える方法が紹介されていますが、ResNetなど分岐のあるモデルには使えません。そこで本記事ではそういったモデルにも対応した動的なモデル改変ができるスクリプトを紹介します。

BatchnormをSyncBatchnormに差し替える

kaggleなどで使えるTPUは8並列になっており、Batchnormを行うとそれぞれのデバイスで統計量を計算するため、バッチサイズが1/8になり(もともとのバッチサイズがあまり大きくないと)不安定になってしまいます。これの対策の一つは単純にバッチサイズを増やすというものがありますが、統計量をデバイス全体で共有する(SyncBatchnorm)という方法もあります。しかしこの場合、転移学習ではbackbone(学習済みモデル)内のレイヤーが普通のBatchnormなので、フリーズせずに学習させたい場合ここもSyncBatchnormに差し替える必要があります。そこでResNet50を例にとって、モデル内のBatchnormを差し替えてみましょう。

(追記)
https://www.tensorflow.org/guide/keras/transfer_learning によるとそもそも学習済みモデルのBatchnormの統計量はいじっちゃだめらしいです... unfreezeしてfinetuningする際もレイヤーへの入力はtraining=Falseを指定して推論モードにしておけ、でないと急に壊れるぞだそうです。
まあモデルをいちから学習させたい&いちいちレイヤーを書き下すの面倒という場合でも使えるので一応使いどころがなくもないはず...

tf.py
from collections import defaultdict
import tensorflow as tf

def get_sync_backbone():
    backbone = tf.keras.applications.ResNet50(include_top=False, weights='imagenet')
    mapping = defaultdict() # 元モデルのレイヤー名=>改変モデルでの同じ位置のレイヤーにおける出力

    for i, layer in enumerate(backbone.layers):
        if i == 0: # 一番底のレイヤ
            inpt = layer.input  # backboneモデルの下端のテンソル(Input)
            x = layer.input
            out_name = layer.output.name
            mapping[layer.output.name] = x  # モデルの上方でこのレイヤと繋がっている場合はこのテンソルを持ってきて入力する
            continue

        # 元モデルのレイヤーに入力されるテンソルに対応した、改変後モデルにおけるテンソルを持ってくる
        if type(layer.input) is list: # layer.inputは複数入力のときだけlistになっている
            input_tensors = list(map(lambda t: mapping[t.name], layer.input))
        else:
            input_tensors = mapping[layer.input.name]

        out_name = layer.output.name
        # ここで差し替え
        if isinstance(layer, tf.keras.layers.BatchNormalization):
            newlayer = tf.keras.layers.experimental.SyncBatchNormalization(
                momentum=0.9, # TensorflowではBatchnormのmomentumが0.99らしい。ここではPytorchと同じ0.9に
                beta_initializer=tf.initializers.constant(layer.beta.numpy()),
                gamma_initializer=tf.initializers.constant(layer.gamma.numpy()),
                moving_mean_initializer=tf.initializers.constant(layer.moving_mean.numpy()),
                moving_variance_initializer=tf.initializers.constant(layer.moving_variance.numpy()))
            x = newlayer(input_tensors)
        else:
            # 差し替えの必要がないレイヤーは再利用
            x = layer(input_tensors)
        mapping[out_name] = x
    return tf.keras.Model(inpt, x)

summaryを見てみると、

Model: "functional_1"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_1 (InputLayer)            [(None, None, None,  0                                            
__________________________________________________________________________________________________
conv1_pad (ZeroPadding2D)       (None, None, None, 3 0           input_1[0][0]                    
__________________________________________________________________________________________________
conv1_conv (Conv2D)             (None, None, None, 6 9472        conv1_pad[1][0]                  
__________________________________________________________________________________________________
sync_batch_normalization (SyncB (None, None, None, 6 256         conv1_conv[1][0]                 
__________________________________________________________________________________________________
conv1_relu (Activation)         (None, None, None, 6 0           sync_batch_normalization[0][0]   
__________________________________________________________________________________________________
pool1_pad (ZeroPadding2D)       (None, None, None, 6 0           conv1_relu[1][0]                 
__________________________________________________________________________________________________

このようにBatchnormがあったところがSyncBatchnormに差し替えられていることが分かります。
Connected toの要素がところどころ[1][0]になっていますが、これはレイヤーを再利用したためでしょう。

最後に

本記事ではTPUなどの複数デバイスにおける転移学習を想定し、学習済みモデルのBatchnormをSyncBatchnormに変更する方法を紹介しましたが、これを行うことで必ずしも学習が改善するとは限りません。こんなことをせずとも純粋にバッチサイズを増やしてしまったほうが楽だし、色々いじる前にまずデバイスを一つに限定してみて、そもそもバッチサイズの分割が原因なのかどうかをまず確認したほうが良いでしょう。私の場合はあれこれ調べた挙句、バッチサイズ云々ではなく学習率が高すぎるのが原因でした(Radamによるwarmupで解決しました)。機械学習ナンもわからん

次のカレンダーはkcz146さんの「絶対書く なんかかく」です。お楽しみに。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む