Chapter 2: Arrays and Shapes

Think in shapes, not loops.

Every ML library you will touch keeps its numbers in NumPy’s or something built like it. An image, a batch of token ids and a layer’s weights are all . Many bugs in ML code come from the wrong : a (32, 10) where the code expected (10, 32). This chapter is about reading .

An is its values, its , and its . Make one from a list with , or from nothing with , and . counts in steps, and spaces a set number of points between two ends. Random values come from a generator that makes; give it a seed and a run can be repeated.

import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6]])
a.shape, a.dtype
Output:
((2, 3), dtype('int64'))
np.zeros((2, 3))
Output:
array([[0., 0., 0.],
       [0., 0., 0.]])
np.full((2, 3), 7)
Output:
array([[7, 7, 7],
       [7, 7, 7]])
np.arange(0, 12, 3)
Output:
array([0, 3, 6, 9])
np.linspace(0, 1, 5)
Output:
array([0.  , 0.25, 0.5 , 0.75, 1.  ])
rng = np.random.default_rng(0)
rng.integers(0, 10, size=4)
Output:
array([8, 6, 5, 2])

To read part of an , use for one value and for a range, one position per . A picks the values where a condition holds. picks rows by a list of positions, in any order. reads the same values into a new , and tells it to work out one length for you.

Array Example — ndarray Step 1 of 6
import numpy as npa = np.arange(12).reshape(3, 4)a[1:, ::2]a[a % 2 == 0]a[[2, 0]]a.reshape(2, -1)a.T

a shape (3, 4) · int64

0123
4567
891011

Twelve values, three rows of four: shape (3, 4), dtype int64.

Read a one at a time. 0 is the first number, the rows of a 2-D ; 1 is the second, the columns. A keeps the it , so a[1:2] is still 2-D.

A single drops its : a[1] is one row with (4,). This is where most people slip, because a[1] and a[1:2] print the same numbers, one pair of brackets apart, and the next line breaks. A the same as the gives back 1-D, since the values it picks no longer form a grid; a over the rows alone, a[a[:, 0] > 2], keeps whole rows.

import numpy as np
a = np.arange(12).reshape(3, 4)
a[1].shape
Output:
(4,)
a[1:2].shape
Output:
(1, 4)
a[:, 1].shape
Output:
(3,)
a[:, 1:2].shape
Output:
(3, 1)
a[a > 5].shape
Output:
(6,)
a[a[:, 0] > 2].shape
Output:
(2, 4)

and both lay an out in one line. hands back the same memory when it can; always . Chapter 3 shows why sharing memory bites. , written .T, reverses the , so a (2, 3) becomes (3, 2) and its rows become columns.

import numpy as np
a = np.arange(6).reshape(2, 3)
a.ravel()
Output:
array([0, 1, 2, 3, 4, 5])
np.shares_memory(a, a.ravel()), np.shares_memory(a, a.flatten())
Output:
(True, False)
a.T
Output:
array([[0, 3],
       [1, 4],
       [2, 5]])

The habit to build is small: before you run a line, say the it will make. When you’re wrong, print .shape and find out why. A model is a long chain of such lines, and a error caught in your head costs nothing.

Homework

The packet drills that habit: predict the of each line, then write the lines yourself. It ends with a small build, pulling the top-scoring rows out of a score matrix.

Chapter 2 workbook

Unzip, follow the README once to set up, then run pytest until it's green.