こしあん
2019-01-19

Kerasのジェネレーターでサンプルが列挙される順番について

Pocket
LINEで送る
Delicious にシェア

2.6k{icon} {views}



Kerasの(カスタム)ジェネレーターでサンプルがどの順番で呼び出されるか、1ループ終わったあとにどういう処理がなされるのか調べてみました。ジェネレーターを自分で定義するとモデルの表現の幅は広がるものの、バグが起きやすくなるので「本当に順番が保証されるのか」や「ハマりどころ」を確認します。

0~9の数字をループさせるジェネレーター

Kerasのジェネレーターは、無限ループさせてその中で訓練データをループさせるという構造を取ります。

def generator(batch_size):
    X_cache, y_cache = [], []
    while True:
        for i in range(10):
            X_cache.append(i)
            y_cache.append(i)
            if len(X_cache)==batch_size:
                X_batch = np.asarray(X_cache)
                y_batch = np.asarray(y_cache)
                X_cache, y_cache = [], []
                yield X_batch, y_batch

このように、Xとyのキャッシュを用意しておいてどんどん突っ込んで、バッチサイズになったらNumpy配列として返すというのがわかりやすいのではないでしょうか(複数GPUのことは考えていないので、並列化はやらないものとします)。

このコードは「0→1→2…→9→0→1」の順にバッチサイズ分切り出して返すだけのジェネレーターです。

恒等出力モデル

Kerasで「入力=出力となるモデル」を作ります。Lambdaレイヤーを使って、「lambda x: x」のような恒等出力関数を導入します。これによって、predictしたときに入力の値がそのまま取り出せるというわけです。

from keras.layers import Lambda, Input
from keras.models import Model
import numpy as np

input = Input((1,))
x = Lambda(lambda x: x)(input)
model = Model(input, x)

検証

バッチサイズを7(中途半端!)な値にして、predict_generatorさせて結果を見ます。generatorの場合はstepsで回す回数を指定します。この場合は、「0~6」と「7~9+0~3」が出てくれば想定どおりの挙動です。

result = model.predict_generator(generator(7), steps=2, max_queue_size=10)
print(result)
[[0.]
 [1.]
 [2.]
 [3.]
 [4.]
 [5.]
 [6.]
 [7.]
 [8.]
 [9.]
 [0.]
 [1.]
 [2.]
 [3.]]

とりあえず想定どおりの結果が出てきましたね。

max_queue_sizeを変えると?

ちなみにmax_queue_sizeはデフォルトで10ですが、これはバッチサイズを大きくしたときに、RAM(メモリ)を圧迫するので下げたほうがいい場合もあります。この値を変えても同じように順番が保証されるのでしょうか?

result = model.predict_generator(generator(7), steps=2, max_queue_size=1)
print(result)
[[0.]
 [1.]
 [2.]
 [3.]
 [4.]
 [5.]
 [6.]
 [7.]
 [8.]
 [9.]
 [0.]
 [1.]
 [2.]
 [3.]]

とりあえず、max_queue_sizeを変えてもジェネレーターがシーケンシャルである限り順番は保証されます。

ありがちなミス1:ジェネレーターの中で並列化(マルチプロセッシング)をする

「ジェネレーターがシーケンシャルである限り」という条件です。よくありがちな例として、訓練(train)のジェネレーターは、並列化しても問題ありません。なぜなら、ジェネレーターの中では、仮に並列化してもXとyの順番を保証することは容易だからです。並列化する関数でXとyを同時に返せば保証されます。

ここでの並列化というのは、multiprocessingやjoblibによるマルチプロセス化を示します。jpegの読み込みでこのマルチプロセス化をすると結構速くなるので便利です。

しかし、テストのジェネレーターのように、ジェネレーターの外側に正しいラベルデータがあって、ジェネレーターは外側の順番に一致するように返していくようなケースだと、この並列化は大変まずいです。なぜなら、並列化は基本的に順番が保証されないからです(これはPythonに限らずどのだいたいの言語でそうです)。「シーケンシャル=並列化をしない」と考えればOKです。

ありがちなミス2:ジェネレーターのインスタンスを使い回す

ジェネレーターのインスタンスを使い回すと、イテレーションの位置が継続されるので、バグのもとになります。

result1 = model.predict_generator(generator(3), steps=1)
result2 = model.predict_generator(generator(3), steps=1)
print(result1)
print(result2)

このようにeval_generatorやpredict_generatorのタイミングで、ジェネレーターのインスタンスを別に直すとイテレーションの位置がリセットされます。

[[0.]
 [1.]
 [2.]]
[[0.]
 [1.]
 [2.]]

しかし、ジェネレーターのインスタンスを一度宣言して使い回すとイテレーションの位置が継続されます

gen = generator(3)
result1 = model.predict_generator(gen, steps=1, max_queue_size=0)
result2 = model.predict_generator(gen, steps=1, max_queue_size=0)
print(result1)
print(result2)

#[[0.]
# [1.]
# [2.]]
#[[8.]
# [9.]
# [0.]]

また、イテレーションの位置はmax_queue_sizeによるキューの分も入っているので、max_queue_sizeの値を変えたり、または呼び出しのたびにも値は変わってきます。

gen = generator(3)
result1 = model.predict_generator(gen, steps=1, max_queue_size=5)
result2 = model.predict_generator(gen, steps=1, max_queue_size=5)
print(result1)
print(result2)

#[[0.]
# [1.]
# [2.]]
#[[1.]
# [2.]
# [3.]]

非常に面倒ですね。順番気にするときはジェネレーターのインスタンスの使い回しをやめましょう

ありがちなミス3:キャッシュを無限ループの中でリセットする

これはただのケアレスミスですが、このコードを

def generator(batch_size):
    X_cache, y_cache = [], []
    while True:
        for i in range(10):
            X_cache.append(i)
            y_cache.append(i)
            if len(X_cache)==batch_size:
                X_batch = np.asarray(X_cache)
                y_batch = np.asarray(y_cache)
                X_cache, y_cache = [], []
                yield X_batch, y_batch

このように、X_cache, y_cacheの宣言の位置を微妙に変えて、無限ループの内側でやるとまたややこしいことになります。

def generator(batch_size):
    while True:
        X_cache, y_cache = [], []
        for i in range(10):
            X_cache.append(i)
            y_cache.append(i)
            if len(X_cache)==batch_size:
                X_batch = np.asarray(X_cache)
                y_batch = np.asarray(y_cache)
                X_cache, y_cache = [], []
                yield X_batch, y_batch

gen = generator(7)
result = model.predict_generator(gen, steps=2, max_queue_size=1)
print(result)
[[0.]
 [1.]
 [2.]
 [3.]
 [4.]
 [5.]
 [6.]
 [0.]
 [1.]
 [2.]
 [3.]
 [4.]
 [5.]
 [6.]]

7,8,9→「リセット」となってしまうので、7~9が呼び出されることが永遠にありません。ケアレスミスですが気をつけましょう。

まとめ

Kerasのジェネレーター絡みはかなりバグが出やすい。そしてpredictのように順番気にする場合は以下の点に気をつけると良さそうです。

  • 順番気にする(シーケンシャルにする)場合は、絶対に並列化をしない
  • キャッシュをしてバッチに変換するのなら、変数の宣言は無限ループの外側に書く
  • 複数回推論や評価をする場合は、ジェネレーターのインスタンスの使い回しをしない

また、より安全にいくために、以下のようにしてしまうといいと思います。

  • サンプル数がバッチサイズの倍数になるように、ダミーデータや既存のデータ(例えば1番目のデータ)を末尾にコピーして端数が出ないように調整する

こんなところでしょうか。テストデータ数が素数だったりすると地味に発狂したりします(ダミーデータ入れればいいだけですが)。



Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

【新刊】インフィニティNumPy――配列の初期化から、ゲームの戦闘、静止画や動画作成までの221問

「本当の実装力を身につける」ための221本ノック――
機械学習(ML)で避けて通れない数値計算ライブラリ・NumPyを、自在に活用できるようになろう。「できる」ための体系的な理解を目指します。基礎から丁寧に解説し、ディープラーニング(DL)の難しいモデルで遭遇する、NumPyの黒魔術もカバー。初心者から経験者・上級者まで楽しめる一冊です。問題を解き終わったとき、MLやDLなどの発展分野にスムーズに入っていけるでしょう。

本書の大きな特徴として、Pythonの本でありがちな「NumPyとML・DLの結合を外した」点があります。NumPyを理解するのに、MLまで理解するのは負担が大きいです。本書ではあえてこれらの内容を書いていません。行列やテンソルの理解に役立つ「従来の画像処理」をNumPyベースで深く解説・実装していきます。

しかし、問題の多くは、DLの実装で頻出の関数・処理を重点的に取り上げています。経験者なら思わず「あー」となるでしょう。関数丸暗記では自分で実装できません。「覚える関数は最小限、できる内容は無限大」の世界をぜひ体験してみてください。画像編集ソフトの処理をNumPyベースで実装する楽しさがわかるでしょう。※紙の本は電子版の特典つき

モザイク除去から学ぶ 最先端のディープラーニング

「誰もが夢見るモザイク除去」を起点として、機械学習・ディープラーニングの基本をはじめ、GAN(敵対的生成ネットワーク)の基本や発展型、ICCV, CVPR, ECCVといった国際学会の最新論文をカバーしていく本です。
ディープラーニングの研究は発展が目覚ましく、特にGANの発展型は市販の本でほとんどカバーされていない内容です。英語の原著論文を著者がコードに落とし込み、実装を踏まえながら丁寧に解説していきます。
また、本コードは全てTensorFlow2.0(Keras)に対応し、Googleの開発した新しい機械学習向け計算デバイス・TPU(Tensor Processing Unit)をフル活用しています。Google Colaboratoryを用いた環境構築不要の演習問題もあるため、読者自ら手を動かしながら理解を深めていくことができます。

AI、機械学習、ディープラーニングの最新事情、奥深いGANの世界を知りたい方にとってぜひ手にとっていただきたい一冊となっています。持ち運びに便利な電子書籍のDLコードが付属しています。

「おもしろ同人誌バザールオンライン」で紹介されました!(14:03~) https://youtu.be/gaXkTj7T79Y?t=843

まとめURL:https://github.com/koshian2/MosaicDeeplearningBook
A4 全195ページ、カラー12ページ / 2020年3月発行

Shikoan's ML Blog -Vol.1/2-

累計100万PV超の人気ブログが待望の電子化! このブログが電子書籍になって読みやすくなりました!

・1章完結のオムニバス形式
・機械学習の基本からマニアックなネタまで
・どこから読んでもOK
・何巻から読んでもOK

・短いものは2ページ、長いものは20ページ超のものも…
・通勤・通学の短い時間でもすぐ読める!
・読むのに便利な「しおり」機能つき

・全巻はA5サイズでたっぷりの「200ページオーバー」
・1冊にたっぷり30本収録。1本あたり18.3円の圧倒的コストパフォーマンス!
・文庫本感覚でお楽しみください

北海道の駅巡りコーナー

日高本線 車なし全駅巡り

ローカル線や秘境駅、マニアックな駅に興味のある方におすすめ! 2021年に大半区間が廃線になる、北海道の日高本線の全区間・全29駅(苫小牧~様似)を記録した本です。マイカーを使わずに、公共交通機関(バス)と徒歩のみで全駅訪問を行いました。日高本線が延伸する計画のあった、襟裳岬まで様似から足を伸ばしています。代行バスと路線バスの織り成す極限の時刻表ゲームと、絶海の太平洋と馬に囲まれた日高路、日高の隠れたグルメを是非たっぷり堪能してください。A4・フルカラー・192ページのたっぷりのボリュームで、あなたも旅行気分を漫喫できること待ったなし!

見どころ:日高本線被災区間(大狩部、慶能舞川橋梁、清畠~豊郷) / 牧場に囲まれた絵笛駅 / 窓口のあっただるま駅・荻伏駅 / 汐見の戦争遺跡のトーチカ / 新冠温泉、三石温泉 / 襟裳岬

A4 全192ページフルカラー / 2020年11月発行


Pocket
LINEで送る
Delicious にシェア

Add a Comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です