构建keras模型

Constructing a keras model

我不明白这段代码中发生了什么:

def construct_model(use_imagenet=True):
    # line 1: how do we keep all layers of this model ?
    model = keras.applications.InceptionV3(include_top=False, input_shape=(IMG_SIZE, IMG_SIZE, 3),
                                          weights='imagenet' if use_imagenet else None) # line 1: how do we keep all layers of this model ?

    new_output = keras.layers.GlobalAveragePooling2D()(model.output)

    new_output = keras.layers.Dense(N_CLASSES, activation='softmax')(new_output)
    model = keras.engine.training.Model(model.inputs, new_output)
    return model

具体来说,我的困惑是,当我们调用最后一个构造函数时

model = keras.engine.training.Model(model.inputs, new_output)

我们指定了输入层和输出层,但它怎么知道我们要保留所有其他层?

也就是说,我们将new_output层附加到我们在第1行加载的预训练模型,即new_output层,然后在最后的构造函数中(最后一行) ,我们只是创建并 return 一个具有指定输入和输出层的模型,但它如何知道我们想要介于两者之间的其他层?

附带问题 1):keras.engine.training.Model 和 keras.models.Model 有什么区别?

附带问题 2:当我们执行 new_layer = keras.layers.Dense(...)(prev_layer) 时究竟发生了什么? ()操作returnnew layer,具体是做什么的?

有这种方法可以从您可以构建的预训练模型构建模型。

参见https://keras.io/applications/#fine-tune-inceptionv3-on-a-new-set-of-classes

base_model = InceptionV3(weights='imagenet', include_top=False)
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(1024, activation='relu')(x)
predictions = Dense(200, activation='softmax')(x)
model = Model(inputs=base_model.input, outputs=predictions)
for layer in base_model.layers:
    layer.trainable = False
model.compile(optimizer='rmsprop', loss='categorical_crossentropy')

每次像 "x=Dense(..." 这样的操作添加一个层时,都会更新有关计算图的信息。您可以交互式地键入它以查看它包含的内容:

x.graph.__dict__

你可以看到各种属性,包括关于上一层和下一层的。这些是内部实施细节,可能会随着时间的推移而改变。

此模型是使用 Functional API Model

创建的

基本上它是这样工作的(也许如果你在阅读这篇文章之前转到下面的"side question 2"它可能会更清楚):

  • 你有一个输入张量(你也可以把它看成"input data")
  • 您创建(或重复使用)图层
  • 你将输入张量传递给一个层(你"call"一个带有输入的层)
  • 你得到一个输出张量

您继续使用这些张量,直到您创建了整个

但这还没有创建 "model"。 (一个你可以训练和使用其他东西)。
您所拥有的只是一张图表,告诉您哪些张量去哪里。

要创建模型,您需要定义它的起点终点。


在例子中。

  • 他们采用现有模型:model = keras.applications.InceptionV3(...)
  • 他们想扩展这个模型,所以他们得到了它的输出张量model.output
  • 他们将这个张量作为 GlobalAveragePooling2D 层的输入
  • 他们得到这一层的输出张量为 new_output
  • 他们将其作为输入传递给另一层:Dense(N_CLASSES, ....)
  • 并得到它的输出 new_output(这个 var 被替换了,因为他们对保留它的旧值不感兴趣...)

但是,由于它与函数 API 一起使用,我们还没有模型,只有图表。为了创建模型,我们使用 Model 定义输入张量和输出张量:

new_model = Model(old_model.inputs, new_output)    

现在你有了你的模型。
如果你像我一样在另一个 var 中使用它 (new_model),旧模型将仍然存在于 model 中。这些模型共享相同的层,无论何时训练其中一个,另一个也会更新。


问题:它如何知道我们想要介于两者之间的其他层?

当你这样做时:

outputTensor = SomeLayer(...)(inputTensor)    

你在输入和输出之间建立了联系。 (Keras 会使用内部的 tensorflow 机制并将这些张量和节点添加到图中)。没有输入,输出张量就不可能存在。整个InceptionV3模型从头到尾都是连在一起的。它的输入张量遍历所有层以产生输出张量。数据遵循的方式只有一种,图表就是方式。

当您获得此模型的输出并使用它获得更多输出时,您所有的新输出都将连接到此,从而连接到模型的第一个输入。

可能添加到张量的属性_keras_history与其跟踪图形的方式密切相关。

所以,做Model(old_model.inputs, new_output)自然会遵循唯一可能的方法:图表。

如果您尝试使用未连接的张量执行此操作,则会出现错误。


附带问题 1

更喜欢从 "keras.models" 导入。基本上,这个模块将从另一个模块导入:

请注意文件 keras/models.pykeras.engine.training 导入 Model。所以,这是一回事。

附带问题 2

不是new_layer = keras.layers.Dense(...)(prev_layer)

output_tensor = keras.layers.Dense(...)(input_tensor)

您在同一行中做两件事:

  • 创建图层 - keras.layers.Dense(...)
  • 调用具有输入张量的层以获得输出张量

如果你想对不同的输入使用相同的图层:

denseLayer = keras.layers.Dense(...) #creating a layer

output1 = denseLayer(input1)  #calling a layer with an input and getting an output
output2 = denseLayer(input2)  #calling the same layer on another input
output3 = denseLayer(input3)  #again   

奖励 - 创建等同于顺序模型的功能模型

如果您创建此顺序模型:

model = Sequential()
model.add(Layer1(...., input_shape=some_shape))   
model.add(Layer2(...))
model.add(Layer3(...))

您所做的与以下内容完全相同:

inputTensor = Input(some_shape)
outputTensor = Layer1(...)(inputTensor)
outputTensor = Layer2(...)(outputTensor)    
outputTensor = Layer3(...)(outputTensor)

model = Model(inputTensor,outputTensor)

有什么区别?

好吧,功能性 API 模型是完全免费的,可以随心所欲地构建。您可以创建分支:

out1 = Layer1(..)(inputTensor)    
out2 = Layer2(..)(inputTensor)

你可以加入张量:

joinedOut = Concatenate()([out1,out2])   

有了它,您可以创建任何您想要的东西,包括各种花哨的东西、分支、门、串联、添加等,这是您无法使用顺序模型。

事实上,Sequential 模型也是一个 Model,只是为了在没有分支的模型中快速使用而创建。