Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use Keras layer implemented by myself? #373

Closed
y-ich opened this issue Jul 7, 2017 · 27 comments
Closed

How to use Keras layer implemented by myself? #373

y-ich opened this issue Jul 7, 2017 · 27 comments
Assignees

Comments

@y-ich
Copy link
Contributor

y-ich commented Jul 7, 2017

(Written by @Kiikurage)
Original question is in Japanese, but answer is also written in English.


(英語のほうがよければ英語で書きますのでその旨お知らせください)

Kerasには入力の各点に学習可能なバイアスを加えるレイヤーがないようなので、カスタムレイヤー(Bias)を作りました。
このレイヤーを含むKerasモデルをWebDNNで利用するにはどうしたらよいでしょうか?

ちなみにBiasは以下のような簡単なものです。

from keras.engine.topology import Layer
from keras import initializers

class Bias(Layer):
    """
    Custom keras layer that simply adds a scalar bias to each location in the input
    """
    
    def __init__(self, initializer='uniform', **kwargs):
        super(Bias, self).__init__(**kwargs)
        self.initializer = initializers.get(initializer)
    
    def build(self, input_shape):
        self.bias = self.add_weight(name='{}_W'.format(self.name), shape=(input_shape[-1],), initializer=self.initializer)
    
    def call(self, x):
        return x + self.bias
@Kiikurage
Copy link
Member

カスタムレイヤーを利用するには変換用の追加コード(カスタムハンドラ)を実装しトランスパイラに登録する必要があります。カスタムハンドラを登録するためのAPIはすでに実装されているのですが、ドキュメンテーションが間に合っておらず申し訳ありません。近日中、早ければ明日にはドキュメントを反映できると思うので、お待ちいただければと思います。

@y-ich
Copy link
Contributor Author

y-ich commented Jul 7, 2017

承知しました。ありがとうございます!

@Kiikurage Kiikurage self-assigned this Jul 8, 2017
@Kiikurage
Copy link
Member

Kiikurage commented Jul 8, 2017

I pushed new examples about custom layer in Keras. Please check them.
https://github.com/mil-tokyo/webdnn/tree/master/example/custom_operator/section0


Kerasの自作レイヤをWebDNNで読み込む方法に関するサンプルを追加しました。
https://github.com/mil-tokyo/webdnn/tree/master/example/custom_operator/section0
このサンプルのsection3で扱う内容がご質問の内容に対する回答になっているかと思いますのでご確認ください。

@Kiikurage Kiikurage changed the title [質問] Kerasのカスタムレイヤーを定義するにはどうしたらいいですか? How to use Keras layer implement by myself? Jul 8, 2017
@Kiikurage Kiikurage changed the title How to use Keras layer implement by myself? How to use Keras layer implemented by myself? Jul 8, 2017
@Kiikurage
Copy link
Member

closed

@y-ich
Copy link
Contributor Author

y-ich commented Dec 9, 2017

Could you help me again?

I implemented a Keras custom layer PaddingAdd2D, which adds inputs with same dimension but different number of channels after zero-padding for missing channels.

I tried to make an IR operator, converter handler, and generator handler for this layer, but I may need time to understand a detail of WebDNN^^;

The code of PaddingAdd2D is below.

import tensorflow as tf
from keras.layers import add, Lambda

def PaddingChannel2D(pad):
    def f(input):
        return tf.pad(input, ((0, 0), (0, 0), (0, 0), (0, pad)))
    return Lambda(f)


def PaddingAdd2D():
    def f(inputs):
        channels = list(map(lambda e: K.int_shape(e)[3], inputs))
        max_channels = max(channels)
        padded = []
        for (i, e) in enumerate(inputs):
            pad = max_channels - channels[i]
            if pad == 0:
                padded.append(e)
            else:
                padded.append(PaddingChannel2D(pad)(e))
        return add(padded)
    return f

I will appreciate any suggestions or sample code!

@Kiikurage
Copy link
Member

Kiikurage commented Dec 9, 2017

In my understanding, PaddingAdd2D works as follows.

# X and Y are tf.Tensor instances
print(X.shape)
>>> (1, 16, 16, 8)
print(Y.shape)
>>> (1, 16, 16, 32)

Z = PaddingAdd2D()(X, Y)

print(Z.shape)
>>> (1, 16, 16, 32) 

Here, computation graph of Keras is as follows,

# computation graph of Keras.

X(C=8) -{Lambda}-> X'(C=32) -+
                             +-{Add}-> Z
                          Y -+

and computation graph of TensorFlow is like follows,

# computation graph of TensorFlow

X(C=8) -{tf.Pad}-> X'(C=32) -+
                             +-{tf.Add}-> Z
                          Y -+

Computation graph of TensorFlow is built by Keras internally.

Everything you have to do is implementing converter handler for each operator in these computation graph. I think you don't have to implement any WebDNN operator and generator handler because the PaddingChannel2D can be emulated by operators implemented in WebDNN.

Implement Converter Handler

WebDNN try to convert TensorFlow computation graph by TensorFlowConverter first, and if it failed, try to convert Keras computation graph by KerasConverter.

Therefore, you have two choices.

1. Implement converter handler for tf.Pad

According to TensorFlow source code, tf.pad() function internally create tf.Pad operation in this case.

In fact, WebDNN already has the converter for this operator, but currently, it supports padding only for spatial axis, padding for channel axis is not supported. I'll update this for supporting general padding operation soon. Of course it's ok to try implementing by yourself based on current implementation.

2. Implement converter handler for keras.layers.Lambda

I think this is not the best solution for some reason. First, the lambda layer doesn't know padding size information (Of course, it can be computed from the shape of input variables, but i think it's too ugly). Second, not all lambda layer is performed as padding layer. Therefore I highly recommend you to choose the first solution.

@Kiikurage Kiikurage reopened this Dec 9, 2017
@Kiikurage
Copy link
Member

I've implemented converter handler for tf.pad function, so maybe your custom layer can be converted without any custom handler implementation. Please try conversion with latest WebDNN.

@y-ich
Copy link
Contributor Author

y-ich commented Dec 9, 2017

Thank you for your kind efforts.

I tried convert_keras.py in the latest webdnn and got an error "NameError: name 'tf' is not defined" at the line of "return Lambda(f)" in PaddingChannel2D.

I could not find any places for "import tensorflow as tf" to resolve this error.

@Kiikurage
Copy link
Member

Can you give me complete error log with command you called?

@y-ich
Copy link
Contributor Author

y-ich commented Dec 9, 2017

Here is a command. (Sorry I forgot it and edited)

WEBDNN = /Users/yuji/OpenSources/webdnn/bin/convert_keras.py
PLUGIN = bias.py
OUTDIR = output

$(OUTDIR)/kernels_webassembly.js: model/model.h5 $(PLUGIN)
	CPLUS_INCLUDE_PATH=/usr/local/include/eigen3 python3 $(WEBDNN) $< --plugin $(PLUGIN) --input_shape '(1,361,49)' --input_shape '(1,361,1)' --out $(OUTDIR)

bias.py is a custom layer handler that you made it for me, model/model.h5 is a model that is saved by keras.

Here is an output.
'/Users/yuji/Projects/AQ.js/WebDNN/model/model.py' is a model that I want to convert and includes PaddingAdd2D code.
(I have no idea why convert_keras.py refers it. Information in model.h5 that was generated according to model.py?)

Using TensorFlow backend.
/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/importlib/_bootstrap.py:219: RuntimeWarning: compiletime version 3.5 of module 'tensorflow.python.framework.fast_tensor_util' does not match runtime version 3.6
return f(*args, **kwds)
[convert_keras.py] Generating feedforward graph
Traceback (most recent call last):
File "/Users/yuji/OpenSources/webdnn/bin/convert_keras.py", line 113, in <module>
main()
File "/Users/yuji/OpenSources/webdnn/bin/convert_keras.py", line 60, in main
model = keras.models.load_model(args.kerasmodel, custom_objects=custom_objects, compile=False)
File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/keras/models.py", line 240, in load_model
model = model_from_config(model_config, custom_objects=custom_objects)
File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/keras/models.py", line 314, in model_from_config
return layer_module.deserialize(config, custom_objects=custom_objects)
File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/keras/layers/__init__.py", line 55, in deserialize
printable_module_name='layer')
File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/keras/utils/generic_utils.py", line 140, in deserialize_keras_object
list(custom_objects.items())))
File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/keras/engine/topology.py", line 2500, in from_config
process_node(layer, node_data)
File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/keras/engine/topology.py", line 2457, in process_node
layer(input_tensors[0], **kwargs)
File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/keras/engine/topology.py", line 603, in __call__
output = self.call(inputs, **kwargs)
File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/keras/layers/core.py", line 651, in call
return self.function(inputs, **arguments)
File "/Users/yuji/Projects/AQ.js/WebDNN/model/model.py", line 13, in f
return Lambda(f)
NameError: name 'tf' is not defined

@y-ich
Copy link
Contributor Author

y-ich commented Dec 9, 2017

This problem seems due to keras.
keras-team/keras#5007

I will investigate it.
Thank you so much!

@y-ich y-ich closed this as completed Dec 9, 2017
@y-ich
Copy link
Contributor Author

y-ich commented Dec 10, 2017

Could you help me again?^^

In order to avoid a strange error of load_model, I implemented PaddingAdd (PaddingAdd2d above) as Layer rather than function using Lambda.

import tensorflow as tf
from keras import backend as K
from keras.engine import Layer
from keras.layers import add

class PaddingAdd(Layer):
    """Layer that adds a list of inputs.
    It takes as input a list of tensors,
    all of the same dimension but different channels, and returns
    a single tensor (also of the largest shape).
    """

    def __init__(self, **kwargs):
        super(PaddingAdd, self).__init__(**kwargs)

    def call(self, inputs):
        channels = list(map(lambda e: K.int_shape(e)[-1], inputs))
        max_channels = max(channels)
        padded = []
        for (i, e) in enumerate(inputs):
            pad = max_channels - channels[i]
            if pad == 0:
                padded.append(e)
            else:
                paddings = [[0, 0] for _ in range(len(e.shape))]
                paddings[-1][1] = pad
                padded.append(tf.pad(e, paddings))
        return add(padded)

By specifying plugin, it seems to pass load_model successfully.
But convert_keras.py requires a converter handler since I implemented it as Layer.
How can I write it?

What I tried is,

import sys
import tensorflow as tf
from keras import backend as K
sys.path.append('.')

from webdnn.frontend.keras.converter import KerasConverter
from webdnn.graph.axis import Axis
from webdnn.graph.operators.axiswise_bias import AxiswiseBias
from webdnn.graph.order import OrderNC, OrderC, OrderCN, OrderNTC, OrderNHWC
from model.padding_add import PaddingAdd

@KerasConverter.register_handler("PaddingAdd")
def padding_add_converter_handler(converter, keras_layer):
    keras_inputs = converter.get_input_tensor(keras_layer)
    channels = list(map(lambda e: K.int_shape(e)[-1], keras_inputs))
    max_channels = max(channels)
    inputs = list(map(converter.get_variable, keras_inputs))
    padded = []
    for (i, e) in enumerate(inputs):
        pad = max_channels - channels[i]
        if pad == 0:
            padded.append(e)
        else:
            paddings = [[0, 0] for _ in range(len(e.shape))]
            paddings[-1][1] = pad
            padded.append(tf.pad(e, paddings))
    webdnn_y = add(padded)
    keras_y = converter.get_output_tensor(keras_layer)[0]
    converter.set_variable(keras_y, webdnn_y)

And I got the following error.
I think that applying tf.pad and add to webdnn variables may be wrong, but have no idea what should be applied.

CPLUS_INCLUDE_PATH=/usr/local/include/eigen3 python3 /Users/yuji/OpenSources/webdnn/bin/convert_keras.py model/vn.h5 --plugin bias.py --plugin padding_add.py --input_shape '(1,361,49)' --input_shape '(1,361,1)' --out output
Using TensorFlow backend.
/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/importlib/_bootstrap.py:219: RuntimeWarning: compiletime version 3.5 of module 'tensorflow.python.framework.fast_tensor_util' does not match runtime version 3.6
  return f(*args, **kwds)
[convert_keras.py] Generating feedforward graph
2017-12-10 12:00:47.748583: I tensorflow/core/platform/cpu_feature_guard.cc:137] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2 AVX
Traceback (most recent call last):
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/tensorflow/python/framework/tensor_util.py", line 468, in make_tensor_proto
    str_values = [compat.as_bytes(x) for x in proto_values]
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/tensorflow/python/framework/tensor_util.py", line 468, in <listcomp>
    str_values = [compat.as_bytes(x) for x in proto_values]
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/tensorflow/python/util/compat.py", line 65, in as_bytes
    (bytes_or_text,))
TypeError: Expected binary or unicode string, got <Variable Variable9 shape=(1, 19, 19, 65), order=[N, H, W, C]>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/yuji/OpenSources/webdnn/bin/convert_keras.py", line 113, in <module>
    main()
  File "/Users/yuji/OpenSources/webdnn/bin/convert_keras.py", line 63, in main
    graph = converter.convert(model)
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/keras/converter.py", line 115, in convert
    return self._convert_fallback(model)
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/keras/converter.py", line 129, in _convert_fallback
    self._convert_operator(node.outbound_layer)
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/keras/converter.py", line 162, in _convert_operator
    return super(KerasConverter, self)._convert_operator(k_op)
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/converter.py", line 117, in _convert_operator
    self._handler_map[self.__class__.__name__][operator_key](self, operator)
  File "padding_add.py", line 26, in padding_add_converter_handler
    padded.append(tf.pad(e, paddings))
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/tensorflow/python/ops/array_ops.py", line 1735, in pad
    result = gen_array_ops._pad(tensor, paddings, name=name)
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/tensorflow/python/ops/gen_array_ops.py", line 2927, in _pad
    "Pad", input=input, paddings=paddings, name=name)
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/tensorflow/python/framework/op_def_library.py", line 513, in _apply_op_helper
    raise err
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/tensorflow/python/framework/op_def_library.py", line 510, in _apply_op_helper
    preferred_dtype=default_dtype)
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/tensorflow/python/framework/ops.py", line 926, in internal_convert_to_tensor
    ret = conversion_func(value, dtype=dtype, name=name, as_ref=as_ref)
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/tensorflow/python/framework/constant_op.py", line 229, in _constant_tensor_conversion_function
    return constant(v, dtype=dtype, name=name)
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/tensorflow/python/framework/constant_op.py", line 208, in constant
    value, dtype=dtype, shape=shape, verify_shape=verify_shape))
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/tensorflow/python/framework/tensor_util.py", line 472, in make_tensor_proto
    "supported type." % (type(values), values))
TypeError: Failed to convert object of type <class 'webdnn.graph.variable.Variable'> to Tensor. Contents: <Variable Variable9 shape=(1, 19, 19, 65), order=[N, H, W, C]>. Consider casting elements to a supported type.

@y-ich y-ich reopened this Dec 10, 2017
@Kiikurage
Copy link
Member

Each converter handler converts other framework's operation (e.g. Layer for Keras) into WebDNN operator.

padded.append(tf.pad(e, paddings))

Your implementation converts Keras layer into TensorFlow's Pad operation, so conversion failed. You have to construct WebDNN computation graph there.

Unfortunately, general padding operation is not supported yet. Instead, concatenation operation is used In the converter for tf::ops::Pad. Please see the implementation.

@y-ich
Copy link
Contributor Author

y-ich commented Dec 10, 2017

Thank you!

Are there any ways to allows to fallback to a handler of tf.pad that you implemented previously for this issue?

I understood that I need to write WebDNN computation graph in the converter, but I think that padding is more primitive operation than PaddingAdd.

@Kiikurage
Copy link
Member

In my understanding, you faced to 2 problems.

  1. When you run convert_keras.py with your keras model, the error name "tf" is not found occurred.
  2. ‎You have to implement your custom Keras operator.

And the first one is because of Keras (and it was solved?).

Then, your model can be converted. As default, KerasCoeverter automatically try to convert your model by using TensorFlowConverter. Therefore you don't have to implement any converter handlers. If you cannot convert the model, it is caused by another problem.

Please retry conversion with enviroment variable export DEBUG=1. When TensorFlowConverter failed, the reason will be displayed.

@y-ich
Copy link
Contributor Author

y-ich commented Dec 10, 2017

1. When you run convert_keras.py with your keras model, the error name "tf" is not found occurred.

Yes. And I solved it by implementing a custom layer rather than a function using Lambda. So the second problem that you pointed out is actually the solution for the first problem.

And since I made the custom layer, I needed to pass a plugin for convert_keras.py to succeed to "load_model".
First, I made an empty plugin below that just import Keras custom layer.

from model.padding_add import PaddingAdd

And I got the following error (reproduced with DEBUG=1).

DEBUG=1 CPLUS_INCLUDE_PATH=/usr/local/include/eigen3 python3 /Users/yuji/OpenSources/webdnn/bin/convert_keras.py model/vn.h5 --plugin bias.py --plugin padding_add.py --input_shape '(1,361,49)' --input_shape '(1,361,1)' --out output
Using TensorFlow backend.
/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/importlib/_bootstrap.py:219: RuntimeWarning: compiletime version 3.5 of module 'tensorflow.python.framework.fast_tensor_util' does not match runtime version 3.6
  return f(*args, **kwds)
[convert_keras.py] Generating feedforward graph
2017-12-10 14:42:43.207456: I tensorflow/core/platform/cpu_feature_guard.cc:137] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2 AVX
[DEBUG] Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/keras/converter.py", line 108, in convert
    return TensorFlowConverter(session=K.get_session(), batch_size=self._batch_size).convert(model.inputs, model.outputs)
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/tensorflow/converter.py", line 103, in convert
    OptimizeRuleGroup([ConstantFolding()], repeat=True).optimize(sub_graph)
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/graph/optimize_rule.py", line 110, in optimize
    graph, flag_changed = sub_rule.optimize(graph)
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/optimizer/sub_rules/constant_folding.py", line 31, in optimize
    op.fold_constance(graph)
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/graph/operators/reshape.py", line 86, in fold_constance
    new_y = ConstantVariable(x.copy().change_order(in_order).data.reshape(out_shape), out_order)
TypeError: 'float' object cannot be interpreted as an integer

[DEBUG] [KerasConverter] TensorflowConverter failed to convert.
Traceback (most recent call last):
  File "/Users/yuji/OpenSources/webdnn/bin/convert_keras.py", line 113, in <module>
    main()
  File "/Users/yuji/OpenSources/webdnn/bin/convert_keras.py", line 63, in main
    graph = converter.convert(model)
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/keras/converter.py", line 115, in convert
    return self._convert_fallback(model)
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/keras/converter.py", line 129, in _convert_fallback
    self._convert_operator(node.outbound_layer)
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/keras/converter.py", line 162, in _convert_operator
    return super(KerasConverter, self)._convert_operator(k_op)
  File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/converter.py", line 115, in _convert_operator
    raise NotImplementedError(f"Operator '{operator_key}' is not handled any converter handlers.")
NotImplementedError: Operator 'PaddingAdd' is not handled any converter handlers.

It seems that I need to resolve "TypeError: 'float' object cannot be interpreted as an integer".

@Kiikurage
Copy link
Member

Thanks. I understand the real problem. It seeas to be the bug of TensorFlowConverter handler, which called reshape operator with shape of float values. I'll fix it soon.

@Kiikurage
Copy link
Member

Can you give me the output of model.summary of your keras moeel?

@y-ich
Copy link
Contributor Author

y-ich commented Dec 10, 2017

Here is an output of model.summary.

__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
vn_x (InputLayer)               (None, 361, 49)      0                                            
__________________________________________________________________________________________________
vn_c (InputLayer)               (None, 361, 1)       0                                            
__________________________________________________________________________________________________
concat (Concatenate)            (None, 361, 50)      0           vn_x[0][0]                       
                                                                 vn_c[0][0]                       
__________________________________________________________________________________________________
Reshape (Reshape)               (None, 19, 19, 50)   0           concat[0][0]                     
__________________________________________________________________________________________________
conv0 (Conv2D)                  (None, 19, 19, 65)   81315       Reshape[0][0]                    
__________________________________________________________________________________________________
conv1 (Conv2D)                  (None, 19, 19, 80)   46880       conv0[0][0]                      
__________________________________________________________________________________________________
conv2 (Conv2D)                  (None, 19, 19, 95)   68495       conv1[0][0]                      
__________________________________________________________________________________________________
conv3 (Conv2D)                  (None, 19, 19, 110)  94160       conv2[0][0]                      
__________________________________________________________________________________________________
padding_add_1 (PaddingAdd)      [(None, 19, 19, 110) 0           conv3[0][0]                      
                                                                 conv0[0][0]                      
__________________________________________________________________________________________________
conv4 (Conv2D)                  (None, 19, 19, 125)  123875      padding_add_1[0][0]              
__________________________________________________________________________________________________
conv5 (Conv2D)                  (None, 19, 19, 140)  157640      conv4[0][0]                      
__________________________________________________________________________________________________
conv6 (Conv2D)                  (None, 19, 19, 155)  195455      conv5[0][0]                      
__________________________________________________________________________________________________
padding_add_2 (PaddingAdd)      [(None, 19, 19, 155) 0           conv6[0][0]                      
                                                                 padding_add_1[0][0]              
__________________________________________________________________________________________________
conv7 (Conv2D)                  (None, 19, 19, 170)  237320      padding_add_2[0][0]              
__________________________________________________________________________________________________
conv8 (Conv2D)                  (None, 19, 19, 185)  283235      conv7[0][0]                      
__________________________________________________________________________________________________
conv9 (Conv2D)                  (None, 19, 19, 200)  333200      conv8[0][0]                      
__________________________________________________________________________________________________
padding_add_3 (PaddingAdd)      [(None, 19, 19, 200) 0           conv9[0][0]                      
                                                                 padding_add_2[0][0]              
__________________________________________________________________________________________________
average_pooling2d_1 (AveragePoo (None, 10, 10, 200)  0           padding_add_3[0][0]              
__________________________________________________________________________________________________
conv10 (Conv2D)                 (None, 10, 10, 220)  396220      average_pooling2d_1[0][0]        
__________________________________________________________________________________________________
conv11 (Conv2D)                 (None, 10, 10, 240)  475440      conv10[0][0]                     
__________________________________________________________________________________________________
conv12 (Conv2D)                 (None, 10, 10, 260)  561860      conv11[0][0]                     
__________________________________________________________________________________________________
padding_add_4 (PaddingAdd)      [(None, 10, 10, 260) 0           conv12[0][0]                     
                                                                 average_pooling2d_1[0][0]        
__________________________________________________________________________________________________
conv13 (Conv2D)                 (None, 10, 10, 280)  655480      padding_add_4[0][0]              
__________________________________________________________________________________________________
conv14 (Conv2D)                 (None, 10, 10, 300)  756300      conv13[0][0]                     
__________________________________________________________________________________________________
padding_add_5 (PaddingAdd)      [(None, 10, 10, 300) 0           conv14[0][0]                     
                                                                 padding_add_4[0][0]              
__________________________________________________________________________________________________
average_pooling2d_2 (AveragePoo (None, 5, 5, 300)    0           padding_add_5[0][0]              
__________________________________________________________________________________________________
conv15 (Conv2D)                 (None, 5, 5, 430)    1161430     average_pooling2d_2[0][0]        
__________________________________________________________________________________________________
conv16 (Conv2D)                 (None, 5, 5, 560)    2167760     conv15[0][0]                     
__________________________________________________________________________________________________
conv17 (Conv2D)                 (None, 5, 5, 690)    3478290     conv16[0][0]                     
__________________________________________________________________________________________________
padding_add_6 (PaddingAdd)      [(None, 5, 5, 690),  0           conv17[0][0]                     
                                                                 average_pooling2d_2[0][0]        
__________________________________________________________________________________________________
average_pooling2d_3 (AveragePoo (None, 1, 1, 690)    0           padding_add_6[0][0]              
__________________________________________________________________________________________________
fc1 (Conv2D)                    (None, 1, 1, 64)     44160       average_pooling2d_3[0][0]        
__________________________________________________________________________________________________
reshape_1 (Reshape)             (None, 1, 64)        0           fc1[0][0]                        
__________________________________________________________________________________________________
fc1/bias (Bias)                 (None, 1, 64)        64          reshape_1[0][0]                  
__________________________________________________________________________________________________
fc2 (Dense)                     (None, 1, 1)         65          fc1/bias[0][0]                   
==================================================================================================
Total params: 11,318,644
Trainable params: 11,318,644
Non-trainable params: 0
__________________________________________________________________________________________________

@Kiikurage
Copy link
Member

Thanks. I've fixed the error. Please retry with latest revision of WebDNN.

@y-ich
Copy link
Contributor Author

y-ich commented Dec 10, 2017

Thank you for your efforts!

Here is an output.

DEBUG=1 CPLUS_INCLUDE_PATH=/usr/local/include/eigen3 python3 /Users/yuji/OpenSources/webdnn/bin/convert_keras.py model/vn.h5 --plugin bias.py --plugin padding_add.py --input_shape '(1,361,49)' --input_shape '(1,361,1)' --out output
Using TensorFlow backend.
/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/importlib/_bootstrap.py:219: RuntimeWarning: compiletime version 3.5 of module 'tensorflow.python.framework.fast_tensor_util' does not match runtime version 3.6
  return f(*args, **kwds)
[convert_keras.py] Generating feedforward graph
2017-12-10 16:17:42.066888: I tensorflow/core/platform/cpu_feature_guard.cc:137] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2 AVX
[DEBUG] [OptimizeRule] apply: ConstantFolding
[DEBUG] Traceback (most recent call last):
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/keras/converter.py", line 108, in convert
    return TensorFlowConverter(session=K.get_session(), batch_size=self._batch_size).convert(model.inputs, model.outputs)
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/tensorflow/converter.py", line 96, in convert
    self._convert_operator(op)
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/converter.py", line 117, in _convert_operator
    self._handler_map[self.__class__.__name__][operator_key](self, operator)
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/tensorflow/ops/gen_array_ops.py", line 644, in strided_slice_handler
    y = x[slices]
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/graph/variable.py", line 357, in __getitem__
    return webdnn.graph.operators.slice.Slice(None, indices=indices)(self)[0]
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/graph/operators/slice.py", line 112, in __call__
    """)
NotImplementedError: 
[Slice] Accessing to one element is not supported:
    (indices) = {?54:0} 


[DEBUG] [KerasConverter] TensorflowConverter failed to convert.
Traceback (most recent call last):
  File "/Users/yuji/OpenSources/webdnn/bin/convert_keras.py", line 113, in <module>
    main()
  File "/Users/yuji/OpenSources/webdnn/bin/convert_keras.py", line 63, in main
    graph = converter.convert(model)
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/keras/converter.py", line 115, in convert
    return self._convert_fallback(model)
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/keras/converter.py", line 129, in _convert_fallback
    self._convert_operator(node.outbound_layer)
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/keras/converter.py", line 162, in _convert_operator
    return super(KerasConverter, self)._convert_operator(k_op)
  File "/Users/yuji/.virtualenvs/py3tensorflow/lib/python3.6/site-packages/webdnn-1.1.0-py3.6.egg/webdnn/frontend/converter.py", line 115, in _convert_operator
    raise NotImplementedError(f"Operator '{operator_key}' is not handled any converter handlers.")
NotImplementedError: Operator 'PaddingAdd' is not handled any converter handlers.

@Kiikurage
Copy link
Member

Oh... Sorry to let you down, but implementing converter handler by yourself is required.
Somewhere in your model, slicing which generates scalar value is used like follows

x = tf.placeholder(np.float32, (2, 3, 4, 5)) # x is tf.Tensor
y = tf.shape(x)[0]  # y is tf.Tensor, whose shape is (1,) and value is [2]. This is slicing operation to scalar value

Currently WebDNN does not support operations like this.

I think follow form may works (not sure).

x = tf.placeholder(np.float32, (2, 3, 4, 5)) # x is tf.Tensor
y = tf.shape(x)[0:1]  # slicing with range

@y-ich
Copy link
Contributor Author

y-ich commented Dec 10, 2017

Could you give me clues?

I am using K.int_shape(input)[-1] to get a number of channels. I think that it should be an integer variable rather than a placeholder because, for example, I need it to specify size of pad for tf.pad.
Is K.int_shape(input)[-1] not good for WebDNN converter?

If so, I will try hard coding about numbers of channels.

@Kiikurage
Copy link
Member

Kiikurage commented Dec 10, 2017

I think it has nothing to do about this problem.

class PaddingAdd(Layer):
    """Layer that adds a list of inputs.
    It takes as input a list of tensors,
    all of the same dimension but different channels, and returns
    a single tensor (also of the largest shape).
    """

    def __init__(self, **kwargs):
        super(PaddingAdd, self).__init__(**kwargs)

    def call(self, inputs):
        channels = list(map(lambda e: K.int_shape(e)[-1], inputs))
        max_channels = max(channels)
        padded = []
        for (i, e) in enumerate(inputs):
            pad = max_channels - channels[i]
            if pad == 0:
                padded.append(e)
            else:
                paddings = [[0, 0] for _ in range(len(e.shape))]
                paddings[-1][1] = pad
                padded.append(tf.pad(e, paddings))
        return add(padded)

This is your custom layer. Here, paddings is list of integers, not tf.Tensor, and any computation graph information is not included there.

For example,

# Keras Computation Graph
x1 -+
    +-{PaddingAdd}- y
x2 -+

In this Keras computation graph, follow TF computation graph is built.

# TF Computation Graph
{Constant}- paddings -+
                      +-{Pad}- padded_x1 -+
                  x1 -+                   |
                                          +-{Add}- y
                                      x2 -+

Here, no slicing operation appeared. Of course as you said, K.int_shape(input)[-1] is performed while TensorFlow builds the computation graph, but the result of that operation is regarded as Constant Tensor in computation graph.

@y-ich
Copy link
Contributor Author

y-ich commented Dec 10, 2017

Ok, so I should make simpler test cases of not only PaddingAdd but also other layers and search the part of problem, right?

Thank you for your collaboration!

@y-ich
Copy link
Contributor Author

y-ich commented Dec 11, 2017

Could you help me again?

I made a simple reproduction code. (https://gist.github.com/y-ich/db973fc2a1a1736adf5570a22e87902f)

PaddingChannel in padding_channel.py is the simplest, general layer for padding channel.
And I could convert it when connecting with an input directly.
But when I connected it via Reshape layer, the error occurred.

What do you think about the cause?
Since I have no knowledge about interconnect part of IR, I am stuck here.

@y-ich
Copy link
Contributor Author

y-ich commented Dec 13, 2017

Writing a converter handler solved this problem.
Thank you!

import sys
import numpy as np
from webdnn.frontend.keras.converter import KerasConverter
from webdnn.graph.axis import Axis
from webdnn.graph.operators.concat import Concat
from webdnn.graph.operators.zero_padding_2d import ZeroPadding2D
from webdnn.graph.order import OrderNHWC
from webdnn.graph.variables.constant_variable import ConstantVariable

sys.path.append('.')

from padding_channel import PaddingChannel

@KerasConverter.register_handler("PaddingChannel")
def padding_channel_converter_handler(converter, keras_layer):
    x = converter.get_variable(converter.get_input_tensor(keras_layer)[0])

    paddings = [[0, 0] for _ in range(len(x.shape))]
    paddings[-1][1] = keras_layer.padding

    for axis, (pad_begin, pad_end) in zip(x.order.axes, paddings):
        xs = []
        xs.append(x)
        if pad_end > 0:
            xs.append(ConstantVariable(np.zeros([pad_end if a is axis else x.shape_dict[a] for a in x.order.axes]), x.order))

        if len(xs) > 1:
            x, = Concat(None, axis=axis)(*xs)

    y = x

    converter.set_variable(converter.get_output_tensor(keras_layer)[0], y)

@y-ich y-ich closed this as completed Dec 13, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants