How to create a Keras Custom Layer using functions not included in the Backend, to perform tensor sampling?

試著忘記壹切 提交于 2020-01-14 06:17:20

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!