【DeepLearning入門】１０個の学習済モデルを使って同時推論を実施してみた♪

今後、学習済モデルを組み合わせて、いろいろなサービスを提供する場面が出てくると思います。
さらに、ここでは詳細を記述しませんが、意識などを持てるような構造をもったDL群を構成しようとすると、多くの学習済モデルを自由に使えることが必須なコーディングスキルになるでしょう。
ということで、今回は一番簡単なものとして、複数の学習済モデルを使って複数の推論を簡単に実施する方法を示したいと思います。
###やったこと
・学習済モデルの定義
・画像出力
・キー制御
###・学習済モデルの定義
まず、decoder-model本体の定義は以下のように関数定義とします。

```py
input_shape = (28, 28, 1)
latent_dim = 2
def model(latent_dim, name='decoder1'):
    # build decoder model
    latent_inputs = Input(shape=(latent_dim,), name='z_sampling')
    x = Dense(7 * 7 * 64, activation='relu')(latent_inputs)
    x = Reshape((7, 7, 64))(x)
    x = Conv2DTranspose(64, (3, 3), activation='relu', strides=2, padding='same')(x)
    x = Conv2DTranspose(32, (3, 3), activation='relu', strides=2, padding='same')(x)
    outputs = Conv2DTranspose(1, (3, 3), activation='sigmoid', padding='same')(x)
    return Model(latent_inputs, outputs, name=name)
```
しかし、例えば１０個の推論を同時にやろうとすると、これだけだとモデル定義は以下のようになり、とても煩雑です。

```py
#instantiate decoder model
decoder0 = model(latent_dim, name='decoder0')
...
decoder9 = model(latent_dim, name='decoder9')
decoder1.summary()
decoder0.load_weights('./mnist1000/0/decoder_mnist_weightsL2_20.h5')
...
decoder9.load_weights('./mnist1000/9/decoder_mnist_weightsL2_20.h5')
```
これを変数名を自動的に変更してどうにかしようと考えるのは参考①にもあるように、だめなようです。
解消するには、いわゆるlistを使うと見やすく簡単になりました。
【参考】
①[Python：変数名を動的に変えたい](https://teratail.com/questions/163087)
②[Python配列のループ処理](https://qiita.com/motoki1990/items/d06fc7559546a8471392)

```py
decoder=("decoder0","decoder1","decoder2","decoder3","decoder4","decoder5","decoder6","decoder7","decoder8","decoder9")
items=[]
for index, item in enumerate(decoder):
    item_model = model(latent_dim, name=item)
    item_model.load_weights('./mnist1000/'+str(index)+'/decoder_mnist_weightsL2_20.h5')
    items.append(item_model)
items[0].summary()
```
###・画像出力
以下では、２次元の潜在空間の座標を指定して、そこから生成される１０枚の画像（ここでは０－９の数字）を出力します。
これも素直（何も考えず）に書くとすごいことになります。
※端折っても長い

```py
def plot_results(z0,z1,s):
    z_sample=np.array([[np.array(z0),np.array(z1)]])
    x_decoded0 = decoder0.predict(z_sample)    
    ...
    x_decoded9 = decoder9.predict(z_sample)        
    fig=plt.figure()
    ax0=fig.add_subplot(2,5,1)    
    ...
    ax9=fig.add_subplot(2,5,10)    
    size=(280,280)
    imgs0=x_decoded0.reshape(28, 28)
    ...
    imgs9=x_decoded9.reshape(28, 28)
    imgs0=cv2.resize(imgs0, size,interpolation = cv2.INTER_CUBIC)    
    ...
    imgs9=cv2.resize(imgs9, size,interpolation = cv2.INTER_CUBIC)
    cv2.imshow("z_sample=[{0:6.3f},{1:6.3f}]".format(z0,z1),imgs0)
    ...
    cv2.imshow("z_sample=[{0:6.3f},{1:6.3f}]".format(z0,z1),imgs9)
    ax1.set_title("z_sample=[{0:6.3f},{1:6.3f}]".format(z0,z1))
    ax0.imshow(imgs0)
    ...
    ax9.imshow(imgs9)    
    plt.savefig("./mnist1000/3/3_1_[{0:6.3f},{1:6.3f}].png".format(z0,z1))
```
端折らなくても以下のように短くなりました。

```py
def plot_results(z0,z1,s):
    z_sample=np.array([[np.array(z0),np.array(z1)]])
    x_decodeds=[]
    for index, item1 in enumerate(items):  
        x_decoded = item1.predict(z_sample)
        x_decodeds.append(x_decoded)

    fig=plt.figure()
    size=(280,280)
    for i in range(0,10,1):
        ax=fig.add_subplot(2,5,i+1)
        img=x_decodeds[i].reshape(28, 28)
        img=cv2.resize(img, size,interpolation = cv2.INTER_CUBIC)    
        cv2.imshow("z_sample=[{0:6.3f},{1:6.3f}]".format(z0,z1),img)
        ax.imshow(img)
    plt.savefig("./mnist1000/0/z/3_1_{}.png".format(s))  #z0,z1
    #plt.savefig("./mnist1000/0/z/3_1_[{0:6.3f},{1:6.3f}].png".format(z0,z1))  #z0,z1
```
###・キー制御
ここは特別な工夫はありません。
以下では、潜在空間の中をキー入力に応じて上下左右に動かしてそれに応じて上記の画像出力を利用して１０個の画像を出力しています。
※因みに潜在空間のどのあたりに今いるかを示すために、バックグラウンドに予め作成した画像を表示しています。（点の座標がある程度正しく表示するように表示位置を調整しています）
※画像保存は潜在空間の座標をつけるか、通し番号にするかにしています。
　通し番号はGifアニメを作成するために使いました。

```py
z0=0
z1=0
s=0
while True:
    img=cv2.imread("./mnist1000/"+str(0)+"/digits_over_latent100L2_3.png")
    cv2.circle(img,(int(300+80*(z0)),int(650-70*(z1))), 10, (0,255,255), -1)
    plot_results(z0,z1,s)
    cv2.imshow('image',img)
    k = cv2.waitKey(0)
    if k == 27:         # wait for ESC key to exit
        cv2.imwrite('./mnist1000/0/z/z_{}.png'.format(s),img) #z0,z1
        #cv2.imwrite('./mnist1000/0/z/z[{0:6.3f},{1:6.3f}].png'.format(z0,z1),img)
        cv2.destroyAllWindows()
        break
    elif k == ord('j'): # 
        cv2.imwrite('./mnist1000/0/z/z_{}.png'.format(s),img) #z0,z1
        #cv2.imwrite('./mnist1000/0/z/z[{0:6.3f},{1:6.3f}].png'.format(z0,z1),img) #z0,z1
        z0=z0+1
    elif k == ord('h'): # 
        cv2.imwrite('./mnist1000/0/z/z_{}.png'.format(s),img)
        #cv2.imwrite('./mnist1000/0/z/z[{0:6.3f},{1:6.3f}].png'.format(z0,z1),img)
        z0=z0-1
    elif k == ord('u'): # 
        cv2.imwrite('./mnist1000/0/z/z_{}.png'.format(s),img)
        #cv2.imwrite('./mnist1000/0/z/z[{0:6.3f},{1:6.3f}].png'.format(z0,z1),img)
        z1=z1+1
    elif k == ord('n'): # 
        cv2.imwrite('./mnist1000/0/z/z_{}.png'.format(s),img)
        #cv2.imwrite('./mnist1000/0/z/z[{0:6.3f},{1:6.3f}].png'.format(z0,z1),img)
        z1=z1-1
    s+=1
```
###結果
| 潜在空間 | 10 decoder_outputs |
|:-:|:-:|
|![0_z.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/337039b4-ab39-681c-b39f-547860f94577.gif)| ![0_3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/8f921148-b675-30bd-9fb3-1515af5fa337.gif)|   
###まとめ
・複数の学習済モデルを使って同時に複数の推論を実施した
・listなどを駆使すると簡単に記載できることが分かった
・MNISTを個別に学習したモデルで１０文字を同じ潜在空間の同じ座標から同時出力した
・潜在空間の中をキー制御することにより動かせた

・複数モデルを使ってもっと自由度の高いアプリを作ろう
・学習済なencoders-latentSpaces-decodersの複数なパスを利用する