chainer / onnx-chainer

Add-on package for ONNX format support in Chainer
MIT License
85 stars 24 forks source link

Validation Error "Nodes in a graph must be topologically sorted", tryed to export stylegan generator #214

Closed knok closed 5 years ago

knok commented 5 years ago

I tried to export chainer-stylegan generator, but not work.

code: https://gist.github.com/knok/5b240ac74a901ad16344758bb6b90fd2 (place it to src/stylegan, put pretraind model in current directory)

The actutal error is the following: Stack trace:

Traceback (most recent call last):
  File "src/stylegan/sg-onnx.py", line 160, in <module>
    onnx_generator = onnx_chainer.export(generator, w, filename="stylegan_gen.onnx")
  File "/opt/p3/lib/python3.7/site-packages/onnx_chainer/export.py", line 191, in export
    external_converters, external_opset_imports)
  File "/opt/p3/lib/python3.7/site-packages/onnx_chainer/export.py", line 334, in _export
    raise e
  File "/opt/p3/lib/python3.7/site-packages/onnx_chainer/export.py", line 331, in _export
    checker.check_model(model)
  File "/opt/p3/lib/python3.7/site-packages/onnx/checker.py", line 86, in check_model
    C.check_model(model.SerializeToString())
onnx.onnx_cpp2py_export.checker.ValidationError: Nodes in a graph must be topologically sorted, however input 'v476' of node: 
input: "BroadcastTo_1" input: "v476" output: "Mul_0" name: "Mul_0" op_type: "Mul"
 is not output of any previous nodes.

I don't know where is the node in the source code. Is the problem is needless computation node?

disktnk commented 5 years ago

Thank you for your report and reproducible source. I could reproduce the errors. The error says a node Mul_0 is set input v476, but the v476 is not found.

The error comes from the below code.

https://github.com/pfnet-research/chainer-stylegan/blob/master/src/stylegan/net.py#L78-L79

v476 seems to be noise variable. noise is made within NoiseBlock and weak reference of noise is lost outside then noise = v476 is not found on exporting. Such a temporary variable is supported partially with #202 , but the PR only handles FunctionNode. In this case, the temporary value noise is for self.b (Scale) input, this is chainer.Chain class.

The following NoiseBlock and _retain is a hot fix to output ONNX graph.

_retain = []

class NoiseBlock(chainer.Chain):
    def __init__(self, ch):
        # ...(snip)

    def get_noise(self, batch_size, ch, shape):
        # ...(snip)

    def __call__(self, h):
        batch_size = h.shape[0]
        noise = self.get_noise(batch_size, self.ch, h.shape[2:])
        noise = chainer.Variable(noise)  # as variable, see issue #216 
        _retain.append(noise) # to keep reference
        h = h + self.b(noise)
        return h

Please attention that this patching is not perfect because values of noise are fixed, not "true" noise. If you want to output get_noise as ONNX graph, you need to replace the method to chainer.functions, but current ONNX-Chainer does not support RandomNormal.

disktnk commented 5 years ago

fixed by #221