Skip to main content

t-SNE implementation in PyTorch

t-Distributed Stochastic Neighbor Embedding (t-SNE) is a popular technique for visualizing high-dimensional data by reducing it to 2 or 3 dimensions. In this article, we will demonstrate how to implement t-SNE using PyTorch, a powerful deep learning framework.

1. Introduction

PyTorch is widely used for its flexibility and ease of use in building and training deep learning models. While PyTorch does not include a built-in t-SNE function, we can manually implement the key components of t-SNE to visualize high-dimensional datasets effectively.

2. Importing necessary libraries

We begin by importing PyTorch and other essential libraries.

import torch
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.preprocessing import StandardScaler

Explanation:

  • torch: The main PyTorch library for building and training neural networks.
  • torch.optim: Provides optimization algorithms for training.
  • sklearn.datasets: Used to load the MNIST dataset.
  • matplotlib.pyplot: For visualizing the results.

3. Loading and preprocessing the dataset

We will use the MNIST dataset of handwritten digits for our example.

# Load the digits dataset
digits = load_digits()
X = digits.data
y = digits.target

# Standardize the data
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Convert to PyTorch tensors
X_tensor = torch.tensor(X_scaled, dtype=torch.float32)

Explanation:

  • StandardScaler: Standardizes the data for better performance in the t-SNE algorithm.
  • torch.tensor: Converts the NumPy array into a PyTorch tensor.

4. Implementing t-SNE in PyTorch

Step 1: Compute pairwise distances

We first compute the pairwise distances between all data points.

def pairwise_distances(X):
sum_X = torch.sum(X ** 2, dim=1)
distances = torch.add(torch.add(-2 * torch.mm(X, X.t()), sum_X).t(), sum_X)
return distances

pairwise_dist = pairwise_distances(X_tensor)

Explanation:

  • pairwise_distances: Computes squared Euclidean distances between all points in the dataset.

Step 2: Compute joint probabilities

Next, we convert the pairwise distances into joint probabilities.

def joint_probabilities(distances, sigma=1.0):
P = torch.exp(-distances / (2 * sigma ** 2))
P_sum = torch.sum(P)
return P / P_sum

P = joint_probabilities(pairwise_dist)

Explanation:

  • joint_probabilities: Applies a Gaussian kernel to convert distances into probabilities.

Step 3: Initialize the low-dimensional map

We initialize the low-dimensional map, which will be optimized.

# Initialize low-dimensional map
Y = torch.randn(X_tensor.shape[0], 2, requires_grad=True)

Explanation:

  • torch.randn: Initializes the low-dimensional map with random values.

Step 4: Optimize the t-SNE objective function

Finally, we optimize the t-SNE objective function using gradient descent.

optimizer = optim.Adam([Y], lr=200)

def tsne_step():
optimizer.zero_grad()
low_dim_distances = pairwise_distances(Y)
Q = joint_probabilities(low_dim_distances, sigma=1.0)
kl_divergence = torch.sum(P * torch.log(P / (Q + 1e-10)))
kl_divergence.backward()
optimizer.step()
return kl_divergence

# Perform optimization for a set number of iterations
for i in range(1000):
loss = tsne_step()
if i % 100 == 0:
print(f"Iteration {i}, Loss: {loss.item()}")

Explanation:

  • tsne_step: Computes the Kullback-Leibler divergence and updates the low-dimensional map using gradient descent.

5. Visualizing the results

We can visualize the results after the optimization process.

# Convert the tensor to a NumPy array for plotting
Y_np = Y.detach().numpy()

# Plot the t-SNE result
plt.figure(figsize=(10, 7))
scatter = plt.scatter(Y_np[:, 0], Y_np[:, 1], c=y, cmap='viridis', s=50, alpha=0.7)
plt.colorbar(scatter, label='Digit Label')
plt.title('t-SNE Visualization of MNIST Digits (PyTorch)')
plt.xlabel('t-SNE Dimension 1')
plt.ylabel('t-SNE Dimension 2')
plt.grid(True)
plt.show()

Explanation:

  • detach().numpy(): Converts the PyTorch tensor to a NumPy array for plotting.

6. Conclusion

This article demonstrated how to implement t-SNE using PyTorch and applied it to the MNIST dataset. The flexibility of PyTorch allows you to build custom machine learning models, and understanding the inner workings of t-SNE can help you apply it to various high-dimensional datasets.