- 投稿日:2020-12-08T22:11:36+09:00
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となるMulti-output regression
入力Xに対して複数の連続値目的変数yがあるモデルがこれ
出口の全結合レイヤのNode数はそのyの数(ここでは3つ)でTrainingさせておく
Classification
Logistic regressionの一種がClassification
離散値である目的変数yを予測するモデルで分類問題と呼ばれる
モデル出口とyの型以外はRegressionと変わらず、ビビるに及ばない
なお、Logistic regressionとClassificationの違いはココで議論されているので興味があれば参照
ここでは便宜上、出口レイヤがTrueやFalseを出力しているが、実際には連続値が出力される
それをユーザがif文などでTrue/Falseに読み替えなければならないBinary classification
モデルにXを入力すると予測値のy_hat(ここでは1(True))が出力される
yが連続値から離散値になり、出口の全結合レイヤの引数がやや変わっただけ
あとは上述のRegressionと同じ
不良品か否かを当てるようなモデル
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 となる
Multiclass classification
これだけ少し毛色が違う
Activationがsoftmaxとなっており、全Nodeからの出力中Trueは1つである
実際は各Nodeから出力されるのは確率値となり、従い全Nodeの出力合計は1.0になる
最大のものを1(True)、それ以外を0(False)とする
犬、猫、人の画像からそのどれかを当てるようなモデルに使う
まとめ
モデル # 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は[犬, 猫, 人]
以上
- 投稿日:2020-12-08T00:49:19+09:00
Yoctoで生成した環境上で動かすことを目的としたTensorflowLite v2.4.0-rc4のスタンドアロン2MBインストーラとlibedgetpu.so.1のビルド
1. はじめに
超ライトな記事を書きます。 ことの発端は下記のやりとりでした。
Yocto
でEdgeTPU
を動かしたいだって!? カオスさしか感じない問い合わせからスタートしたのでした。@PINTO03091
— かさた@幸せな世界を作る (@funny_man_daa) December 4, 2020
お世話になります。
TPUのチュートリアル(オウムの推論)がうまく動かせない状況がずっと続いており解決できず、もし、PINTOさんの方で何かお分かりになれば少しアドバイスいただけると嬉しいです。。。?現在TPUでオウムの推論チュートリアルをhttps://t.co/VJRPFZk15k(aarch64)上で動かそうとしているのですが、Yoctoで生成したlinux OSでapt-getが使えない環境なので、
— かさた@幸せな世界を作る (@funny_man_daa) December 4, 2020
他のlinux環境にて入手したedgetpuファイル(swig/edgetpu_cpp_wrapper.pyが含まれているファイル)を直接移植して、パスなどを合わせてparrotチュートリアルが動くようになりましたが、推論速度が280ms/枚程度であり、実際にTPUが使われていない状況かと思います。
— かさた@幸せな世界を作る (@funny_man_daa) December 4, 2020
しかしTPUを抜くとhttps://t.co/Ga1hB4KUZnがないというエラーで動かなく、TPUを挿すことはアプリを動作させる条件となっているようなのです。ls /sys/bus/usb/devices/で見るとTPUを挿すことでデバイスファイルもできています。
— かさた@幸せな世界を作る (@funny_man_daa) December 4, 2020
つまり「オウムの推論プログラムで、TPUを挿してもCPU推論しかできないが、TPUを挿していないとCPU推論も動かせない」状況となります。何か思い当たる原因や解決策などございませんでしょうか??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
ということで、ライブラリのバージョンが一致しないなら自力でビルドして合わせてしまえばいいじゃん、とすぐに考えてしまうのがビルドジャンキーです。
2. 手順
利用中の環境のコンパイラのバージョンを使用して全ての依存ライブラリをビルドします。 今回は
GLIBC 2.27
でしたので、Ubuntu 18.04
が同じバージョンのコンパイラを保持していることを知っていました。 じゃあ、Ubuntu 18.04
上でビルドしてしまえばいいじゃん。となりました。ビルドに使用するのは下記のリポジトリです。
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/
にインストーラが生成されます。
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-build5分ほどで終わりました。
libedgetpu/out/direct/aarch64/
あるいはlibedgetpu/out/throttled/aarch64/
の配下に EdgeTPU をコントロールするために必要なデリゲート用.soファイルが生成されます。
で、サポートをご依頼いただいた方へ上記の一式をご提供しました。
!!!
— かさた@幸せな世界を作る (@funny_man_daa) December 7, 2020
(throttled) pic.twitter.com/J7eXXd0nsd!!!
— かさた@幸せな世界を作る (@funny_man_daa) December 7, 2020
(direct、こちらの方が早い) pic.twitter.com/dNZSprW6ev動きました!!!
— かさた@幸せな世界を作る (@funny_man_daa) December 7, 2020PINTOさんのHeadPoseDetectionの方も試してみます!!
— かさた@幸せな世界を作る (@funny_man_daa) December 7, 2020あ、私のお遊びモデルを動かすことが本来の目的だったのですね。。。 いずれにせよ、動作したようで何よりです。 それにしても 3ms/推論 ってめちゃくちゃ速いですね。
いまビルドした、できたてほやほや Ubuntu 18.04 aarch64 版の tflite_runtimeの.whl です。https://t.co/OM1gvtD8Wz
— Super PINTO (@PINTO03091) December 7, 2020
併せて GLIBC 2.27 でビルドした https://t.co/6wjeKQEq7P.1 を圧縮して下記に置いてみました。 私は、初心者なので動かしたことがありません。https://t.co/FIsy38PsuH
— Super PINTO (@PINTO03091) December 7, 20203. おわりに
ビルドジャンキーなんで、ビルドだけして楽しんで、作ったバイナリは一切使わないんですよね。 でも、アホみたいにビルドすればどんな環境でもだいたい動くんではないでしょうか?
対応開始序盤で @Nextremer_nb_o さん にヘルプを求めてしまいました。 そこから2人がかりで、あーでもないこーでもない、と議論し初めて、ちょっと楽しかったです。
Yocto で EdgeTPU、動くんすね。 ちょっと感動です。
Happy TensorFlow !!
- 投稿日:2020-12-08T00:47:42+09:00
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.pyfrom 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さんの「絶対書く なんかかく」です。お楽しみに。