Changing a pretrained Keras model with fixed input to a flexible one in tensoflow? - tensorflow

I want to use a pre-trained BagNet (https://github.com/wielandbrendel/bag-of-local-features-models) to extract features. The net has fixed input height and width, the input is (None,3,224,224). Now, I want to build a new model with fexible input sizes. I tried approaches with model.layers.pop()[0] to remove the first layer and replace it with a flexible input but I get errors:
ValueError: Graph disconnected: cannot obtain value for tensor Tensor("input0_6:0", shape=(?, 3, 224, 224), dtype=float32) at layer "input0". The following previous layers were accessed without issue: []
keras_model = bagnets.keras.bagnet8()
keras_model.layers.pop()[0]
x = Input(batch_shape=(None, 3, None, None))
newModel = Model(x, keras_model.output)
How could I ressolve this issue or what are other options?

Related

Multi-channel inputs with Tensorflow Objection Detection API V2

I'd like to construct a network in the Tensorflow V2 object detection API using 5-channel images. However, I am stuck on how to modify the weights of the first convolutional layer using the Tensorflow 2.2 framework.
I have downloaded the pre-trained RetinaNet from the V2 Model Zoo. I then tried the following to modify the weights in the first layer of the checkpoint and save them back:
tf_path = tf.train.latest_checkpoint('./RetinaNet/checkpoint/')
init_vars = tf.train.list_variables(tf_path)
tf_vars = {}
for name, shape in init_vars:
array = tf.train.load_variable(tf_path, name)
try:
if shape[2]==3:#look for a layer who's 3rd input dimension is 3 i.e. the 1st convolutional layer
array=np.concatenate((array,array[:,:,:2,:]),axis=2)
array=array.astype('float32')
tf_vars[name]=tf.Variable(array)
else:
tf_vars[name]=tf.Variable(array)
except:
tf_vars[name]=tf.Variable(array)
saver = tf.compat.v1.train.Saver(var_list=tf_vars)
sess = tf.compat.v1.Session()
saver.save(sess, './RetinaNet/checkpoint/ckpt-0')
I loaded the model back in to make sure the 1st convolutional layer had been changed - all looks ok.
But when I go to train the model, I get the following error:
Model was constructed with shape (None, None, None, 3) for input Tensor("input_1:0", shape=(None, None, None, 3), dtype=float32), but it was called on an input with incompatible shape (64, 128, 128, 5)
Which leads me to believe my method of modifying the weights is not so "ok" after all. Would anyone have some tips on how to modify these weights correctly?
Thanks
This now works but the solution is very hacky... it also means not training from the pretrained weights from the model zoo - so you need to comment everything to do with the fine_tune_checkpoint in the config file.
Then, go to .\Lib\site-packages\official\vision\image_classification\efficientnet and change the number of input channels and number of classes in efficientnet_model.py and efficientnet_config.py.

Issue with feeding value into placeholder tensor for sess.run()

I want to get the value of an intermediate tensor in a convolutional neural network for a specific input. I know how to do this in keras and even though I have trained a model using keras, I'm going to move towards constructing and training the model using only tensorflow. Therefore, I want to move away from something like K.function(input_layer, output_layer) which is fairly simple, and instead use tensorflow. I believe I should use placeholder values, like the following approach:
with tf.compat.v1.Session(graph=tf.Graph()) as sess:
loaded_model = tf.keras.models.load_model(filepath)
graph = tf.compat.v1.get_default_graph()
images = tf.compat.v1.placeholder(tf.float32, shape=(None, 28, 28, 1)) # To specify input at MNIST images
output_tensor = graph.get_tensor_by_name(tensor_name) # tensor_name is 'dense_1/MatMul:0'
output = sess.run([output_tensor], feed_dict={images: x_test[0:1]}) # x_test[0:1] is of shape (1, 28, 28, 1)
print(output)
However, I get the following error message for the sess.run() line: Invalid argument: You must feed a value for placeholder tensor 'conv2d_2_input' with dtype float and shape [?,28,28,1]. I am unsure why I get this message because the image used for feed_dict is of type float and is what I believe to be the correct shape. Any help would be suggested.
You must use the input tensor from the Keras model, not make your own new placeholder, which would be disconnected from the rest of the model:
with tf.Graph().as_default(), tf.compat.v1.Session() as sess:
# Load model
loaded_model = tf.keras.models.load_model(filepath)
# Take model input tensor
images = loaded_model.input
# Take output of the second layer (index 1)
output_tensor = loaded_model.layers[1].output
# Evaluate
output = sess.run(output_tensor, feed_dict={images: x_test[0:1]})
print(output)

Is it possible to change the input shape of a tensorflow pretrained model?

I have a Tensorflow pre-trained model for Image Segmentation that receives 6 bands as input, I would like to change the input size of the model to receive 4 bands so I can retrain with my own dataset, but still not able to do it, no sure if this is even possible?
I tried getting the input node by name and change it using import_graph_def with no success, seems like it is asking to respect the dimensions when trying to substitute.
graph = tf.get_default_graph()
tf_new_input = tf.placeholder(shape=(4, 256, 256), dtype='float32', name='new_input')
tf.import_graph_def(graph_def, input_map={"ImageInputLayer": tf_new_input})
But I am getting the following error:
tensorflow.python.framework.errors_impl.InvalidArgumentError: Dimensions must be equal, but are 4 and 6 for 'import/ImageInputLayer_Sub' (op: 'Sub') with input shapes: [4,256,256], [6,256,256]
You have to convert your 4 channel placeholder input to 6 channel input and also the input image shape should be the same as your 6 channel model expects. You may use any operation but conv2d is an easy operation to perform before you feed it to your existing model. This is how you do it.
with tf.Graph().as_default() as old_graph:
# You have to load your 6 channel input graph here
saver.restore(tf.get_default_session(), <<save_path>>)
# Assuming that input node is named as 'input_node' and
# final node is named as 'softmax_node'
with tf.Graph().as_default() as new_graph:
tf_new_input = tf.placeholder(shape=(None, 256, 256, 4), dtype='float32')
# Map 4 channeled input to 6 channel and
# image input shape should be same as expected by old model.
new_node = tf.nn.conv2d(tf_new_input, (3, 3, 4, 6), strides=1, padding='SAME')
# If you want to obtain output node so that you can further perform operations.
softmax_node = tf.import_graph_def(old_graph, input_map={'input_node:0': new_node},
return_elements=['softmax_node:0'])
user1190882 responded this question very well. Just using this section to post the code for future reference, I had to make a small change by creating the filter in a separate variable since I was getting an error : Shape must be rank 4 but is rank 1 for 'Conv2D' . Also I made a small change since the input format of my model is "Channels First", I added data_format flag.
with tf.Graph().as_default() as new_graph:
tf_new_input = tf.placeholder(shape=(None, 4, 256, 256), dtype='float32')
# Creating separate variable for filter
filterc = tf.Variable(tf.random_normal([3, 3, 4, 6]))
new_node = tf.nn.conv2d(tf_new_input, filterc, strides=1, padding='SAME', data_format='NCHW')
tf.import_graph_def(old_graph, input_map={'ImageInputLayer': new_node})

Cant build a CNN with keras for vectors - problem with dimensions

Let us say that I build an extreamly simple CNN with Keras to classify vectors.
My input (X_train) is a matrix in which each row is a vector and each column is a feature. My input labels (y_train) is matrix where each line is a one hot encoded vector. This is a binary classifier.
my CNN is built as follows:
model = Sequential()
model.add(Conv1D(64,3))
model.add(Activation('relu'))
model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dense(2))
model.add(Activation('sigmoid'))
model.compile(loss = 'binary_crossentropy', optimizer = 'adam', matrics =
['accuracy'])
model.fit(X_train,y_train,batch_size = 32)
But when I try to run this code, I get back this error message:
Input 0 is incompatible with layer conv1d_23: expected ndim=3, found
ndim=2
why would keras expect 3 dims? one dim for samples, and one for features. And more importantly, how can I fix this?
X_train is suppose to have the shape: (batch_size, steps, input_dim), see documentation. It seems like you are missing one of the dimensions.
I would guess input_dim in your case is 1 and that is why it is missing. If so, change the
model.fit
line to
model.fit(tf.expand_dims(X_train,-1), y_train,batch_size = 32)
Your code is not a minimal working example, so I am not able to verify if that is the only problem, but this should hopefully fix your current error message.
A Conv1D layer expects an input with shape (samples, width, channels), so this does not match your input data, producing an error.
The convolution operation is done on the width dimension, so assuming that you want to do convolution on what you call features, then you should reshape your data to add a dummy channels dimension with a value of one:
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 1))

InceptionV3 and transfer learning with tensorflow

I would like to do a transfer learning from the given inceptionV3 in tensorflow example. Following the classify image example and the operator and tensor names given here https://github.com/AKSHAYUBHAT/VisualSearchServer/blob/master/notebooks/notebook_network.ipynb I can create my graph. But when, I put a batch of images of size (100, 299, 299, 3) in the pre-computed inception graph, I get the following shape error at the pool_3 layer :
ValueError: Cannot reshape a tensor with 204800 elements to shape [1, 2048] (2048 elements)
It seems that this inceptionV3 graph doesn't accept image batch as input. am I wrong ?
Actually it works for transfer learning if you extract the right thing. There is no problem feeding a batch of images in the shape of [N, 299, 299, 3] as ResizeBilinear:0 and then using the pool_3:0 tensor. It's the reshaping afterwards that breaks, but you can reshape yourself (you'll have your own layers afterwards anyway). If you wanted to use the original classifier with a batch, you could add your own reshaping on top of pool_3:0 and then add the softmax layer, reusing the weights/biases tensors of the original softmax.
TLDR: With double_img being a stack of two images with shape (2, 299, 299, 3) this works:
pooled_2 = sess.graph.get_tensor_by_name("pool_3:0").eval(session=sess, feed_dict={'ResizeBilinear:0':double_img})
pooled_2.shape
# => (2, 1, 1, 2048)
You're not wrong. This seems like a very reasonable feature request, so I've opened a ticket for it on github. Follow that for updates.
Something like this should do it:
with g.as_default():
inputs = tf.placeholder(tf.float32, shape=[batch_size, 299, 299, 3],
name='input')
with slim.arg_scope(inception.inception_v3_arg_scope()):
logits, end_points = inception.inception_v3( inputs,
num_classes=FLAGS.num_classes, is_training=False)
variables_to_restore = lim.get_variables_to_restore(exclude=exclude)
sess = tf.Session()
saver = tf_saver.Saver(variables_to_restore)
Then you should be able to call the operation:
sess.run("pool_3:0",feed_dict={'ResizeBilinear:0':images})
etarion made a very good point. However, we don't have to reshape it ourselves; instead, we could change the value of shape that reshape takes as input. I.e.,
input_tensor_name = 'import/input:0'
shape_tensor_name = 'import/InceptionV3/Predictions/Shape:0'
output_tensor_name= 'import/InceptionV3/Predictions/Reshape_1:0'
output_tensor = tf.import_graph_def(
graph.as_graph_def(),
input_map={input_tensor_name: image_batch,
shape_tensor_name: [batch_size, num_class]},
return_elements=[output_tensor_name])
These tensor names are based on inception_v3_2016_08_28_frozen.pb.