No history yet

Numerical Foundations

From Loops to Vectorization

In standard Python, performing mathematical operations on a large list of numbers usually involves a for loop. While loops are flexible, they are notoriously slow for numerical computation because Python must interpret each step individually. This overhead becomes a major bottleneck in AI, where models process millions or even billions of numbers.

This is where NumPy changes the game. NumPy's power comes from a concept called vectorization, which allows you to perform operations on entire arrays of data at once. Instead of looping, you apply the operation directly to the array. Under the hood, NumPy runs these operations using highly optimized, pre-compiled C code, which is orders of magnitude faster than a Python loop.

import numpy as np
import time

# Create a large list and a NumPy array
my_list = list(range(1_000_000))
my_array = np.arange(1_000_000)

# --- Using a Python loop ---
start_time = time.time()
result_list = [x * 2 for x in my_list]
end_time = time.time()
print(f"Python loop time: {end_time - start_time:.4f} seconds")

# --- Using NumPy vectorization ---
start_time = time.time()
result_array = my_array * 2
end_time = time.time()
print(f"NumPy vectorized time: {end_time - start_time:.4f} seconds")

# Python loop time: 0.0623 seconds (will vary)
# NumPy vectorized time: 0.0015 seconds (will vary)

The performance difference is striking. Vectorization is not just a convenience; it's a fundamental requirement for building performant AI models.

The Core: ndarray

At the heart of NumPy is the ndarray, which stands for N-dimensional array. Unlike a Python list, which can hold items of different types, every element in a single ndarray must be of the same data type. This homogeneity is key to NumPy's speed, as it allows for efficient storage in a contiguous block of memory. Each array has attributes like ndim (number of dimensions), shape (the size of each dimension), and dtype (the data type).

dtype

noun

An object that describes the type of data (e.g., integer, float) stored in a NumPy array and how many bytes it occupies.

# Create a 2D array (a matrix)
arr = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.int16)

print(f"Dimensions: {arr.ndim}")
# Output: Dimensions: 2

print(f"Shape: {arr.shape}")
# Output: Shape: (2, 3)

print(f"Data Type: {arr.dtype}")
# Output: Data Type: int16

Choosing the right dtype is a crucial optimization step. In machine learning, it's common to use float32 instead of the default float64 to halve the memory footprint of a model's weights without a significant loss in precision. For large models, this can be the difference between fitting on a GPU and running out of memory.

Accessing and Shaping Data

NumPy extends Python's list indexing to multiple dimensions. You can slice arrays to get sub-arrays, which are views of the original data, not copies. This means modifying a slice will change the original array—a feature that saves memory but requires careful handling.

Beyond basic slicing, NumPy offers powerful advanced indexing techniques. You can use an array of integers to select specific elements in any order, or a boolean array to filter elements based on a condition. These methods are essential for preparing and manipulating datasets for model training.

arr = np.arange(10)
# array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

# Integer array indexing: select elements at indices 2, 5, and 8
print(arr[[2, 5, 8]])
# Output: [2 5 8]

# Boolean indexing: select elements greater than 5
mask = arr > 5
print(arr[mask])
# Output: [6 7 8 9]

Equally important is shape manipulation. AI models expect data in specific tensor shapes (e.g., (batch_size, height, width, channels) for images). NumPy's reshape() method allows you to change an array's shape without changing its data. Another key concept is , which describes how NumPy handles operations on arrays of different but compatible shapes. For example, you can add a vector to every row of a matrix without an explicit loop.

Linear Algebra for AI

Machine learning, and especially deep learning, is fundamentally applied linear algebra. Operations like matrix multiplication are the backbone of neural networks. NumPy provides a suite of linear algebra functions in its linalg submodule, but the most common operation, dot product, is built right in with the @ operator.

For two matrices, A and B, the expression A @ B computes their dot product. This is how a neural network layer transforms its input: the input data (a matrix) is multiplied by the layer's weights (another matrix). This is also where dimensions become critically important. For A @ B to be a valid operation, the number of columns in A must equal the number of rows in B.

(abcd)@(xy)=(ax+bycx+dy)\begin{pmatrix} a & b \\ c & d \end{pmatrix} @ \begin{pmatrix} x \\ y \end{pmatrix} = \begin{pmatrix} ax + by \\ cx + dy \end{pmatrix}

Mastering these NumPy operations is the first major step toward implementing AI models from scratch. They provide the fast, efficient numerical foundation that everything else is built upon.