In the 喀拉斯文档 https://keras.io/getting_started/faq/,它说如果我们想选择中间层模型的输出(顺序和功能),我们需要做的如下:
model = ... # create the original model
layer_name = 'my_layer'
intermediate_layer_model = keras.Model(inputs=model.input,
outputs=model.get_layer(layer_name).output)
intermediate_output = intermediate_layer_model(data)
所以,这里我们有两个模型,intermediate_layer_model
是其父模型的子模型。而且他们也是独立的。同样,如果我们得到中间层的输出特征图父模型(或基础模型)的,以及做一些操作有了它并从这个操作中得到一些输出特征图,然后我们也可以估算这个输出特征图返回到父模型。
input = tf.keras.Input(shape=(size,size,3))
model = tf.keras.applications.DenseNet121(input_tensor = input)
layer_name = "conv1_block1" # for example
output_feat_maps = SomeOperationLayer()(model.get_layer(layer_name).output)
# assume, they're able to add up
base = Add()([model.output, output_feat_maps])
# bind all
imputed_model = tf.keras.Model(inputs=[model.input], outputs=base)
这样,我们就有了一个修改后的模型。使用函数式 API 非常容易。一切keras
imagenet 模型(大部分)是用函数式 API 编写的。在模型子类化API中,我们可以使用这些模型。我这里关心的是,如果我们需要这些功能性API模型内部的中间输出特征图怎么办call
功能。
class Subclass(tf.keras.Model):
def __init__(self, dim):
super(Subclass, self).__init__()
self.dim = dim
self.base = DenseNet121(input_shape=self.dim)
# building new model with the desired output layer of base model
self.mid_layer_model = tf.keras.Model(self.base.inputs,
self.base.get_layer(layer_name).output)
def call(self, inputs):
# forward with base model
x = self.base(inputs)
# forward with mid_layer_model
mid_feat = self.mid_layer_model(inputs)
# do some op with it
mid_x = SomeOperationLayer()(mid_feat)
# assume, they're able to add up
out = tf.keras.layers.add([x, mid_x])
return out
问题是,我们在技术上两种型号以联合的方式。但与构建这样的模型不同,这里我们只需要基本模型前向方式的中间输出特征图(来自某些输入)并在其他地方使用它并获得一些输出。像这样
mid_x = SomeOperationLayer()(self.base.get_layer(layer_name).output)
但它给了ValueError: Graph disconnected
。因此,目前,我们必须根据我们想要的中间层从基础模型构建一个新模型。在里面init
我们定义或创建新的方法self.mid_layer_model
模型给出了我们想要的输出特征图,如下所示:mid_feat = self.mid_layer_model(inputs)
。接下来,我们采取mid_faet
并进行一些操作并获得一些输出,最后将它们添加tf.keras.layers.add([x, mid_x])
。因此,通过创建具有所需中间输出的新模型,但同时,我们重复相同的操作两次,即基本模型及其子集模型。也许我遗漏了一些明显的东西,请添加一些东西。是这样吗!或者我们可以采取一些策略。我在论坛问过here https://github.com/tensorflow/tensorflow/issues/47544https://github.com/tensorflow/tensorflow/issues/47544,还没有回复。
Update
这是一个工作示例。假设我们有一个像这样的自定义层
import tensorflow as tf
from tensorflow.keras.applications import DenseNet121
from tensorflow.keras.layers import Add
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Flatten
class ConvBlock(tf.keras.layers.Layer):
def __init__(self, kernel_num=32, kernel_size=(3,3), strides=(1,1), padding='same'):
super(ConvBlock, self).__init__()
# conv layer
self.conv = tf.keras.layers.Conv2D(kernel_num,
kernel_size=kernel_size,
strides=strides, padding=padding)
# batch norm layer
self.bn = tf.keras.layers.BatchNormalization()
def call(self, input_tensor, training=False):
x = self.conv(input_tensor)
x = self.bn(x, training=training)
return tf.nn.relu(x)
我们想要将这一层归咎于 ImageNet 模型并构建一个像这样的模型
input = tf.keras.Input(shape=(32, 32, 3))
base = DenseNet121(weights=None, input_tensor = input)
# get output feature maps of at certain layer, ie. conv2_block1_0_relu
cb = ConvBlock()(base.get_layer("conv2_block1_0_relu").output)
flat = Flatten()(cb)
dense = Dense(1000)(flat)
# adding up
adding = Add()([base.output, dense])
model = tf.keras.Model(inputs=[base.input], outputs=adding)
from tensorflow.keras.utils import plot_model
plot_model(model,
show_shapes=True, show_dtype=True,
show_layer_names=True,expand_nested=False)
这里是从输入到层的计算conv2_block1_0_relu
被计算一次。接下来,如果我们想将此函数式 API 转换为子类化 API,我们必须从基本模型的输入到层构建一个模型conv2_block1_0_relu
第一的。喜欢
class ModelWithMidLayer(tf.keras.Model):
def __init__(self, dim=(32, 32, 3)):
super().__init__()
self.dim = dim
self.base = DenseNet121(input_shape=self.dim, weights=None)
# building sub-model from self.base which gives
# desired output feature maps: ie. conv2_block1_0_relu
self.mid_layer = tf.keras.Model(self.base.inputs,
self.base.get_layer("conv2_block1_0_relu").output)
self.flat = Flatten()
self.dense = Dense(1000)
self.add = Add()
self.cb = ConvBlock()
def call(self, x):
# forward with base model
bx = self.base(x)
# forward with mid layer
mx = self.mid_layer(x)
# make same shape or do whatever
mx = self.dense(self.flat(mx))
# combine
out = self.add([bx, mx])
return out
def build_graph(self):
x = tf.keras.layers.Input(shape=(self.dim))
return tf.keras.Model(inputs=[x], outputs=self.call(x))
mwml = ModelWithMidLayer()
plot_model(mwml.build_graph(),
show_shapes=True, show_dtype=True,
show_layer_names=True,expand_nested=False)
Here model_1
实际上是一个子模型DenseNet
,这可能导致整个模型(ModelWithMidLayer
) 计算相同的操作两次。如果这一观察是正确的,那么这就会引起我们的担忧。