Tensor Flow 📂 Tensors · 4 of 6 36 min read

Tensor Shape, Rank, dtype and tf.reshape Explained

Understand the three facts every tensor has: shape, rank and dtype. Climb from rank 0 to rank 4 with real data examples. Learn why TensorFlow never mixes dtypes, what tf.cast does to your numbers, and how tf.reshape, -1, expand_dims and squeeze work. Watch values move in the reshape animator.

Section 01

The Story — Twelve Eggs, Many Cartons

Same Eggs, Different Boxes
You have 12 eggs. You can put them in one long row of 12. Or in a carton of 2 rows × 6. Or 3 × 4. Or you can stack two small cartons of 2 × 3 on top of each other.

The eggs never change. Only the box changes. In TensorFlow, the box is the shape. Changing the box is called reshape.

And the kind of egg (hen, duck, quail) is like the dtype. You cannot mix kinds in one carton.

Every tensor has three key facts: its shape (size of each dimension), its rank (how many dimensions), and its dtype (the type of each value). Most TensorFlow errors you will ever see are shape or dtype errors. So this lesson is worth learning well.

📐
Shape
x.shape → (2, 3)
The size along each axis. 2 rows and 3 columns.
🪙
Rank
x.ndim → 2
The number of axes. Also called the number of dimensions.
🏷️
dtype
x.dtype → float32
The type of every value inside. All values share one dtype.

Section 02

Try It — Climb the Rank Ladder

🪙 Rank 0 to Rank 4 Interactive

Move the slider. Each step adds one more axis. See what real data has that rank.

RankNameExample shapeReal data
0Scalar()A loss value, a temperature
1Vector(4,)One row of features, an audio clip
2Matrix(3, 4)A table, a grayscale image, a weight matrix
33-D tensor(28, 28, 3)A colour image (height, width, channels)
44-D tensor(32, 28, 28, 3)A batch of 32 colour images

Section 03

Reading Shape, Rank and Size in Code

import tensorflow as tf

x = tf.zeros([2, 3, 4])

print("shape :", x.shape)          # TensorShape, known in Python
print("ndim  :", x.ndim)           # rank as a Python int
print("rank  :", tf.rank(x))       # rank as a tensor
print("size  :", tf.size(x))       # total number of values
print("dims  :", x.shape[0], x.shape[-1])
print("list  :", x.shape.as_list())
OUTPUT
shape : (2, 3, 4) ndim : 3 rank : tf.Tensor(3, shape=(), dtype=int32) size : tf.Tensor(24, shape=(), dtype=int32) dims : 2 4 list : [2, 3, 4]

x.shape vs tf.shape(x)

x.shape — static
Python TensorShape
Known when the tensor is created
Can contain None in a tf.function
tf.shape(x) — dynamic
An int32 tensor
Always has the true size at run time
Use it inside graphs when batch size is unknown
@tf.function(input_signature=[tf.TensorSpec(shape=[None, 3])])
def batch_info(x):
    print("static  :", x.shape)          # printed once at trace time
    tf.print("dynamic :", tf.shape(x))   # printed at run time
    return x

batch_info(tf.zeros([5, 3]))
OUTPUT
static : (None, 3) dynamic : [5 3]

Axes and Negative Indexes

Axes are numbered from the outside in. For a tensor of shape (2, 3, 4), axis 0 has size 2 and axis 2 has size 4. Negative numbers count from the end. So axis=-1 always means the last axis.

Axis012
Negative axis-3-2-1
Size234
Meaning (image)heightwidthchannels

Section 04

dtype — The Type of Every Value

tf.float32
32-bit float — the default
Weights, inputs, outputs. Use this unless you have a reason not to.
tf.float16 / bfloat16
16-bit float
Half the memory. Used for mixed-precision training on modern GPUs and TPUs.
tf.float64
64-bit float
High precision. NumPy's default. Slow on GPUs and rarely needed.
tf.int32 / int64
whole numbers
Labels, indices, counts. tf.constant(5) gives int32.
tf.bool
True / False
Masks and results of comparisons like x > 0.
tf.uint8 / tf.string
bytes and text
uint8 holds raw image pixels 0–255. string holds text before tokenising.

TensorFlow Does Not Mix dtypes for You

NumPy quietly converts types. TensorFlow does not. It raises an error instead. This protects you from silent bugs.

a = tf.constant([1, 2, 3])          # int32
b = tf.constant([0.5, 0.5, 0.5])    # float32

try:
    a + b
except tf.errors.InvalidArgumentError as e:
    print("Error:", e.message.split(":")[0])

print(tf.cast(a, tf.float32) + b)   # fix: cast first
OUTPUT
Error: cannot compute AddV2 as input #1(zero-based) was expected to be a int32 tensor but is a float tensor [Op tf.Tensor([1.5 2.5 3.5], shape=(3,), dtype=float32)

tf.cast — Change the dtype

prices = tf.constant([2.7, -2.7, 9.99])
print(tf.cast(prices, tf.int32))      # cuts toward zero, no rounding!
print(tf.cast(prices, tf.bool))       # non-zero -> True

pixels = tf.constant([0, 128, 255], dtype=tf.uint8)
print(tf.cast(pixels, tf.float32) / 255.0)   # classic image scaling
OUTPUT
tf.Tensor([ 2 -2 9], shape=(3,), dtype=int32) tf.Tensor([ True True True], shape=(3,), dtype=bool) tf.Tensor([0. 0.5019608 1. ], shape=(3,), dtype=float32)
⚠️
Casting Can Lose Information

Float to int cuts off the decimal part. It does not round. Use tf.round first if you want rounding. Values too big for the new type wrap around. For example 300 becomes 44 in uint8.


Section 05

Try It — The dtype Caster

🏷️ What Does tf.cast Do to My Numbers? Interactive

Type some numbers, separated by commas. Pick a target dtype. Numbers with a decimal point start as float32; whole numbers start as int32.

InputOutputWhat happened

Section 06

tf.reshape — Same Values, New Box

tf.reshape(x, new_shape) keeps all the values in the same order. It only changes how they are grouped. There is one rule: the total number of values must stay the same.

The Size Rule
prod(old_shape) == prod(new_shape)
(2, 6) has 12 values. So (3, 4), (12,) and (2, 2, 3) all work. (5, 3) does not.
The -1 Shortcut
tf.reshape(x, [-1, 4])
-1 means "work this out for me". 12 values ÷ 4 = 3. Only one -1 is allowed.
x = tf.range(12)
print(tf.reshape(x, [3, 4]))
print(tf.reshape(x, [2, -1]).shape)       # -1 becomes 6
print(tf.reshape(x, [2, 2, 3]).shape)
print(tf.reshape(x, [-1]).shape)          # flatten to 1-D

try:
    tf.reshape(x, [5, 3])
except tf.errors.InvalidArgumentError as e:
    print("Error:", e.message.split(" [Op")[0])
OUTPUT
tf.Tensor( [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]], shape=(3, 4), dtype=int32) (2, 6) (2, 2, 3) (12,) Error: {{function_node __wrapped__Reshape_device_/job:localhost/replica:0/task:0/device:CPU:0}} Input to reshape is a tensor with 12 values, but the requested shape has 15

Row-Major Order

TensorFlow reads values in row-major order. It walks along the last axis first, like reading a book: left to right, then the next line. Reshape pours values into the new shape in that same order.


Section 07

Try It — The Reshape Animator

🔄 Watch the Values Move Interactive

Each value shows its position number. Enter a start shape and a new shape (you may use one -1). Press Reshape and watch each value slide to its new place. Then try Transpose and compare.

💡
Reshape Is Not Transpose

Start with shape 2,3. Reshape to 3,2: the numbers stay in reading order 0, 1, 2, 3, 4, 5. Now press Transpose: rows become columns, so the order changes to 0, 3, 1, 4, 2, 5. Same final shape, different data. Mixing these up is a very common bug.


Section 08

Adding and Removing Axes of Size 1

Many layers need an extra axis. For example, a model trained on batches cannot take one single image. You add a batch axis of size 1.

img = tf.zeros([28, 28])                  # one grayscale image

batch = tf.expand_dims(img, axis=0)       # add batch axis at the front
print(batch.shape)

with_channel = img[..., tf.newaxis]       # add channel axis at the end
print(with_channel.shape)

print(tf.squeeze(tf.zeros([1, 28, 28, 1])).shape)   # drop ALL size-1 axes
print(tf.squeeze(tf.zeros([1, 28, 28, 1]), axis=0).shape)
OUTPUT
(1, 28, 28) (28, 28, 1) (28, 28) (28, 28, 1)
FunctionWhat it doesExample
tf.reshapeAny new shape with the same size(2, 6) → (3, 4)
tf.expand_dimsInsert one axis of size 1(28, 28) → (1, 28, 28)
tf.squeezeRemove axes of size 1(1, 28, 28, 1) → (28, 28)
tf.transposeReorder the axes (moves data)(2, 3) → (3, 2)
tf.castChange the dtype, not the shapeint32 → float32

Section 09

Shapes You Will Meet in Deep Learning

📊
Tabular data
One row per sample, one column per feature.
(batch, features)
🖼️
Images
TensorFlow uses channels-last by default. Called NHWC.
(batch, height, width, channels)
📝
Text / sequences
Each token becomes a vector after the embedding layer.
(batch, time_steps, features)
🎬
Video
A sequence of image frames.
(batch, frames, h, w, channels)
🏷️
Class labels
Integer labels, or one-hot vectors.
(batch,) or (batch, classes)
⚖️
Dense weights
Kernel maps inputs to units. Bias has one value per unit.
W: (in, units) b: (units,)

Section 10

Golden Rules

📐 Shape, Rank and dtype — Rules to Remember
1
When in doubt, print the shape. Most bugs are shape bugs.
2
Rank = number of axes. axis=-1 always means the last axis.
3
TensorFlow never mixes dtypes by itself. Use tf.cast to match them.
4
Casting float to int cuts the decimals. Round first if you need rounding.
5
Reshape keeps the value order. The total size must match. Use one -1 to let TensorFlow fill in a size.
6
To swap rows and columns, use tf.transpose, never tf.reshape.