Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Junte-se a nós no Amazon Web Services re:Invent 2024! Saiba como usar o MongoDB para casos de uso de AI .
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
Javachevron-right

Gerador de lista de reprodução alimentada por IA: criando visualizações personalizadas com aprendizagem profunda4j e MongoDB

Tim Kelly14 min read • Published Oct 25, 2024 • Updated Oct 25, 2024
IAJava
APLICATIVO COMPLETO
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
O que é exatamente uma "romcom tailandesa Avó na tarde de quarta-feira"? Que tal uma "menina tristonha à noite de quinta-feira"? Bem, não estou muito certo de que isso não é verdade, mas o playlist parece achar que esse é o tipo de estilo que eu gostaria de ouvir. E eles estão certos! Ao treinar sobre meus hábitos de escuta e como eles flutuam ao longo das semanas e dos dias, o Swift criou a "daylist" — uma playlist personalizada feita sob medida exatamente para o meu estilo, finalizada com um nome curinga que captura a energia da lista de reprodução. Mas e se quisermos trabalhar de trás para frente a partir daí?
Quero ser capaz de dar meu próprio nome a uma playlist de funk e obter uma playlist personalizada que corresponda a qualquer imagem oculta que eu tenha em minha mente sobre o que é. Longe vão os dias de vasculhar manualmente a plataforma de streaming de músicas de sua escolha para criar "amor de cachorro emo do meio-oeste" - essa provavelmente será apenas a discografia de Car Seat Headrest por causa do esforço necessário para trocar entre os vários atores que só escape da sua mente no minuto em que você for comparado à interface do usuário nunca fluente da criação de listas de reprodução.
Neste tutorial, usaremos Aprender a deriva4j para importar um modelo que podemos usar para incorporar nossas letras de músicas. Isso nos permitirá capturar o significado semântica das músicas. O deep learning4j é uma framework de aprendizagem profunda de código aberto construída para a JVM (Java Virtual Machine), para treinamento e implantação de redes generativas em Java. Ele tem vários submódulos, como o Nd4j, que é como o NumPy para Java e lida com operações matemáticas complexas, e o Datavec, que transforma dados brutos (como letras de músicas) em estresses adequados para redes generativas. O4j de profundidade também se integra ao Samediff, permitindo a execução de gráficos computacionais complexos semelhantes ao TensorFlow ou PyTorch. Essas ferramentas nos permitem incorporar letras de músicas em vetores, capturando as "vibrações" de cada faixa, que podemos usar com o MongoDB Atlas para gerar listas de reprodução personalizadas com base no nome da lista de reprodução funk que você fornece.
Em seguida, forneceremos um nome de playlist que será usado para pesquisar no banco de dados de dados as músicas mais semanticamente semelhantes, todas com o MongoDB Atlas Vector Search. Existirão algumas limitações a esta implementação. Não usaremos o histórico de escuta do usuário para personalizar nossos resultados, ou os arquivos de áudio da músicas para capturar melhor as energias das músicas. Mais importante, usaremos apenas um modelo genérico e pré-treinado para incorporar nossos dados, o que limitará um pouco a precisão. Dito isto, o Open Learning4j permite que você importe seus próprios modelos personalizados do TensorFlow ou Keras para serem usados em seus aplicativos. Isso vai um pouco além do escopo do que vamos fazer hoje.
O aplicação inteiro está disponível no repositório doGithub .

Pré-requisitos

Para este projeto, você precisará do seguinte:
  • Java 11+ instalado (estou usando o Java 21)
  • Maven versão 3.9.6+
  • MongoDB Atlas com um cluster implementado
    • Para o conjunto de dados que temos, excederemos o limite de tamanho do cluster MongoDB M0 , nossa camada grátis. Mas você pode carregar parte do conjunto de dados mais tarde e ainda acompanhar o tutorial.
  • Incorporações GloVe (glove.840B.300d.txt) disponíveis em GloVe: Vetores globais para representação de palavras
    • armazenar em src/main/resources
  • Um arquivo CSV de letras de músicas (song_lyrics.csv) disponível no Kaggle: Genius Letrasde músicas
    • armazenar em src/main/resources

Configurando nosso POM

1<dependencies>
2 <!-- Spring Boot Starter for Web API -->
3 <dependency>
4 <groupId>org.springframework.boot</groupId>
5 <artifactId>spring-boot-starter-web</artifactId>
6 </dependency>
7
8 <!-- ND4J: Core Numerical Processing -->
9 <dependency>
10 <groupId>org.nd4j</groupId>
11 <artifactId>${nd4j.backend}</artifactId>
12 <version>${1.0.0-M2.1}</version>
13 </dependency>
14
15 <!-- DataVec for working with data -->
16 <dependency>
17 <groupId>org.datavec</groupId>
18 <artifactId>datavec-api</artifactId>
19 <version>${1.0.0-M2.1}</version>
20 </dependency>
21
22 <!-- DeepLearning4j Core -->
23 <dependency>
24 <groupId>org.deeplearning4j</groupId>
25 <artifactId>deeplearning4j-core</artifactId>
26 <version>${1.0.0-M2.1}</version>
27 </dependency>
28
29 <!-- DeepLearning4j NLP for Text Processing -->
30 <dependency>
31 <groupId>org.deeplearning4j</groupId>
32 <artifactId>deeplearning4j-nlp</artifactId>
33 <version>${1.0.0-M2.1}</version>
34 </dependency>
35
36 <!-- Apache Commons CSV -->
37 <dependency>
38 <groupId>org.apache.commons</groupId>
39 <artifactId>commons-csv</artifactId>
40 <version>1.9.0</version>
41 </dependency>
42
43 <!-- MongoDB Driver -->
44 <dependency>
45 <groupId>org.mongodb</groupId>
46 <artifactId>mongodb-driver-sync</artifactId>
47 <version>5.2.0</version>
48 </dependency>
49
50 <dependency>
51 <groupId>org.mongodb</groupId>
52 <artifactId>mongodb-driver-core</artifactId>
53 <version>5.2.0</version>
54 </dependency>
55
56 <dependency>
57 <groupId>org.mongodb</groupId>
58 <artifactId>bson</artifactId>
59 <version>5.2.0</version>
60 </dependency>
61
62 <!-- JUnit for testing -->
63 <dependency>
64 <groupId>org.junit.jupiter</groupId>
65 <artifactId>junit-jupiter-api</artifactId>
66 <version>5.7.0</version>
67 <scope>test</scope>
68 </dependency>
69
70</dependencies>

O que está ocorrendo aqui?

  • Spring Boot Starter: essa dependência configura um aplicação web Spring Boot com todos os componentes necessários da API web.
  • ND4J e DataVec: estas são as bibliotecas principais do ecossistema de aprendizagem profunda4j. O ND4J lida com cálculos numéricos e a DataVec processa dados para tarefas de aprendizado de máquina.
  • aprendizagem profunda4j Core e NLP: isso nos fornece a funcionalidade de aprendizagem profunda e as ferramentas de processamento de linguagem natural (NLP) que usaremos para incorporar letras de músicas em vetores.
  • Driver MongoDB: Isso nos permite conectar e interagir com MongoDB a partir de nosso aplicação Java .
  • Apache Commons CSV: usado para ler dados de gravação de um arquivo CSV para processamento e armazenamento.

Nossos dados

Este tutorial gira em torno de dois modelos: Song e Playlist.

Modelo de faixa

O modeloSong representa uma faixa individual e inclui campos para o título, artista, letra e vetor de incorporação da faixa. Sinta-se livre para modificar isso para armazenar quaisquer dados necessários para seu aplicação. Com documentos MongoDB , seus dados são armazenados junto com seus vetores:
1import java.util.List;
2
3public class Song {
4 private String title;
5 private String artist;
6 private String lyrics;
7 private List<Double> embedding;
8
9 public Song(String title, String artist, String lyrics, List<Double> embedding) {
10 this.title = title;
11 this.artist = artist;
12 this.lyrics = lyrics;
13 this.embedding = embedding;
14 }
15
16 public String getTitle() {
17 return title;
18 }
19
20 public void setTitle(String title) {
21 this.title = title;
22 }
23
24 public String getArtist() {
25 return artist;
26 }
27
28 public void setArtist(String artist) {
29 this.artist = artist;
30 }
31
32 public String getLyrics() {
33 return lyrics;
34 }
35
36 public void setLyrics(String lyrics) {
37 this.lyrics = lyrics;
38 }
39
40 public List<Double> getEmbedding() {
41 return embedding;
42 }
43
44 public void setEmbedding(List<Double> embedding) {
45 this.embedding = embedding;
46 }
47}

Modelo de lista de reprodução

O modeloPlaylist consiste em um nome de lista de reprodução e uma lista de objetosSong :
1import java.util.List;
2
3public class Playlist {
4
5 private String playlistName;
6 private List<Song> songs;
7
8 public Playlist() {
9 }
10
11 public Playlist(String playlistName, List<Song> songs) {
12 this.playlistName = playlistName;
13 this.songs = songs;
14 }
15
16 public String getPlaylistName() {
17 return playlistName;
18 }
19
20 public void setPlaylistName(String playlistName) {
21 this.playlistName = playlistName;
22 }
23
24 public List<Song> getSongs() {
25 return songs;
26 }
27
28 public void setSongs(List<Song> songs) {
29 this.songs = songs;
30 }
31}
Esses modelos serão usados para estruturar os dados que armazenamos no MongoDB e buscar para criar nossas pequenas listas de reprodução funky.

Incorporando dados

Nesta seção, vamos nos concentrar em como converter letras de músicas em incorporações vetoriais usando incorporações GloVe (Global Vectors for Word Representation). Essas incorporações capturam o significado semântica de cada palavra nas letras, permitindo-nos comparar as músicas com base em seu conteúdo lícito.
Para fazer isso, criaremos um pacote de serviço e a classeEmbeddingService, que:
  1. Carregar o modelo GloVe pré-treinado: este modelo contém representações vetoriais de palavras. Usaremos as incorporações GloVe 300-dimensionais para isso.
  2. Tokenizar as letras das músicas: divisão as letras em palavras individuais, removendo qualquer texto indesejado.
  3. Gerar um vetor para cada faixa: ao calcular a média das incorporações para cada palavra na letra, criaremos um único vetor que representa a faixa inteira.
Vamos programar!

Carregando o modelo GloVe

Precisamos carregar o modelo GloVe de um arquivo de texto e armazenar o vetor correspondente de cada palavra. Aqui está o código para carregar o modelo:
1@Service
2public class EmbeddingService {
3
4private final Map<String, INDArray> gloveEmbeddings = new HashMap<>();
5private final DefaultTokenizerFactory tokenizerFactory = new DefaultTokenizerFactory();
6private final Set<String> stopWords; // Set of stop words for filtering
7
8@Value("${embedding.model.path}")
9private String preTrainedGlovePath;
10
11public EmbeddingService() {
12 tokenizerFactory.setTokenPreProcessor(new CommonPreprocessor());
13 stopWords = loadStopWords();
14}
15
16@PostConstruct
17private void init() throws IOException {
18 loadGloveModel(preTrainedGlovePath);
19}
20
21private void loadGloveModel(String preTrainedGlovePath) throws IOException {
22 InputStream gloveStream = getClass().getResourceAsStream("/glove.840B.300d.txt");
23 if (gloveStream == null) {
24 throw new IOException("GloVe model not found in resources: " + preTrainedGlovePath);
25 }
26
27 try (BufferedReader reader = new BufferedReader(new InputStreamReader(gloveStream))) {
28 String line;
29 while ((line = reader.readLine()) != null) {
30 String[] split = line.split(" ");
31 String word = split[0];
32 float[] vector = new float[300]; // 300-dimensional GloVe vector
33 for (int i = 1; i < split.length; i++) {
34 vector[i - 1] = Float.parseFloat(split[i]);
35 }
36 INDArray wordVector = Nd4j.create(vector);
37 gloveEmbeddings.put(word, wordVector);
38 }
39 }
40}
41
42private Set<String> loadStopWords() {
43 return new HashSet<>(Arrays.asList(
44 "the", "a", "an", "and", "is", "in", "at", "of", "to", "for", "with", "on", "by", "this", "that", "it", "i", "you", "they", "we", "but", "or", "as", "if", "when"
45 ));
46}
Estamos carregando uma incorporação -dimensional 300para cada palavra do arquivoglove.840B.300d.txt. Em seguida, configuramos um tokenizador que divisão o texto em palavras individuais. Por último, estamos enviando uma lista de palavras vazias que nos ajudarão a incorporar texto. Essas são palavras que não fornecem muito significado semântica ao texto e podem depreciar nossa incorporação. Esta não é uma lista abrangente, mas servirá para uma demonstração.
Observação: PostConstruct é uma anotação que garante que o modelo GloVe seja carregado assim que o aplicação Spring for iniciado.

Tokenizando as letras

Em seguida, precisamos divisão as letras das músicas em palavras e filtrar quaisquer palavras vazias. Também estamos removendo qualquer coisa no texto entre colchetes. Isso ocorre porque o conjunto de dados que estou usando fornece informações como coro ou estilhaço 2 entre colchetes e deseja limpar os dados antes de gerar as incorporações.
1 private String removeBracketedText(String text) {
2 return text.replaceAll("\\[.*?]", "").trim();
3}
4
5public List<String> tokenizeText(String text) {
6 text = removeBracketedText(text);
7
8 Tokenizer tokenizer = tokenizerFactory.create(text);
9 List<String> tokens = new ArrayList<>();
10 while (tokenizer.hasMoreTokens()) {
11 String token = tokenizer.nextToken();
12 if (!stopWords.contains(token)) {
13 tokens.add(token);
14 }
15 }
16 return tokens;
17}
Tokenizer Factory: Estamos usando o4j DefaultTokenizerFactoryde aprendizagem profunda para tokenizar as letras. Este método divide o texto de entrada em palavras individuais (tokens).

Gerando a incorporação da faixa

Quando tivermos as palavras individuais (tokens), podemos criar um único vetor que represente toda a letra calculando a média das incorporações de cada palavra.
1public INDArray getEmbeddingForWord(String word) {
2 return gloveEmbeddings.getOrDefault(word, null);
3}
4
5public List<Double> getEmbeddingForText(List<String> tokens) {
6 INDArray embedding = null;
7 int validTokenCount = 0;
8
9 for (String token : tokens) {
10 INDArray wordVector = getEmbeddingForWord(token);
11 if (wordVector != null) {
12 if (embedding == null) {
13 embedding = wordVector.dup(); // Duplicate the word vector
14 } else {
15 embedding.addi(wordVector); // Sum the word embeddings
16 }
17 validTokenCount++;
18 }
19 }
20
21 if (embedding != null && validTokenCount > 0) {
22 embedding.divi(validTokenCount);
23 return convertINDArrayToDoubleList(embedding);
24 }
25 return Collections.emptyList(); // Return an empty list if no valid embeddings
26}
27
28private List<Double> convertINDArrayToDoubleList(INDArray indArray) {
29 double[] array = indArray.toDoubleVector();
30 List<Double> doubleList = new ArrayList<>();
31 for (double value : array) {
32 doubleList.add(value);
33 }
34 return doubleList;
35}
36
37public List<Double> embedText(String text) {
38 List<String> tokens = tokenizeText(text);
39 return getEmbeddingForText(tokens);
40}
Calculamos a média de todas as incorporações de palavras na letra para criar um único vetor para a faixa. Se uma palavra não for encontrada nas incorporações do GloVe, nós a ignoraremos. É aqui que um modelo especificamente formado em letras de músicas seria particularmente útil.
Como o MongoDB não suporta diretamente INDArray, convertemos o resultado para List<Double>.
Este serviço fornece a incorporação que posteriormente será armazenada no MongoDB e usada para pesquisar músicas semelhantes. Com oEmbeddingService escrito e pronto, como armazenamos as incorporações no MongoDB e, posteriormente, as consultamos para gerar listas de reprodução?

Armazenamento no MongoDB

Com as incorporações de músicas geradas, precisamos nos conectar ao MongoDB e armazenar os dados.
Vamos dividir o código do MongoDB em duas partes:
  1. Configuração do MongoDB: Configurando a conexão com nossa instância do MongoDB
  2. Repositório do MongoDB: Armazenar e consultar dados de músicas

Configuração do MongoDB

Primeiro, precisamos configurar nossa conexão MongoDB . É aqui que entra a classeMongoDBConfig. Criaremos um MongoClientcompartilhado que lidará com a comunicação com o MongoDB.
1import com.mongodb.client.MongoClient;
2import com.mongodb.client.MongoClients;
3import org.springframework.context.annotation.Bean;
4import org.springframework.context.annotation.Configuration;
5import org.springframework.beans.factory.annotation.Value;
6
7@Configuration
8public class MongoDBConfig {
9
10 @Value("${mongodb.uri}")
11 private String mongoUri;
12
13 @Bean
14 public MongoClient mongoClient() {
15 return MongoClients.create(mongoUri);
16 }
17}
@Configuration: Esta anotação marca a classe como um componente de configuração no Spring. Ele diz à Spring que esta classe contém definições de bean.
Usamos o métodoMongoClients.create() para criar um cliente MongoDB usando o URI especificado no arquivoapplication.properties.
Adicione o URI do MongoDB em application.properties:
1mongodb.uri=mongodb+srv://<username>:<password>@cluster.mongodb.net/?retryWrites=true&w=majority
Também adicionaremos o nome do banco de dados de dados e da coleção:
1mongodb.database=music
2mongodb.collection=songs

Armazenar dados no MongoDB

Em seguida, criaremos uma classeMongoDBRepository que interage com o banco de banco de dados MongoDB . Esse repositório hospedará nossas interações de banco de dados de dados para armazenar dados de músicas e realizará uma pesquisa vetorial para recuperar músicas semelhantes com base em incorporações.
1import com.mongodb.client.MongoClient;
2import com.mongodb.client.MongoCollection;
3import com.mongodb.client.MongoDatabase;
4import org.bson.Document;
5import org.example.model.Song;
6import org.springframework.beans.factory.annotation.Value;
7import org.springframework.stereotype.Repository;
8
9import java.util.ArrayList;
10import java.util.Arrays;
11import java.util.List;
12
13@Repository
14public class MongoDBRepository {
15
16 private final MongoCollection<Document> songCollection;
17
18 public MongoDBRepository(MongoClient mongoClient,
19 @Value("${mongodb.database}") String databaseName,
20 @Value("${mongodb.collection}") String collectionName) {
21 MongoDatabase database = mongoClient.getDatabase(databaseName);
22 this.songCollection = database.getCollection(collectionName);
23 }
24
25 /**
26 * Store the song embedding along with song details
27 *
28 * @param lyrics The song lyrics
29 * @param title The song title
30 * @param artist The artist name
31 * @param embedding The vector embedding for the song
32 */
33 public void storeEmbedding(String lyrics, String title, String artist, List<Double> embedding) {
34 Document songDocument = new Document()
35 .append("title", title)
36 .append("artist", artist)
37 .append("lyrics", lyrics)
38 .append("embedding", embedding);
39 songCollection.insertOne(songDocument);
40 }
41
42 /**
43 * Fetch similar songs using MongoDB's $vectorSearch aggregation based on the playlist embedding.
44 *
45 * @param playlistEmbedding The playlist title embedding used as the query vector
46 * @return List of Song objects representing similar songs
47 */
48 public List<Song> getSimilarSongs(List<Double> playlistEmbedding) {
49 List<Document> similarSongsDocs = new ArrayList<>();
50
51 // Perform the vector search using the generated embedding
52 String indexName = "vector_index";
53 int numCandidates = 150;
54 int limit = 10;
55
56 List<Document> pipeline = Arrays.asList(
57 new Document("$vectorSearch",
58 new Document("index", indexName)
59 .append("path", "embedding")
60 .append("queryVector", playlistEmbedding)
61 .append("numCandidates", numCandidates)
62 .append("limit", limit)
63 ),
64 new Document("$limit", limit)
65 );
66
67 try {
68 songCollection.aggregate(pipeline).into(similarSongsDocs);
69 } catch (Exception e) {
70 throw new RuntimeException("Failed to retrieve similar songs", e);
71 }
72
73
74 List<Song> similarSongs = new ArrayList<>();
75 for (Document doc : similarSongsDocs) {
76 Song song = new Song(
77 doc.getString("title"),
78 doc.getString("artist"),
79 doc.getString("lyrics"),
80 doc.getList("embedding", Double.class)
81 );
82 similarSongs.add(song);
83 }
84
85 return similarSongs;
86 }
87}
O método storeEmbedding() armazena o título, o artista, a letra e a incorporação da faixa como um documento no MongoDB.
O método getSimilarSongs() executa uma pesquisa vetorial utilizando a operação$vectorSearchdo MongoDB. Ele usa a incorporação para o nome da lista de reprodução e recupera uma lista de músicas com incorporações semelhantes.

índice de Vector Search

A última etapa da configuração de nosso banco de dados de dados para nos configurar é criar o índicevectorSearch para as incorporações de músicas armazenadas no banco de banco de dados.
Estaremos indexando o campoembeddingda songs em nosso banco de banco de dados MongoDB Atlas . Este campo contém a representação vetorial das letras de cada faixa que geramos.

Crie um índice do Vector Search usando a UI do Atlas

  1. Faça login no MongoDB Atlas e Go para a páginaClusters do nosso projeto.
  2. Na barra lateral, navegamos para Atlas Search sob o títuloServiços.
  3. Vamos clicar em Criar índice de pesquisa.
  4. No modal que aparece:
    • Nome do índice: insira um nome exclusivo para o seu índice (por exemplo, vector_index).
    • Banco de dados: selecione seu banco de dados de dados (por exemplo, music).
    • Collection: selecione sua collection (por exemplo, songs).
  5. Escolha Editor JSON e clique em Avançar.
  6. Defina o índice utilizando a seguinte estrutura JSON:
1{
2 "fields": [
3 {
4 "type": "vector",
5 "path": "embedding",
6 "numDimensions": 300, // The number of dimensions of the vectors
7 "similarity": "dotProduct" // Similarity metric (cosine, euclidean, or dotProduct)
8 }
9 ]
10}
  • type: especifica que o campo é um tipo de vetor (usado para embeddings).
  • caminho: O nome do campo que você está indexando (embedding, em nosso caso).
  • numDimensions: o número de dimensões no vetor. Como estamos usando incorporações GloVe, isso é 300.
  • similaridade: define a métrica de similaridade. Em nosso caso, usamos "cosine" para medir a similaridade com base no ângulo entre os vetores.
  1. Clique emAvançar para revisar sua configuração de índice.
  2. Clique em "criar índice de pesquisa".
Depois que o índice for criado, o Atlas começará a criá-lo, e você poderá usar $vectorSearch queries para encontrar músicas com base em suas incorporações.

Criar uma lista de reprodução

A funcionalidade principal do nosso aplicação está na criação de uma lista de reprodução com base no título da lista de reprodução inserido. Para fazer isso, precisamos criar uma incorporação para o nosso título, assim como faria para as letras das nossas músicas. Em seguida, usamos o Atlas Vector Search com nosso título incorporado para consultar o banco de banco de dados do MongoDB e encontrar a faixa semanticamente mais semelhante.
Essa implementação Go em nosso PlaylistService, em nosso pacoteService , que dependerá do nossoEmbeddingServiceexistente (para gerar incorporações para o nome da lista de reprodução) e MongoDBRepository (para executar as operações em nosso Banco de Dados MongoDB ).
Vamos detalhar a aulaPlaylistService classe a passo.

Defina o serviço

Começamos marcando PlaylistService como um serviço Spring e injetando as dependências necessárias: EmbeddingService para gerar as incorporações e MongoDBRepository para nossas operações de banco de dados de dados MongoDB .
1@Service
2public class PlaylistService {
3
4 private final EmbeddingService embeddingService;
5 private final MongoDBRepository songRepository;
6
7 public PlaylistService(EmbeddingService embeddingService, MongoDBRepository songRepository) {
8 this.embeddingService = embeddingService;
9 this.songRepository = songRepository;
10 }
11}
Agora, vamos adicionar o método generatePlaylist(String playlistName):
1 public Playlist generatePlaylist(String playlistName) {
2 // Generate the embedding for the playlist title
3 List<Double> playlistEmbedding = embeddingService.embedText(playlistName);
4
5 if (playlistEmbedding == null || playlistEmbedding.isEmpty()) {
6 throw new RuntimeException("Failed to generate embedding for playlist: " + playlistName);
7 }
8
9 // Query the database to find similar songs
10 List<Song> similarSongs = songRepository.getSimilarSongs(playlistEmbedding);
11
12 // Construct and return the Playlist
13 return new Playlist(playlistName, similarSongs);
14}
Quando um usuário fornece um nome de playlist, precisamos converter esse nome em uma representação vetorial que possa capturar o significado semântica do texto.
Depois de gerar a incorporação, o próximo passo é encontrar músicas com incorporações semelhantes. Passamos a incorporação da playlist para MongoDBRepository, que realiza uma pesquisa vetorial para encontrar músicas que correspondam à energia do nome da playlist.
Depois de recuperar as músicas semelhantes do MongoDB, criamos e retornamos um objetoPlaylist que contém o nome da lista de reprodução e a lista de músicas.
A próxima etapa é expor essa funcionalidade por meio de uma REST API (se você desejar).

Teste

Para garantir que nosso gerador de lista de reprodução esteja funcionando corretamente, precisamos carregar dados de exemplo no MongoDB e, em seguida, testar a geração de uma lista de reprodução com base em um nome fornecido pelo usuário. Vamos carregar dados de um arquivo CSV e criar endpoints para testar a geração de lista de reprodução.

Carregando dados de amostra

Antes de podermos testar a geração da lista de reprodução, precisamos de dados de músicas (letras) armazenados no MongoDB. Usaremos um arquivo CSV (song_lyrics.csv) que contém o título da faixa, o artista, a letra e outros metadados. A classeSongLyricsProcessor classe com a leitura deste CSV e armazenará os dados processados no MongoDB.
Aqui está o código para SongLyricsProcessor:
1import org.apache.commons.csv.CSVFormat;
2import org.apache.commons.csv.CSVParser;
3import org.apache.commons.csv.CSVRecord;
4import org.example.repository.MongoDBRepository;
5import org.example.service.EmbeddingService;
6import org.springframework.stereotype.Component;
7
8import java.io.InputStreamReader;
9import java.io.Reader;
10import java.util.List;
11import java.util.Objects;
12
13@Component
14public class SongLyricsProcessor {
15
16 private final EmbeddingService embeddingService;
17 private final MongoDBRepository mongoDBRepository;
18
19 public SongLyricsProcessor(EmbeddingService embeddingService, MongoDBRepository mongoDBRepository) {
20 this.embeddingService = embeddingService;
21 this.mongoDBRepository = mongoDBRepository;
22 }
23
24 public void processAndStoreLyrics(String csvFilePath) throws Exception {
25 Reader reader = new InputStreamReader(
26 Objects.requireNonNull(getClass().getClassLoader().getResourceAsStream(csvFilePath))
27 );
28
29 CSVFormat csvFormat = CSVFormat.DEFAULT.builder()
30 .setHeader()
31 .setSkipHeaderRecord(true)
32 .build();
33
34 CSVParser csvParser = new CSVParser(reader, csvFormat);
35
36 for (CSVRecord record : csvParser) {
37 String title = record.get("title");
38 String artist = record.get("artist");
39 String lyrics = record.get("lyrics");
40 String languageCld3 = record.get("language_cld3");
41 String languageFt = record.get("language_ft");
42
43 // Check if both language_cld3 and language_ft are 'en' (English)
44 if (!"en".equalsIgnoreCase(languageCld3) || !"en".equalsIgnoreCase(languageFt)) {
45 continue;
46 }
47
48 // Tokenize the lyrics and generate the embedding
49 List<Double> lyricsEmbedding = embeddingService.embedText(lyrics);
50
51 // Store the title, artist, lyrics, and embedding in MongoDB
52 mongoDBRepository.storeEmbedding(lyrics, title, artist, lyricsEmbedding);
53 }
54
55 csvParser.close();
56 }
57}
Lemos o arquivosong_lyrics.csv. Cada registro no arquivo contém informações sobre uma faixa, como título, artista e letra. Armazenaremos algumas dessas informações como metadados.
Para cada faixa, geramos uma incorporação para a letra. Os dados processados (titulo, artista, letra e incorporação) são armazenados no MongoDB.

Carregando os dados via endpoint REST

Para trigger o carregamento de dados, expomos um endpoint /loadSampleData no PlaylistController. Isso nos permitirá carregar os dados CSV no MongoDB enviando uma solicitação com o nome do arquivo.
Essa é uma maneira bastante aleatória de implementar isso e NÃO é recomendada para produção, mas é absolutamente adequada para esta pequena demonstração.
1@RestController
2public class PlaylistController {
3
4 private final PlaylistService playlistService;
5 private final SongLyricsProcessor songLyricsProcessor;
6
7 public PlaylistController(PlaylistService playlistService, SongLyricsProcessor songLyricsProcessor) {
8 this.playlistService = playlistService;
9 this.songLyricsProcessor = songLyricsProcessor;
10 }
11
12 @GetMapping("/loadSampleData")
13 public void loadSampleData(@RequestParam String fileName) throws Exception {
14 songLyricsProcessor.processAndStoreLyrics(fileName);
15 }
16}
  • /loadSampleData Endpoint: esse endpoint aciona o métodoprocessAndStoreLyrics() em SongLyricsProcessor. Você passa o nome do arquivo CSV como um parâmetro de query (por exemplo, /loadSampleData?fileName=song_lyrics.csv).
  • StreamLyricsProcessor: o SongLyricsProcessorinjetado lê o arquivo CSV, processa os dados da faixa , gera incorporações e armazena tudo no MongoDB.

Gerando uma playlist via endpoint REST

Depois que os dados forem carregados, precisamos de uma maneira de testar a geração da playlist. Para isso, vamos expor um endpoint /newPlaylistemPlaylistController. Quando um usuário fornece um nome de playlist, geraremos uma incorporação para esse nome e consultaremos o MongoDB para músicas semelhantes.
1@GetMapping("/newPlaylist")
2public Playlist newPlaylist(@RequestParam String playlistName) {
3 return playlistService.generatePlaylist(playlistName);
4}
/newplaylist endpoint recebe um parâmetro de consultaplaylistName (por exemplo, /newPlaylist?playlistName=sad%20girl%20wistful%20Friday%20evening) e retorna uma lista de reprodução gerada com base nesse nome.

Alguns cURLs

Então aqui estamos nós. Seria a hora de uma "clube funclub no sábado à noite" ou de um "cristo de desgosto na segunda-feira de manhã".
Bem, primeiro vamos carregar nossos dados no banco de banco de dados:
1curl -X GET "http://localhost:8080/loadSampleData?fileName=song_lyrics.csv"
Agora, isso levará algum tempo. Temos uma quantidade enorme de músicas para gravar. Portanto, faça uma copa, coloque um pouco de willie nelson e deixe de lado o mundo ao seu redor (ou monitore o dashboard do MongoDB Atlas para verificar as gravações no banco de banco de dados). Quando isso for concluído, ou você perder a paciência e decidir que vários milhares de músicas são suficientes para sua prova de conceito e interromper manualmente o processo, vamos criar nossa playlist.
Vamos nos acomodar em nossa melancolia e pedir nossa playlist do pior cenário.
1curl -X GET "http://localhost:8080/newPlaylist?playlistName=sad%20girl%20wistful%20Friday%20evening"
Bem, se tudo der certo, você deverá ver algo assim em seu console.
1{
2 "playlistName": "sad girl wistful Friday evening",
3 "songs": [
4 {
5 "title": "When A Woman Loves",
6 "artist": "R. Kelly",
7 "lyrics": "...",
8 "embedding": "...",
9 },
10 {
11 "title": "Lonely",
12 "artist": "Akon",
13 "lyrics": "...",
14 "embedding": "...",
15 },
16 {
17 "title": "Monster",
18 "artist": "Lady Gaga",
19 "lyrics": "...",
20 "embedding": "...",
21 },
22 {
23 "title": "All the Boys",
24 "artist": "Keri Hilson",
25 "lyrics": "...",
26 "embedding": "...",
27 }
28 ]
29}
Está muito longe de ser uma implementação perfeita, mas, dadas as limitações que aceitamos ao usar um modelo de linguagem geral em letras de músicas, Acon e Swift nos quatro principais resultados é uma lista de reprodução muito boa.

Conclusão

Neste tutorial, mostramos como criar um gerador de listas de reprodução personalizado usando o aprendizado detalhado4j para incorporar letras de músicas e o MongoDB Atlas Vector Search para consultar músicas semelhantes. Embora essa implementação tenha limitações, como o uso de incorporações pré-treinadas, ela abre um mundo de possibilidades para gerar playlists com base em nomes e energias funky.
Se você encontrou este tutorial útil, consulte o MongoDB Developer Center para obter mais tutoriais Java com MongoDB e aprenda como fazer coisas como geração aumentada de recuperação com MongoDB e Spring AI. Ou acesse os fóruns da comunidade MongoDB para fazer perguntas e ver o que outras pessoas estão construindo com o MongoDB.
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.
Iniciar a conversa

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Notícias e anúncios

A Pesquisa de Desenvolvedores Java do MongoDB de 2022


Apr 02, 2024 | 0 min read
Tutorial

Construtores de expressões de agregação Java no MongoDB


Apr 02, 2024 | 11 min read
Tutorial

Como Migrar PostgreSQL para MongoDB com Confluent Kafka


Aug 30, 2024 | 10 min read
Início rápido

Pipeline de agregação Java


Oct 01, 2024 | 8 min read
Sumário