Math for AI course · Lesson 5 of 180
Tensor Indices for AI: Reading Samples, Tokens, and Features
Learn to read tensor subscripts, translate batch-token-feature addresses into NumPy, and catch valid-looking axis mistakes in AI code.
An AI program reads X[1, 2, 0] and returns 0.8. A nearby expression,
X[1, 0, 2], returns 0.3. Both addresses are valid. Only one means “the
first feature of the last token in the second example.”
The difference is an index: a position used to locate a value inside an ordered structure. Indices are small pieces of notation, but they determine which example, token, feature, pixel, or model output a calculation actually uses. A wrong index can therefore produce a believable number instead of a helpful error.
By the end of this lesson, you will be able to read indexed samples and coordinates, follow a batch-token-feature tensor address, translate a mathematical subscript into NumPy, and detect an address whose numbers are legal but whose axis meanings are wrong. This adds order to the dataset collections from Lesson 4. The official Mathematics for Machine Learning companion places this notation on the route from numerical data to the vectors, matrices, and models used later in the course.
An index answers “which position?”
Suppose an ordered dataset contains three examples:
The subscript in says “select the example at position 2.” It does not say that the example’s value is 2. If the second example is the pair , then names the whole pair.
Add a second subscript to select a coordinate inside that example:
Read from left to right: choose example 2, then coordinate 1. The indices form an address; the number stored at that address is the value.
This also separates position from identity. Moving a sample from position 2 to position 7 changes its index even if its stable ID, image pixels, and label do not change. Indices are useful addresses, not permanent names for real-world objects.
Each subscript chooses one axis
An axis is one direction in which an array is organized. A table has a row axis and a column axis. Here, a tensor is a multidimensional array: it can have those two axes or add more axes for concepts such as batches and tokens.
Consider an activation tensor
where:
- is the number of examples in the batch;
- is the number of token positions per example; and
- is the number of features stored for each token.
The notation says that every entry is a real number and that the tensor has shape . One scalar entry is
which means: choose batch position , token position , and feature position , in that order.
For a tensor with shape , a one-based mathematical convention allows
The shape supplies the range of each index. The axis names supply the meaning. Neither fact replaces the other.
One address, worked all the way to a value
Use the following two-batch tensor. Each batch item contains three token rows, and every token has four feature values.
| Token | Feature 1 | Feature 2 | Feature 3 | Feature 4 |
|---|---|---|---|---|
| 1 | 0.2 | 0.8 | -0.1 | 0.4 |
| 2 | 1.1 | -0.3 | 0.5 | 0.0 |
| 3 | 0.7 | 0.6 | 0.2 | -0.4 |
| Token | Feature 1 | Feature 2 | Feature 3 | Feature 4 |
|---|---|---|---|---|
| 1 | -0.2 | 0.9 | 0.3 | 0.1 |
| 2 | 0.4 | -0.7 | 1.2 | 0.5 |
| 3 | 0.8 | 0.1 | -0.6 | 1.5 |
This stepwise reading matters more than memorizing a letter order. Another project might store tokens before batches or use height, width, and channel axes for images. The code and documentation must state the contract.
NumPy starts at zero
Many mathematics texts label positions beginning at 1. Python and NumPy use zero-based indexing, so the first position is 0. NumPy’s official indexing guide also confirms that one integer per dimension selects one element and that the valid nonnegative index for an axis of size satisfies .
| Intended position | One-based math | Zero-based NumPy |
|---|---|---|
| Batch item 2 | b = 1 | |
| Token 3 | t = 2 | |
| Feature 1 | f = 0 | |
| Complete address | X[1, 2, 0] |
Here is the complete tensor and several verified selections:
import numpy as np
X = np.array([
[[ 0.2, 0.8, -0.1, 0.4],
[ 1.1, -0.3, 0.5, 0.0],
[ 0.7, 0.6, 0.2, -0.4]],
[[-0.2, 0.9, 0.3, 0.1],
[ 0.4, -0.7, 1.2, 0.5],
[ 0.8, 0.1, -0.6, 1.5]],
])
print("shape:", X.shape)
print("one activation:", X[1, 2, 0])
print("last token of example 2:", X[1, 2])
print("feature 1 of every last token:", X[:, 2, 0])
shape: (2, 3, 4)
one activation: 0.8
last token of example 2: [ 0.8 0.1 -0.6 1.5]
feature 1 of every last token: [0.7 0.8]
The colon in X[:, 2, 0] means “keep every position on this axis.” The first
axis remains, while token and feature receive specific indices, so the result
contains one value for each of the two batch items.
The shape attribute
reports (2, 3, 4), but the tuple itself does not say “batch, token, feature.”
Those names come from the program’s data contract.
A legal address can still express the wrong question
Return to the opening expressions:
X[1, 2, 0] # 0.8: batch 2, token 3, feature 1
X[1, 0, 2] # 0.3: batch 2, token 1, feature 3
Both addresses fit the shape. NumPy cannot know that the second line swapped the token and feature meanings. This is a semantic indexing bug: the program retrieves a real entry, but not the entry the calculation intended.
Useful defenses include naming the axis contract beside the tensor, checking the shape at boundaries, and assigning index variables descriptive names:
batch_index = 1
token_index = 2
feature_index = 0
activation = X[batch_index, token_index, feature_index]
assert X.shape == (2, 3, 4)
assert activation == 0.8
The assertion checks this example. The descriptive variables preserve the reasoning that led to the address.
Bounds errors are safer than silent axis errors
For the zero-based shape (2, 3, 4), the last nonnegative address is
X[1, 2, 3]. X[2, 0, 0] is invalid because batch index 2 would request a
third item from an axis containing only two items. NumPy raises IndexError
rather than returning a made-up value.
Negative indices are a Python convention: -1 selects the final position of
an axis, so X[-1, -1, -1] is another way to read X[1, 2, 3]. That shorthand
is valid code, but it should not be silently translated into a one-based
mathematical subscript. State the convention whenever the distinction matters.
The next lesson will use these addresses inside sums. Once clearly means the th example and means its loss, a symbol such as becomes a compact instruction to visit every indexed loss and combine the values.
Check your understanding
Question 1
If x₄ = (2.5, −1.0, 0.3), what do x₄ and x₄,₂ refer to?
Show the step-by-step solution
First read the number of subscripts.
- has one index, so it selects the whole example at position 4.
- The given example is the three-coordinate value .
- adds a coordinate index, so it selects coordinate 2 inside that example.
Therefore,
The subscript 4 is an address; it is not the stored value.
Question 2
Use the visual to calculate X₂,₂,₃. Show each axis choice.
Show the step-by-step solution
Follow the indices in their declared batch-token-feature order.
- chooses batch item 2.
- chooses its second token row: .
- chooses the third coordinate of that row.
Thus,
Choosing feature 3 before token 2 would change the question because the axes are ordered.
Question 3
Translate the one-based mathematical address X₂,₃,₄ into zero-based NumPy and find its value.
Show the step-by-step solution
Subtract 1 from each position when moving from the stated one-based convention to NumPy:
The code address is therefore X[1, 2, 3]. In batch item 2, token 3 is
, and feature 4 is . Hence
Question 4
For a zero-based tensor with shape (2, 3, 4), classify X[0, 2, 3], X[1, 3, 0], and X[2, 0, 0] as in bounds or out of bounds.
Show the step-by-step solution
Translate the shape into the valid nonnegative ranges:
X[0, 2, 3]is in bounds because every index belongs to its range.X[1, 3, 0]is out of bounds because token index 3 is not in .X[2, 0, 0]is out of bounds because batch index 2 is not in .
The first failing axis is enough for NumPy to reject the complete address.
Question 5
A developer intends batch 2, token 3, feature 1 but writes X[1, 0, 2]. Why does no bounds error occur, and how should the line be repaired?
Show the step-by-step solution
For shape (2, 3, 4), the indices (1, 0, 2) are mechanically valid:
NumPy therefore returns the value at batch 2, token 1, feature 3, which is . The library cannot infer the intended axis meanings.
Convert the intended one-based positions to zero-based positions $(1,2,0)`. The repaired line is:
activation = X[1, 2, 0] # 0.8This is a semantic bug rather than a bounds bug, so descriptive variable names and an explicit axis contract are useful defenses.
Question 6
What does X[-1, -1, -1] select, and why is −1 not a permanent identity for that value?
Show the step-by-step solution
In NumPy, -1 means “the last position on this axis.” For shape (2, 3, 4),
the last zero-based positions are (1, 2, 3). Therefore,
The meaning depends on the current shape and ordering. If another batch item or
token were appended, -1 would point somewhere else. It is a relative address,
not a stable ID attached to the stored value.