Understanding TensorFlow: Part 2

Serie 2: Basic operations in TensorFlow 2.x

dan lee
9 min readSep 9, 2021

Here is today’s outline:

  1. Tensor creation
  • Create Tensors by Python basic types and Numpy array
  • Create Tensors by tf.random
  • Create Tensors by special value function of TensorFlow

2. Tensor merge and split

3. Tensor comparison and sort

4. Mathematical operations

1. Tensor creation

The data type in TensorFlow is Tensor. A scalar is a tensor, a vector is a tensor, a matrix is a tensor, and the matrix of a matrix is a tensor, too. Tensor is a compound type. The elements in Tensor are python basic data types that we are quite familiar with, including bool, int, float, double, string, array.

TensorFlow 2.x is also quite friendly to Numpy. It perfectly supports Data type switch between Tensor and Numpy.

Let’s use examples to create tensors and make it more clear.

1.1 Create Tensors by Python basic types and Numpy array

import tensorflow as tf

import numpy as np

# int

print(tf.constant(5))

# float

print(tf.constant(5.0))

# double

print(tf.constant(5.0, dtype=tf.float64))

# bool

print(tf.constant(True))

# string

print(tf.constant(‘hello, world’))

# python list

print(tf.constant([1., 2., 3.]))

# numpy list

print(tf.constant(np.array([1., 2., 3.])))

# Returns (out) =>

tf.Tensor(5, shape=(), dtype=int32)

tf.Tensor(5.0, shape=(), dtype=float32)

tf.Tensor(5.0, shape=(), dtype=float64)

tf.Tensor(True, shape=(), dtype=bool)

tf.Tensor(b’tensorflow’, shape=(), dtype=string)

tf.Tensor([1. 2. 3.], shape=(3,), dtype=float32)

tf.Tensor([1. 2. 3.], shape=(3,), dtype=float64)

Note that the dtype of Tensors created by python list and numpy list are different: float64 and float32. We should keep in mind that the default basic data type that switches from Numpy to Tensor is double. GPU processes float32 is much faster than processing double(float64). If you plan to run your code on GPU, you’d better explicitly point out the dtype should be float32 here by this:

print(tf.constant(np.array([1., 2., 3.]), dtype=tf.float32))

# Returns (out) =>

tf.Tensor([1. 2. 3.], shape=(3,), dtype=float32)

tf.convert_to_tensor can also convert other data types to Tensor. One example is given below, and you can try other data types by yourself. Definitely you would get the same result in both ways. tf.covert_to tensor is often used after we vectorize the x_data to turn its data type into Tensor.

print(tf.constant([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]]))

print(tf.convert_to_tensor([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]]))

# Returns (out) =>

tf.Tensor(

[[0.1 0.2 0.3]

[0.4 0.5 0.6]], shape=(2, 3), dtype=float32)

tf.Tensor(

[[0.1 0.2 0.3]

[0.4 0.5 0.6]], shape=(2, 3), dtype=float32)

1.2 Create Tensors by tf.random

tf.random.normal: Outputs random values from a normal distribution. The default mean is 0.0. the default standard deviation is 1.0. The shape parameter need to be explicitly assigned.

import tensorflow as tf

# tf.random.normal.

print(tf.random.normal([2, 2], mean=0, stddev=1))

# Returns (out) =>

tf.Tensor(

[[ 0.07503338 0.690836 ]

[-0.54832345 -0.8049861 ]], shape=(2, 2), dtype=float32)

tf.random.truncated_normal: The generated values follow a normal distribution with specified mean and standard deviation, except that values whose magnitude is more than 2 standard deviations from the mean are dropped and re-picked.

# tf.random.truncated_normal. Outputs random values from a truncated normal

# distribution.

print(tf.random.truncated_normal([2,2], mean=0, stddev=1, dtype=tf.double))

# Returns (out) =>

tf.Tensor(

[[ 0.77151253 -0.10534472]

[ 0.33723833 -0.29132416]], shape=(2, 2), dtype=float64)

tf.random.uniform: The generated values follow a uniform distribution in the range [minval, maxval). The lower bound minval is included in the range, while the upper bound maxval is excluded.For floats, the default range is [0, 1). For ints, at least maxval must be specified explicitly.

# tf.random.uniform. Outputs random values from a uniform distribution.

print(tf.random.uniform([2, 2], minval=0, maxval=1))

# Returns (out) =>

tf.Tensor(

[[0.37484133 0.54192734]

[0.8302536 0.4088725 ]], shape=(2, 2), dtype=float32)

1.3 Create Tensors by special value function of TensorFlow

tf.ones: Create a tensor with all elements set to 1. This operation returns a dtype tensor with a shape of all elements are set to 1.

# tf.ones

print(tf.ones(shape=(1,3),dtype=tf.float32))

# Returns (out) =>

tf.Tensor([[1. 1. 1.]], shape=(1, 3), dtype=float32)

tf.zeros: Create a tensor with all elements set to 0. This operation returns a dtype tensor with a shape of all elements are set to 0.

# tf.zeors

print(tf.zeros(shape=(1,3),dtype=tf.float32))

# Returns (out) =>

tf.Tensor([[0. 0. 0.]], shape=(1, 3), dtype=float32)

tf.eye: Construct an identity matrix or a batch of matrices.

# tf.eye

print(tf.eye(4, dtype=tf.float32))

# Returns (out) =>

tf.Tensor(

[[1. 0. 0. 0.]

[0. 1. 0. 0.]

[0. 0. 1. 0.]

[0. 0. 0. 1.]], shape=(4, 4), dtype=float32)

tf.ones_like: Create a tensor with all elements set to 1. Given a tensor, this operation returns a tensor of the same type and shape as the tensor with all elements set to 1. Optionally, it can specify the tensor with a new type (dtype).

# tf.ones_like

print(tf.ones_like(tf.linspace(0.0, 1.0, 4)))

# Returns (out) =>

tf.Tensor([1. 1. 1. 1.], shape=(4,), dtype=float32)

tf. zeros_like: Create a tensor with all elements set to 0. Given a tensor, this operation returns a tensor of the same type and shape as the tensor with all elements set to 0. Optionally, it can specify the tensor with a new type (dtype).

# tf.zeros_like

print(tf.zeros_like(tf.linspace(0.0, 1.0, 4)))

# Returns (out) =>

tf.Tensor([0. 0. 0. 0.], shape=(4,), dtype=float32)

tf. linspace: An calculation instruction that evenly divides the given ‘start’ and ‘stop’ numbers by ‘num’ and generates values in the interval.

# tf.linspace

print(tf.linspace(start=0.0,stop=1.0, num=3))

# Returns (out) =>

tf.Tensor([0. 0.5 1. ], shape=(3,), dtype=float32)

tf.fill: Creates a tensor filled with a scalar value. This operation creates a tensor of shape dims and fills it with value.

# tf.fill

print(tf.fill(dims=(1,5), value=1.0))

# Returns (out) =>

tf.Tensor([[1. 1. 1. 1. 1.]], shape=(1, 5), dtype=float32)

2 Tensor merge and split

Merging refers to merging multiple tensors into one tensor in a certain dimension. There are two kinds of Merging-concatenation and stacking. Concatenation does not create new dimensions, but stacking creates new dimensions.

The concatenation can be performed on any dimension. The only constraint is that the length of the non-concatenated dimensions must be the same.

a = tf.random.normal([1,2,3])

b = tf.random.normal([2,2,3])

c = tf.concat([a,b], axis=0)

print(c)

# Returns (out) =>

tf.Tensor(

[[[-0.27534676 -0.16034059 0.52429354]

[-0.5917587 -0.23074977 -1.3515841 ]]

[[-0.4800986 -1.7884752 -0.75610226]

[-1.7305112 -0.7396962 1.0428202 ]]

[[-0.33405465 0.3331637 -0.26588857]

[-0.19221431 -1.5180566 -0.67606276]]], shape=(3, 2, 3), dtype=float32)

tf.concat directly merges data on existing dimensions and does not create a new dimension. If you want to create a new dimension when merging data, you need to use the tf.stack. The parameter axis specifies where to insert the new dimension. When an axis is a positive number, it will insert a new dimension in front of axis. When the axis is negative, it will insert a new dimension in the next position.

tf.stack also needs to meet the merging conditions of tensor stacking. It requires all merged tensors to have the same shape before they are merged.

a = tf.random.normal([2,3])

b = tf.random.normal([2,3])

c = tf.stack([a,b], axis = 0)

print(c)

# Returns (out) =>

tf.Tensor(

[[[ 0.7345577 0.6821109 1.2609174 ]

[ 1.6845615 0.7451898 0.04291777]]

[[-0.33740672 0.71333706 1.2531255 ]

[ 0.66583854 -0.8219059 0.278419 ]]], shape=(2, 2, 3), dtype=float32)

d = tf.stack([a,b], axis = -1)

print(d)

# Returns (out) =>

tf.Tensor(

[[[ 0.53225416 1.041896 ]

[-0.66618955 0.03342717]

[-1.6268841 0.18227476]]

[[ 0.11992308 0.33611476]

[ 0.14574063 -0.13129552]

[-0.59104127 1.382147 ]]], shape=(2, 3, 2), dtype=float32)

The inverse process of the merge operation is splitting. splitting is to split a tensor into multiple tensors. There are two kinds of split-split and unstack. Pay attention to the output shape.

#split

x = tf.random.normal([3,2,3])

print(x)

result = tf.split(x, axis=0, num_or_size_splits =3)

print(result)

# Returns (out) =>

tf.Tensor(

[[[ 0.3446128 1.8321378 -0.3682034 ]

[-0.59962803 -1.3437859 -0.20841458]]

[[-0.28618914 -0.37867725 -2.6242352 ]

[-1.4301056 -2.365182 -0.52069366]]

[[-0.5681175 2.4599526 -0.03526611]

[ 0.7553041 1.8161643 -0.7825681 ]]], shape=(3, 2, 3), dtype=float32)

[<tf.Tensor: shape=(1, 2, 3), dtype=float32, numpy=

array([[[ 0.3446128 , 1.8321378 , -0.3682034 ],

[-0.59962803, -1.3437859 , -0.20841458]]], dtype=float32)>,

<tf.Tensor: shape=(1, 2, 3), dtype=float32, numpy=

array([[[-0.28618914, -0.37867725, -2.6242352 ],

[-1.4301056 , -2.365182 , -0.52069366]]], dtype=float32)>,

<tf.Tensor: shape=(1, 2, 3), dtype=float32, numpy=

array([[[-0.5681175 , 2.4599526 , -0.03526611],

[ 0.7553041 , 1.8161643 , -0.7825681 ]]], dtype=float32)>]

#unstack

x = tf.random.normal([3,2,3])

print(x)

result = tf.unstack(x,axis = 0)

print(result)

# Returns (out) =>

tf.Tensor(

[[[-0.45672733 -0.6107957 -1.5969335 ]

[-0.05063461 0.90097845 -0.51631683]]

[[ 1.440197 -0.28877714 -0.52491015]

[-2.1945505 0.01744763 0.35254684]]

[[-0.66387564 0.63262576 -0.14352717]

[-0.08475702 -1.186847 0.7461847 ]]], shape=(3, 2, 3), dtype=float32)

[<tf.Tensor: shape=(2, 3), dtype=float32, numpy=

array([[-0.45672733, -0.6107957 , -1.5969335 ],

[-0.05063461, 0.90097845, -0.51631683]], dtype=float32)>,

<tf.Tensor: shape=(2, 3), dtype=float32, numpy=

array([[ 1.440197 , -0.28877714, -0.52491015],

[-2.1945505 , 0.01744763, 0.35254684]], dtype=float32)>,

<tf.Tensor: shape=(2, 3), dtype=float32, numpy=

array([[-0.66387564, 0.63262576, -0.14352717],

[-0.08475702, -1.186847 , 0.7461847 ]], dtype=float32)>]

3. Tensor comparison and sort

In actual work scenarios, we may only be interested in data that meets certain conditions, or needs to count the accuracy of a task and other indicators. Generally, we need to compare the predicted result with the true label. We need to calculate accuracy rate though the results that are the same as the labels or greater than a certain threshold. TensorFlow provides tensor comparison operations, including equal or greater or not.

y = tf.random.uniform([10],dtype=tf.int64,maxval=10)

print(y)

y_hat = tf.random.uniform([10],dtype=tf.int64,maxval=10)

print(y_hat)

result = tf.equal(y_hat,y)

print(result)

print(y_hat > 5)

# Returns (out) =>

tf.Tensor([4 8 8 3 5 8 2 1 6 7], shape=(10,), dtype=int64)

tf.Tensor([0 2 2 2 3 9 8 2 4 8], shape=(10,), dtype=int64)

tf.Tensor([False False False False False False False False False False], shape=(10,), dtype=bool)

tf.Tensor([False False False False False True True False False True], shape=(10,), dtype=bool)

In some cases, we need data that is ranked higher. TensorFlow 2.x provides a variety of sorting functions. In addition to the ordinary ascending and descending order, there are more convenient operations, like top data and the index of the largest data.

tensor = tf.random.uniform([10], maxval=10)

print(tensor)

# sort

print(tf.sort(tensor, direction=’DESCENDING’))

# indice of sort result

print(tf.argsort(tensor, direction=’DESCENDING’))

# top k

print(tf.math.top_k(a, 3))

# set all elements greater than 4

print(tf.maximum(a, 5))

# set all elements smaller than 4

print(tf.minimum(a, 5))

# set all elements greater than 3 smaller than 7

print(tf.clip_by_value(a, 3, 7))

# Returns (out) =>

tf.Tensor(

[8.151581 2.8347158 8.287186 1.431247 5.478798 4.6553183 5.820148

4.525962 7.1494665 6.572027 ], shape=(10,), dtype=float32)

tf.Tensor(

[8.287186 8.151581 7.1494665 6.572027 5.820148 5.478798 4.6553183

4.525962 2.8347158 1.431247 ], shape=(10,), dtype=float32)

tf.Tensor([2 0 8 9 6 4 5 7 1 3], shape=(10,), dtype=int32)

TopKV2(values=<tf.Tensor: shape=(3,), dtype=float32, numpy=array([8.287186 , 8.151581 , 7.1494665], dtype=float32)>, indices=<tf.Tensor: shape=(3,), dtype=int32, numpy=array([2, 0, 8], dtype=int32)>)

tf.Tensor(

[8.151581 5. 8.287186 5. 5.478798 5. 5.820148

5. 7.1494665 6.572027 ], shape=(10,), dtype=float32)

tf.Tensor(

[5. 2.8347158 5. 1.431247 5. 4.6553183 5.

4.525962 5. 5. ], shape=(10,), dtype=float32)

tf.Tensor(

[7. 3. 7. 3. 5.478798 4.6553183 5.820148

4.525962 7. 6.572027 ], shape=(10,), dtype=float32)

In big data processing, what we often care about is not each element’s value, but the average, maximum, minimum, and corresponding index in a certain dimension (default global dimension).

tensor = tf.constant([[1,2],[3,4]], dtype=tf.float32)

# max in dimension 1

print(tf.reduce_max(tensor, axis=1))

# min in dimension 0

print(tf.reduce_min(tensor, axis=0))

# average, default global dimension

print(tf.reduce_mean(tensor))

# index of max value in dimension 0

print(tf.argmax(tensor, axis=0))

# index of min value in dimension 1

print(tf.argmin(tensor, axis=1))

# Returns (out) =>

tf.Tensor([2. 4.], shape=(2,), dtype=float32)

tf.Tensor([1. 2.], shape=(2,), dtype=float32)

tf.Tensor(2.5, shape=(), dtype=float32)

tf.Tensor([1 1], shape=(2,), dtype=int64)

tf.Tensor([0 0], shape=(2,), dtype=int64)

One more example, after we use the loss function getting each sample’s loss, the average loss of the samples needs to be calculated.

y_hat = tf.random.normal([10,5])

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

y = tf.one_hot(y,depth=5)

loss = tf.keras.losses.mse(y,y_hat)

loss_mean = tf.reduce_mean(loss)

print(loss_mean)

# Returns (out) =>

tf.Tensor(1.4730119, shape=(), dtype=float32)

Of course, you can also get the maximum and minimum loss, even their indices which indicates the sample with maximum loss or minimum loss.

loss_max = tf.reduce_max(loss)

loss_min = tf.reduce_min(loss)

idx_max = tf.argmax(loss)

idx_min = tf.argmin(loss)

print(loss)

print(loss_max)

print(loss_min)

print(idx_max)

print(idx_min)

# Returns (out) =>

tf.Tensor(

[0.665666 0.50770426 0.37840793 0.550116 0.674733 0.5545435

2.1785192 2.1507242 0.500903 1.2179064 ], shape=(10,), dtype=float32)

tf.Tensor(2.1785192, shape=(), dtype=float32)

tf.Tensor(0.37840793, shape=(), dtype=float32)

tf.Tensor(6, shape=(), dtype=int64)

tf.Tensor(2, shape=(), dtype=int64)

4. Mathematical operations

TensorFlow 2.x support mathematical operations much more friendly that TensorFlow 1.x. The commonly used operations are listed below. It is clear and absolutely meets python programming habit.

a = tf.constant([[1,2],[3,4]], dtype=tf.float32)

b = tf.constant([[5,6],[7,8]], dtype=tf.float32)

# element-wise add

print(a+b)

# Returns (out) =>

f.Tensor(

[[ 6. 8.]

[10. 12.]], shape=(2, 2), dtype=float32)

# element-wise minus

print(a-b)

# Returns (out) =>

tf.Tensor(

[[-4. -4.]

[-4. -4.]], shape=(2, 2), dtype=float32)

# element-wise multipy

print(a*b)

# Returns (out) =>

tf.Tensor(

[[ 5. 12.]

[21. 32.]], shape=(2, 2), dtype=float32)

# element-wise divide

print(‘a/b=’, a/b)

# Returns (out) =>

tf.Tensor(

[[0.2 0.33333334]

[0.42857143 0.5 ]], shape=(2, 2), dtype=float32)

# element-wise floor division

print(‘a//b’, a//b)

# Returns (out) =>

tf.Tensor(

[[0. 0.]

[0. 0.]], shape=(2, 2), dtype=float32)

# element-wise remainder

print(‘b%a=’, b%a)

# Returns (out) =>

tf.Tensor(

[[0. 0.]

[1. 0.]], shape=(2, 2), dtype=float32)

# square

print(a**2)

# square

print(tf.pow(a, 2.0))

# square

print(tf.square(a))

# Returns (out) =>

tf.Tensor(

[[ 1. 4.]

[ 9. 16.]], shape=(2, 2), dtype=float32)

tf.Tensor(

[[ 1. 4.]

[ 9. 16.]], shape=(2, 2), dtype=float32)

tf.Tensor(

[[ 1. 4.]

[ 9. 16.]], shape=(2, 2), dtype=float32)

# square root

print(tf.sqrt(a))

# Returns (out) =>

tf.Tensor(

[[1. 1.4142135]

[1.7320508 2. ]], shape=(2, 2), dtype=float32)

# exp

print(tf.exp(a))

# Returns (out) =>

tf.Tensor(

[[ 2.7182817 7.389056 ]

[20.085537 54.59815 ]], shape=(2, 2), dtype=float32)

# log

print(tf.math.log(a))

# Returns (out) =>

tf.Tensor(

[[0. 0.6931472]

[1.0986123 1.3862944]], shape=(2, 2), dtype=float32)

# maxtrix multipy

print(tf.matmul(a, b))

# Returns (out) =>

tf.Tensor(

[[19. 22.]

[43. 50.]], shape=(2, 2), dtype=float32)

Other series of Understanding TensorFlow:

Serie1: https://medium.com/@Adline125/understanding-tensorflow-series-979e71cc5562

Serie2: https://medium.com/@Adline125/understanding-tensorflow-fcc431891d08

Serie3–1: https://medium.com/@Adline125/understanding-tensorflow-ce18f0e1bbbc

Serie3–2: https://medium.com/@Adline125/understanding-tensorflow-2c6496b71368

Serie4: https://medium.com/@Adline125/understanding-tensorflow-94bdea8e1fd9

--

--

dan lee

NLP Engineer, Google Developer Expert, AI Specialist in Yodo1