Data visualization with TSNE

Screen

Introduction

  • t-SNE (t-distributed Stochastic Neighbor Embedding) is a machine learning algorithm that visualizes high-dimensional data in two or three dimensions.
  • It was developed by Laurens van der Maaten and Geoffrey Hinton in 2008
  • The algorithm is non-linear. Its goal is to take a set of points in a high-dimensional space and find a faithful representation of those points in a lower-dimensional space in 2D or 3D plane
  • TSNE has tunable parameters “perplexity,” which says (loosely) how to balance attention between local and global aspects the data.
  • “perplexity” can be considered to be number of nearest neighbors (the higher the number, the better fit the data) when considered for each data point when constructing the probability distribution of similarities in high-dimensional space.
  • According to authors, “The performance of SNE is fairly robust to changes in the perplexity, and typical values are between 5 and 50”
  • This blog post discusses some aspect of TSNE and its perplexity with different kind of input data. The code will be given as well.
  • What is good perplexity?

    + typical range 5-50: small data (5-30), large data (30-50)
    + 1-5: disconnected structure and poorly representation
    + > 50: overlapping structure and loss detail
    

scikit-learn toy data

  • The data is taken from scikit learn toy datasets
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.manifold import TSNE
from sklearn import cluster, datasets, mixture

# ============
# Generate datasets. We choose the size big enough to see the scalability
# of the algorithms, but not too big to avoid too long running times
# ============
n_samples = 500
seed = 30

Noisy Circle data

  • Let’s create noisy circle data in 2D plane that have X variables containing the coordinate of the points and y variable which contains the labels of predefinded group:
noisy_circles = datasets.make_circles(
    n_samples=n_samples, factor=0.5, noise=0.05, random_state=seed
)
X,y = noisy_circles
  • Let’s plot the noisy circle raw plot:
dfX = pd.DataFrame(X,columns=["X1","X2"])
dfX["Cluster"]=y

sns.scatterplot(x="X1", y="X2",
    data=dfX, 
    hue='Cluster', # color by cluster
    legend=True).set(title="Noisy Circle Raw plot")

Screen

  • Next, let’s apply TSNE to this dataset and visualize in 2D plane:
  • The perplexity is not specified so it perplexity=30 by default
model_tsne = TSNE(n_components=2)
tsne_X = model_tsne.fit_transform(dfX)
df_tsne = pd.DataFrame(tsne_X,columns=['TSNE1','TSNE2'])
df_tsne['Cluster'] = y

sns.scatterplot(x="TSNE1", y="TSNE2",
    data=df_tsne, 
    hue='Cluster', # color by cluster
    legend=True).set(title="TSNE")

Screen

  • We can visualize the transformation of TSNE with different variable of perplexity
  • Here, we compare the raw data with perplexity range from 1 to 200:

Screen

  • And here is the animation code of TSNE for noisy circle with perplexity range from 1 to 100:
import matplotlib.animation as animation
prange = range(0,100)

def animate(p):
    plt.clf()  # Clear the previous frame
    model_tsne = TSNE(n_components=2,perplexity=p+1)
    tsne_X = model_tsne.fit_transform(dfX)
    df_tsne = pd.DataFrame(tsne_X,columns=['TSNE1','TSNE2'])
    df_tsne['Cluster'] = y
    
    sns.scatterplot(x="TSNE1", y="TSNE2",
        data=df_tsne, 
        hue='Cluster', # color by cluster
        legend=True).set(title="Perplexity = {}".format(p))

ani = animation.FuncAnimation(plt.gcf(), animate, frames=len(prange),interval=10)
ani.save('noisy_moon.gif', writer='pillow') 

Screen

Noisy Moon

  • To create noisy moon, we use the datasets make_moons:
noisy_moons = datasets.make_moons(n_samples=n_samples, noise=0.05, random_state=seed)
X,y = noisy_moons
  • The sensitivity of noisy moon with different perplexity is plotted below:

Screen

  • Similarly, we have the animation of TSNE with 2D for this Noisy moon:

Screen

Blobs data with 3 groups

  • Next, Let’s create data with 3 separate group from make_blobs function in datasets
X, y = datasets.make_blobs(n_samples=n_samples, cluster_std=[1.0, 2.5, 0.5],random_state=123)
  • We can visualize the Raw data with TSNE from different perplexity as below on 2D plane: Screen

  • And the animation of TSNE visualization with perplexity range from 1-100: Screen

MNIST data

  • Not only using the 2D plane model, we can use TSNE to visualize the multi-dimension.
  • One of the example that we are using is the digital number data called MNIST
  • To download the MNIST data, we can use tensorflow keras datasets:
from tensorflow.keras.datasets import mnist
(X_train,y_train),(X_test,y_test) = mnist.load_data()
  • The training data contains 60000 images and the testing contains 10000 images.
  • The image has resolution of 28x28 and is in gray scale with color range from 0-255
  • To quickly visualize the MNIST data:
for i in range(64):
    ax = plt.subplot(8, 8, i+1)
    ax.axis('off')
    plt.imshow(X_train[i], cmap='Greys')

Screen

  • Because of its resolution 28x28 = 784, this dataset has the dimension of 784 and we can utilize TSNE to visualize the 10000 testing images in 2D or 3D scale
  • First, let reshape the data:
X = X_test.reshape(10000,28*28)
y = y_test
  • And apply TSNE with 2D plane and default perplexity
model_tsne = TSNE(n_components=2)
tsne_X = model_tsne.fit_transform(X)
df_tsne = pd.DataFrame(tsne_X,columns=['TSNE1','TSNE2'])
df_tsne['Cluster'] = y
  • We can then visualize the MNIST dataset on 2D plane of TSNE:
tsne = sns.lmplot(x="TSNE1", y="TSNE2",
  data=df_tsne, 
  fit_reg=False, 
  hue='Cluster', # color by cluster
  legend=True,
  scatter_kws={"s": 5}, # specify the point size
  height=8)

Screen

  • Animation for perplexity from 1 to 50:

Screen

  • We can even visualize the 784 dimension data in 3D perspective by fitting TSNE with 3 dimensions:
model_tsne = TSNE(n_components=3)
tsne_X = model_tsne.fit_transform(X)
df_tsne = pd.DataFrame(tsne_X,columns=['TSNE1','TSNE2','TSNE3'])
df_tsne['Cluster'] = y
fig = plt.figure(figsize=(50, 30))
ax = plt.axes(projection ='3d')
# defining all 3 axis
x = df_tsne["TSNE1"]
y = df_tsne["TSNE2"]
z = df_tsne["TSNE3"]

# plotting
ax.scatter(x, y, z, c = df_tsne["Cluster"])
ax.set_title('3D line plot geeks for geeks')
plt.show()

Screen

  • Additionally, we can also plot using raw images from Xtest data:
from PIL import Image

tx, ty = df_tsne['TSNE1'], df_tsne['TSNE2']
tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))

width = 4000
height = 3000
max_dim = 10
full_image = Image.new(mode="RGB", size=(width, height),color = (255, 255, 255))
for idx, x in enumerate(X_test):
    tile = Image.fromarray(np.uint8(x * 255))
    rs = max(1, tile.width / max_dim, tile.height / max_dim)
    full_image.paste(tile, (int((width-max_dim) * tx[idx]),
                            int((height-max_dim) * ty[idx])))
full_image
full_image.save("MNIST_number.jpg")

Screen

FMNIST data

  • Fashion MNIST is similar to MNIST data but using fashion image like shirt, trouser, shoes, etc
  • One of the example that we are using is the digital number data called FMNIST
  • To download the FMNIST data, we can use tensorflow keras datasets and we will visualize using testing data
from tensorflow.keras.datasets import fashion_mnist
(X_train,y_train),(X_test,y_test) = fashion_mnist.load_data()
X = X_test.reshape(10000,28*28)
y = y_test
  • Setup the model:
model_tsne = TSNE(n_components=2,perplexity=30)
tsne_X = model_tsne.fit_transform(X)

Cluster_name = ["T-shirt/top","Trouser","Pullover","Dress","Coat",
                "Sandal","Shirt","Sneaker","Bag","Ankle boot"]

df_tsne = pd.DataFrame(tsne_X,columns=['TSNE1','TSNE2'])
df_tsne['Cluster'] = y
df_tsne['Cluster_name'] = pd.Series(y).apply(lambda x:Cluster_name[x])
  • Visualize with TSNE using default perplexity:

tsne1 = sns.lmplot(x="TSNE1", y="TSNE2",
  data=df_tsne, 
  fit_reg=False, 
  hue='Cluster_name', # color by cluster
  legend=True,
  scatter_kws={"s": 5}, # specify the point size
  height=8)

Screen

  • We can also visulize using images and we can clearly the grouping to images by clicking on the image to zoom in :
tx, ty = df_tsne['TSNE1'], df_tsne['TSNE2']
tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))

from PIL import Image
width = 4000
height = 3000
max_dim = 10
full_image = Image.new(mode="RGB", size=(width, height),color = (255, 255, 255))


for idx, x in enumerate(X_test):
    tile = Image.fromarray(np.uint8(x * 255))
    rs = max(1, tile.width / max_dim, tile.height / max_dim)
    full_image.paste(tile, (int((width-max_dim) * tx[idx]),
                            int((height-max_dim) * ty[idx])))

full_image.save("FMNIST.jpg")

Screen

Dashboard with Python Shiny:

Dashboard