Tutorial: realizar a pesquisa de similaridade de vetor em embeddings do OpenAI do Azure usando o Cache do Azure para Redis

Neste tutorial, você percorrerá um caso de uso de pesquisa de similaridade de vetor básico. Você usará as inserções geradas pelo Serviço OpenAI do Azure e os recursos internos de pesquisa de vetor da camada Enterprise de Cache do Azure para Redis para consultar um conjunto de dados de filmes para encontrar a correspondência mais relevante.

O tutorial usa o Conjunto de dados Gráficos de Filmes da Wikipédia que apresenta descrições de plotagem de mais de 35.000 filmes da Wikipédia abrangendo os anos de 1901 a 2017. O conjunto de dados inclui um resumo de enredo de cada filme, além de metadados, como o ano em que o filme foi lançado, os diretores, elenco principal e gênero. Você seguirá as etapas do tutorial para gerar inserções com base no resumo do gráfico e usar os outros metadados para executar consultas híbridas.

Neste tutorial, você aprenderá a:

  • Criar uma instância de Cache do Azure para Redis configurada para pesquisa de vetor
  • Instalar o OpenAI do Azure e outras bibliotecas obrigatórias do Python.
  • Baixar o conjunto de dados de filmes e prepará-lo para análise.
  • Use o modelo text-embedding-ada-002 (Versão 2) para gerar inserções.
  • Criar um índice de vetor no Cache do Azure para Redis
  • Use a similaridade cosseno para classificar os resultados da pesquisa.
  • Usar a funcionalidade de consulta híbrida pelo RediSearch para pré-filtrar os dados e deixar a pesquisa de vetor ainda mais eficiente.

Importante

Este tutorial o orientará na criação de um Jupyter Notebook. Você pode seguir este tutorial com um arquivo de código Python (.py) e obter resultados semelhantes, mas precisará adicionar todos os blocos de código neste tutorial ao arquivo .py e executar uma vez para ver os resultados. Em outras palavras, o Jupyter Notebooks fornece resultados intermediários à medida que você executa células, mas esse não é o comportamento que você deve esperar ao trabalhar em um arquivo de código Python.

Importante

Se você quiser acompanhar em um notebook do Jupyter concluído, baixe o arquivo do notebook do Jupyter chamado tutorial.ipynb e salve-o na nova pasta redis-vector.

Pré-requisitos

Criar uma instância de Cache do Azure para Redis

  1. Siga o guia Início Rápido: criar um cache Redis Enterprise. Na página Avançado, verifique se você adicionou o módulo RediSearch e escolheu a Política de Cluster Empresarial. Todas as outras configurações podem corresponder ao padrão descrito no início rápido.

    O cache leva alguns minutos para ser criado. Você pode passar para a próxima etapa enquanto isso.

Captura de tela que mostra a guia Informações básicas da camada Enterprise preenchida.

Configurar seu ambiente de desenvolvimento

  1. Crie uma pasta no computador local chamada redis-vector no local em que você normalmente salva seus projetos.

  2. Crie um novo arquivo Python (tutorial.py) ou notebook do Jupyter (tutorial.ipynb) na pasta.

  3. Instale os pacotes necessários do Python:

    pip install "openai==1.6.1" num2words matplotlib plotly scipy scikit-learn pandas tiktoken redis langchain
    

Carregar o conjunto de dados

  1. Em um navegador da Web, acesse https://www.kaggle.com/datasets/jrobischon/wikipedia-movie-plots.

  2. Entre ou registre-se com o Kaggle. O registro é necessário para baixar o arquivo.

  3. Selecione o link Baixar no Kaggle para baixar o arquivo archive.zip.

  4. Extraia o arquivo archive.zip e mova o wiki_movie_plots_deduped.csv para a pasta redis-vector.

Importar as bibliotecas e configurar as informações de conexão

Para efetuar uma chamada com êxito no Serviço OpenAI do Azure, um ponto de extremidade e uma chave serão necessários. Você também precisa de um ponto de extremidade e uma chave para se conectar ao Cache do Azure para Redis.

  1. Acesse o recurso do Azure OpenAI no portal do Azure.

  2. O Ponto de extremidade e as Chaves podem ser encontrados na seção Gerenciamento de Recursos. Copie o ponto de extremidade e as chave de acesso, pois você precisará de ambos para autenticar suas chamadas à API. Um ponto de extremidade de exemplo é: https://docs-test-001.openai.azure.com. Você pode usar KEY1 ou KEY2.

  3. Acesse a página Visão geral do recurso Cache do Azure para Redis no portal do Azure. Consultar seu ponto de extremidade.

  4. Clique em Chaves de acesso na seção Configurações. Copie sua chave de acesso. Você pode usar Primary ou Secondary.

  5. Adicione o seguinte código a uma nova célula de código:

    # Code cell 2
    
    import re
    from num2words import num2words
    import os
    import pandas as pd
    import tiktoken
    from typing import List
    from langchain.embeddings import AzureOpenAIEmbeddings
    from langchain.vectorstores.redis import Redis as RedisVectorStore
    from langchain.document_loaders import DataFrameLoader
    
    API_KEY = "<your-azure-openai-key>"
    RESOURCE_ENDPOINT = "<your-azure-openai-endpoint>"
    DEPLOYMENT_NAME = "<name-of-your-model-deployment>"
    MODEL_NAME = "text-embedding-ada-002"
    REDIS_ENDPOINT = "<your-azure-redis-endpoint>"
    REDIS_PASSWORD = "<your-azure-redis-password>"
    
  6. Atualize o valor de API_KEY e RESOURCE_ENDPOINT com os valores de chave e ponto de extremidade da implantação do Azure OpenAI. DEPLOYMENT_NAME deve ser definido como o nome da sua implantação usando o text-embedding-ada-002 (Version 2) modelo de inserções e MODEL_NAME deve ser o modelo de inserções específico usado.

  7. Atualize REDIS_ENDPOINT e REDIS_PASSWORD com o ponto de extremidade e o valor da chave da sua instância de Cache do Azure para Redis.

    Importante

    É altamente recomendável usar variáveis ambientais ou um gerente de segredos como o Azure Key Vault para passar as informações de chave de API, ponto de extremidade e nome de implantação. Essas variáveis são definidas em texto sem formatação aqui para simplificar.

  8. Execute a célula de código 2.

Importar o conjunto de dados no Pandas e processar os dados

Em seguida, você lerá o arquivo csv em um DataFrame do Pandas.

  1. Adicione o seguinte código a uma nova célula de código:

    # Code cell 3
    
    df=pd.read_csv(os.path.join(os.getcwd(),'wiki_movie_plots_deduped.csv'))
    df
    
  2. Execute a célula de código 3. Você deve ver o seguinte resultado:

    Captura de tela dos resultados da execução da célula de código 3 exibindo oito colunas e uma amostragem de 10 linhas de dados.

  3. Em seguida, processe os dados adicionando um índice id, removendo espaços dos títulos das colunas e filtrando os filmes para incluir apenas filmes feitos depois de 1970 e de países de língua inglesa. Essa etapa de filtragem reduz o número de filmes no conjunto de dados, o que reduz o custo e o tempo necessários para gerar inserções. Você está livre para alterar ou remover os parâmetros de filtro com base nas suas preferências.

    Para filtrar os dados, adicione o seguinte código a uma nova célula de código:

    # Code cell 4
    
    df.insert(0, 'id', range(0, len(df)))
    df['year'] = df['Release Year'].astype(int)
    df['origin'] = df['Origin/Ethnicity'].astype(str)
    del df['Release Year']
    del df['Origin/Ethnicity']
    df = df[df.year > 1970] # only movies made after 1970
    df = df[df.origin.isin(['American','British','Canadian'])] # only movies from English-speaking cinema
    df
    
  4. Execute a célula de código 4. Você verá os seguintes resultados:

    Captura de tela dos resultados da execução da célula de código 4 exibindo nove colunas e uma amostragem de 10 linhas de dados.

  5. Crie uma função para limpar os dados removendo o espaço em branco e a pontuação e, use-os no dataframe que contém o gráfico.

    Adicione o seguinte código a uma nova célula de código:

    # Code cell 5
    
    pd.options.mode.chained_assignment = None
    
    # s is input text
    def normalize_text(s, sep_token = " \n "):
        s = re.sub(r'\s+',  ' ', s).strip()
        s = re.sub(r". ,","",s)
        # remove all instances of multiple spaces
        s = s.replace("..",".")
        s = s.replace(". .",".")
        s = s.replace("\n", "")
        s = s.strip()
    
        return s
    
    df['Plot']= df['Plot'].apply(lambda x : normalize_text(x))
    
  6. Por fim, remova todas as entradas que contenham descrições de plotagem que sejam muito longas para o modelo de inserções. (Em outras palavras, eles exigem mais tokens do que o limite de token 8192.) e calcule os números de tokens necessários para gerar as inserções. Isso também afeta os preços da geração de inserção.

    Adicione o seguinte código a uma nova célula de código:

    # Code cell 6
    
    tokenizer = tiktoken.get_encoding("cl100k_base")
    df['n_tokens'] = df["Plot"].apply(lambda x: len(tokenizer.encode(x)))
    df = df[df.n_tokens<8192]
    print('Number of movies: ' + str(len(df)))
    print('Number of tokens required:' + str(df['n_tokens'].sum()))
    
  7. Execute a célula de código 6. Você deverá ver este resultado:

    Number of movies: 11125
    Number of tokens required:7044844
    

    Importante

    Consulte os preços do Serviço OpenAI do Azure para calcular o custo de geração de inserções com base no número de tokens necessários.

Carregue o DataFrame no LangChain

Carregue o DataFrame no LangChain usando a classe DataFrameLoader. Depois que os dados estiverem nos documentos LangChain, é muito mais fácil usar as bibliotecas LangChain para gerar inserções e realizar pesquisas de similaridade. Defina Plotar como o page_content_column para que as inserções sejam geradas nesta coluna.

  1. Adicione o seguinte código a uma nova célula de código:

    # Code cell 7
    
    loader = DataFrameLoader(df, page_content_column="Plot" )
    movie_list = loader.load()
    

Gere inserções e carregue-as no Redis

Agora que os dados foram filtrados e carregados no LangChain, você criará inserções para poder consultar no gráfico para cada filme. O código a seguir configura o OpenAI do Azure, gera inserções e carrega os vetores de inserção em Cache do Azure para Redis.

  1. Adicione o seguinte código a uma nova célula de código:

    # Code cell 8
    
    embedding = AzureOpenAIEmbeddings(
        deployment=DEPLOYMENT_NAME,
        model=MODEL_NAME,
        azure_endpoint=RESOURCE_ENDPOINT,
        openai_api_type="azure",
        openai_api_key=API_KEY,
        openai_api_version="2023-05-15",
        show_progress_bar=True,
        chunk_size=16 # current limit with Azure OpenAI service. This will likely increase in the future.
        )
    
    # name of the Redis search index to create
    index_name = "movieindex"
    
    # create a connection string for the Redis Vector Store. Uses Redis-py format: https://redis-py.readthedocs.io/en/stable/connections.html#redis.Redis.from_url
    # This example assumes TLS is enabled. If not, use "redis://" instead of "rediss://
    redis_url = "rediss://:" + REDIS_PASSWORD + "@"+ REDIS_ENDPOINT
    
    # create and load redis with documents
    vectorstore = RedisVectorStore.from_documents(
        documents=movie_list,
        embedding=embedding,
        index_name=index_name,
        redis_url=redis_url
    )
    
    # save index schema so you can reload in the future without re-generating embeddings
    vectorstore.write_schema("redis_schema.yaml")
    
  2. Execute a célula de código 8. Isso pode demorar mais de 30 minutos para ser concluído. Um arquivo redis_schema.yaml também é gerado. Este arquivo é útil se você quiser se conectar ao seu índice na instância do Cache do Azure para Redis sem gerar novamente incorporações.

Importante

A velocidade com que as inserções são geradas depende da cota disponível para o modelo do Azure OpenAI. Com uma cota de 240 mil tokens por minuto, levará cerca de 30 minutos para processar os 7 milhões de tokens no conjunto de dados.

Execute as consultas de pesquisa de vetor

Agora que o conjunto de dados, a API do serviço OpenAI do Azure e a instância do Redis estão configurados, você pode pesquisar usando os vetores. Neste exemplo, os 10 principais resultados de uma determinada consulta são retornados.

  1. Adicione o código a seguir ao arquivo do Python:

    # Code cell 9
    
    query = "Spaceships, aliens, and heroes saving America"
    results = vectorstore.similarity_search_with_score(query, k=10)
    
    for i, j in enumerate(results):
        movie_title = str(results[i][0].metadata['Title'])
        similarity_score = str(round((1 - results[i][1]),4))
        print(movie_title + ' (Score: ' + similarity_score + ')')
    
  2. Execute a célula de código 9. Você deve ver o seguinte resultado:

    Independence Day (Score: 0.8348)
    The Flying Machine (Score: 0.8332)
    Remote Control (Score: 0.8301)
    Bravestarr: The Legend (Score: 0.83)
    Xenogenesis (Score: 0.8291)
    Invaders from Mars (Score: 0.8291)
    Apocalypse Earth (Score: 0.8287)
    Invasion from Inner Earth (Score: 0.8287)
    Thru the Moebius Strip (Score: 0.8283)
    Solar Crisis (Score: 0.828)
    

    A pontuação de similaridade é retornada junto com a classificação ordinal de filmes por similaridade. Observe que as consultas mais específicas têm pontuações de similaridade que diminuem mais rapidamente na lista.

Pesquisas híbridas

  1. Como o RediSearch também apresenta funcionalidade de pesquisa avançada sobre a pesquisa de vetor, é possível filtrar os resultados pelos metadados no conjunto de dados, como gênero de filme, elenco, ano de lançamento ou diretor. Nesse caso, filtre com base no gênero comedy.

    Adicione o seguinte código a uma nova célula de código:

    # Code cell 10
    
    from langchain.vectorstores.redis import RedisText
    
    query = "Spaceships, aliens, and heroes saving America"
    genre_filter = RedisText("Genre") == "comedy"
    results = vectorstore.similarity_search_with_score(query, filter=genre_filter, k=10)
    for i, j in enumerate(results):
        movie_title = str(results[i][0].metadata['Title'])
        similarity_score = str(round((1 - results[i][1]),4))
        print(movie_title + ' (Score: ' + similarity_score + ')')
    
  2. Execute a célula de código 10. Você deve ver o seguinte resultado:

    Remote Control (Score: 0.8301)
    Meet Dave (Score: 0.8236)
    Elf-Man (Score: 0.8208)
    Fifty/Fifty (Score: 0.8167)
    Mars Attacks! (Score: 0.8165)
    Strange Invaders (Score: 0.8143)
    Amanda and the Alien (Score: 0.8136)
    Suburban Commando (Score: 0.8129)
    Coneheads (Score: 0.8129)
    Morons from Outer Space (Score: 0.8121)
    

Com Cache do Azure para Redis e o Serviço OpenAI do Azure, você pode usar inserções e pesquisa de vetor para adicionar recursos de pesquisa avançados ao seu aplicativo.

Limpar os recursos

Se quiser continuar a usar os recursos que você criou neste artigo, conserve o grupo de recursos.

Caso contrário, se não pretende mais usar os recursos, você poderá excluir o grupo de recursos criado no Azure para evitar a cobrança.

Importante

A exclusão de um grupo de recursos é irreversível. Ao excluir o grupo de recursos, todos os recursos nele são excluídos permanentemente. Não exclua acidentalmente o grupo de recursos ou os recursos incorretos. Se você criou os recursos dentro de um grupo de recursos existente que contém recursos que você quer manter, você pode excluir cada recurso individualmente em vez de excluir o grupo de recursos.

Para excluir um grupo de recursos

  1. Entre no portal do Azure e selecione Grupos de recursos.

  2. Selecione o grupo de recursos que você quer excluir.

    Se existirem muitos grupos de recursos, use a caixa Filtrar para qualquer campo... e digite o nome do seu grupo de recursos que você criou para este artigo. Selecione o grupo de recursos na lista de resultados.

    Captura de tela mostrando uma lista dos grupos de recursos a serem excluídos no painel de trabalho.

  3. Selecione Excluir grupo de recursos.

  4. Você receberá uma solicitação para confirmar a exclusão do grupo de recursos. Digite o nome do grupo de recursos para confirmar e selecione Excluir.

    Captura de tela mostrando um formulário que requer que a exclusão do nome do recurso seja confirmada.

Após alguns instantes, o grupo de recursos, e todos os recursos nele são excluídos.