【问题标题】:Constructing a keras model构建一个keras模型
【发布时间】:2018-12-14 11:17:29
【问题描述】:

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

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) 时究竟会发生什么? () 操作是否返回新层,它究竟做了什么?

【问题讨论】:

    标签: python neural-network keras deep-learning keras-layer


    【解决方案1】:

    这个模型是使用Functional API Model创建的

    基本上它是这样工作的(也许如果你在阅读本文之前转到下面的“附带问题2”可能会更清楚):

    • 您有一个输入张量(您也可以将其视为“输入数据”)
    • 您创建(或重复使用)一个图层
    • 您将输入张量传递给层(您“调用”具有输入的层)
    • 你得到一个输出张量

    您一直使用这些张量,直到您创建了整个图表

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

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


    在示例中。

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

    但是,由于它与函数式 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,但创建用于在没有分支的模型中快速使用。

    【讨论】:

    • 这应该是最好的答案了。
    • 太好了,谢谢丹尼尔。一些后续行动:所以即使在模型构造函数中我们传递了 input_tensor 和 output_tensor,它以某种方式维护了连接这两者的整个图?通过什么机制?换句话说,以这种方式构建的模型似乎知道整个图结构,而没有明确地传递给它。是不是因为张量有,有点像指针,指向层,所以你只需要调用 .next() 就可以得到整个层图作为链表?您能否在上面详细说明您的好答案来解释这一点?
    • 另一个是,听起来你在暗示 keras 中有两种范式,顺序和功能。您能否编辑您的答案以说明这一点,解释它们在哪些方面相似/不同等?这将是一个很好的参考。非常感谢。
    • 完成 :) --- 我不确定隐藏图有什么要说的。它存在并由 keras 保存。当你调用一个层时,它会在内部调用一些可以改进此会话中现有图形的东西。我认为整个想法就是不要担心它。 Keras 旨在成为顶级、易于使用的 API。
    【解决方案2】:

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

    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__
    

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

    【讨论】:

    • 对不起,但这根本不能解决我的问题。问题是,它怎么知道包含所有中间层,即使在构造函数中只提供输入和输出层?底层机制是什么?
    • 您可以在 Keras 源代码中获得明确的答案,例如在github.com/keras-team/keras/blob/master/keras/engine/… 中实现了“所有层都继承的基层类”
    猜你喜欢
    • 2019-11-24
    • 1970-01-01
    • 1970-01-01
    • 2018-06-21
    • 2017-12-10
    • 1970-01-01
    • 1970-01-01
    • 2021-03-02
    • 1970-01-01
    相关资源
    最近更新 更多