異なる種類の入力データを一旦それぞれ別の処理をして,その結果を途中で連結する(concatenateする)ような深層学習モデルって結構見かけます.
私もそのようなモデルを作成しようとしてますので,いろいろ調べた結果,前回のFunctionalモデルの書き方だと実現できそうなことがわかりました.
ちなみに前回はこちら↓
それで,今回も入力画像は同じですが,それぞれ別の画像フィルタで処理をして,その結果を最後に連結するというモデルを構築しました.
プログラムはこちら
from keras import Model
from keras.layers import Flatten, Concatenate, Input, Conv2D
import numpy as np
input_img = Input(shape=(4,3,1))
x = Conv2D(1, (3,3), padding='same')(input_img)
y = Conv2D(1, (3,3), padding='same')(input_img)
output_img = Concatenate(axis=2)([x,y])
model = Model(input_img, output_img)
model.summary()
4×3ピクセルの画像を入力とします.その画像に対して2種類のフィルタリングを並列に実行して,その結果を連結して出力する.ただそれだけです.
Concatenateでaxis=2としてますが,これは横方向に連結することを意味します.縦方向の連結であればaxis=1と設定します.
実行するとモデルのサマリーが表示されます.
conv2dとconv2d_1がそれぞれinput_1とつながっており,concatenateはconv2dとconv2d_1がつながっているように書かれてます.
# フィルタの重みを設定
w = model.layers[1].get_weights() #conv2dの重み
w[0] = np.array([[1,1,1],[1,1,1],[1,1,1]])[:,:,None,None] #フィルタ係数
w[1] = np.array([0]) #バイアス
model.layers[1].set_weights(w)
w = model.layers[2].get_weights() #conv2d_1の重み
w[0] = np.array([[-1,-1,-1],[-1,-1,-1],[-1,-1,-1]])[:,:,None,None]
w[1] = np.array([0])
model.layers[2].set_weights(w)
そして,2つのConv2Dレイヤーのフィルタ係数をそれぞれ設定します.簡単のため,conv2dはすべて1,conv2d_1はすべて-1としました.バイアスはどちらも0です.
test_im = np.ones((4,3))
test_im1 = test_im[None,:,:,None]
入力画像を作成します.動作を理解するため,4×3ピクセルの1チャネルのみという非常に単純な画像としました.
それに対し,画像枚数,チャネル数に相当する次元をNoneで加えてます.つまり,test_im1は(画像枚数,縦幅,横幅,チャネル数)の4次元のテンソルとなります.この場合は,(1,4,3,1)になります.
それでは,実行してみます.
f_img = model.predict(test_im1)
print(f_img.shape)
print(f_img[0,:,:,0])
結果は,こちらのようになりました.
出力画像は (1,4,6,1) のテンソルとなっているので,出力画像の枚数=1, 縦幅=4, 横幅=6, チャネル数=1であり,横幅=6となってるところに注目です.
入力画像は4×3ピクセルのフィルタ後の画像を横方向に連結しているので,4×6ピクセルになっているというわけです.
最後の実行結果をみても,左半分はconv2dのフィルタリングの結果で,右半分はconv2d_1のフィルタリングの結果.これらが連結されて1つの画像になっているのがよくわかります.
畳み込みニューラルネットの場合,この後で2次元から1次元に変形し(Flatten),その後,全結合層(多層パーセプトロン)で識別処理という流れになるのが一般的でしょうね.
さらに自由度の高いモデル記法として,サブクラス化というのもあるようですが,それはまたいつか勉強して記事にします.