我想得到一个图像嵌入,以便了解网络认为哪些图像更接近,哪些图像差异很大。最初,我打算使用Keras中的Tensorboard回调函数,但文档对我来说不够清晰,我找不到任何有用的例子来复制它。因此,为了确保我理解自己在做什么,我更愿意自己创建嵌入。
为此,我计划下载已经在我的数据上训练好的模型,移除最后几层(最后的dropout层和dense层),并在验证图像上进行预测以获取与每个图像相关联的特征。然后,我将简单地对这些特征进行主成分分析(PCA),并根据它们的头三个主成分值绘制图像。
但我觉得我误解了某些东西,因为当我移除最后几层时,模型的预测结果仍然是类别数量的大小,但对我来说,它应该是最有一层的大小,在我的例子中是128。
下面是用于澄清的代码(我只放了似乎有助于回答问题的行,但如果需要更多细节,请随时询问):
#model creationbase_model = applications.inception_v3.InceptionV3(include_top=False, weights='imagenet', pooling='avg', input_shape=(img_rows, img_cols, img_channel))#Adding custom Layersadd_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 modelmodel = 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_2model.layers[-1].pop()#remove dropout_1model.layers[-1].pop()model.summary() # last alyer output shape is : (None, 128), so the removal worked#predictmodel.predict(np.reshape(image,[1,image.shape[0],image.shape[1],3])) #output only two values
我哪里做错了?你有什么建议吗?
回答:
当使用Keras函数式API添加自定义层时的解决方案:
如果你使用Keras函数式API来添加你的自定义层,以下解决方案可以正常工作:
# base model creationbase_model = applications.inception_v3.InceptionV3(include_top=False, weights='imagenet', pooling='avg', input_shape=(150, 150, 3))# adding custom Layersx = 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 modelmodel = Model(inputs=base_model.input, outputs=out)model.compile(loss='categorical_crossentropy', optimizer='adam')
这是如何通过定义新模型来提取自定义层的激活值的方法:
# construct a new model to get the activations of custom layersnew_model = Model(model.inputs, [model.layers[-3].output, model.layers[-2].output, model.layers[-1].output])# predict one one random input sampleinp = np.random.rand(1, 150, 150, 3)output = new_model.predict([inp])# verify that's what we wantprint(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 Kfunc = 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 modesample_input = np.random.rand(1, 150, 150, 3)# train modeoutput = func([sample_input, 1])# test modeouput = func([sample_input, 0])
请注意,你需要使用K.learning_phase()
,因为模型包含了在测试和训练模式下行为不同的层,如BatchNormalization
和Dropout
。
注意:上述解决方案在使用Sequential
类添加自定义层时无法正常工作。这是因为在构建model
时使用add_model(base_model.output)
,整个add_model
被存储为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中的一个错误或无法实现的功能。
附注:实际上,在模型对象的layers
属性上使用pop()
不起作用,因为你需要更新模型的一些内部属性(虽然,对于顺序模型,已经实现了一个内置的pop()
方法)。