Um mecanismo de recomendação de músicas e playlists do Spotify
Rachelle Palmer6 min read • Published Jun 23, 2022 • Updated Nov 13, 2023
APLICATIVO COMPLETO
Lucas De Oliveira, Chandrish Ambati e Anish Mukherjee, da Universidade de São Francisco, contribuíram com este projeto incrível.
Em 2018, o Spotify organizou um RecSys Challenge da Association for Computing Machinery (ACM) no qual publicou um conjunto de dados de um milhão de playlists, desafiando os participantes a recomendar uma lista de 500 músicas com base em uma playlist criada pelo usuário.
Como fãs de música e cientistas de dados, topamos esse desafio na hora. Então logo concordamos que combinar embeddings de músicas com algum método de vizinhos mais próximos provavelmente geraria resultados muito bons. O principal era descobrir como resolver essa tarefa de recomendação em grande escala com mais de 4 bilhões de playlists selecionadas por usuários no Spotify, sendo que esse número continuava a crescer. Essa constatação gerou grandes dúvidas sobre como treinar um modelo decente, já que todos esses dados provavelmente não caberiam em uma memória física ou em um único servidor.
Este projeto resultou em um pipeline ETL escalável utilizando
- Apache Spark
- MongoDB
- Amazon S3
- Databricks (PySpark)
Eles foram usados para treinar um modelo Word2Vec de aprendizado profundo para criar incorporações de músicas e playlists para recomendação. Buscamos visualizações de dados que criamos no Projetor de Incorporação do Tensorflow.
A tarefa mais tediosa desse projeto foi coletar o maior número possível de letras das músicas nas playlists. Começamos isolando as músicas exclusivas nos arquivos da playlist pelo URI da faixa; no total, tínhamos mais de 2 milhões de músicas únicas. Em seguida, usamos o nome da faixa e o nome do artista para procurar as letras na web. Inicialmente, usamos solicitações simples do Python para extrair as informações da letra, mas isso ficou muito lento para nossos propósitos. Em seguida, usamos asyncio, que nos permite fazer solicitações simultaneamente. Isso acelerou significativamente o processo, reduzindo o tempo de download de letras de 10.000 músicas de 15 minutos para menos de um minuto. No final, só foi possível coletar letras de 138.000 músicas.
O conjunto de dados original contém 1 milhão de playlists distribuídas em 1 mil arquivos JSON, totalizando cerca de 33 GB de dados. Usamos o PySpark no Databricks para pré-processar esses arquivos JSON separados em um único DataFrame SparkSQL e, em seguida, juntamos esse DataFrame com as letras que salvamos.
Embora as etapas de coleta e pré-processamento de dados mencionadas acima sejam demoradas, o modelo também precisa ser retreinado e reavaliado com frequência, por isso é fundamental armazenar dados em um banco de dados dimensionável. Além disso, gostaríamos de usar um banco de dados sem esquema para expansão futura em conjuntos de dados fosse compatível com vários tipos de dados. Considerando nossas necessidades, concluímos que o MongoDB seria a solução ideal como armazenamento de dados e recursos.
Para nossas análises, lemos nossos dados pré-processados do MongoDB em um Spark DataFrame e agrupamos os registros por ID da playlist (PID), agregando todas as músicas de uma playlist em uma lista na coluna song_list. Usando o modelo Word2Vec no Spark MLlib, treinamos a incorporação de músicas inserindo listas de IDs de faixas de uma playlist no modelo, da mesma forma que você enviaria uma lista de palavras de uma frase para treinar a incorporação de palavras. Conforme mostrado abaixo, treinamos a incorporação de músicas em apenas 3 linhas do código PySpark:
1 from pyspark.ml.feature import Word2Vec 2 word2Vec = Word2Vec(vectorSize=32, seed=42, inputCol="song_list").setMinCount(1) 3 word2Vec.sexMaxIter(10) 4 model = word2Vec.fit(df_play)
Em seguida, salvamos os embeddings de músicas no MongoDB para uso posterior. Abaixo está um snapshot do DataFrame de incorporações de músicas que salvamos:
Por fim, estendemos nossa tarefa de recomendação além das simples recomendações de músicas para recomendar playlists inteiras. Dada uma playlist de entrada, retornaríamos as mil playlists mais próximas ou semelhantes. Adotamos uma abordagem “continuous bag of songs” para esse problema calculando as incorporações de playlists como a média de todas as incorporações de músicas nessa playlist.
Este fluxo de trabalho começou com a leitura das incorporações de músicas do MongoDB em um DataFrame SparkSQL. Em seguida, calculamos a incorporação de uma playlist pegando a média de todas as incorporações de músicas nessa playlist e as salvamos no MongoDB.
Você ainda está lendo? Ufa!
Treinamos a incorporação de letras carregando as letras de uma música, separando as palavras em listas e alimentando essas palavras em um modelo Word2Vec para produzir 32 vetores dimensionais para cada palavra. Em seguida, tomamos a incorporação média em todas as palavras como a incorporação lírica da música. Por fim, nosso objetivo analítico aqui era determinar se os usuários criam playlists com base em temas comuns de letras, verificando se a distância de incorporação de músicas em pares e a distância de incorporação de letras em pares entre duas músicas estavam correlacionadas. Como era de se esperar, parece que não são.
Você pode estar se perguntando por que usamos um modelo de linguagem (Word2Vec) para treinar essas incorporações. Por que não usar um Pin2Vec ou um modelo de rede neural personalizado para prever classificações implícitas? Por motivos práticos, queríamos trabalhar exclusivamente no ecossistema Spark e lidar com os dados de forma distribuída. Essa foi uma restrição definida no projeto com antecedência e nos desafiou a pensar de forma criativa.
No entanto, achamos o Word2Vec um modelo de candidato tentador também por motivos teóricos. O modelo Word2Vec usa o contexto de uma palavra para treinar incorporações estáticas treinando as incorporações da palavra de entrada para prever as palavras circundantes. Essencialmente, a incorporação de qualquer palavra é determinada pela sua coocorrência com outras palavras. Isso tinha um mapeamento claro de nosso próprio problema: ao usar um modelo Word2Vec, a distância entre as incorporações de músicas refletiria a coocorrência das músicas em 1 milhão de playlists, tornando-a uma medida útil para uma recomendação baseada na distância (vizinhos mais próximos). Ele modelaria efetivamente como as pessoas agrupam as músicas, usando o comportamento do usuário como fator determinante da similaridade.
Além disso, o modelo Word2Vec aceita entrada em forma de lista de palavras. Para cada playlist, tínhamos uma lista de IDs de faixas, o que tornava atraente o trabalho com o modelo Word2Vec.
Depois de tudo isso, finalmente estávamos prontos para visualizar nossos resultados e fazer algumas recomendações interativas. Decidimos representar visualmente nossos resultados de incorporação usando o Projetor de incorporação do Tensorflow, que mapeia as incorporações de músicas e listas de reprodução de 32 dimensões em uma visualização interativa de um espaço de incorporação de 3D. Você tem a opção de usar PCA ou tSNE para redução de dimensionalidade e similaridade de cosseno ou distância euclidiana para medir distâncias entre vetores.
A vantagem de usar o projetor do Tensorflow é que ele nos oferece uma bela ferramenta de visualização e uma calculadora de distância, tudo em um. Tente pesquisar uma música no painel direito e, se a música fizer parte do conjunto de dados original, você verá as músicas “most similar” aparecerem abaixo dela.
Ficamos impressionados com a facilidade de usar o MongoDB para armazenar e carregar nossos dados de forma confiável. Como estávamos usando computação distribuída, teria sido inviável executar nosso pipeline do início ao fim sempre que quiséssemos atualizar nosso código ou ajustar o modelo. O MongoDB permitiu que salvássemos nossos resultados incrementais para processamento e modelagem posteriores, o que nos poupou horas de espera para que o código fosse executado novamente.
Funcionou bem com todas as ferramentas que usamos todos os dias e as ferramentas que escolhemos – não tivemos áreas de atrito.
Ficamos impactados com a forma como esse método de treinamento de incorporações realmente funcionava. Embora o projetor de incorporação de músicas de 2 milhões esteja visualmente aglomerado, vemos que as recomendações que ele produz são realmente muito boas para agrupar músicas.
Considere a recomendação de incorporação para "A Day In The Life", dos Beatles:
Ou a recomendação para “Heart of the City (Ain’t No Love)” de Jay Z:
Fã de Taylor Swift? Aqui estão as recomendações para "New Romantics":
Ficamos muito satisfeitos ao encontrar clusters que ocorrem naturalmente nas incorporações da playlist. Mais notavelmente, vemos um grupo contendo principalmente rock cristão, um com música natalina, um para reggaeton e um grande grupo onde os gêneros abrangem sua extensão de forma contínua e intuitiva.
Observe também que, quando selecionamos uma playlist, temos muitas playlists recomendadas com os mesmos nomes. Isso, em essência, valida nossas incorporações de músicas. Lembre-se de que as incorporações de playlists foram criadas considerando a incorporação média de todas as suas músicas; o nome das playlists não foi levado em conta. Os nomes semelhantes apenas reforçam conceitualmente esse fato.
Ficamos satisfeitos com a conclusão desse projeto, mas há mais coisas que poderiam ser feitas aqui.
- Poderíamos usar essas incorporações de músicas treinadas em outras tarefas downstream e ver como elas são eficazes. Além disso, você pode baixar as incorporações de músicas aqui: Incorporações | Metainformações
- Poderíamos examinar outros métodos de treinar essas incorporações usando algumas redes neurais recorrentes e uma implementação aprimorada desse modelo Word2Vec.