Tutorial 8 - TensorFlow

View notebook on Github Open In Collab

This tutorial is adapted from a 2022 blog post on the website Made With ML, by Goku Mohandas.

We will import TensorFlow and numpy and set the seed for their random number generators for reproducibility.

[2]:
import numpy as np
import tensorflow as tf

tf_version = tf.__version__

print(f'TF version: {tf_version}')
TF version: 2.8.2
[2]:
SEED = 1
[3]:
# Set seed for reproducability
np.random.seed(seed=SEED)
tf.random.set_seed(seed=SEED)

Basics

First, we will cover some basics such as creating TensorFlow tensors and converting from common data structures to TensorFlow tensors.

[4]:
x = tf.random.normal((2, 3))
print(f'Type: {x.dtype}')
print(f'shape: {x.shape}')
print(f'values:\n{x}')
Type: <dtype: 'float32'>
shape: (2, 3)
values:
[[-1.1012203   1.5457517   0.383644  ]
 [-0.87965786 -1.2246722  -0.9811211 ]]
[ ]:
# Zeros and ones tensors
x = tf.zeros((2, 3), dtype=tf.float64)
print(x)
x = tf.ones((2, 3))
print(x)
[ ]:
# List => Tensor
x = tf.constant([[1, 2, 3],
                  [4, 5, 6]])

print(f'shape: {x.shape}')
print(f'values:\n{x}')
[8]:
# Numpy array => Tensor
x = tf.constant(np.random.rand(2, 3))
print(f'shape: {x.shape}')
print(f'values:\n{x}')
shape: (2, 3)
values:
[[4.17022005e-01 7.20324493e-01 1.14374817e-04]
 [3.02332573e-01 1.46755891e-01 9.23385948e-02]]

Operations

[9]:
# Addition
x = tf.random.normal((2, 3))
y = tf.random.normal((2, 3))
z = x + y
print(f'shape: {z.shape}')
print(f'values:\n{z}')
shape: (2, 3)
values:
[[-0.05392435 -1.4948881   0.6654824 ]
 [ 0.4435789   1.0243716   0.5050061 ]]
[10]:
# Dot product
x = tf.random.normal((2, 3))
y = tf.random.normal((3, 2))
z = tf.matmul(x, y)
print(f'shape: {z.shape}')
print(f'values:\n{z}')
shape: (2, 2)
values:
[[ 2.04325     1.1587756 ]
 [-1.8957058  -0.67201185]]
[11]:
# Transpose
x = tf.random.normal((2, 3))
print(f"shape: {x.shape}")
print(f"values: \n{x}")
y = tf.transpose(x)
print(f"shape: {y.shape}")
print(f"values: \n{y}")
shape: (2, 3)
values:
[[-1.1771783  -0.90325946  0.8419609 ]
 [-0.06870949 -0.96161884 -0.51533026]]
shape: (3, 2)
values:
[[-1.1771783  -0.06870949]
 [-0.90325946 -0.96161884]
 [ 0.8419609  -0.51533026]]
[3]:
# Reshape
x = tf.random.normal((2, 3))
z = tf.reshape(x, (3, 2))
print(f"shape: {z.shape}")
print(f"values: \n{z}")
shape: (3, 2)
values:
[[ 0.73264295  0.99835527]
 [ 1.555784    1.0374023 ]
 [-0.0362004  -0.18817899]]
[14]:
# Dangers of reshaping (unintended consequences)
x = tf.constant([
    [[1,1,1,1], [2,2,2,2], [3,3,3,3]],
    [[10,10,10,10], [20,20,20,20], [30,30,30,30]]
])
print(f"shape: {x.shape}")
print(f"x: \n{x}\n")

a = tf.reshape(x, (x.shape[1], -1))
print(f"\nshape: {a.shape}")
print(f"a: \n{a}\n")

b = tf.transpose(x, perm=[1, 0, 2])
print(f"\nshape: {b.shape}")
print(f"b: \n{b}\n")

c = tf.reshape(b, (b.shape[0], -1))
print(f"\nshape: {c.shape}")
print(f"c: \n{c}")
shape: (2, 3, 4)
x:
[[[ 1  1  1  1]
  [ 2  2  2  2]
  [ 3  3  3  3]]

 [[10 10 10 10]
  [20 20 20 20]
  [30 30 30 30]]]


shape: (3, 8)
a:
[[ 1  1  1  1  2  2  2  2]
 [ 3  3  3  3 10 10 10 10]
 [20 20 20 20 30 30 30 30]]


shape: (3, 2, 4)
b:
[[[ 1  1  1  1]
  [10 10 10 10]]

 [[ 2  2  2  2]
  [20 20 20 20]]

 [[ 3  3  3  3]
  [30 30 30 30]]]


shape: (3, 8)
c:
[[ 1  1  1  1 10 10 10 10]
 [ 2  2  2  2 20 20 20 20]
 [ 3  3  3  3 30 30 30 30]]
[15]:
# Dimensional operations
x = tf.random.normal((2, 3))
print(f"values: \n{x}")
y = tf.reduce_sum(x, axis=0) # sum over columns
print(f"values: \n{y}")
z = tf.reduce_sum(x, axis=1) # sum over rows
print(f"values: \n{z}")
values:
[[ 0.9868413   0.57056284  0.17946035]
 [ 0.83900064  1.0045967  -0.0642297 ]]
values:
[1.8258419  1.5751595  0.11523065]
values:
[1.7368646 1.7793677]

Indexing

Now we will look at how to extract, separate, and join values from tensors.

[18]:
x = tf.random.normal((3, 4))
print (f"x: \n{x}")
print()
print(f"x[:1]: \n{x[0]}")
print()
print(f"x[:1, 1:3]: \n{x[:1, 1:3]}")

x:
[[-0.8340743  -1.0207754  -0.6342568   0.51541275]
 [-0.590669    1.6425287   0.60286343 -0.61301523]
 [ 0.6574386   0.16823037  1.4946445   0.8306155 ]]

x[:1]:
[-0.8340743  -1.0207754  -0.6342568   0.51541275]

x[:1, 1:3]:
[[-1.0207754 -0.6342568]]

Slicing

[20]:
# Select with dimensional indices
x = tf.random.normal((2, 3))
print(f"values: \n{x}")

col_indices = tf.constant([0, 2])
chosen = tf.gather(x, axis=1, indices=col_indices) # values from column 0 & 2
print(f"values: \n{chosen}")

row_indices = tf.constant([0, 1])
col_indices = tf.constant([0, 2])
chosen = tf.gather_nd(x, indices=[row_indices, col_indices]) # values from (0, 0) & (1, 2)
print(f"values: \n{chosen}")
values:
[[-0.92759573  0.06353194  0.26116046]
 [-1.4687079  -0.90832067 -0.87465733]]
values:
[[-0.92759573  0.26116046]
 [-1.4687079  -0.87465733]]
values:
[0.06353194 0.26116046]

Joining

[21]:
# Concatenation
x = tf.random.normal((2, 3))
print(f"Values: \n{x}")
y = tf.concat([x, x], axis=0) # stack by rows (dim=1 to stack by columns)
print(f"Values: \n{y}")
Values:
[[ 2.260564   -0.6558232  -0.46591297]
 [ 0.4690215   1.026158   -0.11631647]]
Values:
[[ 2.260564   -0.6558232  -0.46591297]
 [ 0.4690215   1.026158   -0.11631647]
 [ 2.260564   -0.6558232  -0.46591297]
 [ 0.4690215   1.026158   -0.11631647]]

Gradients

  • \(y = 3x + 2\)

  • \(z = \sum{y}/N\)

  • \(\frac{\partial(z)}{\partial(x)} = \frac{\partial(z)}{\partial(y)} \frac{\partial(y)}{\partial(x)} = \frac{1}{N} * 3 = \frac{1}{12} * 3 = 0.25\)

[22]:
# Tensors with gradient book keeping
x = tf.random.normal((3, 4))

# Tensorflow needs graph context to track gradients
with tf.GradientTape() as g:
    g.watch(x)
    y = 3*x + 2
    z = tf.reduce_mean(y)

dz_dx = g.gradient(z, x)

print(dz_dx)
tf.Tensor(
[[0.25 0.25 0.25 0.25]
 [0.25 0.25 0.25 0.25]
 [0.25 0.25 0.25 0.25]], shape=(3, 4), dtype=float32)

CUDA

This section details how to check if we are able to use GPU to accelerate our machine learning or deep learning models.

The Compute Unified Device Architecture or CUDA is a parallel computing platform and API that allows software to use certain types of GPUs for general purpose processing. It is an extension of the C and C++ programming languages.

TensorFlow makes using the GPU quite transparent. If the GPU compatible version of TF is installed along with the proper drivers, TF will use the GPU.

Although training is usually faster on the GPU, depending on model size and hardware specs, it can take quite a while to copy the model and your data to the GPU.

Link to metapackage for easily installing TensorFlow GPU using a conda ‘metapackage’. This is just a special package which installs the required GPU drivers alongside TensorFlow.

[23]:
# Is CUDA available?
print(tf.test.is_built_with_cuda())
False
[26]:
# Set device to first gpu (if available)
device = "/gpu:0" if tf.test.is_built_with_cuda() else "cpu"
print(device)
cpu
[27]:
with tf.device(device):
    a = tf.constant([1, 2, 3])
[5]:
# Print info about local cpu/gpu devices through tensorflow library
from tensorflow.python.client import device_lib

# The most useful information is found in the first two lines of the output.
# 1st line is name of device (cpu/gpu and number).
# If there were 2 cpus on the device, there would be another entry in the list under name: '/device:cpu:1'
# The second line give the memory limit in bits.
device_lib.list_local_devices()
[5]:
[name: "/device:CPU:0"
 device_type: "CPU"
 memory_limit: 268435456
 locality {
 }
 incarnation: 3513264468343507029
 xla_global_id: -1]

BACK TO TOP