Welcome to the eighth post in our series on Linear Algebra for Machine Learning, continuing Part II: Core Theorems and Algorithms! After exploring orthogonality and projections, we now tackle matrix inverses and systems of linear equations, critical tools for solving for model parameters and understanding backpropagation 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 and Python exercises to reinforce your understanding.
A square matrix (size ) is invertible if there exists a matrix such that:
where is the identity matrix (1s on the diagonal, 0s elsewhere). The inverse “undoes” the transformation represented by . A matrix is invertible if and only if:
For a 2x2 matrix , the inverse is:
For larger matrices, computing the inverse involves methods like Gaussian elimination, but we’ll use NumPy for practical computation.
A system of linear equations can be written as:
where is an coefficient matrix, is the vector of unknowns, and is the constant vector. If is invertible, the solution is:
However, solving directly with can be numerically unstable for large
matrices. Instead, methods like LU decomposition (used by np.linalg.solve
) are
more efficient and stable.
For example, consider:
We solve for using the inverse or a solver.
In ML, invertibility ensures unique solutions for parameters (e.g., in linear regression). Non-invertible matrices indicate redundant features or insufficient data, requiring regularization or alternative methods.
Matrix inverses and linear systems are vital in ML:
Understanding these concepts enables you to solve for model parameters efficiently and diagnose issues like multicollinearity.
Let’s compute matrix inverses and solve linear systems using NumPy and PyTorch, with visualizations to illustrate solutions.
Install the required libraries if needed:
pip install numpy torch matplotlib
Let’s compute the inverse of a 2x2 matrix:
import numpy as np
# Define a 2x2 matrix
A = np.array([[2, 1],
[1, 3]])
# Compute inverse
A_inv = np.linalg.inv(A)
# Verify A * A_inv = I
identity = A @ A_inv
# Print results
print("Matrix A:\n", A)
print("\nInverse A^-1:\n", A_inv)
print("\nA @ A^-1 (should be identity):\n", identity)
print("Is identity?", np.allclose(identity, np.eye(2)))
Output:
Matrix A:
[[2 1]
[1 3]]
Inverse A^-1:
[[ 0.6 -0.2]
[-0.2 0.4]]
A @ A^-1 (should be identity):
[[1. 0.]
[0. 1.]]
Is identity? True
This computes and verifies , confirming correctness.
Let’s solve :
# Define b
b = np.array([5, 4])
# Solve using np.linalg.solve
x = np.linalg.solve(A, b)
# Solve using inverse (for comparison)
x_inv = A_inv @ b
# Print results
print("Matrix A:\n", A)
print("Vector b:", b)
print("Solution x (np.linalg.solve):", x)
print("Solution x (A^-1 @ b):", x_inv)
print("Solutions match?", np.allclose(x, x_inv))
Output:
Matrix A:
[[2 1]
[1 3]]
Vector b: [5 4]
Solution x (np.linalg.solve): [2.2 0.6]
Solution x (A^-1 @ b): [2.2 0.6]
Solutions match? True
This solves
,
yielding , using both np.linalg.solve
and the inverse
method.
Let’s visualize the system as intersecting lines:
import matplotlib.pyplot as plt
# Define lines: 2x + y = 5, x + 3y = 4
x_vals = np.linspace(-1, 4, 100)
y1 = (5 - 2 * x_vals) # From 2x + y = 5
y2 = (4 - x_vals) / 3 # From x + 3y = 4
# Plot
plt.figure(figsize=(6, 6))
plt.plot(x_vals, y1, label='2x + y = 5', color='blue')
plt.plot(x_vals, y2, label='x + 3y = 4', color='red')
plt.scatter(x[0], x[1], color='green', s=100, label='Solution')
plt.text(x[0], x[1], f'({x[0]:.1f}, {x[1]:.1f})', color='green', fontsize=12)
plt.grid(True)
plt.xlim(-1, 4)
plt.ylim(-1, 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('Solution to Linear System')
plt.legend()
plt.show()
This plots the two equations as lines, with their intersection at the solution .
Let’s solve the system in PyTorch:
import torch
# Convert to PyTorch tensors
A_torch = torch.tensor([[2.0, 1.0], [1.0, 3.0]])
b_torch = torch.tensor([5.0, 4.0])
# Solve using torch.linalg.solve
x_torch = torch.linalg.solve(A_torch, b_torch)
# Print result
print("PyTorch solution x:", x_torch.numpy())
Output:
PyTorch solution x: [2.2 0.6]
This confirms PyTorch’s solution matches NumPy’s.
Try these Python exercises to deepen your understanding. Solutions will be discussed in the next post!
np.linalg.solve
and the inverse
method, and verify the solutions match.In the next post, we’ll explore rank, nullspace, and the fundamental theorem of linear algebra, key for data compression and understanding system solutions. We’ll provide more Python code and exercises to continue building your ML expertise.
Happy learning, and see you in Part 9!