<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Word2vec | Bits And Music</title>
    <link>https://bitsandmusic.com/tag/word2vec/</link>
      <atom:link href="https://bitsandmusic.com/tag/word2vec/index.xml" rel="self" type="application/rss+xml" />
    <description>Word2vec</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-us</language><lastBuildDate>Mon, 05 Feb 2024 00:00:00 +0000</lastBuildDate>
    <image>
      <url>https://bitsandmusic.com/images/icon_hua5672c1e15dce4d511903ad7fb945fd0_28771_512x512_fill_lanczos_center_2.png</url>
      <title>Word2vec</title>
      <link>https://bitsandmusic.com/tag/word2vec/</link>
    </image>
    
    <item>
      <title>Next Stop, Vector Databases: Building a Music Discovery App - 3</title>
      <link>https://bitsandmusic.com/post/building-music-discovery-app-3/</link>
      <pubDate>Mon, 05 Feb 2024 00:00:00 +0000</pubDate>
      <guid>https://bitsandmusic.com/post/building-music-discovery-app-3/</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclaimer 1:&lt;/strong&gt; &lt;em&gt;This is the third instalment in the &lt;a href=&#34;https://bitsandmusic.com/series/how-not-to-build-a-music-discovery-app/&#34;&gt;How Not to Build a Music Discovery App&lt;/a&gt; series, based on our paper titled &lt;a href=&#34;https://archives.ismir.net/ismir2021/latebreaking/000054.pdf&#34;&gt;Bit of This, Bit Of That: Revisiting Search and Discovery&lt;/a&gt;. In &lt;a href=&#34;https://bitsandmusic.com/post/building-music-discovery-app-1/&#34;&gt;Part 1&lt;/a&gt;, we present the initial monolithic version, and in &lt;a href=&#34;https://bitsandmusic.com/post/building-music-discovery-app-2/&#34;&gt;Part 2&lt;/a&gt;, we discuss the transition to a microservice-based architecture.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer 2:&lt;/strong&gt; &lt;em&gt;The design choices mentioned in this article are made with a low-cost setup in mind. As a result, some of the design choices may not be the most straightforward.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer 3&lt;/strong&gt; &lt;em&gt;You can explore our music discovery platform, This &amp;amp; That Music, here: &lt;a href=&#34;https://discover.thisandthatmusic.com/&#34;&gt;https://discover.thisandthatmusic.com/&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&#34;brief-summary&#34;&gt;Brief Summary&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Genre-fluid music&lt;/strong&gt; is any musical item (song or a playlist) which contains more than a single genre. Think &lt;a href=&#34;https://www.youtube.com/watch?v=pXRviuL6vMY&#34;&gt;Stressed Out&lt;/a&gt; by Twenty-One Pilots. Or &lt;a href=&#34;https://www.youtube.com/watch?v=FoYdeEDdtK4&#34;&gt;Peaches En Regalia&lt;/a&gt; by Frank Zappa. Genre-fluid music has been &lt;a href=&#34;https://www.waterandmusic.com/tracking-genre-diversity-and-fluidity-in-the-billboard-charts&#34;&gt;gaining popularity over the last few decades&lt;/a&gt;. However, the search interfaces in music apps like Spotify and Apple Music are still designed for single-genre searches. Our paper proposes a platform to discover gene-fluid music through a combination of expressive search and user experience created around the core idea of genre-fluid search.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://bitsandmusic.com/post/building-music-discovery-app-1/&#34;&gt;Part 1&lt;/a&gt; of this series outlines the initial monolithic architecture used to build this platform. In &lt;a href=&#34;https://bitsandmusic.com/post/building-music-discovery-app-2/&#34;&gt;Part 2&lt;/a&gt;, we break the monolith into three components: web server, discovery engine, and vector search server, using PostgreSQL for keyword search, Spotify ANNOY library for sparse genre-vector search, Gensim Word2vec for similarity-based search, and Redis as the cache storage.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&#34;https://bitsandmusic.com/assets/images/discovery-app/version2.drawio.svg&#34;  /&gt;
  &lt;figcaption&gt;
      &lt;p&gt;&lt;b&gt;Previously designed microservice-based architecture (version2).&lt;/b&gt;&lt;/p&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt; 

&lt;h2 id=&#34;limitations&#34;&gt;Limitations&lt;/h2&gt;

&lt;p&gt;In the design version &lt;code&gt;version2&lt;/code&gt;, we broke the monolithic architecture into smaller components to simplify horizontal scaling. However, there were some accompanying design issues as well. These are as follows:&lt;/p&gt;

&lt;h3 id=&#34;too-many-search--lookup-sources&#34;&gt;Too Many Search &amp;amp; Lookup Sources&lt;/h3&gt;

&lt;p&gt;Our search/lookup source schema looks like the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.postgresql.org/&#34;&gt;PostgreSQL&lt;/a&gt; for full-text search&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://redis.io/&#34;&gt;Redis&lt;/a&gt; for storing cached and app data&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/spotify/annoy&#34;&gt;Spotify ANNOY&lt;/a&gt; data structures for sparse vector search&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://radimrehurek.com/gensim/models/word2vec.html&#34;&gt;Gensim library&lt;/a&gt; for dense vector search.&lt;/li&gt;
&lt;li&gt;In-memory sparse genre-vectors needed by the scoring module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That seems a bit much. Having these many search/lookup sources adds to the complexity of managing all these, such as scaling complexity and performance tuning.&lt;/p&gt;

&lt;h3 id=&#34;suboptimal-vector-database-implementation&#34;&gt;Sub-Optimal Vector Database Implementation&lt;/h3&gt;

&lt;p&gt;Our vector search component, which uses the Spotify ANNOY library and the Gensim library for vector search, packaged as a &lt;a href=&#34;https://fastapi.tiangolo.com/&#34;&gt;Fastapi&lt;/a&gt; service, is not the most optimized vector-search component.&lt;/p&gt;

&lt;p&gt;Spotify ANNOY was already a &lt;a href=&#34;https://ann-benchmarks.com/glove-100-angular_10_angular.html&#34;&gt;mid-tier vector search library&lt;/a&gt; in terms of speed in 2023, and we &lt;a href=&#34;https://bitsandmusic.com/post/building-music-discovery-app-2/#reducing-memory-consumption&#34;&gt;used the mmap mode&lt;/a&gt; on top of that to increase the search time further.&lt;/p&gt;

&lt;p&gt;In addition, we also have the same problem of using two different libraries for vector search where using one &lt;a href=&#34;https://www.pinecone.io/learn/vector-database/&#34;&gt;vector database&lt;/a&gt; would have sufficed.&lt;/p&gt;

&lt;h3 id=&#34;redis-overhead&#34;&gt;Redis Overhead&lt;/h3&gt;

&lt;p&gt;Redis is a very convenient caching solution, but as the application data schema becomes more complex, we start to see code snippets like this everywhere.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;all_integer_items = [int(item.decode(&#39;utf-8&#39;)) for item in all_cached_items]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We must cast the byte response to our required data type as &lt;a href=&#34;https://redis.io/docs/data-types/&#34;&gt;Redis stores the data as string data type&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, Redis works best with flat storage (list, set, or dictionary) and is not designed to store nested data. This makes Redis a less-than-ideal data storage (or caching) candidate as the application data becomes more complicated.&lt;/p&gt;

&lt;p&gt;Finally, since we know that making multiple calls vs making a single bulk call can make all the difference from a performance perspective, Redis also provides limited support in that context by only supporting &lt;a href=&#34;https://redis.io/commands/mget/&#34;&gt;bulk GET queries for dictionaries&lt;/a&gt; and not lists.&lt;/p&gt;

&lt;h2 id=&#34;design-changes&#34;&gt;Design Changes&lt;/h2&gt;

&lt;p&gt;Based on the abovementioned limitations, we can make the following design changes:&lt;/p&gt;

&lt;h3 id=&#34;replacing-redis-with-mongodb&#34;&gt;Replacing Redis With MongoDB&lt;/h3&gt;

&lt;p&gt;In the design versions 1 and 2, we used Redis for caching and storing the application data. This worked fine until we ran into the problems of having to write additional serialization/deserialization code for storing and using the retrieved data from Redis, having no direct support for storing nested data, and limited bulk query capabilities.&lt;/p&gt;

&lt;p&gt;We can solve all those problems by moving to a &lt;a href=&#34;https://aws.amazon.com/nosql/&#34;&gt;NoSQL&lt;/a&gt; solution, such as &lt;a href=&#34;https://www.mongodb.com/&#34;&gt;MongoDB&lt;/a&gt;*. By doing so, we get two advantages straight off the bat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No more serialization deserialization overhead&lt;/li&gt;
&lt;li&gt;No more data storage format restrictions. We can store our data as JSONs with support for nesting as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also get one more benefit by migrating to MongoDB: With its new storage engine, &lt;a href=&#34;https://www.mongodb.com/docs/manual/core/wiredtiger/&#34;&gt;WiredTiger&lt;/a&gt;, we can choose the amount of memory to be allocated to it, meaning we can control the performance-memory tradeoff by controlling the amount of memory allocated to MongoDB.&lt;/p&gt;

&lt;h3 id=&#34;using-elasticsearch-for-allthingssearch&#34;&gt;Using Elasticsearch For All-Things-Search&lt;/h3&gt;

&lt;p&gt;Instead of using two separate solutions (PostgreSQL and SpotifyANNOY/Gensim) for full-text search and vector search, we can use a service which supports both, such as &lt;a href=&#34;https://www.elastic.co/elasticsearch&#34;&gt;Elasticsearch&lt;/a&gt;. Elasticsearch has existed for a long time as a distributed full-text search engine. However, it has also added the &lt;a href=&#34;https://elastic.co/elasticsearch/vector-database&#34;&gt;vector search functionality&lt;/a&gt; over the past few years. Using this as our out-of-box vector database gives us the benefit of having a well-established, stable, and highly optimized service for our search use cases, making our application design much cleaner and more optimized.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&#34;https://bitsandmusic.com/assets/images/discovery-app/version3.drawio.svg&#34;  /&gt;
  &lt;figcaption&gt;
      &lt;p&gt;&lt;b&gt;Version3 design architecture. Search source aggregation achieved by using Elasticsearch for full-text and vector search. MongoDB replaces Redis for caching and application data storage.&lt;/b&gt;&lt;/p&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&#34;search-workflow&#34;&gt;Search Workflow&lt;/h2&gt;

&lt;p&gt;The search workflow in &lt;code&gt;version3&lt;/code&gt; remains similar to that discussed in &lt;code&gt;version2&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The web server forwards the user query to the core discovery service.&lt;/li&gt;
&lt;li&gt;The query parser module parses the query, builds a query payload and forwards it to the caching module.&lt;/li&gt;
&lt;li&gt;The caching module checks whether the query results have already been stored in MongoDB. In case of a cache hit, steps 4–6 are skipped, and the result set is returned to the browser. In case of a cache miss, the query payload is forwarded to the Candidate Aggregation module.&lt;/li&gt;
&lt;li&gt;The candidate aggregation module sends the query to Elasticsearch for vector search, which returns 10k candidates.&lt;/li&gt;
&lt;li&gt;The scoring module scores the candidates with respect to the query.&lt;/li&gt;
&lt;li&gt;The filtering module removes duplicate candidates with regard to the item name and primary artist composition.&lt;/li&gt;
&lt;li&gt;The detail population module finally populates the result set candidates.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;thoughts&#34;&gt;Thoughts&lt;/h2&gt;

&lt;h3 id=&#34;the-good&#34;&gt;The Good&lt;/h3&gt;

&lt;h4 id=&#34;search-source-aggregation&#34;&gt;Search Source Aggregation&lt;/h4&gt;

&lt;p&gt;What this setup succeeds in achieving is search source aggregation. We replace PostgreSQL, Spotify ANNOY, and Gensim entirely with Elasticsearch, making our design much cleaner and easier to manage in terms of infrastructure management, scaling, and performance tuning.&lt;/p&gt;

&lt;h4 id=&#34;mongodb-convenience&#34;&gt;MongoDB Convenience&lt;/h4&gt;

&lt;p&gt;By using MongoDB in place of Redis for caching purposes and application data storage, we now have data stored in a JSON format that closely resembles how we use the data in our application. And we no longer need to cast the data back to their intended data types, resulting in a cleaner code. All of this comes with an option to specify the memory allocated to MongoDB, thus making this setup suitable for the hardware resources available.&lt;/p&gt;

&lt;h3 id=&#34;the-bad&#34;&gt;The Bad&lt;/h3&gt;

&lt;h4 id=&#34;incomplete-inmemory-cleanup&#34;&gt;In-Complete In-Memory Cleanup&lt;/h4&gt;

&lt;p&gt;The in-memory cleanup remains incomplete, with the sparse genre vectors remaining in the memory. We can store those in MongoDB, but fetching genre vectors from MongoDB despite sufficient memory allocation would still result in an increased fetch time compared to in-memory.&lt;/p&gt;

&lt;h4 id=&#34;10k-scoring-time&#34;&gt;10k Scoring Time&lt;/h4&gt;

&lt;p&gt;Since the beginning, one persistent issue with the scoring module has been that it takes longer than expected to calculate scores for 10,000 candidates. This results in an overall increase in search response time.&lt;/p&gt;

&lt;h3 id=&#34;the-ugly&#34;&gt;The Ugly&lt;/h3&gt;

&lt;p&gt;While Elasticsearch is better than Spotify ANNOY (mmap mode) in terms of search performance and convenience it provides by handling both vector search and full-text search, the memory consumption in this setup went past all our self-imposed restrictions. With a total index size of around 25GB, we must allocate a whole new 64GB server for search, which amounts to roughly &lt;a href=&#34;https://instances.vantage.sh/aws/ec2/m4.4xlarge&#34;&gt;$576 monthly for running a self-hosted single instance&lt;/a&gt; of Elasticsearch. Ouch!&lt;/p&gt;

&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;We cleaned up the application design by using Elasticsearch for both full-text and vector search queries. And it came at the expense of memory consumption. We also replaced Redis with MongoDB for caching and data storage purposes, thus aligning the data storage format (JSON) with the usage format. We must reduce costs significantly as we advance while keeping the design cleaner. Another problem is the 10k scoring time problem, which needs to be substantially reduced. And lastly, the memory cleanup remains to be completed.&lt;/p&gt;

&lt;p&gt;Until the next iteration.&lt;/p&gt;

&lt;p&gt;&lt;sub&gt;* In place of MongoDB, we can also use &lt;a href=&#34;https://redis.com/modules/redis-json/&#34;&gt;RedisJSON&lt;/a&gt;, a document-based database similar to MongoDB that supports data storage and retrieval as JSON.&lt;/sub&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;&lt;em&gt;Ready to explore genre-fluid music? Visit our music discovery platform, This &amp;amp; Thats Music, here: &lt;a href=&#34;https://discover.thisandthatmusic.com/&#34;&gt;https://discover.thisandthatmusic.com/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Of Monoliths &amp; In-Memory Litter: Building A Music Discovery App - 1</title>
      <link>https://bitsandmusic.com/post/building-music-discovery-app-1/</link>
      <pubDate>Wed, 10 Jan 2024 00:00:00 +0000</pubDate>
      <guid>https://bitsandmusic.com/post/building-music-discovery-app-1/</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclaimer 1:&lt;/strong&gt; &lt;em&gt;This post is the first post in the series about building a music discovery platform based on our paper: Bit Of This, Bit Of That: Revisiting Search and Discovery&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer 2:&lt;/strong&gt; &lt;em&gt;The design choices mentioned in this article are made keeping in mind a low-cost setup. As a result, some of the design choices may not be the most straightforward ones.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer 3&lt;/strong&gt; &lt;em&gt;You can explore our music discovery platform, This &amp;amp; That Music, here: &lt;a href=&#34;https://discover.thisandthatmusic.com/&#34;&gt;https://discover.thisandthatmusic.com/&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The term &lt;strong&gt;genre-fluid&lt;/strong&gt; music sounds so fancy and sophisticated and something you aren&#39;t supposed to know, yet you do. Even if you didn&#39;t realize you did, you do. After all, it&#39;s all around us. Think &lt;a href=&#34;https://www.youtube.com/watch?v=r7qovpFAGrQ&#34;&gt;Old Town Road&lt;/a&gt; by Lil Nas X. How would you describe its genre? Is it country, or is it hip-hop? Or is it both? And that&#39;s what genre fluidity is: any musical item consisting of more than a single genre.&lt;/p&gt;

&lt;p&gt;Over the past few decades, &lt;a href=&#34;https://www.waterandmusic.com/tracking-genre-diversity-and-fluidity-in-the-billboard-charts/&#34;&gt;genre-fluidity has been gaining prominence in the billboard charts&lt;/a&gt;. However, search interfaces have yet to keep up with this trend and continue to be designed for single-genre searches, while genre fluidity is served mostly via recommendations and curated playlists. This may soon change as recent advancements in AI, specifically &lt;a href=&#34;https://www.computerworld.com/article/3697649/what-are-large-language-models-and-how-are-they-used-in-generative-ai.html&#34;&gt;Large Language Models (LLMs)&lt;/a&gt;, offer more expansive and expressive access to content.&lt;/p&gt;

&lt;p&gt;So why are we talking about genre fluidity and old-town roads? A few years ago, we published a &lt;a href=&#34;https://archives.ismir.net/ismir2021/latebreaking/000054.pdf&#34;&gt;paper&lt;/a&gt; proposing a platform to discover genre-fluid music through a combination of expressive search and a user experience created around the core idea of genre-fluid search. This post explains the initial architecture we used to build that discovery system and the lessons we learned.&lt;/p&gt;

&lt;h2 id=&#34;search&#34;&gt;Search&lt;/h2&gt;

&lt;p&gt;Our proposed system enables users to search for music by combining different genres and specifying the proportion of each genre they want in the results. We call it &lt;strong&gt;genre-fluid search&lt;/strong&gt;. Adopting the design principles of existing apps, we keep the existing &lt;strong&gt;keyword search&lt;/strong&gt; (&amp;quot;Adele playlists&amp;quot;) along with our proposed search to keep the cognitive load as low as possible.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&#34;https://bitsandmusic.com/assets/images/discovery-app/design.png&#34;  /&gt;
  &lt;figcaption&gt;
      &lt;p&gt;System search modes: Keyword search mode is the conventional search mode, while genre search mode is the proposed one.&lt;/p&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&#34;data-summary&#34;&gt;Data Summary&lt;/h2&gt;

&lt;p&gt;Before going into the depth of the architecture, here is a summary of the data used for creating the system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are a total of 88 genres supported by the system.&lt;/li&gt;
&lt;li&gt;There are three entity types: artists, playlists, and tracks.&lt;/li&gt;
&lt;li&gt;The system has around one million playlists, seven million tracks, and a million artists.&lt;/li&gt;
&lt;li&gt;Each entity has demographic data, such as name, popularity, preview audio link, and cover image.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;embeddings&#34;&gt;Embeddings&lt;/h2&gt;

&lt;p&gt;We use neural embeddings for our search and similarity-based recommendation purposes. There are two types of embeddings in our system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Genre-based embeddings&lt;/li&gt;
&lt;li&gt;Similarity-based embeddings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get the &lt;strong&gt;genre-based embedding&lt;/strong&gt; for each entity, we annotate entities with their associated genres so each entity can be represented as an 88-length sparse vector. This vector consists of real values, meaning the nonzero values range between 0 and 1. In addition to entity-genre annotation, we make use of genre relationships and have those relationships reflected in the entity genre annotation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Similarity-based embeddings&lt;/strong&gt; are obtained by training &lt;a href=&#34;https://en.wikipedia.org/wiki/Word2vec&#34;&gt;word2vec models&lt;/a&gt; on our corpus, treating playlists as sentences and tracks (and artists) as words. These embeddings are 300-length vectors, with values ranging between 0 and 1. Finally, to perform Approximate Nearest Neighbor search on our embeddings, we use the &lt;a href=&#34;https://github.com/spotify/annoy&#34;&gt;Spotify ANNOY library&lt;/a&gt; and store the indexes for each entity on disk.&lt;/p&gt;

&lt;h2 id=&#34;data-structures-and-storage&#34;&gt;Data Structures And Storage&lt;/h2&gt;

&lt;p&gt;The main consideration for our search workflow is retrieval as fast as possible, which means it should be memory-based instead of disk or &lt;a href=&#34;https://en.wikipedia.org/wiki/Mmap&#34;&gt;mmap-based&lt;/a&gt;. We use the Python &lt;a href=&#34;https://docs.python.org/3/library/pickle.html&#34;&gt;pickle module&lt;/a&gt; to store our entity details objects as a dictionary, with entity-id being our key and entity details stored in an array ([name, preview audio link, cover image link]). Popularity lookups are stored in the same format as well. We store the sparse genre embeddings as &lt;a href=&#34;https://docs.fileformat.com/misc/h5/&#34;&gt;H5 files&lt;/a&gt;. Word2vec and Spotify ANNOY data structures are saved as their corresponding supported files.&lt;/p&gt;

&lt;h2 id=&#34;system-modules&#34;&gt;System Modules&lt;/h2&gt;

&lt;p&gt;The main components of this system, packaged as a Python Flask application, are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query Parser Module&lt;/li&gt;
&lt;li&gt;Candidate Aggregation Module&lt;/li&gt;
&lt;li&gt;Scoring Module&lt;/li&gt;
&lt;li&gt;Filtering Module&lt;/li&gt;
&lt;li&gt;Detail Population Module&lt;/li&gt;
&lt;li&gt;Caching Module&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;query-parser-module&#34;&gt;Query Parser Module&lt;/h3&gt;

&lt;p&gt;This module converts the search query typed in natural language (&lt;em&gt;&amp;quot;rock with playlists some country&amp;quot;&lt;/em&gt;) into an 88-genre vector. We apply the genre-relationship rules mentioned in the embeddings section to the query vector.&lt;/p&gt;

&lt;h3 id=&#34;candidate-aggregation-module&#34;&gt;Candidate Aggregation Module&lt;/h3&gt;

&lt;p&gt;Even though we create a tree data structure built with &lt;a href=&#34;https://en.wikipedia.org/wiki/Binary_space_partitioning&#34;&gt;binary space partitioning&lt;/a&gt; using the Spotify ANNOY library from our genre embeddings, we use our scoring module instead of the conventional query-to-candidate distance as the final scoring metric. We create two indexes per entity type for genre embeddings to get the maximum possible candidates for a given query: one using the &lt;a href=&#34;https://en.wikipedia.org/wiki/Angular_distance&#34;&gt;Angular distance&lt;/a&gt; and another using the &lt;a href=&#34;https://en.wikipedia.org/wiki/Dot_product&#34;&gt;Dot Product distance&lt;/a&gt;. We sequentially query the indexes and retrieve &lt;code&gt;N=10,000&lt;/code&gt; candidates from the indexes using the candidate aggregator module.&lt;/p&gt;

&lt;h3 id=&#34;scoring-module&#34;&gt;Scoring Module&lt;/h3&gt;

&lt;p&gt;We use our custom scoring function to calculate a score for each of those 10,000 candidates, where we consider things such as the candidate values corresponding to each query genre, missing genres in a candidate, additional candidate genres not specified in the query, and candidate popularity.&lt;/p&gt;

&lt;h3 id=&#34;filtering-module&#34;&gt;Filtering Module&lt;/h3&gt;

&lt;p&gt;Since our corpus contains user-created playlists, it is possible to have multiple playlists with the same name. Also, many playlists for a given search query can mainly comprise the same artist. To avoid returning duplicate results in their name or primary artist composition, we have a separate module to filter out such items and finally return &lt;code&gt;K=300&lt;/code&gt; result items sorted by their score.&lt;/p&gt;

&lt;h3 id=&#34;detail-population-module&#34;&gt;Detail Population Module&lt;/h3&gt;

&lt;p&gt;This is where we get details for each result item and prepare the final paginated result set to be sent back to the client.&lt;/p&gt;

&lt;h3 id=&#34;caching-module&#34;&gt;Caching Module&lt;/h3&gt;

&lt;p&gt;Since our system serves results without considering user personalization, each query should get the same response. With this in mind, we can cache our query results using &lt;a href=&#34;https://redis.io/&#34;&gt;Redis,&lt;/a&gt; as it is the simplest and the most common solution. We serialize each query as a string and store the indexes returned by the filtering module as the value for each query. Only the query and detail population modules get called for repeated searches, bypassing the rest.&lt;/p&gt;

&lt;h2 id=&#34;overall-system-design&#34;&gt;Overall System Design&lt;/h2&gt;

&lt;figure&gt;
  &lt;img src=&#34;https://bitsandmusic.com/assets/images/discovery-app/version1.drawio.svg&#34;  /&gt;
  &lt;figcaption&gt;
      &lt;p&gt;System Design Architecture `version1`. Monolithic. Simple.&lt;/p&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Here&#39;s how the application design looks, zoomed out.
The query &lt;em&gt;&amp;quot;Rock playlists with blues&amp;quot;&lt;/em&gt; is first entered in a search box in a web application.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The query parser module parses this text and converts it into an 88-length vector.&lt;/li&gt;
&lt;li&gt;The query vector is sent to the server, where the caching module checks whether the query results have already been stored in Redis. In case of a cache hit, steps 3-5 are skipped, and the result set is returned to the browser. In case of a cache miss, the query vector is forwarded to the Candidate Aggregator module.&lt;/li&gt;
&lt;li&gt;The candidate aggregator module uses this query vector to query the Spotify ANNOY indices, retrieves 10000 candidates, and passes it onto the scorer module.&lt;/li&gt;
&lt;li&gt;The scorer module runs our custom scoring function to calculate scores for each of those 10000 candidates.&lt;/li&gt;
&lt;li&gt;The filtering module removes duplicate candidates by name and primary artist. If the resulting set has items under a certain threshold &lt;code&gt;K&lt;/code&gt;, it adds the items removed in the filtering stage.&lt;/li&gt;
&lt;li&gt;Finally, the demographic data is retrieved for the first &lt;code&gt;k=30&lt;/code&gt; candidates to be returned to the browser.&lt;/li&gt;
&lt;li&gt;Candidates calculated are stored in Redis corresponding to the search query.&lt;/li&gt;
&lt;li&gt;In order to support existing keyword-based searches, we use the &lt;a href=&#34;https://www.postgresql.org/&#34;&gt;PostgreSQL database&lt;/a&gt; as it supports full-text search from disk with a decent performance.&lt;br&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;thoughts&#34;&gt;Thoughts&lt;/h2&gt;

&lt;h3 id=&#34;the-good&#34;&gt;The Good&lt;/h3&gt;

&lt;p&gt;The best thing about this setup is its simplicity. Its monolithic design is as simple as possible, adding little coding complexity overhead. The straightforward nature of this design makes it an ideal choice for prototyping. The design is also modular, making it easier to move away from monolithic architecture in the future. Lastly, using PostgreSQL over other full-text solutions, such as Elasticsearch, works fine without needing the memory most memory-based full-text search solutions need.&lt;/p&gt;

&lt;h3 id=&#34;the-bad&#34;&gt;The Bad&lt;/h3&gt;

&lt;h4 id=&#34;inmemory-litter&#34;&gt;In-Memory Litter&lt;/h4&gt;

&lt;p&gt;Using pickle module for serializing data to disk is excellent for entry-level applications and prototypes. They are even more efficient in data storage than other candidates, such as JSON. However, there are better options than pickle when it comes to interoperability, with JSON being one of them. Additionally, moving across different Python versions can also lead to problems.&lt;/p&gt;

&lt;p&gt;Another problem that having in-memory objects creates is high memory consumption. While data access is high-speed and straightforward, it also makes deploying the application more complex, as the memory needed for a single instance of the application equals the size of these in-memory objects. This data tied to an instance&#39;s memory cannot be shared with other application instances.&lt;/p&gt;

&lt;h4 id=&#34;search-time&#34;&gt;Search Time&lt;/h4&gt;

&lt;p&gt;Owing to our sequential search for Spotify ANNOY indices and the scoring function being run over 10k candidates, the search time is well over 5 seconds for a new query, which is far over the ideal response time.&lt;/p&gt;

&lt;h4 id=&#34;caching-at-a-cost&#34;&gt;Caching At A Cost&lt;/h4&gt;

&lt;p&gt;Caching using Redis is great until it is not. It usually starts with simpler string serialized keys and values, which later becomes a whole thing of writing logic to serialize and deserialize data.&lt;/p&gt;

&lt;h4 id=&#34;monolith&#34;&gt;Monolith&lt;/h4&gt;

&lt;p&gt;The monolithic architecture has notable drawbacks, such as the need to manage, test, and deploy all components as one unit. Additionally, the entire system is bound to a single programming language, which may become less than ideal as the application expands and becomes more complex.&lt;/p&gt;

&lt;h3 id=&#34;the-ugly&#34;&gt;The Ugly&lt;/h3&gt;

&lt;p&gt;Deploying an application that requires 32GB of memory presents a challenge due to the complexity and high cost of horizontal scaling. For instance, the monthly cost of an &lt;a href=&#34;https://instances.vantage.sh/aws/ec2/t4g.2xlarge&#34;&gt;AWS t4g.2xlarge instance&lt;/a&gt;, which could support such an application, is approximately $180.&lt;/p&gt;

&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;The design mentioned above is a decent place to start as it lets the main application idea take focus, owing to its simplicity. Its limiting factors, however, are quite a few and critical as well. Monolith is all well and good up to a certain point, beyond which it stops making sense. In-memory objects lead to substantial memory consumption, complicating the scaling and deployment processes. As we aim to improve this design, our approach would involve reducing memory usage and transitioning from a monolithic structure to a more modular, component-based design.&lt;/p&gt;

&lt;p&gt;Until the next design.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;&lt;em&gt;Ready to explore genre-fluid music? Visit our music discovery platform, This &amp;amp; Thats Music, here: &lt;a href=&#34;https://discover.thisandthatmusic.com/&#34;&gt;https://discover.thisandthatmusic.com/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Building Music Playlists Recommendation System</title>
      <link>https://bitsandmusic.com/post/playlist-recommender-system/</link>
      <pubDate>Mon, 09 Sep 2019 00:00:00 +0000</pubDate>
      <guid>https://bitsandmusic.com/post/playlist-recommender-system/</guid>
      <description>&lt;h2 id=&#34;quick-summary&#34;&gt;Quick Summary&lt;/h2&gt;

&lt;h3 id=&#34;goal&#34;&gt;Goal&lt;/h3&gt;

&lt;p&gt;The goal of this work is to represent playlists in a way which captures the true essence of the playlist, i.e. information such as type, genre, variety, order, and the number of songs in the playlist, and which can be used for tasks such as playlist discovery and recommendation.&lt;/p&gt;

&lt;h3 id=&#34;contribution&#34;&gt;Contribution&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Built a recommendation engine for playlists using sequence-2-sequence learning.&lt;/li&gt;
&lt;li&gt;Evaluated the work using a recommendation-based evaluation task.&lt;/li&gt;
&lt;li&gt;Assembled a dataset of 1 million Spotify playlists and 13 million tracks for this work.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&#34;applications&#34;&gt;Applications&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Playlist Discovery/Recommendation Engine&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our system can be used for playlist discovery and recommendation. Given a query playlist, the system returns the playlists from the database which are most similar to the input playlist.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&#34;https://bitsandmusic.com/assets/images/playlists/recommend.png&#34;  /&gt;
  &lt;figcaption&gt;
      &lt;p&gt;Image displaying the usage for our playlist recommendation engine. The system returns playlists most similar to the query playlist&lt;/p&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&#34;developer-friendly-outline&#34;&gt;Developer Friendly Outline&lt;/h2&gt;

&lt;p&gt;Here’s a quick outline of our proposed approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download playlists data using Spotify developer API and everynoise.com.&lt;/li&gt;
&lt;li&gt;Filter the data by removing noise (rare songs, duplicate songs, outlier sized playlists, etc.)&lt;/li&gt;
&lt;li&gt;Train a sequence-2-sequence⁹ model over the data to learn playlist embeddings.&lt;/li&gt;
&lt;li&gt;Annotate the data (songs and playlists) for genre information.&lt;/li&gt;
&lt;li&gt;Evaluate the embeddings using our proposed evaluation tasks.&lt;/li&gt;
&lt;li&gt;Build a recommendation engine by populating a KD-tree with the learned playlist embeddings, and retrieving search results by utilizing the nearest neighbor approach.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Playlists have become a significant part of our music listening experience today. There are over three billion of these on Spotify alone¹. There are playlists for every moment, every mood, every season, and so on. With millions of songs at their fingertips, users today have grown accustomed² to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Immediate attainment of their music demands.&lt;/li&gt;
&lt;li&gt;An extended experience.
While recommendation engines service the first aspect, playlists handle the second aspect of this changing behavior, making playlist recommendation extremely important, both for the users and music companies.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;what-is-a-playlist-and-why-should-i-care&#34;&gt;What is a Playlist, and Why Should I Care?&lt;/h2&gt;

&lt;blockquote&gt;
&lt;h3 id=&#34;a-playlist-is-a-set-of-songs-supposed-to-be-listened-together-usually-in-an-explicit-order-³&#34;&gt;“A playlist is a set of songs supposed to be listened together, usually in an explicit order.” ³&lt;/h3&gt;
&lt;/blockquote&gt;

&lt;p&gt;Playlists are extremely important today from the perspective of both users and music researchers. From the user perspective, playlists are an effective way to discover new music and artists. From the researcher perspective, it is important to understand that music is consumed through listening and playlists formalize that listening experience³. Playlists are a unit component which can be discovered and recommended, just like artists, songs and albums.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3 id=&#34;from-the-researcher-perspective-it-is-important-to-understand-that-music-is-consumed-through-listening-and-playlists-formalize-that-listening-experience-³&#34;&gt;”From the researcher perspective, it is important to understand that music is consumed through listening and playlists formalize that listening experience” ³&lt;/h3&gt;
&lt;/blockquote&gt;

&lt;h2 id=&#34;so-whats-the-problem&#34;&gt;So What’s the problem?&lt;/h2&gt;

&lt;p&gt;As mentioned before, owing to the meteoric rise in the usage of playlists, playlist recommendation is crucial to music services today. However, over the past couple of years, from a research perspective, playlist recommendation has become analogous to playlist prediction/creation⁷ ⁸ and continuation⁵ ⁶ rather than playlist discovery. However, playlist discovery forms a significant part of the overall playlist recommendation pipeline, as it is an effective way to help users discover existing playlists on the platform.&lt;/p&gt;

&lt;h2 id=&#34;discovery-is-the-name-of-the-game&#34;&gt;Discovery is the name of the Game&lt;/h2&gt;

&lt;p&gt;Our work aims to represent playlists in a way which can be used to discover and recommend existing playlists. We use sequence-to-sequence learning⁹ to learn embeddings for playlists that capture their semantic meaning without any supervision. These fixed-length embeddings can then be used for recommendation purposes.&lt;/p&gt;

&lt;h2 id=&#34;intuition-behind-the-approach&#34;&gt;Intuition Behind the Approach&lt;/h2&gt;

&lt;h3 id=&#34;why-sequencetosequence-learning&#34;&gt;Why Sequence-to-sequence Learning?&lt;/h3&gt;

&lt;p&gt;The primary intuition behind choosing sequence-to-sequence learning is that playlists can be interpreted just as sentences, and songs as words in a sentence. In the past few years, sequence-to-sequence learning has been widely used to learn effective sentence embeddings in applications like neural machine translation¹⁰. We make use of the relationship playlist:songs:: sentences:words, and take inspiration from research in the field of natural language processing to model playlist embeddings the way sentences are embedded.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3 id=&#34;we-make-use-of-the-relationship-playlistsongs--sentences-words-and-take-inspiration-from-research-in-natural-language-processing-to-model-playlist-embeddings-the-way-sentences-are-embedded&#34;&gt;We make use of the relationship playlist:songs :: sentences: words, and take inspiration from research in natural language processing to model playlist embeddings the way sentences are embedded.&lt;/h3&gt;
&lt;/blockquote&gt;

&lt;hr&gt;

&lt;h2 id=&#34;sequencetosequence-learning&#34;&gt;Sequence-to-Sequence Learning&lt;/h2&gt;

&lt;p&gt;The name sequence-to-sequence learning in its very core implies that the network is trained to take in sequences and output sequences. So instead of predicting single word, the network outputs the entire sentence, which could be a translated in a foreign language, or the next predicted sentence from the corpus, or even the same sentence if the network is trained like an autoencoder.&lt;/p&gt;

&lt;p&gt;For this work, we use seq2seq framework as an autoencoder where the task of the network is to reconstruct the input playlist and in doing so, learn a compact representation of the input playlist, which captures the properties of the playlist.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&#34;https://bitsandmusic.com/assets/images/playlists/formulation.png&#34;  /&gt;
  &lt;figcaption&gt;
      &lt;p&gt;The overall concept of using seq2seq network like an autoencoder.&lt;/p&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&#34;seq2seq-models&#34;&gt;Seq2seq Models&lt;/h3&gt;

&lt;p&gt;We use the Attention technique for the seq2seq models used in this work to learn the playlist embeddings which capture the long-term dependencies between the songs in the playlist because of the relatively longer length of playlists (50–1000 songs). We experiment with 2 variants of seq2seq models:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Unidirectional seq2seq networks&lt;/li&gt;
&lt;li&gt;Bidirectional seq2seq networks&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;A bidirectional seq2seq network&lt;/strong&gt; is different from the unidirectional variant in the sense that a bidirectional RNN is used, meaning the hidden state is the concatenation of a forward RNN and a backward RNN that read the sequences in two opposite directions. This allows the network to capture more contextual information for the decoder to predict the output symbol.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&#34;data-curation-filtering-and-annotation&#34;&gt;Data: Curation, Filtering, and Annotation&lt;/h2&gt;

&lt;h3 id=&#34;need&#34;&gt;Need&lt;/h3&gt;

&lt;p&gt;For this work, we need a list of playlists (sentences) with each playlist consisting of a list of songs (words). For solving this problem in it’s simplest form, we need just the playlist IDs and the song IDs mapped with the appropriate playlist IDs.&lt;/p&gt;

&lt;h3 id=&#34;dataset-creation&#34;&gt;Dataset Creation&lt;/h3&gt;

&lt;p&gt;We download the data using the Spotify Web API. To collect a big enough set of terms to query the Spotify system, we use everynoise.com. This interactive website contains a list of some 2600+ genres, graphed out according to their relationship with each other, along with an audio example for each genre. We parse the data from the home page of this website and get the list of all the genres. Then for each genre, we download playlists (along with the corresponding song information) using the Spotify Web API. The whole flow is shown in Fig 1.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&#34;https://bitsandmusic.com/assets/images/playlists/download.png&#34;  /&gt;
  &lt;figcaption&gt;
      &lt;p&gt;Fig 1: Data download workflow&lt;/p&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Here are the downloaded data details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 Million Playlists&lt;/li&gt;
&lt;li&gt;3 Million Artists.&lt;/li&gt;
&lt;li&gt;13 Million Tracks&lt;/li&gt;
&lt;li&gt;3 Million Unique Tracks&lt;/li&gt;
&lt;li&gt;3 Million Albums&lt;/li&gt;
&lt;li&gt;2680 Genres&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;data-filtering&#34;&gt;Data Filtering&lt;/h3&gt;

&lt;p&gt;We follow [1] in doing the data clean up by removing the rare tracks, and outlier sized playlists (having a number of songs less than 10 or greater than 5000). This leaves up with 755k unique playlists and 2.4 million unique tracks.&lt;/p&gt;

&lt;h3 id=&#34;annotation&#34;&gt;Annotation&lt;/h3&gt;

&lt;figure&gt;
  &lt;img src=&#34;https://bitsandmusic.com/assets/images/playlists/genres.png&#34;  /&gt;
  &lt;figcaption&gt;
      &lt;p&gt;Image of a subset of genres taken from everynoise.com&lt;/p&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;Problem: Genres, Genres Everywhere&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Although this step is not directly needed for the training part, however, it is crucial for the evaluation phase. This step aims to label the playlists with their appropriate genre. There are certain problems with the information available so far:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spotify provides genre information for the artist, but not the song. Labeling the song genre same as the artist genre would not be entirely correct as an artist can have songs of different genres.&lt;/li&gt;
&lt;li&gt;Assigning the songs the same genre as that of the playlist, which in turn is derived from the query term for which it was fetched, would be problematic. The issue in going this route is the subjectivity associated with it. Provided this fine-grained annotation, what would be the difference between soft rock, 80’s rock, classic rock, and rock from the perspective of classification?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hence, we need to bring down the number of genres (output labels) from 2680 to a more manageable number.&lt;/p&gt;

&lt;h3 id=&#34;proposed-solution&#34;&gt;Proposed Solution&lt;/h3&gt;

&lt;figure&gt;
  &lt;img src=&#34;https://bitsandmusic.com/assets/images/playlists/annotate.png&#34;  /&gt;
  &lt;figcaption&gt;
      &lt;p&gt;Data annotation workflow&lt;/p&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;ol&gt;
&lt;li&gt;To solve for this, we train a word2vec¹¹ model on our corpus to get song embeddings, which capture the semantic characteristics (such as genre) of the songs by their co-occurrence in the corpus.&lt;/li&gt;
&lt;li&gt;The resulting song embeddings are then clustered into 200 clusters (arbitrarily chosen number in an attempt to maintain the balance between the feasibility of the annotation process and size of formed clusters. Smaller cluster size and lesser annotation time are desired).&lt;/li&gt;

&lt;li&gt;&lt;p&gt;For each cluster:&lt;/p&gt;

&lt;p&gt;• Artist genre is applied to each corresponding song and a genre-frequency (count) dictionary is created. A sample genre-count dictionary for cluster with 17 songs would look like {rock: 5, indie-rock:3, blues: 2,soft-rock: 7}&lt;/p&gt;

&lt;p&gt;• From this dictionary, the genre having a clear majority is assigned as the genre for all the songs in that cluster.&lt;/p&gt;

&lt;p&gt;• All the songs in a cluster with no clear genre majority are discarded for annotation.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Based on the observed genre-distribution in the data, and as a result of clustering sub-genres (such as soft-rock) into parent genres (such as rock), the genres finally are chosen for annotating the clusters are:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3 id=&#34;rock-metal-blues-country-reggae-latin-electronic-hip-hop-classical&#34;&gt;Rock, Metal, Blues, Country, Reggae, Latin, Electronic, Hip Hop Classical.&lt;/h3&gt;
&lt;/blockquote&gt;

&lt;p&gt;To validate our approach, we train a classifier on our dataset consisting of annotated song embeddings. With training and test set kept separate at the time of training, we achieve a 94% test accuracy.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&#34;https://bitsandmusic.com/assets/images/playlists/tsne.png&#34;  /&gt;
  &lt;figcaption&gt;
      &lt;p&gt;t-SNE plot for genre-annotated songs, with 1000 sampled songs for each genre&lt;/p&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;For playlist-genre annotation&lt;/strong&gt;, only the playlists having all the songs annotated, are considered for annotation. Further, only those playlists are assigned genres for which more than 70% of the songs agree on a genre.&lt;/p&gt;

&lt;hr&gt;

&lt;h2 id=&#34;evaluation&#34;&gt;Evaluation&lt;/h2&gt;

&lt;p&gt;Since the aim of our work is to learn playlist embeddings which can be used for recommendation, we evaluate the quality of embeddings using a recommendation task.&lt;/p&gt;

&lt;h3 id=&#34;recommendation-task&#34;&gt;Recommendation Task&lt;/h3&gt;

&lt;p&gt;The recommendation being inherently subjective in nature is best evaluated by having user-labeled data. However, in the absence of such annotated datasets, we evaluate our proposed approach by measuring the extent to which the playlist space created by the embedding models is relevant, in terms of the similarity of genre and length information of closely-lying playlists.
We use the Approximate Nearest Neighbors Algorithm using Spotify ANNOY library¹³ to populate the tree structure with the playlist embeddings. A query playlist is randomly selected and the search results are compared with the queried playlist in terms of genre and length information. There are nine possible genre labels. For comparing length, ten output classes (spanning the range {30…250} corresponding to bins of size 20 are created. An average of 100 precision values for each query is considered.&lt;/p&gt;

&lt;h3 id=&#34;baseline-comparison&#34;&gt;Baseline Comparison&lt;/h3&gt;

&lt;p&gt;To evaluate the performance of our proposed technique, we need some sort of baseline performance as well. As our baseline model, we experiment with a weighted variant of Bag-of-words model¹⁴, which uses a weighted averaging scheme to get the sentence embedding vectors followed by their modification using singular-value decomposition (SVD). This method of generating sentence embeddings proves to be a stronger baseline compared to traditional averaging.&lt;/p&gt;

&lt;h2 id=&#34;results&#34;&gt;Results&lt;/h2&gt;

&lt;p&gt;The Recommendation task, as shown in Figure below, captures some interesting insights about the effectiveness of different models for capturing different characteristics. Firstly, high precision values demonstrate the relevance of the playlist embedding space which is the first and foremost expectation from a recommendation system. Also, BoW models capture genre information better than seq2seq models, while length information is better captured by the seq2seq models, demonstrating the suitability of different models for different tasks.&lt;/p&gt;

&lt;p&gt;&lt;img float=&#34;left&#34; src=&#34;https://bitsandmusic.com/assets/images/playlists/length_final.png&#34;&gt;
&lt;img src=&#34;https://bitsandmusic.com/assets/images/playlists/genre_final.png&#34;&gt;&lt;/p&gt;

&lt;h2 id=&#34;applications-1&#34;&gt;Applications&lt;/h2&gt;

&lt;p&gt;One of the direct applications of this work is a recommendation engine for playlists. Given a query, the system would recommend/retrieve similar playlists form the corpus. The tree data structure discussed in Recommendation Task section can be directly used for this purpose. Given a query playlist, its k-nearest neighbors would be the most similar items to it and would be the system recommendations. Demonstration for our work can be seen in the video.&lt;/p&gt;

&lt;p&gt;
&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube.com/embed/gybl9YHeEa8&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p align=&#34;center&#34; style=&#34;font-size: smaller;&#34;&gt;Recommendation System Demo video&lt;/p&gt;&lt;/p&gt;

&lt;p&gt;And that’s that. We have presented a seq2seq based approach for learning playlist embeddings, which can be used for tasks such as playlist discovery and recommendation. Our approach can also be extended for learning even better playlist-representations by integrating content-based (lyrics, audio, etc.) song-embedding models, and for generating new playlists by using variational sequence models.
In the paper, there are many more evaluation techniques for assessing the quality of playlist embeddings with respect to the encoded information, which is out of scope for this post. I will be discussing that in another post.
Until next time!&lt;/p&gt;

&lt;p&gt;P.S — Here’s the link to the &lt;a href=&#34;https://arxiv.org/abs/1907.01098&#34;&gt;paper&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;

&lt;p&gt;&lt;sub&gt;[1] &lt;a href=&#34;https://newsroom.spotify.com/2018-10-10/celebrating-a-decade-of-discovery-on-spotify/&#34;&gt;https://newsroom.spotify.com/2018-10-10/celebrating-a-decade-of-discovery-on-spotify/&lt;/a&gt;&lt;/sub&gt;&lt;br&gt;
&lt;sub&gt;[2] Keunwoo Choi, George Fazekas, and Mark Sandler. Towards playlist generation algorithms using rnns trained on within track transitions.arXiv preprint arXiv:1606.02096, 2016&lt;/sub&gt;&lt;br&gt;
&lt;sub&gt;[3] Fields, Ben, and Paul Lamere. “Finding A Path Through The Jukebox — The Playlist Tutorial, ISMIR.” ISMIR, Utrecht (2010).&lt;/sub&gt;&lt;br&gt;
&lt;sub&gt;[4] De Mooij, A. M., and W. F. J. Verhaegh. “Learning preferences for music playlists.” Artificial Intelligence 97.1–2 (1997): 245–271.&lt;/sub&gt;&lt;br&gt;
&lt;sub&gt;[5] Ching-Wei Chen, Paul Lamere, Markus Schedl, and Hamed Zamani. Recsys challenge 2018: Automatic music playlist continuation. InProceedings of the 12th ACM Conference on Recommender Systems, pages 527–528.ACM, 2018&lt;/sub&gt;&lt;br&gt;
&lt;sub&gt;[6] Maksims Volkovs, Himanshu Rai, Zhaoyue Cheng, Ga Wu, Yichao Lu, and Scott Sanner. Two-stage model for automatic playlist continuation at scale. In Proceedings of the ACM Recommender Systems Challenge 2018, page 9. ACM, 2018&lt;/sub&gt;&lt;br&gt;
&lt;sub&gt;[7] Andreja Andric and Goffredo Haus. Automatic playlist generation based on tracking user’s listening habits.Multimedia Tools and Applications, 29(2):127–151, 2006.&lt;/sub&gt;&lt;br&gt;
&lt;sub&gt;[8] Beth Logan. Content-based playlist generation: Exploratory experiments. InISMIR, 2002.&lt;/sub&gt;&lt;br&gt;
&lt;sub&gt;[9] lya Sutskever, Oriol Vinyals, and Quoc V Le. Sequence to sequence learning with neural networks. Advances in neural information processing systems, pages 3104–3112, 2014.&lt;/sub&gt;&lt;br&gt;
&lt;sub&gt;[10] Dzmitry Bahdanau, Kyunghyun Cho, and Yoshua Bengio. Neural machine translation by jointly learning to align and translate.arXiv preprint arXiv:1409.0473, 2014.&lt;/sub&gt;&lt;br&gt;
&lt;sub&gt;[11] Mikolov, Tomas, et al. “Efficient estimation of word representations in vector space.” arXiv preprint arXiv:1301.3781 (2013).&lt;/sub&gt;&lt;br&gt;
&lt;sub&gt;[12] Anita Shen Lillie.MusicBox: Navigating the space of your music. PhD thesis, Massachusetts Institute of Technology, 2008&lt;/sub&gt;&lt;br&gt;
&lt;sub&gt;[13] Bernhardsson, E. “ANNOY: Approximate nearest neighbors in C++/Python optimized for memory usage and loading/saving to disk.” GitHub &lt;a href=&#34;https://github&#34;&gt;https://github&lt;/a&gt;. com/spotify/annoy (2017).&lt;/sub&gt;&lt;br&gt;
&lt;sub&gt;[14] Arora, Sanjeev, Yingyu Liang, and Tengyu Ma. “A simple but tough-to-beat baseline for sentence embeddings.” (2016).&lt;/sub&gt;&lt;br&gt;&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
