今更ながらDropoutを検証してみる~その2~
TL;DR
- dropoutを入れてmnistで99.66%を達成した
dropoutの本気を見た
前回の記事でdropoutは安定しただけ、と書いたが、 もうちょい試すと99.66%を達成したのでこちらにて。
modelというかソース
このようにPReLUで活性化したレイヤの後全てにDropout(0.1)を入れた。
from keras import datasets import numpy as np ((train_x,train_y),(test_x,test_y)) = datasets.mnist.load_data() train_x = (train_x/255).astype("float32").reshape(-1,28,28,1) test_x = (test_x /255).astype("float32").reshape(-1,28,28,1) train_y = np.eye(10)[train_y] test_y = np.eye(10)[test_y] from tensorflow.keras.layers import * from tensorflow.keras.models import * from tensorflow.keras.optimizers import Adam from tensorflow.keras.callbacks import ModelCheckpoint from tensorflow.keras.losses import categorical_crossentropy prelu_model_drop01 = Sequential() prelu_model_drop01.add(Conv2D(filters=32,kernel_size=(3,3),input_shape=(28,28,1),padding="same")) prelu_model_drop01.add(PReLU()) prelu_model_drop01.add(Dropout(0.1)) prelu_model_drop01.add(BatchNormalization()) prelu_model_drop01.add(Conv2D(filters=32,kernel_size=(3,3),padding="same")) prelu_model_drop01.add(PReLU()) prelu_model_drop01.add(Dropout(0.1)) prelu_model_drop01.add(BatchNormalization()) prelu_model_drop01.add(MaxPool2D(pool_size=(2,2),padding="same")) # 28,28 -> 14,14 prelu_model_drop01.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop01.add(PReLU()) prelu_model_drop01.add(Dropout(0.1)) prelu_model_drop01.add(BatchNormalization()) prelu_model_drop01.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop01.add(PReLU()) prelu_model_drop01.add(Dropout(0.1)) prelu_model_drop01.add(BatchNormalization()) prelu_model_drop01.add(MaxPool2D(pool_size=(2,2),padding="same")) # 14,14 -> 7,7 prelu_model_drop01.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop01.add(PReLU()) prelu_model_drop01.add(Dropout(0.1)) prelu_model_drop01.add(BatchNormalization()) prelu_model_drop01.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop01.add(PReLU()) prelu_model_drop01.add(Dropout(0.1)) prelu_model_drop01.add(BatchNormalization()) prelu_model_drop01.add(MaxPool2D(pool_size=(2,2),padding="same")) # 7,7 -> 4,4 prelu_model_drop01.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop01.add(PReLU()) prelu_model_drop01.add(Dropout(0.1)) prelu_model_drop01.add(BatchNormalization()) prelu_model_drop01.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop01.add(PReLU()) prelu_model_drop01.add(Dropout(0.1)) prelu_model_drop01.add(BatchNormalization()) prelu_model_drop01.add(MaxPool2D(pool_size=(2,2),padding="same")) # 4,4 -> 2,2 prelu_model_drop01.add(Conv2D(filters=128,kernel_size=(2,2),padding="same")) prelu_model_drop01.add(PReLU()) prelu_model_drop01.add(Dropout(0.1)) prelu_model_drop01.add(BatchNormalization()) prelu_model_drop01.add(MaxPool2D(pool_size=(2,2),padding="same")) # 2,2 -> 1,1 prelu_model_drop01.add(Flatten()) prelu_model_drop01.add(Dense(10,activation="softmax")) prelu_model_drop01.summary() prelu_model_drop01.compile(optimizer=Adam(lr=0.0001),metrics=['accuracy'],loss="categorical_crossentropy") prelu_model_drop01_history = prelu_model_drop01.fit(train_x,train_y,batch_size=16,epochs=64,validation_data=(test_x,test_y))
結果
32 epochで99.66%を達成しました。
また引き続き安定しているように見えます。
結論
弱いdropoutを活性化層の後に入れまくるべし。(ホントか?)
今回もふざけた広告を
純愛ドロップアウト 【電子限定特典付き】 (バンブーコミックス 麗人uno!コミックス)
- 作者: 三坂ニウム
- 出版社/メーカー: 竹書房
- 発売日: 2019/07/19
- メディア: Kindle版
- この商品を含むブログを見る
今更ながらDropoutを検証してみる
TL;DR;
dropoutで適切なパラメータを振ると学習が安定するよ。
val_accの最大値は更新しませんでした。
Dropoutの効果は本当にあるのか
今までいろんなモデル(つっても画像の異常判定とか、U-NET、オートエンコーダーくらいだけど)を組んできたが、
Dropoutがあんまり機能した記憶がないので、ホンマかいな、というのを試してみた。
みなさん大好きmnistで。
(というより誰でもいつでも使えるものになるとmnistに)
検証モデル
まずは下記投稿で最高結果を出したモデルで。
from keras import datasets import numpy as np ((train_x,train_y),(test_x,test_y)) = datasets.mnist.load_data() train_x = (train_x/255).astype("float32").reshape(-1,28,28,1) test_x = (test_x /255).astype("float32").reshape(-1,28,28,1) train_y = np.eye(10)[train_y] test_y = np.eye(10)[test_y] from tensorflow.keras.layers import * from tensorflow.keras.models import * from tensorflow.keras.optimizers import Adam from tensorflow.keras.callbacks import ModelCheckpoint from tensorflow.keras.losses import categorical_crossentropy prelu_model = Sequential() prelu_model.add(Conv2D(filters=32,kernel_size=(3,3),input_shape=(28,28,1),padding="same")) prelu_model.add(PReLU()) prelu_model.add(BatchNormalization()) prelu_model.add(Conv2D(filters=32,kernel_size=(3,3),padding="same")) prelu_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 28,28 -> 14,14 prelu_model.add(PReLU()) prelu_model.add(BatchNormalization()) prelu_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model.add(PReLU()) prelu_model.add(BatchNormalization()) prelu_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model.add(PReLU()) prelu_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 14,14 -> 7,7 prelu_model.add(BatchNormalization()) prelu_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model.add(PReLU()) prelu_model.add(BatchNormalization()) prelu_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model.add(PReLU()) prelu_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 7,7 -> 4,4 prelu_model.add(BatchNormalization()) prelu_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model.add(PReLU()) prelu_model.add(BatchNormalization()) prelu_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model.add(PReLU()) prelu_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 4,4 -> 2,2 prelu_model.add(BatchNormalization()) prelu_model.add(Conv2D(filters=128,kernel_size=(2,2),padding="same")) prelu_model.add(PReLU()) prelu_model.add(BatchNormalization()) prelu_model.add(Conv2D(filters=128,kernel_size=(2,2),padding="same")) prelu_model.add(PReLU()) prelu_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 2,2 -> 1,1 prelu_model.add(BatchNormalization()) prelu_model.add(Flatten()) prelu_model.add(Dense(10,activation="softmax")) prelu_model.summary() prelu_model.compile(optimizer=Adam(lr=0.0001),metrics=['accuracy'],loss="categorical_crossentropy") prelu_model_history = prelu_model.fit(train_x,train_y,batch_size=16,epochs=64,validation_data=(test_x,test_y))
このモデルに対して各アクティベーション層後にDropoutを設定する。
この記事によると、
それではDropoutを適用していこう。 まずは、Hinton氏の提案通り入力層は0.2、隠れ層は0.5にしておく。 TFLearnのdropout関数は1-dropout率を指定する。
ヒントン氏が入力層0.2,隠れ層は0.5と言っているので、それに習い、そのように設定。
ただ、なんとなく出力層直前は0.2にしてみました。
ヒントン氏に敗れた人はなんというか知りませんが…
prelu_model_drop05 = Sequential() prelu_model_drop05.add(Conv2D(filters=32,kernel_size=(3,3),input_shape=(28,28,1),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.2)) prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=32,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(Dropout(0.5)) prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 28,28 -> 14,14 prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.5)) prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.5)) prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.5)) prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 14,14 -> 7,7 prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.5)) prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.5)) prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 7,7 -> 4,4 prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.5)) prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.5)) prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 4,4 -> 2,2 prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=128,kernel_size=(2,2),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.5)) prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=128,kernel_size=(2,2),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.2)) prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 2,2 -> 1,1 prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Flatten()) prelu_model_drop05.add(Dense(10,activation="softmax")) prelu_model_drop05.summary()
結果
Dropoutはポンコツでした。
Dropout無しが99.47%のval_accだったのに対し(あれ、99.5%超えてない)
Dropout有りが97.47%が限界でした。
検証2
たぶんDropoutの設計がイケてなかったのではないでしょうか…。
Dropoutレイヤが多すぎたか、パラメータの0.2,0.5が行けなかった気がします。
ヒントン氏を信じてパラメータはそのままに、 レイヤを減らしてみましょう。
で、作成したモデルがこちら。 (dropoutしてないヤツは変わらず)
prelu_model_drop05 = Sequential() prelu_model_drop05.add(Conv2D(filters=32,kernel_size=(3,3),input_shape=(28,28,1),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.2)) prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=32,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(Dropout(0.5)) prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 28,28 -> 14,14 prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(BatchNormalization())<figure class="figure-image figure-image-fotolife" title="dropout減らし">[f:id:kazuhitogo:20190906102602p:plain]<figcaption>dropout減らし</figcaption></figure> prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.5)) prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 14,14 -> 7,7 prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.5)) prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 7,7 -> 4,4 prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.5)) prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 4,4 -> 2,2 prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Conv2D(filters=128,kernel_size=(2,2),padding="same")) prelu_model_drop05.add(PReLU()) prelu_model_drop05.add(Dropout(0.2)) prelu_model_drop05.add(MaxPool2D(pool_size=(2,2),padding="same")) # 2,2 -> 1,1 prelu_model_drop05.add(BatchNormalization()) prelu_model_drop05.add(Flatten()) prelu_model_drop05.add(Dense(10,activation="softmax")) prelu_model_drop05.summary()
結果2
うーん、いまいちすぎる。 が、dropout無し版がepoch 61にしてval_acc99.6%超えましたね。笑
検証3
ヒントン教授を疑うことにして、 dropoutを全て(0.2)で検証します。
prelu_model_drop02 = Sequential() prelu_model_drop02.add(Conv2D(filters=32,kernel_size=(3,3),input_shape=(28,28,1),padding="same")) prelu_model_drop02.add(PReLU()) prelu_model_drop02.add(Dropout(0.2)) prelu_model_drop02.add(BatchNormalization()) prelu_model_drop02.add(Conv2D(filters=32,kernel_size=(3,3),padding="same")) prelu_model_drop02.add(Dropout(0.2)) prelu_model_drop02.add(MaxPool2D(pool_size=(2,2),padding="same")) # 28,28 -> 14,14 prelu_model_drop02.add(PReLU()) prelu_model_drop02.add(BatchNormalization()) prelu_model_drop02.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop02.add(PReLU()) prelu_model_drop02.add(BatchNormalization()) prelu_model_drop02.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop02.add(PReLU()) prelu_model_drop02.add(Dropout(0.2)) prelu_model_drop02.add(MaxPool2D(pool_size=(2,2),padding="same")) # 14,14 -> 7,7 prelu_model_drop02.add(BatchNormalization()) prelu_model_drop02.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop02.add(PReLU()) prelu_model_drop02.add(BatchNormalization()) prelu_model_drop02.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop02.add(PReLU()) prelu_model_drop02.add(Dropout(0.2)) prelu_model_drop02.add(MaxPool2D(pool_size=(2,2),padding="same")) # 7,7 -> 4,4 prelu_model_drop02.add(BatchNormalization()) prelu_model_drop02.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop02.add(PReLU()) prelu_model_drop02.add(BatchNormalization()) prelu_model_drop02.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) prelu_model_drop02.add(PReLU()) prelu_model_drop02.add(Dropout(0.2)) prelu_model_drop02.add(MaxPool2D(pool_size=(2,2),padding="same")) # 4,4 -> 2,2 prelu_model_drop02.add(BatchNormalization()) prelu_model_drop02.add(PReLU()) prelu_model_drop02.add(BatchNormalization()) prelu_model_drop02.add(Conv2D(filters=128,kernel_size=(2,2),padding="same")) prelu_model_drop02.add(PReLU()) prelu_model_drop02.add(Dropout(0.2)) prelu_model_drop02.add(MaxPool2D(pool_size=(2,2),padding="same")) # 2,2 -> 1,1 prelu_model_drop02.add(BatchNormalization()) prelu_model_drop02.add(Flatten()) prelu_model_drop02.add(Dense(10,activation="softmax")) prelu_model_drop02.summary() prelu_model_drop02.compile(optimizer=Adam(lr=0.0001),metrics=['accuracy'],loss="categorical_crossentropy") prelu_model_drop02_history = prelu_model_drop05.fit(train_x,train_y,batch_size=16,epochs=64,validation_data=(test_x,test_y))
はてさて。
結果3
かなり改善されました。
最高のval_accこそ負けるものの(dropout0.2有り99.58%,無し99.6%) 学習がかなり安定しております。
もう少しまんべんなくdropoutしてパラメータが0.1とかだとさらなる改善が期待できそうです。
あとは学習率のスケジュールとかをすると更によいかもしれません。
今回はふざけた広告を笑
純愛ドロップアウト 【電子限定特典付き】 (バンブーコミックス 麗人uno!コミックス)
- 作者: 三坂ニウム
- 出版社/メーカー: 竹書房
- 発売日: 2019/07/19
- メディア: Kindle版
- この商品を含むブログを見る
ReLUファミリー活性化関数を検証してみる(ReLU,leakyReLU,PReLU)
TL;DR
ReLUファミリー最強の活性化関数は今の所PReLU。
ただしRReLUは未検証。
mnistでモデルのみで99.5%を超えたい
How to score 97%, 98%, 99%, and 100% | Kaggle 上記によると
99.5%: If you design a good CNN architecture and then add special features like pooling layers, data augmentation, dropout, batch normalization, decaying learning rate, advanced optimization; You'll have no problem breaking the landmark 99.5% in only 20 epochs!
(意訳) まともなDLモデラーならaccuracy99.5%超えないとやべーよ!!
とのことなので、DL歴2年になった私が改めてやってみようと思った次第。
ただし、このままじゃ面白くないので多少の縛りを。
DataAugmentationを使わない
つまり今日このブログに載せているソースコードをそのまま実行すれば乱数の問題があるにせよそれっぽく再現できる。
というよりDLモデラーならオーグメンテーション無しでも実現したいところ。
epoch数は48までヨシとする(上記の英文では20 epoch以内との記載)
乱数ガチャが辛いため
dropoutを使わない
なんとなく気分、というか別途検証予定のため
で、とりあえず、↓のモデル(keras)で、29epoch目で99.5%を超えたわけですが…。
from keras import datasets import numpy as np ((train_x,train_y),(test_x,test_y)) = datasets.mnist.load_data() train_x = (train_x/255).astype("float32").reshape(-1,28,28,1) test_x = (test_x /255).astype("float32").reshape(-1,28,28,1) train_y = np.eye(10)[train_y] test_y = np.eye(10)[test_y] from tensorflow.keras.layers import * from tensorflow.keras.models import * from tensorflow.keras.optimizers import Adam leaky_model = Sequential() leaky_model.add(Conv2D(filters=32,kernel_size=(3,3),input_shape=(28,28,1),padding="same")) leaky_model.add(LeakyReLU(alpha=0.1)) leaky_model.add(BatchNormalization()) leaky_model.add(Conv2D(filters=32,kernel_size=(3,3),padding="same")) leaky_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 28,28 -> 14,14 leaky_model.add(LeakyReLU(alpha=0.1)) leaky_model.add(BatchNormalization()) leaky_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) leaky_model.add(LeakyReLU(alpha=0.1)) leaky_model.add(BatchNormalization()) leaky_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) leaky_model.add(LeakyReLU(alpha=0.1)) leaky_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 14,14 -> 7,7 leaky_model.add(BatchNormalization()) leaky_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) leaky_model.add(LeakyReLU(alpha=0.1)) leaky_model.add(BatchNormalization()) leaky_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) leaky_model.add(LeakyReLU(alpha=0.1)) leaky_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 7,7 -> 4,4 leaky_model.add(BatchNormalization()) leaky_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) leaky_model.add(LeakyReLU(alpha=0.1)) leaky_model.add(BatchNormalization()) leaky_model.add(Conv2D(filters=64,kernel_size=(3,3),padding="same")) leaky_model.add(LeakyReLU(alpha=0.1)) leaky_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 4,4 -> 2,2 leaky_model.add(BatchNormalization()) leaky_model.add(Conv2D(filters=128,kernel_size=(2,2),padding="same")) leaky_model.add(LeakyReLU(alpha=0.1)) leaky_model.add(BatchNormalization()) leaky_model.add(Conv2D(filters=128,kernel_size=(2,2),padding="same")) leaky_model.add(LeakyReLU(alpha=0.1)) leaky_model.add(MaxPool2D(pool_size=(2,2),padding="same")) # 2,2 -> 1,1 leaky_model.add(BatchNormalization()) leaky_model.add(Flatten()) leaky_model.add(Dense(10,activation="softmax")) leaky_model.summary() leaky_model.compile(optimizer=Adam(lr=0.0001),metrics=['accuracy'],loss="categorical_crossentropy") leaky_model_history = leaky_model.fit(train_x,train_y,batch_size=16,epochs=48,validation_data=(test_x,test_y))
活性化関数って本当にこれでいいの?というのが気なったので、
- LeakyReLU(0.0) (= ReLU)
- LeakyReLU(0.1)
- LeakyReLU(0.2)
- LeakyReLU(0.3)
- PReLU
でどれが一番いいのか、を上記モデルのLeakReLU部分だけを変えて検証してみました。 (他は一切手を加えておりません)
[1505.00853] Empirical Evaluation of Rectified Activations in Convolutional Network
によると、Rondomized ReLU(RReLU)がいいらしいですが、
kerasにまだ実装されていないとのことなので↓
【ReLU, PReLU, シグモイド etc...】ニューラルネットでよく使う活性化関数の効果をKerasで調べてみた - プロクラシスト
今回はこの5通りを試しました。
結果
val_acc
PReLUがepoch 30で瞬間最大風速99.55%を記録しました。
次点でleaky 0.3がepoch 43で99.53%ですね。
ただ、一番悪かったのもreluとleaky0.2で99.45%なのでまぁ誤差といえば誤差な気がします。
もう一回バッチを回すと結果が変わるきがしてならない。
ねんのためval_lossも。
まぁほぼval_accと一緒ですね。
まとめ
PReLUが一番よい。accuracyにして0.1%程度。
有意差はわからない。
とりあえずデータサイエンティストを名乗るための99.5%は超えた。
以上。
現場で使える! TensorFlow開発入門 Kerasによる深層学習モデル構築手法 (AI & TECHNOLOGY)
- 作者: 太田満久,須藤広大,黒澤匠雅,小田大輔
- 出版社/メーカー: 翔泳社
- 発売日: 2018/04/19
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (2件) を見る
overpassを使ってサンフランシスコの緯度経度半径1km以内にバーガーキングがあるかを探す
どうしようもなくバーガーキングを食べたくなったあなたへ。
サンフランシスコにバーガーキングを目当てに旅行に行く人はなかなか居ないとは思いますが、 そんな人向けに。
実装
import overpass import urllib # proxy設定する人 ## proxies = { ## "http":"http://hogehoge", ## "https":"http://hogehoge" ## } api = overpass.API() api = overpass.API(endpoint="https://overpass-api.de/api/interpreter",timeout=600) ## api.proxies=proxies # サンフランシスコの緯度経度から半径1km以内のfast food response = api.get('node(around:1000.0,37.77493,-122.419416)["amenity"="fast_food"];out;') for data in response["features"]: if 'name' in data["properties"] and data["properties"]["name"] == "Burger King": print(data)
結果
2件ありました。
{"geometry": {"coordinates": [-122.421096, 37.7831831], "type": "Point"}, "id": 432817342, "properties": {"addr:city": "San Francisco", "addr:housenumber": "819", "addr:street": "Van Ness Avenue", "amenity": "fast_food", "name": "Burger King", "source": "survey"}, "type": "Feature"} {"geometry": {"coordinates": [-122.421096, 37.7831831], "type": "Point"}, "id": 432817342, "properties": {"addr:city": "San Francisco", "addr:housenumber": "819", "addr:street": "Van Ness Avenue", "amenity": "fast_food", "name": "Burger King", "source": "survey"}, "type": "Feature"}
解説
overpassというライブラリがあり、
openstreetmapのAPIをpythonからコールできるようにしたもの。
よくわからないクエリ方式(overpass QL)もしくは、XMLでリクエストを投げると、JSONでデータを返してくれる。
今回はサンフランシスコの緯度経度と1km以内のfastfoodというoverpass QLを投げて、
そのなかのバーガーキングを表示しました。
OpenLayers4で遊ぼう 無料の地図データをWebに表示! (技術の泉シリーズ(NextPublishing))
- 作者: 佐藤奈々子
- 出版社/メーカー: インプレスR&D
- 発売日: 2018/04/27
- メディア: Kindle版
- この商品を含むブログを見る
pythonのheapqが気に入らないので自分で実装しなおす(優先度付きキュー)
優先度付きキュー
って何?
って方はこちらをご参照。
こちらの本でお勉強もよろし(わかりやすかった)
- 作者: 石田保輝,宮崎修一
- 出版社/メーカー: 翔泳社
- 発売日: 2017/06/06
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
ざっくり言うと、あるデータの塊から、
最小(あるいは最大)の値を取り出すために特化したデータ構造を、
ヒープ木とか二分ヒープとか優先度付き待ち行列とか優先度付きキューとかと言う。
ここらへんの表記ゆれはよくわからない。
またデータを追加するときにenqueue(エンキュー)、
最小値を取り出す(そして削除)のをdequeue(デキュー)という。
特徴として、先頭の値を取り出すのがO(1)と高速だし、 追加や削除もO(log n)とそこそこ高速である。
また最後までdequeueし続けるとヒープソートの完成である。
pythonの優先度付きキュー
pythonにはheapqというライブラリが備わっており、
enqueueはheappush,dequeueはheappopで備わっているのだが、
下記事項が気に食わないので作り直した。
オブジェクト志向ではない
事前に配列を自分で宣言し、heappushやheappopに配列を引数で突っ込む必要があり、直感的でない
降順をサポートしない
優先度は昇順しか使えないので、降順にしたい場合は別途正負反転させる等の工夫が必要
heappush,heappopってなんやねん
enqueue,dequeueのほうがかっこよくない?
ということで実装。
実装
pure pythonで実装。
class heap: def __init__(self,order="asc"): self._heap = [] self.order=order def get_head(self): return self._heap[0] if self.order == "asc" else -self._heap[0] def get_heap(self): return self._heap if self.order == "asc" else list(map(lambda x:-x,self._heap)) def enqueue(self,items): if type(items) != list: items = [items] # 降順ソートの場合は正負反転 if self.order!="asc": items = list(map(lambda x:-x,items)) for item in items: self._heap.append(item) self_idx = len(self._heap)-1 parent_idx = self_idx//2 while self_idx > 0: if self._heap[self_idx] <= self._heap[parent_idx]: self._heap[self_idx], self._heap[parent_idx] = self._heap[parent_idx], self._heap[self_idx] self_idx = parent_idx parent_idx = self_idx//2 else: break def dequeue(self): head = self._heap[0] self._heap[0] = self._heap[-1] self._heap.pop(-1) self_idx = 0 child_idx = [1,2] while True: # 子供が存在しなかったらbreak if child_idx[0] > len(self._heap)-1: break # 左の子供しか存在しない場合 elif child_idx[0] == len(self._heap)-1: # 左の子供のほうが大きい場合 if self._heap[self_idx] < self._heap[child_idx[0]]: break else: self._heap[self_idx],self._heap[child_idx[0]] = self._heap[child_idx[0]],self._heap[self_idx] self_idx = (self_idx*2)+1 child_idx = [self_idx*2+1,self_idx*2+2] # 両方の子供が存在する場合 else: # 子供のほうが大きい場合 if self._heap[self_idx] < min([self._heap[child_idx[0]],self._heap[child_idx[1]]]): break # 子供のほうが小さい場合 else: # 左の子供のほうが小さい場合は左の子供と親の値を交換 if self._heap[child_idx[0]] <= self._heap[child_idx[1]]: self._heap[self_idx],self._heap[child_idx[0]] = self._heap[child_idx[0]],self._heap[self_idx] self_idx = (self_idx*2)+1 child_idx = [self_idx*2+1,self_idx*2+2] # 右の子供のほうが小さい場合は右の子供と親の値を交換 else: self._heap[self_idx],self._heap[child_idx[1]] = self._heap[child_idx[1]],self._heap[self_idx] self_idx = (self_idx*2)+2 child_idx = [self_idx*2+1,self_idx*2+2] return head if self.order == "asc" else -head
使い方
# heapインスタンス生成 heap = heap() # [10,9,8,7,6,5,4,3,2,1]というデータを突っ込む heap.enqueue([10-i for i in range(10)]) # 先頭のデータを取得(参照のみでデータ変化なし) heap.get_head() # 1 # heap全体の取得(参照のみでデータ変化なし) heap.get_heap() # [1, 2, 3, 5, 4, 8, 9, 6, 10, 7] ←先頭が最小値であることと、木構造で見たときに「親の値<子供の値」のみ保証 # データを突っ込むその2(先頭に0を突っ込む) heap.enqueue(0) # 先頭が書き換わったことの確認 heap.get_head() # 0 # dequeue print(heap.dequeue()) # 0が返り0は消滅 # 先頭が書き換わる heap.get_head() # 1 # heapsort heap_sort_result = [] for i in range(len(heap.get_heap())): heap_sort_result.append(heap.dequeue()) print(heap_sort_result) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
こんな感じで使えます。
同等のことをheapqでやってみる。
import heapq # heapインスタンス生成相当(ただのリスト宣言) Q = [] # データを突っ込む for i in [10-i for i in range(10)]: heapq.heappush(Q,i) # 先頭の値を取得 print(Q[0]) # heap全体の取得 print(Q) # データを突っ込むその2(先頭に0を突っ込む) heapq.heappush(Q,0) # 先頭が書き換わったことの確認 print(Q[0]) # dequeue heapq.heappop(Q) # heapsort heap_sort_result = [] for _ in range(len(Q)): heap_sort_result.append(heapq.heappop(Q)) print(heap_sort_result)
毎回リストを引数につっこまなくてよくなりました。
また、降順はこんな感じ。
# インスタンス生成時に降順を指定 heap = heap(order = "desc") # 昇順にデータを突っ込む heap.enqueue([i+1 for i in range(10)]) # [1,2,3,4,5,6,7,8,9,10]を格納 #10が先頭に来ている heap.get_head() # heap sort(降順) heap_sort_result = [] for i in range(len(heap.get_heap())): heap_sort_result.append(heap.dequeue()) print(heap_sort_result) # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
これをheapqでやるとこうなる
import heapq # インスタンス生成時に降順を指定(という名のただのリスト生成) Q = [] # 昇順にデータを突っ込む for i in [i+1 for i in range(10)]: heapq.heappush(Q,-i) #10が先頭に来ている print(-Q[0]) # heap sort(降順) heap_sort_result = [] for i in range(len(Q)): heap_sort_result.append(heapq.heappop(Q)) heap_sort_result = list(map(lambda x:-x,heap_sort_result)) print(heap_sort_result) # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
heapqは降順ソートをサポートしていないので、 毎回正負反転が必要で頭がこんがらがってしまう。
なので、こちらをオススメをしたい。
処理速度
とはいえ、処理速度はどうだろう。 1~1,000,000を突っ込んでheap sortしてみる
今回作ったやつ
%%time heap = heap(order = "desc") heap.enqueue([i+1 for i in range(1000000)]) heap_sort_result = [] for i in range(len(heap.get_heap())): heap_sort_result.append(heap.dequeue())
Wall time: 31.2 s
heapq
%%time import heapq # インスタンス生成時に降順を指定(という名のただのリスト生成) Q = [] # 昇順にデータを突っ込む for i in [i+1 for i in range(1000000)]: heapq.heappush(Q,-i) # heap sort(降順) heap_sort_result = [] for i in range(len(Q)): heap_sort_result.append(heapq.heappop(Q)) heap_sort_result = list(map(lambda x:-x,heap_sort_result))
Wall time: 1.16 s
ぐえ、ボロ負け。
仕方ないのでnumbaで高速化を図る…
のは次回以降。
mnistをDataRobotに解かせてみる
DataRobotってどうなのよ
DataRobotを触れる機械があったので触ってみた。
画像は扱えない、とのことだったけれども、
画像を画像として扱うからであって、
それぞれのピクセルの輝度を列展開して、
輝度表にしてやればできないことはないだろ…ってのを、
無理やりやってみた。
結果から言うと、悪くない。
- 作者: Gerardus Blokdyk
- 出版社/メーカー: 5starcooks
- 発売日: 2018/10/07
- メディア: ペーパーバック
- この商品を含むブログを見る
データ準備
あまり大きいデータは辛いので28*28のライトなあのデータを使いました。
そうですね、mnistですね。
リンクを開くのがめんどい人のために一行で説明すると、
手書きで書かれた数字の判別に使われるデータセットです。
ご存知の通りDLはこちらから。
手動で解凍するも、いらないバイトがある、とのことなので、↓
ちょこっと改変してこんな感じでCSV化。
import numpy as np def load_img(file_name): with open(file_name, 'rb') as f: data = np.frombuffer(f.read(), np.uint8, offset=16) data = data.reshape(-1, 784) return data def load_label(file_name): with open(file_name, 'rb') as f: labels = np.frombuffer(f.read(), np.uint8, offset=8) return labels train_x = load_img("./train-images.idx3-ubyte") test_x = load_img("./t10k-images.idx3-ubyte") train_y = load_label("./train-labels.idx1-ubyte") test_y = load_label("./t10k-labels.idx1-ubyte") np.savetxt("train_x.csv",train_x,delimiter=",") np.savetxt("train_y.csv",train_y,delimiter=",") np.savetxt("test_x.csv",test_x,delimiter=",") np.savetxt("test_y.csv",test_y,delimiter=",")
さて、これで1行1画像784カラムに展開したcsvがtrain_x,test_xに、
それぞれのラベルがtrain_y,test_yに出来たので、
trainデータをdatarobotに食わせました。
結果
リーダボード(某kaggleを意識してますね)にはこんな感じで表示。
ホールドアウトを外して60000枚すべてで学習した時の絵ですが、 Log Lossで0.07弱は…まぁそれなりですね。
表示されているBlender(アンサンブルのことをDataRobotではBlenderというらしい…)を除いた、 LightGBMとXGBoostでtest_xを予測してみたところ、
XGBoost:
正答:9816 誤答:184 acc:98.16%
LightGBM:
正答:9815 誤答:185 acc:98.15%
でした。
わずか(0.01%というか1問というか)ながらXGBoostが勝ちましたが、
データセットを変えたら違う結果が出るだろうし間違いなく誤差でしょう。
性能としてはコサイン類似度を使ったK近傍法と同レベルではありますね…。
一つ不満があるとすれば、
DataRobotは自動で最適と思われるモデルを選んでくれるはずなのに、
XGBoostは私が手動で突っ込んだモデルだ、ということでしょうか。
(XGBoostをやらないという選択肢が基本的に私にはないのですが)
考察
さて、DataRobotには特徴量を自動で選択してくれる機能があるので、それを見てみる。
↓がDataRobotが有用と判断したピクセルである。
595の変数を有用と判断したようです。 (596と表示されていますが実際はLabelがあるので595)
ただここに書かれているXXXpxだけだとどこを見ているのかわからないので、 採用されたピクセル箇所をヒートマップにしてみました。
上下の端っこが使われないのはたぶん文字が書かれていなから、だとしても、
右上から左下にかけて、斜めに全く使われないのはなんでなんですかね。
ここに文字が書かれていない、ということはないので、 すべて塗りつぶされていて判別に使えない…?
右利きの人が多いのでその特徴…?
そんなこともないか…?
わかりませんが、DataRobotさん的には使えないと判断したようです。
逆に左端右端はがっつり使えるようですね。
また、どの特徴量が一番使えたか、もDataRobotは見ることができます。
一番使えたピクセルを100%としてそれを相対的にどれくらい使えたか、をプロットしているもので、
212pxだけで判別できる、という意味ではありません。
ところで212pxってどこなんでしょう。
あたりが気になるのでトップ5をプロットしてみました。
色が濃い順です。
ここらへんのピクセルに数字判別の特徴が出るっぽいですね。
知らんけど。
間違えた例
さて、間違えた例も一個くらい見ておきましょう
4を6と間違えた例
うーん、畳み込みで形を覚えているわけではなく、
各ピクセルの輝度の決定木がベースなので、6が書かれているところに4が乗っちゃった感じですね。
きっと。
7を2と間違えた例
これは人間でも間違える…。
最後に
DataRobotを嫌うData Scientistは多いですが、
個人的にはその問題が機械学習で解けるか、
を手早く知るツールとしては優秀なのではないでしょうか。
今回は画像だったので、当然CNNを使えばもっとよい性能は出ますし、
チューニングの余地もあるかとは思いますが、
仕事をAIで…の文脈だと、出来る出来ないの判断は簡単にすぐにしてくれる、
という意味では、
個人的には良いツールかな、と思います。
(全モデルの学習時間半日程度)
現場で使える!TensorFlow開発入門 Kerasによる深層学習モデル構築手法
- 作者: 太田満久,須藤広大,黒澤匠雅,小田大輔
- 出版社/メーカー: 翔泳社
- 発売日: 2018/04/19
- メディア: Kindle版
- この商品を含むブログを見る
移動の軌跡をpythonを用いて地図に表示する&画像化する
移動したときの軌跡を表示
今やGPSがくっついた端末を持ち運ぶことも多いので、 移動の軌跡を表示したい、なんていう要望もあるかと。
そんなことをやってみました。 本当は業務で必要になって土日に勉強しただけだが…。
- 作者: Rob Story,Matt Davis
- 出版社/メーカー: O'Reilly Media
- 発売日: 2016/12/25
- メディア: ペーパーバック
- この商品を含むブログを見る
【改訂新版】[オープンデータ+QGIS]統計・防災・環境情報がひと目でわかる地図の作り方
- 作者: 朝日孝輔,大友翔一,水谷貴行,山手規裕
- 出版社/メーカー: 技術評論社
- 発売日: 2018/12/28
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
folium
pythonに地図表示ライブラリのfoliumというのがあったのでこちらを使ってみた。
python-visualization.github.io
これはブラウザ上でいろんなレイヤを作成するライブラリなので、 基本的にはhtml/css/svgを吐き出す装置だと思ってくれればよい。
使い方
一般的には下記の使い方手順。
マップを作成する(キャンバスのようなもの)
権利とかにうるさくないopenstreetmapで生成が基本
地図の上に表示したいものを追加する
線とか、アイコンとか、ポリゴンとか
表示する or 保存する
jupyter notebookを使っていればノートブックに表示もできるし、 htmlを出力して別途使うことができる。
表示するデータ
今回は山手線の駅と東海道新幹線の駅をAさんとBさんが歩いてみたことにする。
とてもこんな速度で歩けるとも思えないが…。
人 | 日時 | 緯度 | 経度 |
---|---|---|---|
A | 2019/2/22 12:00 | 35.619700 | 139.728553 |
A | 2019/2/22 12:30 | 35.626446 | 139.723444 |
A | 2019/2/22 13:00 | 35.626446 | 139.723444 |
A | 2019/2/22 13:30 | 35.633998 | 139.715828 |
A | 2019/2/22 14:00 | 35.633998 | 139.715828 |
… | … | … | … |
B | 2019/2/22 12:00 | 35.681382 | 139.766084 |
B | 2019/2/22 12:30 | 35.655767 | 139.753262 |
B | 2019/2/22 13:00 | 35.655767 | 139.753262 |
B | 2019/2/22 13:30 | 35.630152 | 139.740440 |
B | 2019/2/22 14:00 | 35.630152 | 139.740440 |
… | … | … | … |
こんなデータの持ち方をしているとする。
表示の仕様
2種類やるとする。
一枚に2つの移動を色を変えて表示
ついでに各線をクリックするとポップアップで誰が通ったのかも表示する
一人に一枚の地図を用意し、それをhtmlに保存し、画像にも保存する。
一枚に2つの移動を色を変えて表示
さて、コーディング
# あとで使うライブラリも併せて読み込み import os import folium import pandas as pd from time import sleep as slp from selenium import webdriver import glob from selenium.webdriver.chrome.options import Options # csvをpandas dataframeに保存 data = pd.read_csv("./gps.csv",encoding="shift_jis") # 今回はA,Bの人しか居ないが、 # 一応何人来てもいいように人のリストを作り、 # それをforループで回す person_list = data["人"].unique() # 地図に表示する色 # foliumでサポートしているのは下記19色 color_list=['red','blue','green','purple','orange','darkred','lightred','beige','darkblue','darkgreen','cadetblue','darkpurple','white','pink','lightblue','lightgreen','gray','black','lightgray'] # 地図オブジェクトを作成 m = folium.Map(tiles='OpenStreetMap') for idx,person in enumerate(person_list): # 一人分のデータだけをdata_tempに格納する data_temp = data[data["人"] == person] # data_tempの順番を日時で昇順ソート data_temp = data_temp.sort_values('日時', ascending=True) # data_tempの緯度経度だけを data_temp_lat_lon = data_temp[["緯度","経度"]] # 緯度経度を配列に格納 locs = data_temp_lat_lon.values # 色を指定 line_color = color_list[idx%len(color_list)] # 地図に線を追加する。緯度経度の配列をそのまま線として使う folium.PolyLine(locs,color=line_color,popup=person).add_to(m) # 地図の表示範囲を緯度経度の最低最大とする m.fit_bounds([[data["緯度"].min(),data["経度"].min()], [data["緯度"].max(),data["経度"].max()]]) # 地図を表示する m
そうするといい感じに表示されます。
一人に一枚の地図を用意し、それをhtmlに保存し、画像にも保存する
さて、こちらのほう。
htmlに保存するところまではfolium側でメソッドが用意されています。
m = folium.Map(tiles='OpenStreetMap') m.save("hoge.html")
で保存できるのですが、
そこで表示される画像を保存したい、だとfoliumだけでは対応できません。
そこで、ご存知seleniumを使ってスクショを取って保存を使いました。
さて、コーディング(続き)
for person in person_list: # 一人分のデータだけをdata_tempに格納する data_temp = data[data["人"] == person] # data_tempの順番を日時で昇順ソート data_temp = data_temp.sort_values('日時', ascending=True) # data_tempの緯度経度だけをデータフレームに残す data_temp_lat_lon = data_temp[["緯度","経度"]] # 地図オブジェクトを生成 m = folium.Map(tiles='OpenStreetMap') # 緯度経度を配列に格納 locs = data_temp_lat_lon.values # 地図に線を追加する。緯度経度の配列をそのまま線として使う folium.PolyLine(locs).add_to(m) # 地図の表示範囲を緯度経度の最低最大とする m.fit_bounds([[data_temp["緯度"].min(),data_temp["経度"].min()], [data_temp["緯度"].max(),data_temp["経度"].max()]]) # htmlに保存する # ./html/xx.htmlに保存される m.save(outfile="./html/"+person+".html") # seleniumでブラウザを開く browser = webdriver.Chrome() # 画面を最大化 browser.maximize_window() # URLを指定(ローカルファイル) tmpurl="file:///./html/" + person + ".html" # URLを開く browser.get(tmpurl) # 一応3秒寝かす slp(3) # スクリーンショットを取る browser.save_screenshot("./png/"+person+".png") # ブラウザを閉じる browser.quit()
そうするといい感じに画像を保存してくれる。
ちなみに線ではなくMarkerやCircleで表示したい場合は、
folium.PolyLine(locs).add_to(m)
の部分を
for loc in locs: folium.Circle(loc).add_to(m)
ないし
for loc in locs: folium.Marker(loc).add_to(m)
にするとよろし。
これをまたパワポに自動で貼り付けるというのもやったのだが、それはまた別の話。