Menu

Working of TensorFlow

How TensorFlow Works?

Before writing your first model, it is worth spending a few minutes understanding the core ideas that everything else is built on. TensorFlow's architecture rests on two concepts: tensors and computational graphs, and its execution model in TF 2.x gives you the best of both worlds.

Tensors Explained Simply

A tensor is a generalisation of numbers, vectors, and matrices to any number of dimensions. The name sounds exotic, but you work with tensors in everyday life without calling them that:

  • Scalar (rank-0 tensor): a single number — e.g. the temperature outside right now.
  • Vector (rank-1 tensor): a list of numbers, e.g. the pixel brightness values along one row of an image.
  • Matrix (rank-2 tensor): a grid of numbers, e.g. a grayscale image where each value is one pixel's brightness.
  • 3-D tensor: a stack of matrices, e.g. a colour image with shape (height, width, 3) where 3 is the RGB channel count.
  • 4-D tensor: a batch of colour images with shape (batch_size, height, width, 3) — this is what most image models receive during training.

TensorFlow represents all data inputs, labels, model weights, intermediate activations, and gradients as tensors. According to the official TensorFlow documentation, TensorFlow operates on multidimensional arrays or tensors represented as tf. Tensor objects, similar to NumPy arrays but able to run on GPUs and participate in automatic differentiation.

import tensorflow as tf

# Rank-0: scalar

scalar = tf.constant(42.0)

print(scalar.shape)   # ()

print(scalar.dtype)   # float32

# Rank-1: vector

vector = tf.constant([1.0, 2.0, 3.0, 4.0])

print(vector.shape)   # (4,)

# Rank-2: matrix

matrix = tf.constant([[1, 2, 3],

[4, 5, 6]])

print(matrix.shape)   # (2, 3)

# TensorFlow offers a rich operator library

print(tf.math.add(1, 2))          # tf.Tensor(3, ...)

print(tf.math.square(5))          # tf.Tensor(25, ...)

print(tf.math.reduce_sum([1,2,3]))   # tf.Tensor(6, ...)

print(tf.matmul(matrix, tf.transpose(matrix)))  # 2x2 result

Did You Know?

TensorFlow tensors are immutable by default — like Python strings, you cannot change a value in place. For mutable state (such as model weights that update during training), TensorFlow provides tf.Variable, which supports in-place updates via assign() and assign_add().

Computational Graphs in TensorFlow

A computational graph is a directed graph where each node represents a mathematical operation (tf.Operation) and each edge represents the tensor flowing between operations (tf.Tensor). According to the official TensorFlow guide, since these graphs are data structures, they can be saved, run, and restored all without the original Python code.

Why does the graph matter? Because once TensorFlow has the full picture of all operations in a function, it can hand them to Grappler, TensorFlow's built-in graph optimisation engine, which performs:

  • Constant folding: evaluates expressions involving only constants at compile time rather than runtime.
  • Common subexpression elimination: avoids computing the same value twice.
  • Operation fusion: merges multiple small operations into a single efficient kernel, reducing memory bandwidth.
  • Automatic parallelisation: identifies independent sub-computations and executes them concurrently.

Eager Execution vs Graph Execution

TensorFlow 1.x required you to define the full graph upfront, then open a Session to run it, a design that was powerful but notoriously difficult to debug. A Medium article from November 2025 described the frustration clearly: 'After executing the code, there was occasionally no output. The challenge was not a coding error; it was how TensorFlow 1.x was designed to operate.'

TensorFlow 2.x changed that by enabling eager execution by default. Operations execute immediately as Python calls them, just like NumPy, no sessions, no graphs to compile manually. The official TensorFlow documentation states: 'As of TensorFlow 2, eager execution is turned on by default. Eager execution enables a more interactive frontend to TensorFlow.'

Aspect

Eager Execution (TF 2.x default)

Graph Execution (@tf.function)

Behaviour

Operations run immediately, like Python

Traced once into a graph; reused on each call

Debugging

Standard Python debuggers (pdb, print)

Harder must use tf.print inside the graph

NumPy integration

Natural call .numpy() anytime

Restricted inside graph scope

Speed

Slight overhead per op (Python ↔ TF)

Optimised; Grappler runs automatically

Best for

Development, prototyping, debugging

Production inference, large-scale training

Control flow

Regular Python if/for/while

Must use tf.cond, tf.while_loop

import tensorflow as tf

# Eager (default

print(f'Eager enabled: {tf.executing_eagerly()}')  # True

x = tf.constant([[1.0, 2.0], [3.0, 4.0]])

y = tf.constant([[5.0, 6.0], [7.0, 8.0]])

z = tf.matmul(x, y)   # executes immediately

print(z.numpy())    # [[19. 22.] [43. 50.]]

Graph (via decorator)

@tf.function

def square_and_sum(a, b):

return tf.reduce_sum(a**2 + b**2)

result = square_and_sum(x, y)

print(result)  # tf.Tensor(190.0, ...)

# Toggle eager off to verify graph mode (useful during debugging)

# tf.config.run_functions_eagerly(True) runs @tf.function eagerly

Recommended Practice

Use eager execution during development and debugging, the immediate feedback is invaluable. Once your model is verified and correct, add @tf.function to performance-critical sections (the training step, the inference function) and let TensorFlow compile and optimise them for speed.