【问题标题】:get the before last feature of network for embedding, is not working获取网络的最后一个功能进行嵌入,不起作用
【发布时间】:2019-02-25 21:21:40
【问题描述】:

我想要一个图像嵌入,以了解网络看到哪些图像更接近,哪些图像对他来说似乎非常不同。 首先,我想在 Keras 中使用 Tensorboard 回调,但文档对我来说不够清晰,我找不到任何有用的示例来重现它。因此,为了确保理解我在做什么,我更喜欢自己进行嵌入。

为此,我计划下载已经在我的数据上训练过的模型,删除最后一层(最后一个 dropout 和密集层),并预测验证图像以获得与每个图像相关的特征。然后我会简单地对这些特征进行 PCA 并根据它们的前三个主成分值绘制图像。

但我想我误解了一些东西,因为当我删除最后一层时,模型预测仍然是类数的大小,但对我来说它应该是最后一层的大小,在我的模型中是 128案例。

以下是澄清代码(我只是在其中放置了似乎对回答问题有用的行,但请不要犹豫询问更多详细信息):

#model creation
base_model = applications.inception_v3.InceptionV3(include_top=False, 
                                                   weights='imagenet',
                                                   pooling='avg', 
                                                   input_shape=(img_rows, img_cols, img_channel))
#Adding custom Layers
add_model = Sequential()
add_model.add(Dense(128, activation='relu',input_shape=base_model.output_shape[1:],
                    kernel_regularizer=regularizers.l2(0.001)))
add_model.add(Dropout(0.60))
add_model.add(Dense(2, activation='sigmoid'))   
# creating the final model
model = Model(inputs=base_model.input, outputs=add_model(base_model.output))

然后我在具有两个类的数据集上训练模型,并加载模型及其权重以生成特征:

model = load_model(os.path.join(ROOT_DIR,'model_1','model_cervigrams_all.h5'))
#remove the last two layers
#remove dense_2
model.layers[-1].pop()
#remove dropout_1
model.layers[-1].pop()
model.summary() # last alyer output shape is : (None, 128), so the removal worked
#predict
model.predict(np.reshape(image,[1,image.shape[0],image.shape[1],3])) #output only two values

我哪里错了?你有什么建议吗?

【问题讨论】:

    标签: python machine-learning keras embedding keras-layer


    【解决方案1】:

    使用 Keras 功能 API 添加自定义层时的解决方案:

    如果您使用Keras functional API 添加自定义图层,则以下解决方案可以正常工作:

    # base model creation
    base_model = applications.inception_v3.InceptionV3(include_top=False, 
                                                       weights='imagenet',
                                                       pooling='avg', 
                                                       input_shape=(150, 150, 3))
    # adding custom Layers
    x = Dense(128, activation='relu',input_shape=base_model.output_shape[1:],
                        kernel_regularizer=regularizers.l2(0.001))(base_model.output)
    x = Dropout(0.60)(x)
    out = Dense(2, activation='sigmoid')(x)
    
    # creating the final model
    model = Model(inputs=base_model.input, outputs=out)
    model.compile(loss='categorical_crossentropy', optimizer='adam')
    

    以下是如何通过定义新模型来提取自定义层的激活:

    # construct a new model to get the activations of custom layers
    new_model = Model(model.inputs, [model.layers[-3].output,
                                     model.layers[-2].output,
                                     model.layers[-1].output])
    
    # predict one one random input sample
    inp = np.random.rand(1, 150, 150, 3)
    output = new_model.predict([inp])
    
    # verify that's what we want
    print(output[0].shape)  # shape of first dense layer output, prints: (1, 128) 
    print(output[1].shape)  # shape of dropout layer output, prints: (1, 128)
    print(output[2].shape)  # shape of second dense layer output, prints: (1, 2)
    

    或者,您可以定义一个 Keras 函数:

    from keras import backend as K
    
    func = K.function(inputs=model.inputs + [K.learning_phase()],
                      outputs=[model.layers[-3].output,
                               model.layers[-2].output, 
                               model.layers[-1].output])
    
    # usage of the defined function: 
    #     the inputs should be a *list* of input arrays
    #     plus 1 or 0 for the train/test mode
    sample_input = np.random.rand(1, 150, 150, 3)
    
    # train mode
    output = func([sample_input, 1])
    
    # test mode
    ouput = func([sample_input, 0])
    

    请注意,you need to 使用 K.learning_phase(),因为模型包含 BatchNormalizationDropout 等层,它们在测试和训练模式下的行为不同。


    注意:如果您使用Sequential 类添加自定义层,上述解决方案将无法正常工作。 这是因为在model 的构造中使用add_model(base_model.output) 时,整个@987654334 @ 存储为model 的一层。您可以通过运行model.summary()print(model.layers[-1]) 来验证这一点。并且没有办法访问这个顺序模型的中间层的输出。当然也可以使用model.layers[-1].layers[1].output(也就是dropout层):

    new_model = Model(model.inputs, model.layers[-1].layers[1].output)
    new_model.predict(...)
    

    但是,它会抱怨由于没有输入顺序模型的原始输入,导致图断开连接:

    ValueError: Graph disconnected: cannot obtain value for tensor Tensor("dense_7_input:0", shape=(?, 2048), dtype=float32) at layer "dense_7_input". The following previous layers were accessed without issue: []
    

    实际上,我预计顺序模型的内层(即model.layers[-1].layer[1:])有额外的入站和出站节点,但似乎情况并非如此。我不知道我是否在这里遗漏了一些东西,或者它在某种程度上是一个错误或在 Keras 中不可能做到。


    旁注:实际上,在模型对象does not work since you need to update some of the internal attributes of the modellayers 属性上使用pop()(尽管,仅针对顺序模型实现了内置的pop() 方法)。

    【讨论】:

    • 非常感谢您的回答,有两种可能性!我真的很喜欢第二种可能性。当我向这个“func”输入与输入 model.predict() 相同的输入时,它不起作用(但适用于 model.predict)。我试图放入列表但仍然无法正常工作。我究竟做错了什么?对我来说,它应该与 model.inputs 相同。
    • 关于第一个解决方案,我似乎无法从顺序模型中的层获得输出:model.layers[-1].output : "AttributeError: Layersequential_1 有多个入站节点,因此“层输出”的概念定义不明确。请改用get_output_at(node_index)。”和 model.layers[-2].output 直接给出'base_model'的输出......:s如果你对这两件事有小建议,那将是非常完美的,这样我才能完全理解你的答案:)谢谢你非常有帮助!
    • @miki 对不起!我没有测试我的代码就发布了这个答案。实际上,您可以访问顺序子模型中层的输出。如果您运行print(model.layers[-1]) 或执行model.summary(),您会意识到整个序列模型被编码为model 的一层。无论我尝试什么,我都无法构建一个模型,其输出是顺序模型中中间层的输出。不过,如果您最初使用功能 API 添加了图层,那么这是可能的。所以我的回答是错误的,我目前正在调查以找到解决方案。接受我的道歉!
    • 感谢您的回答和时间!我不知道如何更改模型以便可以轻松使用您的解决方案。当我尝试将功能 API 与此初始模型一起使用时,我做错了:base_model = Dense(128, activation='relu')(base_model) 给出错误(Layer dense_3 被调用时输入不是符号张量)。即使它改变了我最初构建模型的方式,也许你会有一个解决方案?
    • @miki 我更新了我对使用功能 api 添加自定义层的情况的回答。请看一看。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    相关资源
    最近更新 更多