Repository: apache/tvm

Issue:
TVMError: relay.frontend.from_keras() fails with models compiled in tensorflow 2.3
The following error occurs when feeding a tensorflow 2.3 compiled model to relay.frontend.from_keras()

`AttributeError: 'Node' object has no attribute 'node_indices'`

The following code can be used to reproduce the error.

```
import numpy as np
import tensorflow as tf
import tensorflow.keras as keras
from matplotlib import pyplot as plt
from PIL import Image
from tensorflow.keras.applications.resnet50 import preprocess_input

import tvm
import tvm.relay as relay
from tvm import te
from tvm.contrib.download import download_testdata

keras_resnet50 = keras.applications.resnet50.ResNet50(
    include_top=True, input_shape=(224, 224, 3), classes=1000
)

img_url = "https://github.com/dmlc/mxnet.js/blob/master/data/cat.png?raw=true"
img_path = download_testdata(img_url, "cat.png", module="data")
img = Image.open(img_path).resize((224, 224))

# input preprocess
data = np.array(img)[np.newaxis, :].astype("float32")
data = preprocess_input(data).transpose([0, 3, 1, 2])
print("input_1", data.shape)

shape_dict = {"input_1": data.shape}
mod, params = relay.frontend.from_keras(keras_resnet50, shape_dict)
```


**Reason** 

The issue occurs due to changes in the attributes of the Node class in [tensorflow 2.3](https://github.com/tensorflow/tensorflow/blob/r2.3/tensorflow/python/keras/engine/node.py) as compared to [tensorflow 2.2](https://github.com/tensorflow/tensorflow/blob/r2.2/tensorflow/python/keras/engine/node.py)

**Environment**:

- Python : 3.7.7
- Tensorflow : 2.3
- TVM : 0.7.dev1
- OS : Ubuntu 16.04


Discussion:

Resolving patch:
diff --git a/python/tvm/relay/frontend/keras.py b/python/tvm/relay/frontend/keras.py
--- a/python/tvm/relay/frontend/keras.py
+++ b/python/tvm/relay/frontend/keras.py
@@ -1073,11 +1073,18 @@ def _convert_input_layer(keras_layer):
                 # The one exception is InputLayer. Changing input variable names after conversion
                 # would confuse users, so we should keep them as far as possible. Fortunately,
                 # they are named uniquely to input_1, input_2, input_3... by default.
-                zip_node = zip(
-                    _as_list(node.node_indices),
-                    _as_list(node.tensor_indices),
-                    _as_list(node.inbound_layers))
-                for n_idx, t_idx, inbound_layer in zip_node:
+                # node_indices attribute removed in tensorflow 2.3, however iterate_inbound() can
+                # be used
+                if hasattr(node, 'node_indices'):
+                    zip_node = zip(
+                        _as_list(node.inbound_layers),
+                        _as_list(node.node_indices),
+                        _as_list(node.tensor_indices),
+                        _as_list(node.input_tensors))
+                    node_attributes = zip_node
+                else:
+                    node_attributes = node.iterate_inbound()
+                for inbound_layer, n_idx, t_idx, _ in node_attributes:
                     if isinstance(inbound_layer, input_layer_class):
                         expr_name = inbound_layer.name
                         _convert_input_layer(inbound_layer)


Test patch: