Welcome back to Artintellica’s RL with PyTorch! After conquering matrix multiplication and transpose, you’re ready for the next foundation: geometry with tensors. Modern machine learning is deeply geometric. Understanding vector length, distance, angle, and projections lets you reason about similarity, optimization, and even why RL agents make the decisions they do.
In this post, you’ll:
Let’s unlock the geometric heart of tensors!
The Euclidean norm (or norm) of vector is its length:
Also written as .
The Euclidean distance between vectors and is:
The cosine similarity between vectors and is:
Cosine similarity is a key measure in ML and RL for comparing states, actions, or gradients.
The projection of onto (think: the shadow of on ), denoted , is:
This decomposes into a component “along” (the projection) and the remainder (orthogonal).
Let’s see each operation in PyTorch and visualize!
import torch
v: torch.Tensor = torch.tensor([3.0, 4.0])
norm_v: torch.Tensor = torch.norm(v, p=2)
print("Vector v:", v)
print("Euclidean norm (||v||):", norm_v.item())
Output:
Should print since .
a: torch.Tensor = torch.tensor([1.0, 2.0, 3.0])
b: torch.Tensor = torch.tensor([4.0, 6.0, 8.0])
dist: torch.Tensor = torch.dist(a, b, p=2)
print("a:", a)
print("b:", b)
print("Euclidean distance between a and b:", dist.item())
# or equivalently...
alt_dist: torch.Tensor = torch.norm(a - b)
print("Alternative distance (torch.norm):", alt_dist.item())
# Cosine similarity with a manual formula
a_norm: torch.Tensor = torch.norm(a)
b_norm: torch.Tensor = torch.norm(b)
cos_sim: torch.Tensor = torch.dot(a, b) / (a_norm * b_norm)
print("Cosine similarity between a and b:", cos_sim.item())
# Built-in version for batches
cos_sim_builtin: torch.Tensor = torch.nn.functional.cosine_similarity(a.unsqueeze(0), b.unsqueeze(0))
print("Cosine similarity (PyTorch builtin, batch):", cos_sim_builtin.item())
import matplotlib.pyplot as plt
# 2D vectors for visualization
a2d: torch.Tensor = torch.tensor([3.0, 1.0])
b2d: torch.Tensor = torch.tensor([2.0, 0.0])
# Compute projection
proj_length: torch.Tensor = torch.dot(a2d, b2d) / torch.dot(b2d, b2d)
proj_vec: torch.Tensor = proj_length * b2d
print("a2d:", a2d)
print("b2d:", b2d)
print("Projection of a2d onto b2d:", proj_vec)
# Plot
plt.figure(figsize=(6, 6))
plt.quiver(0, 0, a2d[0], a2d[1], angles='xy', scale_units='xy', scale=1, color='b', label='a2d')
plt.quiver(0, 0, b2d[0], b2d[1], angles='xy', scale_units='xy', scale=1, color='r', label='b2d')
plt.quiver(0, 0, proj_vec[0], proj_vec[1], angles='xy', scale_units='xy', scale=1, color='g', label='proj_b(a)')
plt.legend()
plt.xlim(-1, 5)
plt.ylim(-1, 3)
plt.grid(True)
plt.axhline(0, color='black', linewidth=0.3)
plt.axvline(0, color='black', linewidth=0.3)
plt.title("Vector Projection in 2D")
plt.xlabel("x")
plt.ylabel("y")
plt.show()
Use these to deepen your understanding and intuition!
v = [6, 8]
(float).a = [1, 7, 2, 5]
and b = [5, 1, 2, -1]
(float).a
and b
as Exercise 2.torch.nn.functional.cosine_similarity
.u = [4, 3]
and v = [5, 0]
.u
onto v
.u
, v
, and the projection vector from the origin.import torch
import matplotlib.pyplot as plt
# EXERCISE 1
v: torch.Tensor = torch.tensor([6.0, 8.0])
norm_v: torch.Tensor = torch.norm(v)
print("Norm of v:", norm_v.item())
# EXERCISE 2
a: torch.Tensor = torch.tensor([1.0, 7.0, 2.0, 5.0])
b: torch.Tensor = torch.tensor([5.0, 1.0, 2.0, -1.0])
dist: torch.Tensor = torch.norm(a - b)
print("Distance between a and b:", dist.item())
# EXERCISE 3
dot: torch.Tensor = torch.dot(a, b)
norm_a: torch.Tensor = torch.norm(a)
norm_b: torch.Tensor = torch.norm(b)
cosine_sim: torch.Tensor = dot / (norm_a * norm_b)
print("Cosine similarity (formula):", cosine_sim.item())
# Using built-in
cosine_sim_builtin: torch.Tensor = torch.nn.functional.cosine_similarity(a.unsqueeze(0), b.unsqueeze(0))
print("Cosine similarity (builtin):", cosine_sim_builtin.item())
# EXERCISE 4
u: torch.Tensor = torch.tensor([4.0, 3.0])
v: torch.Tensor = torch.tensor([5.0, 0.0])
proj_length: torch.Tensor = torch.dot(u, v) / torch.dot(v, v)
proj_vec: torch.Tensor = proj_length * v
print("Projection of u onto v:", proj_vec)
plt.figure(figsize=(6, 6))
plt.quiver(0, 0, u[0], u[1], angles='xy', scale_units='xy', scale=1, color='b', label='u')
plt.quiver(0, 0, v[0], v[1], angles='xy', scale_units='xy', scale=1, color='r', label='v')
plt.quiver(0, 0, proj_vec[0], proj_vec[1], angles='xy', scale_units='xy', scale=1, color='g', label='proj_v(u)')
plt.legend()
plt.xlim(-1, 6)
plt.ylim(-1, 6)
plt.grid(True)
plt.axhline(0, color='black', linewidth=0.3)
plt.axvline(0, color='black', linewidth=0.3)
plt.title("Projection of u onto v")
plt.xlabel("x")
plt.ylabel("y")
plt.show()
Geometry underpins how we measure, compare, and manipulate data in ML and RL. In this post, you’ve:
Next up: We’ll use these geometric tools for linear transformations—rotations, scalings, and the transformations at the heart of data processing and neural networks.
See you in Part 1.8!