A short while ago I published a playlist generator built using Sentence Transformers and Gradio. This post breaks down the project, covering two technical details: how embeddings were generated and how the multi-step Gradio demo was built.
Sentence Transformers (ST) provides tools for generating sentence embeddings, which have a variety of uses. With access to a dataset of song lyrics, I leveraged ST's semantic search to generate playlists from a text prompt. The goal was to create an embedding from the prompt, use it for semantic search across pre-generated lyrics embeddings, and wrap everything in a Gradio app using the new Blocks API, hosted on Hugging Face Spaces.
Sentence Transformers: Embeddings and Semantic Search
Embeddings are key in Sentence Transformers! ST offers a large collection of pre-trained embedding models. For many use cases like semantic search over song lyrics, pre-trained models work excellently out of the box. I chose the sentence-transformers/msmarco-MiniLM-L-6-v3 model, which has a max sequence length of 512 word pieces. Since entire songs often exceed this, I split lyrics into verses, embedded each verse, and found the search works much better.
To generate embeddings, call the .encode() method and pass a list of strings, then save the embeddings (e.g., pickled).
from sentence_transformers import SentenceTransformer
import pickle
embedder = SentenceTransformer('msmarco-MiniLM-L-6-v3')
verses = [...] # Load your strings
corpus_embeddings = embedder.encode(verses, show_progress_bar=True)
with open('verse-embeddings.pkl', "wb") as fOut:
pickle.dump(corpus_embeddings, fOut)
Upload the pickled embeddings to a Hugging Face dataset for sharing. Finally, use the embeddings for semantic search:
from sentence_transformers import util
import pandas as pd
prompt_embedding = embedder.encode(prompt, convert_to_tensor=True)
hits = util.semantic_search(prompt_embedding, corpus_embeddings, top_k=20)
hits = pd.DataFrame(hits[0], columns=['corpus_id', 'score'])
# Use corpus_id to look up the original song
Since semantic search may find multiple verses from the same song, increase top_k to get more distinct songs. With top_k=20, I almost always get at least 9 distinct songs.
Making a Multi-Step Gradio App
For the demo, users enter a text prompt (or choose from examples), and the app conducts a semantic search to find the top 9 most relevant songs. Users can then select a song to view its lyrics. Here's how the app loads data at startup:
from sentence_transformers import SentenceTransformer, util
from huggingface_hub import hf_hub_download
import os, pickle, pandas as pd
corpus_embeddings = pickle.load(open(hf_hub_download("NimaBoscarino/playlist-generator", repo_type="dataset", filename="verse-embeddings.pkl"), "rb"))
songs = pd.read_csv(hf_hub_download("NimaBoscarino/playlist-generator", repo_type="dataset", filename="songs.csv"))
lyrics = pd.read_csv(hf_hub_download("NimaBoscarino/playlist-generator", repo_type="dataset", filename="lyrics.csv"))
model = SentenceTransformer('msmarco-MiniLM-L-6-v3')
The Gradio app uses the Blocks API to create a multi-step interface: first a text input, then a dropdown for song selection, and finally a text box for lyrics. The semantic search function is called when the user submits a prompt, updating the dropdown with results. Selecting a song displays its lyrics.
Some Thoughts
This project demonstrates how Sentence Transformers and Gradio can be combined to build interactive semantic search apps. The key steps are chunking text appropriately and using pre-trained models for embeddings. The multi-step Gradio interface makes it easy for users to explore results.