42.100. Intro to TensorFlow for Deep Learning#

42.100.1. 1. What is TensorFlow#

TensorFlow is an open source and an end to end platform used for building machine learning models. Being end to end, you can prepare data, build models, diagnose, improve, and deploy them.

TensorFlow uses Keras at its backend. Keras is a well beautifully designed API for building deep learning models in popular fields such as Computer Vision and Natural Language Processing.

TensorFlow has got a strong community, from users, learning resources and whole range of technical supports. Not only it powers majority of Google apps such as YouTube, Maps and Google Photos, it is also widely used across startups and other big techs. If you would like to know who is using TensorFlow, here you go!.

42.100.2. 2. TensorFlow Model APIs#

TensorFlow being suited for variety of tasks, there are 3 ways to build deep learning models.

42.100.2.1. 2.1 Sequential API#

This is the simplest model building option. When building a model, you start from the input to the output, no other way around. This API is suited for tasks that donā€™t require multiple inputs or outputs, or skip connections.

Sequential API can be a good API to use for things like image classification or regression tasks. With things like object detection and segmentation, we need another API, which is Functional API.

Below is how a sequential model looks like.

# Building a sequential model 

from tensorflow import keras

 
model = keras.models.Sequential([
        
        keras.layers.Dense(16, activation='relu'),
        keras.layers.Dense(32, activation='relu'),
        keras.layers.Dense(1, activation='sigmoid')
        
])
    

42.100.2.2. Functional API#

This type of API makes it easy to build models that can take multiple inputs/outputs, or skip connections.

It is well suited in advanced things like object detection and segmentation. In object detection, there are two main involved things.

One is recognizing the object(classification) and other is localizing the object(regression: predicting the bounding boxes coordinates).

NiIL9N3v.jpeg You canā€™t build an object detection model with Sequential API, but Functional API instead!! Source.

Below is how a functional model looks like.

# Building a same model in Functional API

from tensorflow import keras

inputs = keras.Input()
x = keras.layers.Dense(16, activation='relu')(inputs)
x = keras.layers.Dense(32, activation='relu')(x)
output = keras.layers.Dense(1, activation='sigmoid')(x)
    
model = keras.Model(inputs, output)

42.100.3. Model SubClassing#

SubClassing API is for building custom models and having full control of every step in model building and training.

In most cases, Sequential and Functional API will be all you need to build almost anything.

Below is how a subclassing model looks like.

# Building a same custom model 

from tensorflow import keras

class MLP(keras.Model):

  def __init__(self, **kwargs):
    super(MLP, self).__init__(**kwargs)
    self.dense_1 = keras.layers.Dense(16, activation='relu')
    self.dense_2 = keras.layers.Dense(32, activation='relu')
    self.dense_3 = keras.layers.Dense(1, activation='sigmoid')

  def call(self, inputs):
    x = self.dense_1(inputs)
    x = self_dense_2(x)
    x = self_dense_3(x)
    
    return x

# Instantiate the model

mlp = MLP()

42.100.4. 3. The Basics of Tensors#

42.100.4.1. 3.1 Intro to Tensors#

A tensor is a multidimensional array of the same data type. A tensor can be a scalar (single number), a vector, or a matrix.

If you have used NumPy, tensors are like NumPy arrays, except that tensors have GPU(Graphical Processing Unit) support.

A typical tensor has the following information:

  • Shape: The length or number of elements of each of the tensor dimension/axes.

  • Rank: The number of dimensions/axes in a tensor. A scalar tensor (a single number) has rank 0, a vector has a rank 1 (a vector is a 1D), and a matrix has rank 2 (or 2D).

  • Axis/Dimension: This is a particular dimension of a tensor

  • Size: This is the total number of items in the tensor.

But why tensor/NumPy array things?

Well, almost all types of data ca be represented as an array of numbers. Take an example:

  • Image can be represented as an array of pixels.

  • Any text data can be converted into an array of numbers (or tokens representing words)

  • Video (made of sequence of images) can be represented as an array of numbers.

Having the ability to convert these raw data into tensors/arrays make it easy to preprocess it, either when performing conventional numerical computations or when it is the data we are preparing to feed to a machine learning model. Take a simple example, we can not feed a raw text to a machine learning model. That text has to be converted into numbers.

Thatā€™s is for the basic intro to bensors. For more about tensors, check out TensorFlowā€™s introduction to tensors, or this rich wikipedia tensor page.

In later parts, we will see how to:

  • Create a tensor with tf.constant()

  • Create a tensor with tf.variable()

  • Create tensors from existing functions

  • Select data in a tensor

  • Perform operations in tensor

  • Manipulate tensor shape

42.100.4.2. 3.2 Creating a Tensor with tf.constant()#

A Tensor can be a scalar, a vector or a matrix. Letā€™s use tf.constant() to create these tensor types.

A tensor created with tf.constant() is immutable.

# I will first import tensorflow as tf
# Also import numpy as np
# If you are using Colab, no need to install them 
import tensorflow as tf
import numpy as np
# Creating a scalar tensor
# You can specify dtype but TF will detect its if left unspecified

scalar_tensor = tf.constant(10)
# Displaying created tensor

print(scalar_tensor)
# We can also create a vector or rank 1 tensor
# Simply put, a vector is one dimensional
# We can create it from a list of values

vect_tensor = tf.constant([1.0,2.0,3.0,4.0,5.0,6.0])
print(vect_tensor)

A vector of 1 dimensional values was created. As you can see, the data type is float32 because the values were floats. TensorFlow detects that automatically from values if the data type was not mentioned.

Letā€™s now create a tensor with rank 2 or two dimensions. This is actually a matrix.

mat_tensor = tf.constant([[2,4],
                         [6,8],
                         [10,12]], dtype=tf.int32)
print(mat_tensor)

If you can see in the displayed tensor above, the shape is (3,2) which means our tensor has 3 rows and 2 columns.

You can also check the number of dimensions or axes of a tensor using tensor_name.ndim

scalar_tensor.ndim

A scalar tensor does not have any dimension. Itā€™s just a single value. But if we do the same thing for a vector or matrix, you will see something different.

# A vector has 1 dimension

vect_tensor.ndim
# A matrix has 2D or more dimensions

mat_tensor.ndim

Just like NumPy array, a tensor can have many dimensions. Letā€™s create a tensor with 3 dimensions.

tensor_3d = tf.constant([
                         [[1,2,3,4,5],
                         [6,7,8,9,8]],
                         [[1,3,5,7,9],
                         [2,4,6,8,1]],
                         [[1,2,3,5,4],
                         [3,4,5,6,7]], ])

print(tensor_3d)
tensor_3d.ndim

A tensor can be converted into NumPy array by calling tensor_name.numpy or np.array(tensor_name).

TensorFlow plays well with NumPy. And if not yet done, TensorFlow recently posted that they are working on gettint the whole of NumPy into TensorFlow.

# Converting a tensor into a NumPy array

n_array = tensor_3d.numpy()

n_array
# Using np.array(tensor_name)

np.array(tensor_3d)

42.100.4.3. 3.3 Creating a Tensor with tf.Variable()#

A tensor created with tf.constant() is immutable, it can not be changed. Such kind of tensor can not be used as weights in neural networks because they need to be changed/updated in backpropogation for example.

With tf.Variable(), we can create tensors that can be mutable and thus can be used in things like updating the weights of neural networks like said above.

Creating variable tensor is as simple as the former.

var_tensor = tf.Variable([
                         [[1,2,3,4,5],
                         [6,7,8,9,8]],
                         [[1,3,5,7,9],
                         [2,4,6,8,1]],
                         [[1,2,3,5,4],
                         [3,4,5,6,7]], ])

print(var_tensor)

It can also be converted to NumPy array, just like tensors created with tf.constant()

# Converting a variable tensor into NumPy array

var_tensor.numpy()

42.100.4.4. 3.4 Creating a Tensor from Existing Functions#

There some types of uniform tensors that you would not want to create from scratch, when in fact, they are already built.

Take an example of 1ā€™s tensor, 0ā€™s, and random tensors. Letā€™s create them.

# Creating 1's tensor

ones_tensor = tf.ones([4,4])

print(ones_tensor)
# Creating 1's tensor

ones_tensor_1 = tf.ones([1,10])

print(ones_tensor_1)
# Creating zeros' tensor

tensor_0 = tf.zeros([3,3])
print(tensor_0)

We can also create a tensor with random values. During the weights initialization in neural networks, weights take random values.

You might be thinking that why arenā€™t we building neural networks now and I get you. Understanding the basics of tensors and especially working with TensorFlow is useful when it comes to creating custom neural network layers, loss functions, or optimizers.

The later labs will not deal with custom layers/losses/optimizers, this is only just an introduction, so that this can serve as a reference whenever you want to take a step back into the backbone of TensorFlow high level API.

# Generating a tensor with random values 

# We first have to create a generator object

rand_tensor = tf.random.Generator.from_seed(3)

rand_tensor = rand_tensor.normal(shape=[3,3])
print(rand_tensor)

Changing seed number in tf.random.Generator.from_seed(3) will change the values returned by random function.

We can also shuffle the existing tensor, created with tf.constant() or tf.Variable().

# Create a typical tensor 

example_tensor = tf.constant([[1,3],
                             [3,4],
                             [4,5]])

print(example_tensor)
def shuffle_tensor(tensor):

  """
  Take a tensor as input and return the shuffled tensor
  """
  # Shuffle the order of the created tensor

  tensor_shuffled = tf.random.shuffle(tensor)

  return print(tensor_shuffled)
shuffle_tensor(example_tensor)

If you rerun the above cell more than once, you will get different orders of tensor.

shuffle_tensor(example_tensor)

In order to prevent that, we can use tf.random.set_seed(seed_number) to always get the same order/values.

# Set seed 

tf.random.set_seed(42)

shuffle_tensor(example_tensor)

Everytime you can run shuffle_tensor function with a same seed, you will get the same order.

You can learn more about Random number generation at TensorFlow docs.

42.100.4.5. 3.5 Selecting Data in Tensor#

We can also select values in any tensor, both single dimensional tensor and multi dimensional tensor.

# Let's create a tensor

tensor_1d = tf.constant([1,2,3,4,5,6,7])

Letā€™s select multiple values in tensor created above.

print('The first value:', tensor_1d[0].numpy())
print('The second value:', tensor_1d[2].numpy())
print('From the 3 to 5th values:', tensor_1d[3:5].numpy())
print('From the 3 to last value:', tensor_1d[3:].numpy())
print('The last value:', tensor_1d[-1].numpy())
print('Select value before the last value:', tensor_1d[-2].numpy())
print('Select all tensor values:', tensor_1d[:].numpy())

Selecting/indexing data in tensor is similar to Python list indexing, and NumPy also.

Letā€™s also select data in 2D tensor.

tensor_2d = tf.constant([[1,3],
                          [3,4],
                          [4,5]])
print('The first row:', tensor_2d[0,:].numpy())
print('The second column:', tensor_2d[:,1].numpy())
print('The last low:', tensor_2d[-1,:].numpy())
print('The first value in the last row:', tensor_2d[-1,0].numpy())
print('The last value in the last column:', tensor_2d[-1,-1].numpy())

42.100.4.6. 3.6 Performing Operations on Tensors#

All numeric operations can be performed on tensor. Letā€™s see few of them.

# Creating example tensors 

tensor_1 = tf.constant([1,2,3])
tensor_2 = tf.constant([4,5,6])
# Adding a scalar value to a tensor

print(tensor_1 + 4)
# Adding two tensors 

print(tensor_1 + tensor_2)
# Can also add with tf.add() or tf.math.add()

print(tf.add(tensor_1, tensor_2))
# multiplying tensors with tf.multiply()

print(tf.multiply(tensor_1, tensor_2))

You can learn more at official docs, tf.math() specifically. Almost all maths operations can be done on tensors.

42.100.4.7. 3.7 Manipulating the Shape of Tensor#

There are times you would want to reshape a tensor. Here is how to go about it.

print(example_tensor)

Letā€™s reshape the above tensor into (2,3).

tens_reshaped = tf.reshape(example_tensor, [2,3])
print(tens_reshaped)
# Also to (6,1)

print(tf.reshape(example_tensor, [6,1]))
# You can also shape a tensor into a list

print(example_tensor.shape.as_list())
# You can also flatten a tensor

print(tf.reshape(example_tensor, [-1]))

There are rules to reshaping a tensor. The new shape has to be resonable. Take an example below, it would create an erroe because there is no way you can reshape the example_tensor into (5,5).

# Running the cell below will create an error

# print(tf.reshape(example_tensor, [5,5]))

Thatā€™s it for the introduction to TensorFlow and the basics of tensors. ā€˜TensorFlow API revolves around tensorsā€™(CC: Aurelion Geron), and thatā€™s why we didnā€™t get straight to doing big things with TF high level API immediately without looking into what makes them possible.

42.101. Acknowledgments#

Thanks to Jean de Dieu Nyandwi for creating 2_intro_to_tensorflow_for_deeplearning. It inspires the majority of the content in this chapter.