Keras 拼接兩個網絡模型-拼接【轉載】

感謝原文作者:木盞

 

神經網絡玩得越久就越會嘗試一些網絡結構上的大改動。


先說意圖


有兩個模型:模型A和模型B。模型A的輸出可以連接B的輸入。將兩個小模型連接成一個大模型,A-B,既可以同時訓練又可以分離訓練。

流行的算法裏經常有這麼關係的兩個模型,對GAN來說,生成器和判別器就是這樣子;對VAE來說,編碼器和解碼器就是這樣子;對目標檢測網絡來說,backbone和整體也是可以拆分的。所以,應用範圍還是挺廣的。


實現方法


首先說明,我的實現方法不一定是最佳方法。也是實在沒有借鑑到比較好的方法,所以才自己手動寫了一個。

第一步,我們有現成的兩個模型A和B;我們想把A的輸出連到B的輸入,組成一個整體C。

第二步, 重構新模型C;我的方法是:讀出A和B各有哪些layer,然後一層一層重新搭成C。

可以看一個自編碼器的代碼(木盞本人所編寫):

class AE:
    def __init__(self, dim, img_dim, batch_size):
        self.dim = dim
        self.img_dim = img_dim
        self.batch_size = batch_size
        self.encoder = self.encoder_construct()
        self.decoder = self.decoder_construct()
 
    def encoder_construct(self):
        x_in = Input(shape=(self.img_dim, self.img_dim, 3))
        x = x_in
        x = Conv2D(self.dim // 16, kernel_size=(5, 5), strides=(2, 2), padding='SAME')(x)
        x = BatchNormalization()(x)
        x = LeakyReLU(0.2)(x)
        x = Conv2D(self.dim // 8, kernel_size=(5, 5), strides=(2, 2), padding='SAME')(x)
        x = BatchNormalization()(x)
        x = LeakyReLU(0.2)(x)
        x = Conv2D(self.dim // 4, kernel_size=(5, 5), strides=(2, 2), padding='SAME')(x)
        x = BatchNormalization()(x)
        x = LeakyReLU(0.2)(x)
        x = Conv2D(self.dim // 2, kernel_size=(5, 5), strides=(2, 2), padding='SAME')(x)
        x = BatchNormalization()(x)
        x = LeakyReLU(0.2)(x)
        x = Conv2D(self.dim, kernel_size=(5, 5), strides=(2, 2), padding='SAME')(x)
        x = BatchNormalization()(x)
        x = LeakyReLU(0.2)(x)
        x = GlobalAveragePooling2D()(x)
        encoder = Model(x_in, x)
        return encoder
 
    def decoder_construct(self):
        map_size = K.int_shape(self.encoder.layers[-2].output)[1:-1]
        # print(type(map_size))
        z_in = Input(shape=K.int_shape(self.encoder.output)[1:])
        z = z_in
        z_dim = self.dim
        z = Dense(np.prod(map_size) * z_dim)(z)
        z = Reshape(map_size + (z_dim,))(z)
        z = Conv2DTranspose(z_dim // 2, kernel_size=(5, 5), strides=(2, 2), padding='SAME')(z)
        z = BatchNormalization()(z)
        z = Activation('relu')(z)
        z = Conv2DTranspose(z_dim // 4, kernel_size=(5, 5), strides=(2, 2), padding='SAME')(z)
        z = BatchNormalization()(z)
        z = Activation('relu')(z)
        z = Conv2DTranspose(z_dim // 8, kernel_size=(5, 5), strides=(2, 2), padding='SAME')(z)
        z = BatchNormalization()(z)
        z = Activation('relu')(z)
        z = Conv2DTranspose(z_dim // 16, kernel_size=(5, 5), strides=(2, 2), padding='SAME')(z)
        z = BatchNormalization()(z)
        z = Activation('relu')(z)
        z = Conv2DTranspose(3, kernel_size=(5, 5), strides=(2, 2), padding='SAME')(z)
        z = Activation('tanh')(z)
        decoder = Model(z_in, z)
        return decoder
 
    def build_ae(self):
        input_x = Input(shape=(self.img_dim, self.img_dim, 3))
        x = input_x
        for i in range(1, len(self.encoder.layers)):
            x = self.encoder.layers[i](x)
        for j in range(1, len(self.decoder.layers)):
            x = self.decoder.layers[j](x)
        y = x
        auto_encoder = Model(input_x, y)
        return auto_encoder

模型A就是這裏的encoder,模型B就是這裏的decoder。所以,連接的精髓在build_ae()函數,直接用for循環讀出各層,然後一層一層重新構造新的模型,從而實現連接效果。因爲keras也是基於圖的框架,這個操作並不會很費時,因爲沒有實際地計算。

僅供參考。


問題和思考

  1.  這是網絡結構上的拼接,如何實現權重的load,如finetune的方法實現。
  2. 這種拆分式然後重組不具備通用性,有沒有一種方法可以把model 當 layer 來使用, 這樣就直接可以像layer那樣嵌套了。


 

 
發佈了14 篇原創文章 · 獲贊 4 · 訪問量 8515
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章