Tensorflow for Complete Beginners: Getting Started with Tensors | by Joyjit Chatterjee | Nov, 2020  | Towards Data Science

Tensorflow is a buzz word nowadays in this exciting world of Artificial Intelligence (AI), especially as Deep Learning continues to rapidly accelerate progress in AI. But for someone just starting with Tensorflow, the experience can be scary and daunting, as the terminologies and usage of the beautiful library can be confusing for complete beginners. When I first started learning Tensorflow, I faced similar challenges, and hope to simplify some of the intricacies through this article. This article requires a basic understanding of Python to get a clearer picture of Tensorflow.
How does Tensorflow work?
The basic (and simplest) way to describe Tensorflow is that it is a cool library in Python (and probably other programming languages as well) which allows us to create computational graphs to develop neural network models. The basic element which comprises Tensorflow objects is a Tensor, and all computations which are performed occur in these Tensors. So literally (in my words), these Tensors flow in an orderly manner when you develop any neural network model, and give rise to the final outputs when evaluated. So yes, learning what tensors really are and how we can use them is the first step to getting started with Tensorflow.
So what are Tensors?
We are all familiar with data types in programming, right? 1, 2, 3 etc. are integers, numbers with decimal points (1.5,3.141,5.2555 etc.) are floating point values and another common one is strings (such as “Hello. How are you today?”). When we have a collection of multiple elements together, we generally term that as a list (e.g. [1,4,7]).
A collection of lists can be used to develop a matrix/matrices like this one:
[[1,4,7], [5,8,12], [1,66,88]]
Now, let’s come to Tensors- they are basically higher dimensional representations/generalisations of conventional matrices and vectors. It could even be a 1-D matrix, a 2-D matrix, a 4-D matrix, or an n-dimensional matrix! So we can have multi-dimensional arrays beyond a single list in a Tensor. That’s it- that is what a tensor really is in the most basic sense. Just like we can assign variables values in conventional programming, we could do the same in Tensorflow. Just that there is a specialised way to do it. Similar, just like you could perform multiple operations on conventional variables (or constants) in programming, we can do that with Tensors. We can slice tensors and select a portion of its elements, have various data types for tensors (integers, floating point, strings etc.) and much more.
Getting started with using Tensorflow in Python
The very first step is to install the beautiful library! pip is all you need here. Note that if you’re using Google Colab (which is very similar to Jupyter notebooks in Python), you don’t even need to install this library as it is already installed and available ready for use. Thank you, Google!
#Install the tensorflow module (not required in Google Colab, but is needed in your local PC)!pip install tensorflow
As we are using Colab for this article, the message “requirement already satisfied” is shown in the output.
Requirement already satisfied: tensorflow in /usr/local/lib/python3.6/dist-packages (2.3.0)
Tensorflow, at present, has an older version of type 1.x, while the latest releases use 2.x. So let’s select the current tensorflow version.
#Here, we would go with the latest (2.x) release of tensorflow by selecting the version#Note: By default, Colab tends to use the 2.x version%tensorflow_version 2.x
Let’s import the tensorflow module using the golden keyword import! And yes, you could check what version of the library you’re using by simply printing its version attribute.
#import the tensorflow moduleimport tensorflow as tf#Display the tensorflow version we are usingprint(tf.version)
<module ‘tensorflow._api.v2.version’ from ‘/usr/local/lib/python3.6/dist-packages/tensorflow/_api/v2/version/__init__.py’>
Now, let’s get started with creating Tensors. We would create some for int, float and string here. Notice the specialised way of creating tensors using tf.Variable attribute. This means that we are creating a Tensor which would be a variable nature, so we can change/modify its values by performing specialised operations, just like we would do in conventional programming with variables. Note that while we would have used int, float and string to declare the corresponding variables for such data types in conventional programming, we will use tf.int16 (which means we are defining a 16 bit integer), tf.float32 (to define a 32 bit floating point value) and tf.string to do so in Tensorflow. Note that we could also use tf.float16, tf.int32 and so on, depending on the requirements for the value we want to store. If you have ever used C++, you would be aware of things like int, short int, long long int, float, double etc. used to declare variables with smaller (or larger) number of bits, so we just do something similar here in Tensorflow.
#The science (and art) of creating tensorsscalar_val = tf.Variable(123,tf.int16)floating_val = tf.Variable(123.456,tf.float32)string_val = tf.Variable(“hello everyone. Nice to learn tensorflow!”,tf.string)
And now, it’s very easy to print out the values of these Tensors!
#Let us display the values (print) these tensorsprint(scalar_val)print(floating_val)print(string_val)
<tf.Variable ‘Variable:0’ shape=() dtype=int32, numpy=123>
<tf.Variable ‘Variable:0’ shape=() dtype=float32, numpy=123.456> <tf.Variable ‘Variable:0’ shape=() dtype=string, numpy=b’hello everyone. Nice to learn tensorflow!’>
We can determine the shape and rank of the Tensors, like you might have heard in mathematics modules at school. If not, shape simply refers to the number of elements which are contained along each dimension of the tensor. While rank is the deepest level of nesting within the Tensor. Going into some code might make it more clear.
#The idea behind shape and rank of tensors#Shape: Describes the dimension of the tensor (total elements contained along each dimension)scalar_val_shap = tf.shape(scalar_val)print(scalar_val_shap)floating_val_shap = tf.shape(floating_val)print(floating_val_shap)
tf.Tensor([], shape=(0,), dtype=int32)
tf.Tensor([], shape=(0,), dtype=int32)
So we get a shape of 0 right? It just means that these are scalar values rather than lists or nested lists.
#Now, if we use e.g. lists/nested lists instead of just a “single” scalar valuelist_tensor1 = tf.Variable([1,3,5,6],tf.int16)print(list_tensor1)print(tf.shape(list_tensor1))list_tensor2 = tf.Variable([[1,2,3],[4,5,6]],tf.int16)print(list_tensor2)print(tf.shape(list_tensor2))#how about the rank? It describes the level of nesting within the tensor in simple words.print(tf.rank(list_tensor1))print(tf.rank(list_tensor2))
You can see that the list_tensor1 has 4 elements, so that’s what gives it a shape of (4,). Note that (4,) is nothing but (4,1), just showing 4 elements within a single list. Next, for list_tensor2, observe a shape of (2,3) signifying that we have a nested list- with 2 lists, each containing 3 elements.
<tf.Variable ‘Variable:0’ shape=(4,) dtype=int32, numpy=array([1, 3, 5, 6], dtype=int32)>
tf.Tensor([4], shape=(1,), dtype=int32)
<tf.Variable ‘Variable:0’ shape=(2, 3) dtype=int32, numpy=array([[1, 2, 3], [4, 5, 6]], dtype=int32)>
tf.Tensor([2 3], shape=(2,), dtype=int32)
tf.Tensor(1, shape=(), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32)
Similarly, if you look at the rank, you see a rank of 1 for the list_tensor1, simply because we just have a single list, no nesting here! Now, for list_tensor2, you can see a rank of 2- signifying that we have a level 2 nesting i.e. a big list contains more lists. Had it been something like one big list, contains another small list, and that small list contains more smaller lists, we would have had a rank of 3. Hope this would make it clear.
tf.Tensor(1, shape=(), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32)
We can change the shape of the tensors (of course, until it is mathematically valid) using tf.reshape. Again, note the specialised way to do this using Python.
#Reshaping tensorsreshaped_list_tensor2 = tf.reshape(list_tensor2,[6])print(reshaped_list_tensor2)list_tensor3 = tf.Variable([[1,2,3,1],[1,9,10,11],[1,5,11,22],[16,17,18,19]],tf.int16)print(list_tensor3)print(tf.rank(list_tensor3))print(tf.shape(list_tensor3))reshaped_list_tensor3 = tf.reshape(list_tensor3,[2,8,1])print(reshaped_list_tensor3)#or like thisreshaped_list_tensor3 = tf.reshape(list_tensor3,[8,2,1])print(reshaped_list_tensor3)#or automatically determine the shape by only giving one dimension!reshaped_list_tensor3 = tf.reshape(list_tensor3,[1,-1])print(reshaped_list_tensor3)
And you get the following outputs. It's too long to probably give a read, but I guess if you check out the Colab Notebook here, it would be clearer. The basic thing happening here is that if you specify [6] here for the shape, tensorflow would reshape your tensors to have 6 elements with a single dimension (so it would be simply a 1D list). If you do a [2,8,1], it would make 2 lists, each with 8 elements. You could also do [8,2,1]- creating 8 lists, each with 2 elements as you would see. If you don’t want to go into specifying the whole of dimensions for the reshape yourself, you could as well simply use a -1. So, a reshape of [1,-1] would reshape the list_tensor3 to create reshaped_list_tensor3, so 1 list with ??? Yes, 16 elements. So note that while you could use reshape however you want, it is essential to ensure that the total number of elements after reshape do not change during the process.
tf.Tensor([1 2 3 4 5 6], shape=(6,), dtype=int32)
<tf.Variable ‘Variable:0’ shape=(4, 4) dtype=int32, numpy=array([[ 1, 2, 3, 1], [ 1, 9, 10, 11], [ 1, 5, 11, 22], [16, 17, 18, 19]], dtype=int32)>
tf.Tensor(2, shape=(), dtype=int32)
tf.Tensor([4 4], shape=(2,), dtype=int32)
tf.Tensor( [[[ 1] [ 2] [ 3] [ 1] [ 1] [ 9] [10] [11]] [[ 1] [ 5] [11] [22] [16] [17] [18] [19]]], shape=(2, 8, 1), dtype=int32)
tf.Tensor( [[[ 1] [ 2]] [[ 3] [ 1]] [[ 1] [ 9]] [[10] [11]] [[ 1] [ 5]] [[11] [22]] [[16] [17]] [[18] [19]]], shape=(8, 2, 1), dtype=int32)
tf.Tensor([[ 1 2 3 1 1 9 10 11 1 5 11 22 16 17 18 19]], shape=(1, 16), dtype=int32)
You could create a Tensor full of 1s (or 0s) using special commands tf.ones or tf.zeros. And then, similar operations could be performed on them too.
Now, we come to slicing. Just like you could slice and extract some elements from a matrix or a Python list for instance, you could do the same with Tensors. See an example below, wherein, we first create a onefull tensor (my name for a tensor full of 1 values only) and another with zerofull. The dimensions of the tensors you want to create go within the square brackets [], so a [4,4,4,1] creates a 4D tensor, while a [5,5] creates a 2D tensor- with 5 rows and 5 columns (which is nothing but a matrix) in the simplest sense.
Some slicing operations are shown below for these created tensors. So that you can get the elements you like from the tensors and not consider those you don’t like :-)
#creating a tensor full of 1s (or 0s)tensor_onefull = tf.ones([4,4,4,1])print(tensor_onefull)tensor_zerofull = tf.zeros([5,5])print(tensor_zerofull)#extracting specific values from tensors (similar to slicing in conventional programming)tensor_sliced_onefull = tensor_onefull[0]print(tensor_sliced_onefull)tensor_sliced_zerofull = tensor_zerofull[0,1]print(tensor_sliced_zerofull)
And here is the nice output (better readable in the Colab notebook).
tf.Tensor( [[[[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]]] [[[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]]] [[[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]]] [[[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]]]], shape=(4, 4, 4, 1), dtype=float32)
tf.Tensor( [[0. 0. 0. 0. 0.] [0. 0. 0. 0. 0.] [0. 0. 0. 0. 0.] [0. 0. 0. 0. 0.] [0. 0. 0. 0. 0.]], shape=(5, 5), dtype=float32)
tf.Tensor( [[[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]]], shape=(4, 4, 1), dtype=float32)
tf.Tensor(0.0, shape=(), dtype=float32)
#another example from previously created tensorprint(list_tensor3)tf_slicedexampleagain = list_tensor3[0,-2:]print(tf_slicedexampleagain)#selecting multiple rowstf_slicedexampleagain = list_tensor3[1::]print(tf_slicedexampleagain)
And again, some cool output (basic but yes, simplest things are the coolest).
<tf.Variable ‘Variable:0’ shape=(4, 4) dtype=int32, numpy=array([[ 1, 2, 3, 1], [ 1, 9, 10, 11], [ 1, 5, 11, 22], [16, 17, 18, 19]], dtype=int32)>
tf.Tensor([3 1], shape=(2,), dtype=int32)
tf.Tensor( [[ 1 9 10 11] [ 1 5 11 22] [16 17 18 19]], shape=(3, 4), dtype=int32)
You can find the complete Colab notebook for this article here on my Github https://github.com/joyjitchatterjee/DeepLearning-Tensorflow-Basics/blob/master/Tensorflow_Beginning.ipynb.
And that’s it. Hope you enjoyed reading this article. Thanks for the read, and apologies for any typos/bugs if any. I hope to cover more on Tensorflow in my next posts.
If you wish to, you can connect with me on LinkedIn at http://linkedin.com/in/joyjitchatterjee/
REFERENCES
Tensorflow documentation (https://www.tensorflow.org/guide)TensorFlow 2.0 Complete Course — Python Neural Networks for Beginners Tutorial (https://www.youtube.com/watch?v=tPYj3fFJGjk&list=PLYLyA78Q4izuAlaaOER3qwUZkqLTZf7qk)Get started with TensorFlow and Deep Learning (https://medium.com/@rishit.dagli/get-started-with-tensorflow-and-deep-learning-part-1-72c7d67f99fc)
Tensorflow documentation (https://www.tensorflow.org/guide)
TensorFlow 2.0 Complete Course — Python Neural Networks for Beginners Tutorial (https://www.youtube.com/watch?v=tPYj3fFJGjk&list=PLYLyA78Q4izuAlaaOER3qwUZkqLTZf7qk)
Get started with TensorFlow and Deep Learning (https://medium.com/@rishit.dagli/get-started-with-tensorflow-and-deep-learning-part-1-72c7d67f99fc)