Repository: adap/flower

Issue:
Error when converting model to .tflite
Hello @akhilmathurs ,
I'm trying to run the_tflite.py for the example provided on https://flower.dev/blog/2021-12-15-federated-learning-on-android-devices-with-flower
I'm getting the following error:
`flower/examples/android/android/tflite_convertor/tfltransfer/heads/keras_model_head.py", line 51, in __init__
    input_def = next(self._predict_signature.inputs.values().__iter__())
AttributeError: 'list' object has no attribute 'values'
`
I didn't do any modifications, how can I solve this issue?


Discussion:
I would try to replicate the issue and debug what is happening here.

The steps for reproducing this issue are shown next:

```bash
cd examples/android
python3 -m poetry install
python3 -m poetry run python3 tflite_convertor/convert_to_tflite.py
```

Which shows the next console output:

```text
2022-01-06 16:22:15.508872: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2022-01-06 16:22:15.625521: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
Traceback (most recent call last):
  File "tflite_convertor/convert_to_tflite.py", line 54, in <module>
    10, base_path, heads.KerasModelHead(head), optimizers.SGD(1e-3), train_batch_size=32
  File "/home/tbd/dev/flower/examples/android/tflite_convertor/tfltransfer/heads/keras_model_head.py", line 51, in __init__
    input_def = next(self._predict_signature.inputs.values().__iter__())
AttributeError: 'list' object has no attribute 'values'
```

The presence of this `values()` refers to some _Tensorflow v1_ function calls.

```python
$ grep -iRn "tensorflow.compat"                                                                                                                                                                                                                                           
utils.py:20:from tensorflow.compat import v1 as tfv1
optimizers/sgd.py:21:import tensorflow.compat.v1 as tfv1
optimizers/adam.py:21:import tensorflow.compat.v1 as tfv1
tflite_transfer_converter.py:29:from tensorflow.compat import v1 as tfv1
heads/keras_model_head.py:25:from tensorflow.compat import v1 as tfv1
heads/softmax_classifier_head.py:22:from tensorflow.compat import v1 as tfv1
heads/logits_saved_model_head.py:25:from tensorflow.compat import v1 as tfv1
```

Resolving patch:
diff --git a/examples/android/tflite_convertor/tfltransfer/heads/keras_model_head.py b/examples/android/tflite_convertor/tfltransfer/heads/keras_model_head.py
--- a/examples/android/tflite_convertor/tfltransfer/heads/keras_model_head.py
+++ b/examples/android/tflite_convertor/tfltransfer/heads/keras_model_head.py
@@ -40,29 +40,31 @@ class KerasModelHead(object):
     def __init__(self, keras_model):
         # Convert Keras model to SavedModel.
         saved_model_dir = tempfile.mkdtemp("tflite-transfer-keras-model")
-        keras_model.save(saved_model_dir, save_format="tf")
+        tf.compat.v1.keras.experimental.export_saved_model(keras_model, saved_model_dir)
 
         # Pre-fetch some information about the model.
-        loaded_model = tf.saved_model.load(
-            saved_model_dir, tags=[tf.saved_model.SERVING]
-        )
-        self._predict_signature = loaded_model.signatures["serving_default"]
-
-        input_def = next(self._predict_signature.inputs.values().__iter__())
-        self._input_shape = tuple(dim.size for dim in input_def.tensor_shape.dim[1:])
+        with tfv1.Session(graph=tf.Graph()) as sess:
+            metagraph = tfv1.saved_model.load(
+                sess, [tf.saved_model.SERVING], saved_model_dir
+            )
+            self._predict_signature = metagraph.signature_def.get("serving_default")
 
-        variables = keras_model.variables
-        self._variable_names = [variable.name for variable in variables]
-        self._initial_params = [variable.eval() for variable in variables]
-        trainable_variables = keras_model.trainable_variables
-        self._trainable_variable_names = [
-            variable.name for variable in trainable_variables
-        ]
+            input_def = next(self._predict_signature.inputs.values().__iter__())
+            self._input_shape = tuple(
+                dim.size for dim in input_def.tensor_shape.dim[1:]
+            )
 
-        loaded_model = tf.saved_model.load(
-            saved_model_dir, tags=[tf.saved_model.SERVING]
-        )
-        self._eval_signature = loaded_model.signatures["serving_default"]
+            variables = tfv1.global_variables()
+            self._variable_names = [variable.name for variable in variables]
+            self._initial_params = [variable.eval() for variable in variables]
+            trainable_variables = keras_model.trainable_variables
+            self._trainable_variable_names = [
+                variable.name for variable in trainable_variables
+            ]
+
+        with tfv1.Session(graph=tf.Graph()) as sess:
+            eval_metagraph = tfv1.saved_model.load(sess, ["eval"], saved_model_dir)
+            self._eval_signature = eval_metagraph.signature_def.get("eval")
 
         if len(self._predict_signature.inputs) != 1:
             raise ValueError("Only single-input head models are supported")
diff --git a/examples/android/tflite_convertor/tfltransfer/heads/logits_saved_model_head.py b/examples/android/tflite_convertor/tfltransfer/heads/logits_saved_model_head.py
--- a/examples/android/tflite_convertor/tfltransfer/heads/logits_saved_model_head.py
+++ b/examples/android/tflite_convertor/tfltransfer/heads/logits_saved_model_head.py
@@ -44,15 +44,18 @@ def __init__(
         self.signature_key = signature_key
 
         # Pre-fetch some information about the model.
-        loaded_model = tf.saved_model.load(model_dir, tags=[tag])
-        self._signature = loaded_model.signatures[signature_key]
+        with tfv1.Session(graph=tf.Graph()) as sess:
+            metagraph = tfv1.saved_model.load(sess, [tag], model_dir)
+            self._signature = metagraph.signature_def.get(signature_key)
 
-        input_def = next(self._signature.inputs.values().__iter__())
-        self._input_shape = tuple(dim.size for dim in input_def.tensor_shape.dim[1:])
+            input_def = next(self._signature.inputs.values().__iter__())
+            self._input_shape = tuple(
+                dim.size for dim in input_def.tensor_shape.dim[1:]
+            )
 
-        variables = tfv1.global_variables()
-        self._variable_names = [variable.name for variable in variables]
-        self._initial_params = [variable.eval() for variable in variables]
+            variables = tfv1.global_variables()
+            self._variable_names = [variable.name for variable in variables]
+            self._initial_params = [variable.eval() for variable in variables]
 
         if len(self._signature.inputs) != 1:
             raise ValueError("Only single-input head models are supported")


Test patch:
diff --git a/examples/android/tflite_convertor/tfltransfer/model_correctness_test.py b/examples/android/tflite_convertor/tfltransfer/model_correctness_test.py
--- a/examples/android/tflite_convertor/tfltransfer/model_correctness_test.py
+++ b/examples/android/tflite_convertor/tfltransfer/model_correctness_test.py
@@ -23,6 +23,7 @@
 
 import numpy as np
 import tensorflow as tf
+from tensorflow.compat import v1 as tfv1
 
 # pylint: disable=g-bad-import-order
 from tfltransfer import bases
@@ -364,7 +365,7 @@ def setUpClass(cls):
             include_top=False,
             weights="imagenet",
         )
-        mobilenet_keras.save(mobilenet_dir, save_format="tf")
+        tfv1.keras.experimental.export_saved_model(mobilenet_keras, mobilenet_dir)
         cls.mobilenet_dir = mobilenet_dir
 
     def setUp(self):
diff --git a/examples/android/tflite_convertor/tfltransfer/tflite_transfer_converter_test.py b/examples/android/tflite_convertor/tfltransfer/tflite_transfer_converter_test.py
--- a/examples/android/tflite_convertor/tfltransfer/tflite_transfer_converter_test.py
+++ b/examples/android/tflite_convertor/tfltransfer/tflite_transfer_converter_test.py
@@ -21,6 +21,7 @@
 import unittest
 
 import tensorflow as tf
+from tensorflow.compat import v1 as tfv1
 from tensorflow.keras import layers
 from tensorflow.keras.regularizers import l2
 
@@ -46,7 +47,7 @@ def setUpClass(cls):
             [layers.Dense(units=DEFAULT_INPUT_SIZE, input_shape=(DEFAULT_INPUT_SIZE,))]
         )
         model.build()
-        model.save(cls._default_base_model_dir, save_format="tf")
+        tfv1.keras.experimental.export_saved_model(model, cls._default_base_model_dir)
 
     def setUp(self):
         super(TestTfliteTransferConverter, self).setUp()