こしあん
2018-11-11

Kerasで重みを共有しつつ、必要に応じて入力の位置を変える方法

Pocket
LINEで送る
Delicious にシェア

4.1k{icon} {views}



Kerasで訓練させて、途中から新しく入力を作ってそこからの出力までの値を取りたいということがたまにあります。例えば、Variational Auto Encoderのサンプリングなんかそうです。このあまり書かれていないのでざっとですが整理しておきます。

こういうことをやりたい

言葉で書いても伝わりづらいのでこういう例です。

「必要に応じて入力の位置を変える」とはこういうことです。訓練する際は「入力Aから出力まで」を訓練させますが、ある場面では途中の「入力Bから出力まで」を取りたいというような状況です。

別々にモデルを作ってしまうと訓練したレイヤー2の重みが共有されないので、ここは共有レイヤーとして定義する必要があります。

共有レイヤー

重みを共有したい場合は「共有レイヤー」として定義するのが基本です。Kerasの公式にも説明があります。

https://keras.io/ja/getting-started/functional-api-guide/

Functional APIを使います。例えば普段Denseだったら、

from keras.layers import Dense, Input

input = Input((784,))
x = Dense(10)(input)

のように「関数名(引数)(変数)」のように書きますが、最後の(変数)の部分だけ取っ払って、レイヤーのオブジェクトとして保持しておく書き方です。つまり、

dense_layer = Dense(10)

x1 = dense_layer(input_1)
x2 = dense_layer(input_2)

こうです。Keras以外のPythonでも、例えば関数のオブジェクトを変数に代入しておいて、その変数に引数を与えて動的に関数を呼び出すというようなテクニックがあるので、この記法自体は特に珍しいものでもありません。

実装例

重みとか思いっきり無視していますが、例えばこのように実装してみました。

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

# 乱数を発生させる関数
def create_rand(inputs):
    return K.random_normal(shape=(10,10))

# 10倍させる関数
def multiple_ten(inputs):
    return inputs * 10

# 2つのモデルを作る
def create_models():
    # 1つは乱数→10倍、もう1つは10倍だけさせる。レイヤーの重み(今はないけど)は共有する
    # 共有する10倍のレイヤー
    layer10 = Lambda(multiple_ten)

    # 1つ目のモデル
    input_a = Input((1,)) # ダミーインプット
    xa = Lambda(create_rand)(input_a)
    xa = layer10(xa)
    model_a = Model(input_a, xa)

    # 2つ目のモデル
    input_b = Input((10,))
    xb = layer10(input_b)
    model_b = Model(input_b, xb)

    return model_a, model_b

if __name__ == "__main__":
    ma, mb = create_models()
    # まずはモデルAで乱数を発生させて10倍
    X_dummy = np.zeros((10,1), dtype=np.float32) # ダミー入力
    ya = ma.predict(X_dummy)
    print("Model A")
    print(ya)
    # 次にモデルBでただ10倍させる
    Xb = np.arange(100).reshape(10,10).astype(np.float32) # これはダミーではない
    yb = mb.predict(Xb)
    print("Model B")
    print(yb)

1つ目のレイヤーは乱数を発生させるレイヤー、2つ目のレイヤーは入力をただ10倍させるレイヤー。2つ目を共有レイヤーとして定義します。実行結果は次の通り。

Model A
[[-14.171424    -2.784944    -0.97046345  -4.6345415   -9.60215
    1.8610847    1.0379182   -5.4268694  -15.079054   -31.015457  ]
 [-10.067506    15.017413    -8.313494     5.030054    -3.2046678
    7.5128846   -4.1036777  -12.066201     1.1947591    1.7783409 ]
 [ -3.242796     0.5635674   10.531197    -1.3924004   -2.7359848
    6.5062428    0.72998834  -7.281403     6.821098     0.26847383]
 [  3.928729    -0.22778808  13.042159    -3.9730072   12.684418
   11.40539    -12.954694    -3.2590175   -5.90364     -0.8891677 ]
 [  6.5432982   -2.4574468    0.573207    -5.8621364    4.8784103
   15.820572   -13.944576     8.554482     9.679798    -3.3461163 ]
 [  0.06409714  -4.345841   -11.098437     9.078588    13.999712
   -1.8768744   -8.40677      0.6909147  -12.422537   -13.587     ]
 [  9.191393    -7.7897334  -16.26502     -8.654823     1.434179
   12.283398     4.5111756   13.538376     8.84922      8.5673275 ]
 [  1.0559345  -16.302864    -0.8685695   -1.8529015   -3.3979917
   -2.737006    10.955934    -5.886425   -12.419347    -2.1857073 ]
 [ -9.781853    -2.9850006    2.6867461   -5.804907    11.225485
   19.90472    -12.013854   -12.737608     5.637822    11.66047   ]
 [  6.5952387   11.908323     4.9963694    2.6850624   -8.195278
   -3.7208378   -1.9654443    7.847238    -0.08818804  -6.2225204 ]]
Model B
[[  0.  10.  20.  30.  40.  50.  60.  70.  80.  90.]
 [100. 110. 120. 130. 140. 150. 160. 170. 180. 190.]
 [200. 210. 220. 230. 240. 250. 260. 270. 280. 290.]
 [300. 310. 320. 330. 340. 350. 360. 370. 380. 390.]
 [400. 410. 420. 430. 440. 450. 460. 470. 480. 490.]
 [500. 510. 520. 530. 540. 550. 560. 570. 580. 590.]
 [600. 610. 620. 630. 640. 650. 660. 670. 680. 690.]
 [700. 710. 720. 730. 740. 750. 760. 770. 780. 790.]
 [800. 810. 820. 830. 840. 850. 860. 870. 880. 890.]
 [900. 910. 920. 930. 940. 950. 960. 970. 980. 990.]]

モデルAでは入力を無視してただレイヤー内で発生させた乱数を返しているだけです。これがレイヤー2で10倍されて返ってきます。

モデルBでは乱数発生レイヤーはないので、ただ入力が10倍されて返ってきます。ここでの入力は0~99までの数なので、結果は10倍された0~990となっています。

これでよいのではないでしょうか。

ちょっとモデルが大きくなったら共通部分をモデルにしてしまう

共有レイヤーだとモデルが大きいときに困ります。そういうときは、共通の部分を1つのモデルにしてしまえばいいのではないでしょうか。

# 2つのモデルを作る
def create_models():
    # 1つは乱数→10倍、もう1つは10倍だけさせる。レイヤーの重み(今はないけど)は共有する
    # 共有する10倍のレイヤーのモデル
    input_common = Input((10,))
    xcommon = Lambda(multiple_ten)(input_common)
    model_common = Model(input_common, xcommon)

    # 1つ目のモデル
    input_a = Input((1,)) # ダミーインプット
    xa = Lambda(create_rand)(input_a)
    xa = model_common(xa)
    model_a = Model(input_a, xa)

    # 2つ目のモデル
    input_b = Input((10,))
    xb = model_common(input_b)
    model_b = Model(input_b, xb)

    return model_a, model_b

これでも一応動きます。2つ目のモデルでInputが2重になっているように見えますが、Inputのプレースホルダを定義しないとModelが作れないのでこうしています。この例のように同一のモデルを返してやればいいケースでは、2つ目のモデルを作らずに「return model_a, model_common」でもいいですね。

まとめ

以上です。まず重みを共有させるには、レイヤーのオブジェクトを変数にして「共有レイヤー」として定義する。そして入力の位置を変えるには、入力の位置ごとにInputを定義する。モデルが大きくなったらModelでカプセル化する。こんなところでしょうか。



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円の圧倒的コストパフォーマンス!
・文庫本感覚でお楽しみください

Vol.1 電子550円
Vol.2 電子550円

北海道の駅巡りコーナー

日高本線 車なし全駅巡り

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

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

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


Pocket
LINEで送る
Delicious にシェア

Add a Comment

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