<jupyter_start><jupyter_text>Eager Execution in TensorFlow 2.XEstaimted time needed: **15** minutes ObjectivesAfter completing this lab you will be able to:-   Understand the impact of eager execution and the need to enable it In this notebook we will overview Eager Execution in  TensorFlow 2.xTable of Contents    Instructions    Eager Execution    Tensorflow Operations Without Eager Execution Mode    Tensorflow Operations With Eager Execution Mode    Dynamic Control Flow    Thank You  Instructions  Installing TensorFlow We begin by installing TensorFlow version 2.2.0 and its required prerequistes.<jupyter_code>!pip install grpcio==1.24.3
!pip install tensorflow==2.2.0<jupyter_output><empty_output><jupyter_text>Notice: This notebook has been created with TensorFlow version 2.2, and might not work with other versions. Therefore we check:<jupyter_code>import tensorflow as tf
if not tf.__version__ == '2.2.0':
    print(tf.__version__)
    raise ValueError('please upgrade to TensorFlow 2.2.0, or restart your Kernel (Kernel->Restart & Clear Output)')<jupyter_output><empty_output><jupyter_text>Eager Execution TensorFlow's **eager execution** is an imperative programming environment that evaluates operations immediately, without building graphs, operations return concrete values instead of constructing a computational graph to run later. This makes it easy to get started with TensorFlow and debug models. With **TensorFlow 2.x**, **Eager Execution is enabled by default**. This allows TensorFlow code to be executed and evaluated line by line. Before version 2.x was released, every graph had to be run wihthin a TensorFlow **session**. This only allowed for the entire graph to be run all at once. This made it hard to debug the computation graph.  Eager execution is a flexible machine learning platform for research and experimentation, providing:-   **An intuitive interface**-Structure your code naturally and use Python data structures. Quickly iterate on small models and   small data.-   **Easier debugging**- Execute operations directly to inspect code line by line and test changes. Use standard Python debugging tools for immediate error reporting.-   **Natural control flow**—Use Python control flow instead of graph control flow, simplifying the specification of dynamic models. As I mentioned above, in **Tensorflow 2.x**, eager execution is enabled by default. To verify that please run the below code.<jupyter_code>tf.executing_eagerly()<jupyter_output><empty_output><jupyter_text>Now you can run TensorFlow operations and the results will return immediately: But first let me show you how things get done without the eager execution in tensorflow.  Tensorflow Operations Without Eager Execution Mode So, there is a **disable_eager_execution()** function in TensorFlow 2.x. You can call the function like this:<jupyter_code>from tensorflow.python.framework.ops import disable_eager_execution
disable_eager_execution()<jupyter_output><empty_output><jupyter_text>Note: This function can only be called at the beginning before any Graphs, Ops, or Tensors have been  created. Now, verify that the eager execution is disabled or not by running the below code.<jupyter_code>tf.executing_eagerly()<jupyter_output><empty_output><jupyter_text>As you can see **False** in the output that means it is disabled now. Just execute the next cell. You will notice that we've created an object **a** of type **tensorflow.python.framework.ops.Tensor**<jupyter_code>import numpy as np
a = tf.constant(np.array([1., 2., 3.]))
type(a)<jupyter_output><empty_output><jupyter_text>Let's create another Tensor **b** and apply the dot product between them. This gives us **c**<jupyter_code>b = tf.constant(np.array([4.,5.,6.]))
c = tf.tensordot(a, b, 1)
type(c)
print(c)<jupyter_output><empty_output><jupyter_text>Note that **c** is a **tensorflow.python.framework.ops.Tensor** as well. So any node of the execution graph resembles a Tensor type. **But so far not a single computation happened**. You need to execute the graph. You can pass any graph or subgraph to the TensorFlow runtime for execution. Each TensorFlow graph runs within a TensorFlow Session, therefore we need to create it first: **Note:** Session can be accessed via **tf.compat.v1.Session()** in Tensorflow 2.x.<jupyter_code>session = tf.compat.v1.Session()
output = session.run(c)
session.close()
print(output)<jupyter_output><empty_output><jupyter_text>Now you see the correct result of 32. But the problem is that debugging is pretty hard if you can only run complete graphs. So let's actually re-enable the **eager execution**.  Tensorflow Operations With Eager Execution Mode  IMPORTANT! => Please don't forget restart the kernel by clicking on "Kernel"->"Restart" so that the changes take effect.**Enable or Disable Eager execution has to happen on program startup. This is the reason we have to restart the kernel.**  Import the required libraries again.<jupyter_code>import tensorflow as tf
import numpy as np<jupyter_output><empty_output><jupyter_text>Run the below command to re-enable the eager execution.<jupyter_code>from tensorflow.python.framework.ops import enable_eager_execution
enable_eager_execution()<jupyter_output><empty_output><jupyter_text>Now you can run TensorFlow operations and the results will return immediately:<jupyter_code>x = [[4]]
m = tf.matmul(x, x)
print("Result, {}".format(m))<jupyter_output><empty_output><jupyter_text>Enabling eager execution changes how TensorFlow operations behave—now they immediately evaluate and return their values to Python. Since there isn't a computational graph to build and run later in a session, it's easy to inspect results using print() or a debugger.<jupyter_code>a = tf.constant(np.array([1., 2., 3.]))
type(a)<jupyter_output><empty_output><jupyter_text>So the very same code created a different type of object. So now **a** is of type **tensorflow.python.framework.ops.EagerTensor**. This is great, because without changing code we obtain a tensor object which allows us to have a look inside, without execting a graph in a session:<jupyter_code>print(a.numpy())<jupyter_output><empty_output><jupyter_text>Isn't this amazing? So from now on we can threat Tensors like ordinary python objects, work with them as usual, insert debug statements at any point or even use a debugger. So let's continue this example:<jupyter_code>b = tf.constant(np.array([4.,5.,6.]))
c = tf.tensordot(a, b,1)
type(c)<jupyter_output><empty_output><jupyter_text>Again, **c** is an **tensorflow.python.framework.ops.EagerTensor** object which can be directly read:<jupyter_code>print(c.numpy())<jupyter_output><empty_output><jupyter_text>Without creating a session or a graph we obtained the result of the defined computation.  Dynamic Control FlowA major benefit of eager execution is that all the functionality of the host language is available while your model is executing. So, for example, it is easy to write [fizzbuzz](https://en.wikipedia.org/wiki/Fizz_buzz?cm_mmc=Email_Newsletter-_-Developer_Ed%2BTech-_-WW_WW-_-SkillsNetwork-Courses-IBMDeveloperSkillsNetwork-DL0120EN-SkillsNetwork-20629446&cm_mmca1=000026UJ&cm_mmca2=10006555&cm_mmca3=M12345678&cvosrc=email.Newsletter.M12345678&cvo_campaign=000026UJ&cm_mmc=Email_Newsletter-_-Developer_Ed%2BTech-_-WW_WW-_-SkillsNetwork-Courses-IBMDeveloperSkillsNetwork-DL0120EN-SkillsNetwork-20629446&cm_mmca1=000026UJ&cm_mmca2=10006555&cm_mmca3=M12345678&cvosrc=email.Newsletter.M12345678&cvo_campaign=000026UJ):<jupyter_code>def fizzbuzz(max_num):
  counter = tf.constant(0)
  max_num = tf.convert_to_tensor(max_num)
  for num in range(1, max_num.numpy()+1):
    num = tf.constant(num)
    if int(num % 3) == 0 and int(num % 5) == 0:
      print('FizzBuzz')
    elif int(num % 3) == 0:
      print('Fizz')
    elif int(num % 5) == 0:
      print('Buzz')
    else:
      print(num.numpy())
    counter += 1
fizzbuzz(15)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08604592480444108, 'fraction_numerical': 0.020691395407519558, 'mean_word_length': 5.810137457044673, 'pattern_counts': {'":': 0, '<': 65, '<?xml version=': 0, '>': 68, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '356541', 'license': 'MIT', 'n_tokens_mistral': 2432, 'n_tokens_neox': 2279, 'n_words': 870, 'path': 'ML0120EN-Eager_Execution.ipynb', 'repo_name': 'indervirbanipal/building-deep-learning-models-with-tensorflow', 'size': '7926'}