Welcome to the tenth post in our series on Linear Algebra for Machine Learning, continuing Part II: Core Theorems and Algorithms! After exploring rank, nullspace, and the Fundamental Theorem, we now dive into eigenvalues and eigenvectors, powerful concepts that underpin covariance analysis, principal component analysis (PCA), stability analysis, and spectral clustering in machine learning (ML). In this post, we’ll cover the mathematical foundations, their ML applications, and how to implement them in Python using NumPy and PyTorch. We’ll include visualizations to provide geometric intuition and Python exercises to deepen your understanding.
For a square matrix (size ), a scalar is an eigenvalue, and a non-zero vector is an eigenvector if:
This means applying to scales by without changing its direction (or reverses it if ). Eigenvectors are defined up to a scalar multiple, and each eigenvalue may have multiple eigenvectors forming a subspace.
To find eigenvalues, solve the characteristic equation:
where is the identity matrix. The resulting polynomial (degree ) has up to roots (eigenvalues), which may be real or complex and include multiplicities.
For a 2x2 matrix :
Solve this quadratic equation for .
For each eigenvalue , solve the system:
The non-trivial solutions are the eigenvectors corresponding to .
Eigenvectors represent directions that remain invariant (up to scaling) under the transformation . Eigenvalues indicate the scaling factor:
Eigenvalues and eigenvectors are critical in ML:
Understanding these concepts enables you to analyze data structure, reduce dimensionality, and optimize algorithms.
Let’s compute eigenvalues and eigenvectors using NumPy and PyTorch, with visualizations to illustrate their geometric meaning.
Install the required libraries if needed:
pip install numpy torch matplotlib
Let’s compute for a 2x2 matrix:
import numpy as np
import matplotlib.pyplot as plt
# Define a 2x2 matrix
A = np.array([
[2, 1],
[1, 2]
])
# Compute eigenvalues and eigenvectors
eigenvalues, eigenvectors = np.linalg.eig(A)
# Print results
print("Matrix A:\n", A)
print("\nEigenvalues:", eigenvalues)
print("\nEigenvectors (columns):\n", eigenvectors)
Output:
Matrix A:
[[2 1]
[1 2]]
Eigenvalues: [3. 1.]
Eigenvectors (columns):
[[ 0.70710678 -0.70710678]
[ 0.70710678 0.70710678]]
The eigenvalues are , , with eigenvectors approximately and , corresponding to directions along and .
Let’s visualize the transformation:
# Visualize eigenvectors and their transformations
def plot_eigenvectors(A, eigenvalues, eigenvectors):
plt.figure(figsize=(6, 6))
origin = np.zeros(2)
# Plot original and transformed eigenvectors
colors = ['blue', 'red']
for i in range(len(eigenvalues)):
v = eigenvectors[:, i]
Av = A @ v
plt.quiver(*origin, *v, color=colors[i], scale=1, scale_units='xy', angles='xy', alpha=0.5)
plt.quiver(*origin, *Av, color=colors[i], scale=1, scale_units='xy', angles='xy')
plt.text(v[0], v[1], f'v{i+1}', color=colors[i], fontsize=12)
plt.text(Av[0], Av[1], f'Av{i+1}', color=colors[i], fontsize=12)
plt.grid(True)
plt.xlim(-4, 4)
plt.ylim(-4, 4)
plt.axhline(0, color='black', linewidth=0.5)
plt.axvline(0, color='black', linewidth=0.5)
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Eigenvectors and Their Transformations')
plt.show()
plot_eigenvectors(A, eigenvalues, eigenvectors)
This plots the eigenvectors (blue, red) and their transformations . Since and , the transformed vectors are scaled versions along the same directions.
Let’s verify :
# Verify eigenvalue equation
for i in range(len(eigenvalues)):
v = eigenvectors[:, i]
Av = A @ v
lambda_v = eigenvalues[i] * v
print(f"\nEigenvector {i+1}:", v)
print(f"A @ v_{i+1}:", Av)
print(f"λ_{i+1} * v_{i+1}:", lambda_v)
print(f"Match?", np.allclose(Av, lambda_v))
Output:
Eigenvector 1: [0.70710678 0.70710678]
A @ v_1: [2.12132034 2.12132034]
λ_1 * v_1: [2.12132034 2.12132034]
Match? True
Eigenvector 2: [-0.70710678 0.70710678]
A @ v_2: [-0.70710678 0.70710678]
λ_2 * v_2: [-0.70710678 0.70710678]
Match? True
This confirms the eigenvalue equation holds.
Let’s compute eigenvalues in PyTorch:
import torch
# Convert to PyTorch tensor
A_torch = torch.tensor(A, dtype=torch.float32)
# Compute eigenvalues
eigenvalues_torch = torch.linalg.eigvals(A_torch)
# Print results
print("PyTorch eigenvalues:", eigenvalues_torch.numpy())
Output:
PyTorch eigenvalues: [3.+0.j 1.+0.j]
PyTorch returns complex eigenvalues, but here they are real and match NumPy’s.
Try these Python exercises to deepen your understanding. Solutions will be discussed in the next post!
In the next post, we’ll explore Singular Value Decomposition (SVD), a cornerstone for dimensionality reduction and noise filtering. We’ll provide more Python code and exercises to continue building your ML expertise.
Happy learning, and see you in Part 11!