{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "TensorFlow-Introduction.ipynb",
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-5u3a4csUPyn"
      },
      "source": [
        "#TensorFlow 2.0 Introduction\n",
        "In this notebook you will be given an interactive introduction to TensorFlow 2.0. We will walk through the following topics within the TensorFlow module:\n",
        "\n",
        "- TensorFlow Install and Setup\n",
        "- Representing Tensors\n",
        "- Tensor Shape and Rank\n",
        "- Types of Tensors\n",
        "\n",
        "\n",
        "If you'd like to follow along without installing TensorFlow on your machine you can use **Google Collaboratory**. Collaboratory is a free Jupyter notebook environment that requires no setup and runs entirely in the cloud."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "F7ThfbiQl96l"
      },
      "source": [
        "##Installing TensorFlow\n",
        "To install TensorFlow on your local machine you can use pip.\n",
        "```console\n",
        "pip install tensorflow\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JYQWyAJ2mez6"
      },
      "source": [
        "![alt text](https://)If you have a CUDA enabled GPU you can install the GPU version of TensorFlow. You will also need to install some other software which can be found here: https://www.tensorflow.org/install/gpu \n",
        "```console\n",
        "pip install tensorflow-gpu\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JJjNMaSClWhg"
      },
      "source": [
        "## Importing TensorFlow\n",
        "The first step here is going to be to select the correct version of TensorFlow from within collabratory!\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "vGcE8x2Gkw9K"
      },
      "source": [
        "%tensorflow_version 2.x  # this line is not required unless you are in a notebook"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "4N7XbNDVY8P3",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "dbbed47c-7e93-4b00-d528-09ff08c69c03"
      },
      "source": [
        "import tensorflow as tf  # now import the tensorflow module\n",
        "print(tf.version)  # make sure the version is 2.x"
      ],
      "execution_count": 2,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "<module 'tensorflow._api.v2.version' from '/usr/local/lib/python3.7/dist-packages/tensorflow/_api/v2/version/__init__.py'>\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "duDj86TfWFof"
      },
      "source": [
        "##Tensors \n",
        "\"A tensor is a generalization of vectors and matrices to potentially higher dimensions. Internally, TensorFlow represents tensors as n-dimensional arrays of base datatypes.\" (https://www.tensorflow.org/guide/tensor)\n",
        "\n",
        "It should't surprise you that tensors are a fundemental apsect of TensorFlow. They are the main objects that are passed around and manipluated throughout the program. Each tensor represents a partialy defined computation that will eventually produce a value. TensorFlow programs work by building a graph of Tensor objects that details how tensors are related. Running different parts of the graph allow results to be generated.\n",
        "\n",
        "Each tensor has a data type and a shape. \n",
        "\n",
        "**Data Types Include**: float32, int32, string and others.\n",
        "\n",
        "**Shape**: Represents the dimension of data.\n",
        "\n",
        "Just like vectors and matrices tensors can have operations applied to them like addition, subtraction, dot product, cross product etc.\n",
        "\n",
        "In the next sections we will discuss some different properties of tensors. This is to make you more familiar with how tensorflow represnts data and how you can manipulate this data.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TAk6QhGUwQRt"
      },
      "source": [
        "###Creating Tensors\n",
        "Below is an example of how to create some different tensors.\n",
        "\n",
        "You simply define the value of the tensor and the datatype and you are good to go! It's worth mentioning that usually we deal with tensors of numeric data, it is quite rare to see string tensors.\n",
        "\n",
        "For a full list of datatypes please refer to the following guide.\n",
        "\n",
        "https://www.tensorflow.org/api_docs/python/tf/dtypes/DType?version=stable"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "epGskXdjZHzu"
      },
      "source": [
        "string = tf.Variable(\"this is a string\", tf.string) \n",
        "number = tf.Variable(324, tf.int16)\n",
        "floating = tf.Variable(3.567, tf.float64)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "D0_H71HMaE-5"
      },
      "source": [
        "###Rank/Degree of Tensors\n",
        "Another word for rank is degree, these terms simply mean the number of dimensions involved in the tensor. What we created above is a *tensor of rank 0*, also known as a scalar. \n",
        "\n",
        "Now we'll create some tensors of higher degrees/ranks."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "hX_Cc5IfjQ6-"
      },
      "source": [
        "rank1_tensor = tf.Variable([\"Test\"], tf.string) \n",
        "rank2_tensor = tf.Variable([[\"test\", \"ok\"], [\"test\", \"yes\"]], tf.string)"
      ],
      "execution_count": 3,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "55zuGMc7nHjC"
      },
      "source": [
        "**To determine the rank** of a tensor we can call the following method."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Zrj0rAWLnMNv",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "9e650882-fd3a-4bd8-9aee-df6916d130da"
      },
      "source": [
        "tf.rank(rank2_tensor)"
      ],
      "execution_count": 4,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<tf.Tensor: shape=(), dtype=int32, numpy=2>"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 4
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hTv4Gz67pQbx"
      },
      "source": [
        "The rank of a tensor is direclty related to the deepest level of nested lists. You can see in the first example ```[\"Test\"]``` is a rank 1 tensor as the deepest level of nesting is 1. \n",
        "Where in the second example ```[[\"test\", \"ok\"], [\"test\", \"yes\"]]``` is a rank 2 tensor as the deepest level of nesting is 2."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RaVrANK8q21q"
      },
      "source": [
        "###Shape of Tensors\n",
        "Now that we've talked about the rank of tensors it's time to talk about the shape. The shape of a tensor is simply the number of elements that exist in each dimension. TensorFlow will try to determine the shape of a tensor but sometimes it may be unknown.\n",
        "\n",
        "To **get the shape** of a tensor we use the shape attribute.\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "L_NRXsFOraYa"
      },
      "source": [
        "rank2_tensor.shape"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wVDmLJeFs086"
      },
      "source": [
        "###Changing Shape\n",
        "The number of elements of a tensor is the product of the sizes of all its shapes. There are often many shapes that have the same number of elements, making it convient to be able to change the shape of a tensor.\n",
        "\n",
        "The example below shows how to change the shape of a tensor."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "dZ8Rbs2xtNqj"
      },
      "source": [
        "tensor1 = tf.ones([1,2,3])  # tf.ones() creates a shape [1,2,3] tensor full of ones\n",
        "tensor2 = tf.reshape(tensor1, [2,3,1])  # reshape existing data to shape [2,3,1]\n",
        "tensor3 = tf.reshape(tensor2, [3, -1])  # -1 tells the tensor to calculate the size of the dimension in that place\n",
        "                                        # this will reshape the tensor to [3,3]\n",
        "                                                                             \n",
        "# The numer of elements in the reshaped tensor MUST match the number in the original"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "M631k7UDv1Wh"
      },
      "source": [
        "Now let's have a look at our different tensors."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "IFNmUxaEv6s3"
      },
      "source": [
        "print(tensor1)\n",
        "print(tensor2)\n",
        "print(tensor3)\n",
        "# Notice the changes in shape"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "q88pJucBolsp"
      },
      "source": [
        "###Slicing Tensors\n",
        "You may be familiar with the term \"slice\" in python and its use on lists, tuples etc. Well the slice operator can be used on tensors to select specific axes or elements.\n",
        "\n",
        "When we slice or select elements from a tensor, we can use comma seperated values inside the set of square brackets. Each subsequent value refrences a different dimension of the tensor.\n",
        "\n",
        "Ex: ```tensor[dim1, dim2, dim3]```\n",
        "\n",
        "I've included a few examples that will hopefully help illustrate how we can manipulate tensors with the slice operator."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "b0YrD-hRqD-W"
      },
      "source": [
        "# Creating a 2D tensor\n",
        "matrix = [[1,2,3,4,5],\n",
        "          [6,7,8,9,10],\n",
        "          [11,12,13,14,15],\n",
        "          [16,17,18,19,20]]\n",
        "\n",
        "tensor = tf.Variable(matrix, dtype=tf.int32) \n",
        "print(tf.rank(tensor))\n",
        "print(tensor.shape)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Wd85uGI7qyfC"
      },
      "source": [
        "# Now lets select some different rows and columns from our tensor\n",
        "\n",
        "three = tensor[0,2]  # selects the 3rd element from the 1st row\n",
        "print(three)  # -> 3\n",
        "\n",
        "row1 = tensor[0]  # selects the first row\n",
        "print(row1)\n",
        "\n",
        "column1 = tensor[:, 0]  # selects the first column\n",
        "print(column1)\n",
        "\n",
        "row_2_and_4 = tensor[1::2]  # selects second and fourth row\n",
        "print(row2and4)\n",
        "\n",
        "column_1_in_row_2_and_3 = tensor[1:3, 0]\n",
        "print(column_1_in_row_2_and_3)\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UU4MMhB_rxvz"
      },
      "source": [
        "###Types of Tensors\n",
        "Before we go to far, I will mention that there are diffent types of tensors. These are the most used and we will talk more in depth about each as they are used.\n",
        "- Variable\n",
        "- Constant\n",
        "- Placeholder\n",
        "- SparseTensor\n",
        "\n",
        "With the execption of ```Variable``` all these tensors are immuttable, meaning their value may not change during execution.\n",
        "\n",
        "For now, it is enough to understand that we use the Variable tensor when we want to potentially change the value of our tensor.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "F2OoXbe7aSVl"
      },
      "source": [
        "#Sources\n",
        "Most of the information is taken direclty from the TensorFlow website which can be found below.\n",
        "\n",
        "https://www.tensorflow.org/guide/tensor"
      ]
    }
  ]
}