1. 程式人生 > 程式設計 >使用keras實現孿生網路中的權值共享教程

使用keras實現孿生網路中的權值共享教程

首先宣告,這裡的權值共享指的不是CNN原理中的共享權值,而是如何在構建類似於Siamese Network這樣的多分支網路,且分支結構相同時,如何使用keras使分支的權重共享。

Functional API

為達到上述的目的,建議使用keras中的Functional API,當然Sequential 型別的模型也可以使用,本篇部落格將主要以Functional API為例講述。

keras的多分支權值共享功能實現,官方文件介紹

上面是官方的連結,本篇部落格也是基於上述官方文件,實現的此功能。(插一句,keras雖然有中文文件,但中文文件已停更,且中文文件某些函式介紹不全,建議直接看英文官方文件)

不共享引數的模型

以MatchNet網路結構為例子,為方便顯示,將卷積模組個數減為2個。首先是展示不共享引數的模型,以便觀看完整的網路結構。

整體的網路結構如下所示:

程式碼包含兩部分,第一部分定義了兩個函式,FeatureNetwork()生成特徵提取網路,ClassiFilerNet()生成決策網路或稱度量網路。網路結構的視覺化在部落格末尾。在ClassiFilerNet()函式中,可以看到呼叫了兩次FeatureNetwork()函式,keras.models.Model也被使用的兩次,因此生成的input1和input2是兩個完全獨立的模型分支,引數是不共享的。

from keras.models import Sequential
from keras.layers import merge,Conv2D,MaxPool2D,Activation,Dense,concatenate,Flatten
from keras.layers import Input
from keras.models import Model
from keras.utils import np_utils
import tensorflow as tf
import keras
from keras.datasets import mnist
import numpy as np
from keras.utils import np_utils
from keras.callbacks import EarlyStopping,ModelCheckpoint,TensorBoard,ReduceLROnPlateau
from keras.utils.vis_utils import plot_model

# ---------------------函式功能區-------------------------
def FeatureNetwork():
  """生成特徵提取網路"""
  """這是根據,MNIST資料調整的網路結構,下面註釋掉的部分是,原始的Matchnet網路中feature network結構"""
  inp = Input(shape = (28,28,1),name='FeatureNet_ImageInput')
  models = Conv2D(filters=24,kernel_size=(3,3),strides=1,padding='same')(inp)
  models = Activation('relu')(models)
  models = MaxPool2D(pool_size=(3,3))(models)

  models = Conv2D(filters=64,padding='same')(models)
  # models = MaxPool2D(pool_size=(3,strides=(2,2))(models)
  models = Activation('relu')(models)

  models = Conv2D(filters=96,padding='valid')(models)
  models = Activation('relu')(models)

  models = Conv2D(filters=96,padding='valid')(models)
  models = Activation('relu')(models)
  models = Flatten()(models)
  models = Dense(512)(models)
  models = Activation('relu')(models)
  model = Model(inputs=inp,outputs=models)
  return model

def ClassiFilerNet(): # add classifier Net
  """生成度量網路和決策網路,其實maychnet是兩個網路結構,一個是特徵提取層(孿生),一個度量層+匹配層(統稱為決策層)"""
  input1 = FeatureNetwork()           # 孿生網路中的一個特徵提取
  input2 = FeatureNetwork()           # 孿生網路中的另一個特徵提取
  for layer in input2.layers:          # 這個for迴圈一定要加,否則網路重名會出錯。
    layer.name = layer.name + str("_2")
  inp1 = input1.input
  inp2 = input2.input
  merge_layers = concatenate([input1.output,input2.output])    # 進行融合,使用的是預設的sum,即簡單的相加
  fc1 = Dense(1024,activation='relu')(merge_layers)
  fc2 = Dense(1024,activation='relu')(fc1)
  fc3 = Dense(2,activation='softmax')(fc2)

  class_models = Model(inputs=[inp1,inp2],outputs=[fc3])
  return class_models

# ---------------------主調區-------------------------
matchnet = ClassiFilerNet()
matchnet.summary() # 列印網路結構
plot_model(matchnet,to_file='G:/csdn攻略/picture/model.png') # 網路結構輸出成png圖片

共享引數的模型

FeatureNetwork()的功能和上面的功能相同,為方便選擇,在ClassiFilerNet()函式中加入了判斷是否使用共享引數模型功能,令reuse=True,便使用的是共享引數的模型。

關鍵地方就在,只使用的一次Model,也就是說只建立了一次模型,雖然輸入了兩個輸入,但其實使用的是同一個模型,因此權重共享的。

from keras.models import Sequential
from keras.layers import merge,ReduceLROnPlateau
from keras.utils.vis_utils import plot_model

# ----------------函式功能區-----------------------
def FeatureNetwork():
  """生成特徵提取網路"""
  """這是根據,MNIST資料調整的網路結構,下面註釋掉的部分是,原始的Matchnet網路中feature network結構"""
  inp = Input(shape = (28,padding='valid')(models)
  models = Activation('relu')(models)

  # models = Conv2D(64,strides=2,padding='valid')(models)
  # models = Activation('relu')(models)
  # models = MaxPool2D(pool_size=(3,2))(models)
  models = Flatten()(models)
  models = Dense(512)(models)
  models = Activation('relu')(models)
  model = Model(inputs=inp,outputs=models)
  return model

def ClassiFilerNet(reuse=False): # add classifier Net
  """生成度量網路和決策網路,其實maychnet是兩個網路結構,一個是特徵提取層(孿生),一個度量層+匹配層(統稱為決策層)"""

  if reuse:
    inp = Input(shape=(28,name='FeatureNet_ImageInput')
    models = Conv2D(filters=24,padding='same')(inp)
    models = Activation('relu')(models)
    models = MaxPool2D(pool_size=(3,3))(models)

    models = Conv2D(filters=64,padding='same')(models)
    # models = MaxPool2D(pool_size=(3,2))(models)
    models = Activation('relu')(models)

    models = Conv2D(filters=96,padding='valid')(models)
    models = Activation('relu')(models)

    models = Conv2D(filters=96,padding='valid')(models)
    models = Activation('relu')(models)

    # models = Conv2D(64,padding='valid')(models)
    # models = Activation('relu')(models)
    # models = MaxPool2D(pool_size=(3,2))(models)
    models = Flatten()(models)
    models = Dense(512)(models)
    models = Activation('relu')(models)
    model = Model(inputs=inp,outputs=models)

    inp1 = Input(shape=(28,1)) # 建立輸入
    inp2 = Input(shape=(28,1)) # 建立輸入2
    model_1 = model(inp1) # 孿生網路中的一個特徵提取分支
    model_2 = model(inp2) # 孿生網路中的另一個特徵提取分支
    merge_layers = concatenate([model_1,model_2]) # 進行融合,使用的是預設的sum,即簡單的相加

  else:
    input1 = FeatureNetwork()           # 孿生網路中的一個特徵提取
    input2 = FeatureNetwork()           # 孿生網路中的另一個特徵提取
    for layer in input2.layers:          # 這個for迴圈一定要加,否則網路重名會出錯。
      layer.name = layer.name + str("_2")
    inp1 = input1.input
    inp2 = input2.input
    merge_layers = concatenate([input1.output,outputs=[fc3])
  return class_models

如何看是否真的是權值共享呢?直接對比特徵提取部分的網路引數個數!

不共享引數模型的引數數量:

使用keras實現孿生網路中的權值共享教程

共享引數模型的引數總量

使用keras實現孿生網路中的權值共享教程

共享引數模型中的特徵提取部分的引數量為:

使用keras實現孿生網路中的權值共享教程

由於截圖限制,不共享引數模型的特徵提取網路引數數量不再展示。其實經過計算,特徵提取網路部分的引數數量,不共享引數模型是共享引數的兩倍。兩個網路總引數量的差值就是,共享模型中,特徵提取部分的引數的量

網路結構視覺化

不共享權重的網路結構

使用keras實現孿生網路中的權值共享教程

共享引數的網路結構,其中model_1代表的就是特徵提取部分。

使用keras實現孿生網路中的權值共享教程

以上這篇使用keras實現孿生網路中的權值共享教程就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。