File size: 3,103 Bytes
c729991 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | """t-SNE visualisation of the 256-dimensional embedding space.
Produces both 2D and 3D scatter plots coloured by emotion label.
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn.manifold import TSNE
from audiovec.config import EMOTION_MAPPING
from audiovec.model import extract_embedding_model
def compute_embeddings(
model: "tf.keras.Model",
X: np.ndarray,
) -> np.ndarray:
"""Run the full dataset through the embedding model and return 256-d vectors."""
import tensorflow as tf
embedding_model = extract_embedding_model(model)
return embedding_model.predict(X, verbose=0)
def plot_2d_tsne(
X_embed: np.ndarray,
y_encoded: np.ndarray,
save_path: str | None = "embeddings_2d.png",
) -> None:
"""Reduce embeddings to 2D with t-SNE and scatter-plot them."""
tsne = TSNE(n_components=2, random_state=42)
X_reduced = tsne.fit_transform(X_embed)
plt.figure(figsize=(10, 8))
scatter = plt.scatter(X_reduced[:, 0], X_reduced[:, 1],
c=y_encoded, cmap="viridis")
cbar = plt.colorbar(scatter, ticks=sorted(np.unique(y_encoded)))
cbar.ax.set_yticklabels(
[EMOTION_MAPPING[i + 1] for i in sorted(np.unique(y_encoded))]
)
plt.title("2D t-SNE projection of audio emotion embeddings")
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=150)
print(f"Saved 2D plot → {save_path}")
plt.show()
def plot_3d_tsne(
X_embed: np.ndarray,
y_encoded: np.ndarray,
emotion_labels: list[str],
save_path: str | None = "embeddings_3d.png",
) -> None:
"""Reduce embeddings to 3D with t-SNE and plot with Plotly."""
import plotly.express as px
tsne = TSNE(n_components=3, random_state=42)
X_reduced = tsne.fit_transform(X_embed)
# Map numeric labels to readable names for the colour legend
label_names = [emotion_labels[i] for i in y_encoded]
fig = px.scatter_3d(
x=X_reduced[:, 0],
y=X_reduced[:, 1],
z=X_reduced[:, 2],
color=label_names,
title="3D t-SNE projection of audio emotion embeddings",
)
fig.update_traces(marker=dict(size=5))
fig.write_html("embeddings_3d.html")
print("Saved 3D interactive plot → embeddings_3d.html")
if save_path:
try:
fig.write_image(save_path)
print(f"Saved 3D static plot → {save_path}")
except Exception as e:
print(f"Note: could not save static 3D image ({e}). The interactive HTML is saved.")
fig.show()
def visualize(
model: "tf.keras.Model",
X: np.ndarray,
y_encoded: np.ndarray,
) -> None:
"""Convenience: compute embeddings and show both 2D + 3D plots."""
emotion_labels = [EMOTION_MAPPING[i + 1] for i in sorted(np.unique(y_encoded))]
print("Extracting embeddings…")
X_embed = compute_embeddings(model, X)
print(f"Embedding shape: {X_embed.shape}")
print("Computing 2D t-SNE…")
plot_2d_tsne(X_embed, y_encoded)
print("Computing 3D t-SNE…")
plot_3d_tsne(X_embed, y_encoded, emotion_labels)
|