问题
I'm trying to create a custom layer in keras
. This layer should perform a sampling over the input tensor (according to a probability distribution), and output a tensor of same size, with only values that have been sampled, the rest being zero. However no sampling functions are available in keras.backend
to my knowledge. Note that this layer hasn't any trainable parameters, I just want a function that modifies the previous output.
For now I'm trying to convert the input tensor from a Tensor
object to a numpy.ndarray
using keras.backend.eval()
. According to stackoverflow question #47577060, it seems impossible. It would have been nice though, to then apply the usual numpy function for sampling np.random.choice
. This function only takes 1-dimension np.array, both for input and probability distribution (can't use tensors). Note that the probability distribution I'm mentioning is actually the input itself (My goal is to sample the high value elements with higher probability)
The custom layer for sampling is called MyLayer
and is defined by
def MyLayer(input_tensor): #Here we sample from the tensor directly!
# Convert to numpy array: in keras the input_tensor has shape [None,H,W,D] and is Keras.Tensor object...
input_tensor = keras.backend.eval(input_tensor) # convert to np.array #** THIS IS WHERE IT FAILS **#
input_tensor = input_tensor[0,:,:,:] # first dimension is None so we discard
# need to transform the np.array tensor into a matrix (custom function)
input_matrix = tensor_to_matrix(input_tensor)
# create the probability distribution that the sampling will follow
# the probability must be the matrix itself (to sample the highest elements in priority)
probability_matrix = input_matrix/np.max(input_matrix) # must be normalized to sum to 1
prob_vec = probability_matrix.flatten('F') # vectorize it, column-first
# create list of same size where each element is the value and its own position (i,j). it is necessary to create a string "value/i/j" for each element (i have no other idea)
matrix_value_position = []
for j in range(input_matrix.shape[1]):
for i in range(input_matrix.shape[0]):
t = str(input_matrix[i,j])+'/'+str(i)+'/'+str(j) #it will be parsed later to recover value,i,j
matrix_value_position.append(t)
vec_value_position = np.array(matrix_value_position)
# Sample points according to a probability distribution
num_samples = 10000
sample = np.random.choice(vec_value_position, num_samples, p=prob_vec) #**THIS IS WHERE IT SAMPLES**#
# parse the strings that have been sampled, store them in a numpy array
samples_results = []
for i in range(len(sample)):
samples_results.append(np.array(sample[i].split('/')).astype(float))
samples_results = np.array(samples_results)
# reconstruct the matrix from the samples (the rest is zero)
reconstructed_matrix = np.zeros((input_matrix.shape[0],input_matrix.shape[1]))
for s in samples_results:
i = int(s[1])
j = int(s[2])
reconstructed_matrix[x,y] = float(s[0]) #retrieve the value sampled at position [i,j]
# return a np.array tensor (custom function)
output_tensor_numpy = reverse_tensor_expand(reconstructed_matrix, input_tensor.shape)
# convert back to Keras Tensor object
output_tensor_keras = keras.backend.variable(value=output_tensor_numpy, dtype='float32')
return output_tensor_keras
Then I apply the layer with the following (it is the second layer):
model = keras.Sequential()
model.add(Conv2D(filters=6, kernel_size=(7, 7), activation='relu', input_shape=(28,28,1)))
model.add(Lambda(MyLayer, output_shape=MyLayerOutputShape)) #note: output_shape is equal to input_shape. MyLayerOutputShape is the identity
#compile
model.compile(loss='categorical_crossentropy',optimizer='rmsprop',metrics=['accuracy'])
print(model.summary())
The error returned was
---------------------------------------------------------------------------
InvalidArgumentError Traceback (most recent call last)
/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py in _do_call(self, fn, *args)
1333 try:
-> 1334 return fn(*args)
1335 except errors.OpError as e:
/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py in _run_fn(feed_dict, fetch_list, target_list, options, run_metadata)
1318 return self._call_tf_sessionrun(
-> 1319 options, feed_dict, fetch_list, target_list, run_metadata)
1320
/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py in _call_tf_sessionrun(self, options, feed_dict, fetch_list, target_list, run_metadata)
1406 self._session, options, feed_dict, fetch_list, target_list,
-> 1407 run_metadata)
1408
InvalidArgumentError: You must feed a value for placeholder tensor 'conv2d_71_input' with dtype float and shape [?,28,28,1]
[[{{node conv2d_71_input}} = Placeholder[dtype=DT_FLOAT, shape=[?,28,28,1], _device="/job:localhost/replica:0/task:0/device:CPU:0"]()]]
During handling of the above exception, another exception occurred:
InvalidArgumentError Traceback (most recent call last)
<ipython-input-316-26717973065e> in <module>()
1 model_cust = keras.Sequential()
2 model_cust.add(Conv2D(filters=6, kernel_size=(7, 7), activation='relu', input_shape=(28,28,1)))
----> 3 model_cust.add(Lambda(MyLayer, output_shape=QuantumSamplingLayerOutputShape))
4 #compile
5 model_cust.compile(loss='categorical_crossentropy',optimizer='rmsprop',metrics=['accuracy'])
/anaconda3/lib/python3.6/site-packages/keras/engine/sequential.py in add(self, layer)
179 self.inputs = network.get_source_inputs(self.outputs[0])
180 elif self.outputs:
--> 181 output_tensor = layer(self.outputs[0])
182 if isinstance(output_tensor, list):
183 raise TypeError('All layers in a Sequential model '
/anaconda3/lib/python3.6/site-packages/keras/engine/base_layer.py in __call__(self, inputs, **kwargs)
455 # Actually call the layer,
456 # collecting output(s), mask(s), and shape(s).
--> 457 output = self.call(inputs, **kwargs)
458 output_mask = self.compute_mask(inputs, previous_mask)
459
/anaconda3/lib/python3.6/site-packages/keras/layers/core.py in call(self, inputs, mask)
685 if has_arg(self.function, 'mask'):
686 arguments['mask'] = mask
--> 687 return self.function(inputs, **arguments)
688
689 def compute_mask(self, inputs, mask=None):
<ipython-input-312-a97b6c80e163> in MyLayer(input_tensor)
2
3 # Convert to numpy array: in keras the input_tensor has shape [None,H,W,D] and is Keras.Tensor object...
----> 4 input_tensor = keras.backend.eval(input_tensor) # convert to np.array #** THIS IS WHERE IT FAILS **#
5 input_tensor = input_tensor[0,:,:,:] # first dimension is None so we discard
6
/anaconda3/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py in eval(x)
671 ```
672 """
--> 673 return to_dense(x).eval(session=get_session())
674
675
/anaconda3/lib/python3.6/site-packages/tensorflow/python/framework/ops.py in eval(self, feed_dict, session)
711
712 """
--> 713 return _eval_using_default_session(self, feed_dict, self.graph, session)
714
715
/anaconda3/lib/python3.6/site-packages/tensorflow/python/framework/ops.py in _eval_using_default_session(tensors, feed_dict, graph, session)
5155 "the tensor's graph is different from the session's "
5156 "graph.")
-> 5157 return session.run(tensors, feed_dict)
5158
5159
/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py in run(self, fetches, feed_dict, options, run_metadata)
927 try:
928 result = self._run(None, fetches, feed_dict, options_ptr,
--> 929 run_metadata_ptr)
930 if run_metadata:
931 proto_data = tf_session.TF_GetBuffer(run_metadata_ptr)
/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py in _run(self, handle, fetches, feed_dict, options, run_metadata)
1150 if final_fetches or final_targets or (handle and feed_dict_tensor):
1151 results = self._do_run(handle, final_targets, final_fetches,
-> 1152 feed_dict_tensor, options, run_metadata)
1153 else:
1154 results = []
/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py in _do_run(self, handle, target_list, fetch_list, feed_dict, options, run_metadata)
1326 if handle is None:
1327 return self._do_call(_run_fn, feeds, fetches, targets, options,
-> 1328 run_metadata)
1329 else:
1330 return self._do_call(_prun_fn, handle, feeds, fetches)
/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py in _do_call(self, fn, *args)
1346 pass
1347 message = error_interpolation.interpolate(message, self._graph)
-> 1348 raise type(e)(node_def, op, message)
1349
1350 def _extend_graph(self):
InvalidArgumentError: You must feed a value for placeholder tensor 'conv2d_71_input' with dtype float and shape [?,28,28,1]
[[node conv2d_71_input (defined at /anaconda3/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:517) = Placeholder[dtype=DT_FLOAT, shape=[?,28,28,1], _device="/job:localhost/replica:0/task:0/device:CPU:0"]()]]
If anyone has an idea to solve this issue, or define another way to perform this sampling layer, and be Keras compatible, I'd appreciate it a lot
Thank you
来源:https://stackoverflow.com/questions/54907018/how-to-create-a-keras-custom-layer-using-functions-not-included-in-the-backend