我不明白这段代码中发生了什么:
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层,然后在最终的构造函数(最后一行)中,我们创建并返回一个带有指定的输入和输出层,但是它如何知道我们还需要其他哪些层?
侧面问题1):keras.engine.training.Model和keras.models.Model有什么区别?
问题2):当我们进行new_layer = keras.layers.Dense(...)(prev_layer)时,会发生什么? ()操作会返回新层吗?它到底是做什么的?
答案 0 :(得分:5)
此模型是使用Functional API Model
创建的基本上它是这样工作的(也许如果在阅读本文之前进入下面的“附带问题2”,它可能会变得更加清晰):
在创建整个图形之前,您将继续使用这些张量。
但是,这还没有创建一个“模型”。 (您可以训练和使用其他东西的人。)
您所拥有的只是一张图,告诉您哪些张量到达哪里。
要创建模型,请定义它的开始终点。
在示例中。
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)
现在您有了模型。
如果像我(new_model
)一样在另一个变量中使用它,则旧模型仍将存在于model
中。而且这些模型共享相同的层,以这种方式,每当您训练其中一个时,另一个也会更新。
当您这样做:
outputTensor = SomeLayer(...)(inputTensor)
您在输入和输出之间建立了连接。 (Keras将使用内部张量流机制并将这些张量和节点添加到图中)。没有输入就不能存在输出张量。整个InceptionV3
模型是从头到尾连接的。它的输入张量遍历所有层以产生输出张量。数据遵循的只有一种可能的方式,而图形就是这种方式。
当您获得该模型的输出并使用它来获取更多输出时,所有新的输出都将与此连接,从而与模型的第一个输入连接。
添加到张量的属性_keras_history
可能与其跟踪图形的方式密切相关。
因此,Model(old_model.inputs, new_output)
自然会遵循唯一可能的方法:图形。
如果您尝试使用未连接的张量执行此操作,则会出现错误。
首选从“ keras.models”导入。基本上,该模块将从其他模块导入:
请注意,文件keras/models.py
从Model
导入了keras.engine.training
。所以,这是同一回事。
不是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
,但是创建时是为了在没有分支的模型中快速使用。
答案 1 :(得分:0)
有一种方法可以从您可能会建立的预训练模型中构建模型。
请参见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__
您可以看到各种各样的属性,包括关于上一层和下一层的属性。这些是内部实施细节,并且可能会随着时间的推移而变化。