Tutorial: Explorar Azure OpenAI em Microsoft inserções de modelos de fundimento e pesquisa de documentos

Este tutorial orientará você usando a API Azure OpenAI embeddings API para executar document search em que você consultará uma base de dados de conhecimento para encontrar o documento mais relevante.

Neste tutorial, você aprenderá a:

  • Baixe um conjunto de dados de exemplo e prepare-o para análise.
  • Crie as variáveis de ambiente para o endpoint de recursos e a chave de API.
  • Use um dos seguintes modelos: text-embedding-ada-002 (versão 2), text-embedding-3-large, text-embedding-3-small models.
  • Utilize a similaridade de cosseno para classificar os resultados da pesquisa.

Pré-requisitos

Configurar

bibliotecas de Python

Caso ainda não tenha feito isso, você precisará instalar as seguintes bibliotecas:

pip install openai num2words matplotlib plotly scipy scikit-learn pandas tiktoken

Baixar o conjunto de dados BillSum

BillSum é um conjunto de dados de projetos de lei do Congresso dos Estados Unidos e do estado da Califórnia. Para fins ilustrativos, vamos olhar apenas para as contas dos EUA. O corpus consiste em projetos de lei das sessões 103-115 (1993-2018) do Congresso. Os dados foram divididos em 18.949 contas de trem e 3.269 contas de teste. O corpus BillSum concentra-se na legislação com comprimento médio de 5.000 a 20.000 caracteres. Mais informações sobre o projeto e o artigo acadêmico original do qual esse conjunto de dados é derivado podem ser encontradas no repositório GitHub do projeto BillSum

Este tutorial usa o arquivo bill_sum_data.csv que pode ser baixado de nossos dados de exemplo GitHub.

Você também pode baixar os dados de exemplo executando o seguinte comando em seu computador local:

curl "https://raw.githubusercontent.com/Azure-Samples/Azure-OpenAI-Docs-Samples/main/Samples/Tutorials/Embeddings/data/bill_sum_data.csv" --output bill_sum_data.csv

Nota

no momento, não há suporte para autenticação baseada em Microsoft Entra ID para inserções com a API v1.

Recuperar chave e ponto de extremidade

Para fazer uma chamada com êxito contra Azure OpenAI, você precisa de um endpoint e um key.

Nome da variável Valor
ENDPOINT O ponto de extremidade de serviço pode ser encontrado na seção Keys & Endpoint ao visualizar o recurso no portal do Azure. Como alternativa, você pode encontrar o endpoint por meio da página Deployments no portal Microsoft Foundry. Um exemplo de ponto de extremidade é: https://docs-test-001.openai.azure.com/.
API-KEY Esse valor pode ser encontrado na seção Keys & Endpoint ao examinar seu recurso no portal do Azure. Você pode usar um KEY1 ou KEY2.

Acesse seu recurso no portal do Azure. A seção Chaves & Ponto de Extremidade está localizada em Gerenciamento de Recursos. Copie o ponto de extremidade e a chave de acesso, pois você precisará de ambos para autenticar suas chamadas à API. Você pode usar um KEY1 ou KEY2. Sempre ter duas chaves permite girar e regenerar chaves com segurança sem causar uma interrupção de serviço.

Screenshot da interface da visão geral de um recurso Azure OpenAI no portal Azure com o ponto de extremidade e a localização das chaves de acesso circulados em vermelho.

Variáveis de ambiente

Crie e atribua variáveis de ambiente persistentes para sua chave de API.

Importante

Use chaves de API com cuidado. Não inclua a chave de API diretamente em seu código e nunca a publique publicamente. Se você usar uma chave de API, armazene-a com segurança em Azure Key Vault. Para obter mais informações sobre como usar chaves de API com segurança em seus aplicativos, consulte as chaves API com Azure Key Vault.

Para obter mais informações sobre a segurança dos serviços de IA, consulte Autenticar solicitações para Serviços de IA do Azure.

setx AZURE_OPENAI_API_KEY "REPLACE_WITH_YOUR_KEY_VALUE_HERE" 

Depois de definir as variáveis de ambiente, talvez seja necessário fechar e reabrir notebooks Jupyter ou qualquer IDE que você esteja usando para que as variáveis de ambiente sejam acessíveis. Embora seja altamente recomendável usar o Jupyter Notebooks, se por algum motivo você não puder, precisará modificar qualquer código que esteja retornando um dataframe pandas usando print(dataframe_name) em vez de apenas chamar o dataframe_name diretamente, como geralmente é feito no final de um bloco de código.

Execute o seguinte código no IDE de Python preferencial:

Importar bibliotecas

import os
import re
import requests
import sys
from num2words import num2words
import os
import pandas as pd
import numpy as np
import tiktoken
from openai import OpenAI

Agora precisamos ler nosso arquivo csv e criar um pandas DataFrame. Depois que o DataFrame inicial é criado, podemos exibir o conteúdo da tabela executando df.

df=pd.read_csv(os.path.join(os.getcwd(),'bill_sum_data.csv')) # This assumes that you have placed the bill_sum_data.csv in the same directory you are running Jupyter Notebooks
df

Saída:

Captura de tela dos resultados iniciais da tabela DataFrame do arquivo csv.

A tabela inicial tem mais colunas do que precisamos, criaremos um DataFrame menor chamado df_bills que conterá apenas as colunas para text, summarye title.

df_bills = df[['text', 'summary', 'title']]
df_bills

Saída:

Captura de tela dos resultados menores da tabela DataFrame com apenas texto, resumo e colunas de título exibidas.

Em seguida, executaremos algumas limpezas de dados leves removendo o espaço em branco redundante e limpando a pontuação para preparar os dados para a tokenização.

pd.options.mode.chained_assignment = None #https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#evaluation-order-matters

# 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_bills['text']= df_bills["text"].apply(lambda x : normalize_text(x))

Agora, precisamos remover qualquer fatura que seja muito longa para o limite de tokens (8.192 tokens).

tokenizer = tiktoken.get_encoding("cl100k_base")
df_bills['n_tokens'] = df_bills["text"].apply(lambda x: len(tokenizer.encode(x)))
df_bills = df_bills[df_bills.n_tokens<8192]
len(df_bills)
20

Nota

Nesse caso, todas as faturas estão abaixo do limite de tokens de entrada do modelo de incorporação, mas você pode usar a técnica acima para remover entradas que, de outra forma, causariam falha na incorporação. Diante do conteúdo que excede o limite de inserção, você também pode dividir o conteúdo em partes menores e, em seguida, inserir as partes uma de cada vez.

Examinaremos novamente df_bills.

df_bills

Saída:

Captura de tela do DataFrame com uma nova coluna chamada n_tokens.

Para entender o n_tokens coluna um pouco mais, bem como como o texto finalmente é tokenizado, pode ser útil executar o seguinte código:

sample_encode = tokenizer.encode(df_bills.text[0]) 
decode = tokenizer.decode_tokens_bytes(sample_encode)
decode

Para nossos documentos, estamos truncando intencionalmente a saída, mas executar esse comando em seu ambiente retornará o texto completo do índice zero tokenizado em partes. Você pode ver que, em alguns casos, uma palavra inteira é representada com um único token, enquanto em outras partes de palavras são divididas entre vários tokens.

[b'SECTION',
 b' ',
 b'1',
 b'.',
 b' SHORT',
 b' TITLE',
 b'.',
 b' This',
 b' Act',
 b' may',
 b' be',
 b' cited',
 b' as',
 b' the',
 b' ``',
 b'National',
 b' Science',
 b' Education',
 b' Tax',
 b' In',
 b'cent',
 b'ive',
 b' for',
 b' Businesses',
 b' Act',
 b' of',
 b' ',
 b'200',
 b'7',
 b"''.",
 b' SEC',
 b'.',
 b' ',
 b'2',
 b'.',
 b' C',
 b'RED',
 b'ITS',
 b' FOR',
 b' CERT',
 b'AIN',
 b' CONTRIBUT',
 b'IONS',
 b' BEN',
 b'EF',
 b'IT',
 b'ING',
 b' SC',

Se você verificar o comprimento da decode variável, ela corresponderá ao primeiro número na coluna n_tokens.

len(decode)
1466

Agora que entendemos mais sobre como a tokenização funciona, podemos passar para a inserção. É importante observar que ainda não tokenizamos os documentos. A n_tokens coluna é simplesmente uma maneira de garantir que nenhum dos dados que passamos para o modelo para tokenização e inserção exceda o limite de token de entrada de 8.192. Quando passarmos os documentos para o modelo de inserções, ele dividirá os documentos em tokens semelhantes (embora não necessariamente idênticos) aos exemplos acima e converterá os tokens em uma série de números de ponto flutuante que serão acessíveis por meio da pesquisa de vetor. Essas inserções podem ser armazenadas localmente ou em um banco de dados Azure para dar suporte à Pesquisa de Vetor. Como resultado, cada fatura terá seu próprio vetor de inserção correspondente na nova ada_v2 coluna no lado direito do DataFrame.

No exemplo abaixo, estamos chamando o modelo de inserção uma vez por cada item que desejamos inserir. Ao trabalhar com grandes projetos de inserção, você pode, como alternativa, passar ao modelo uma matriz de entradas para inserir em vez de uma entrada por vez. Quando você passa para o modelo uma matriz de entradas, o número máximo de itens de entrada por chamada para o endpoint de incorporação é 2048.

client = OpenAI(
  api_key = os.getenv("AZURE_OPENAI_API_KEY"),  
  base_url="https://YOUR-RESOURCE-NAME.openai.azure.com/openai/v1/"
)

def generate_embeddings(text, model="text-embedding-ada-002"): # model = "deployment_name"
    return client.embeddings.create(input = [text], model=model).data[0].embedding

df_bills['ada_v2'] = df_bills["text"].apply(lambda x : generate_embeddings (x, model = 'text-embedding-ada-002')) # model should be set to the deployment name you chose when you deployed the text-embedding-ada-002 (Version 2) model
df_bills

Saída:

Captura de tela dos resultados formatados do comando df_bills.

Enquanto executamos o bloco de código de pesquisa abaixo, inseriremos a consulta de pesquisa "Posso obter informações sobre a receita fiscal da empresa de cabo?" com o mesmo modelo text-embedding-ada-002 (versão 2). Em seguida, encontraremos a inserção de fatura mais próxima ao texto recém-inserido de nossa consulta classificada pela similaridade cosseno.

def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

def get_embedding(text, model="text-embedding-ada-002"): # model = "deployment_name"
    return client.embeddings.create(input = [text], model=model).data[0].embedding

def search_docs(df, user_query, top_n=4, to_print=True):
    embedding = get_embedding(
        user_query,
        model="text-embedding-ada-002" # model should be set to the deployment name you chose when you deployed the text-embedding-ada-002 (Version 2) model
    )
    df["similarities"] = df.ada_v2.apply(lambda x: cosine_similarity(x, embedding))

    res = (
        df.sort_values("similarities", ascending=False)
        .head(top_n)
    )
    if to_print:
        display(res)
    return res

res = search_docs(df_bills, "Can I get information on cable company tax revenue?", top_n=4)

Saída:

Captura de tela dos resultados formatados de res após a execução da consulta de pesquisa.

Por fim, mostraremos o resultado principal da pesquisa de documentos com base na consulta do usuário em toda a base de dados de conhecimento. Isso retorna o resultado principal da "Lei do Direito do Contribuinte de Acesso de 1993". Este documento tem um índice de similaridade cosseno de 0,76 entre a consulta e o documento.

res["summary"][9]
"Taxpayer's Right to View Act of 1993 - Amends the Communications Act of 1934 to prohibit a cable operator from assessing separate charges for any video programming of a sporting, theatrical, or other entertainment event if that event is performed at a facility constructed, renovated, or maintained with tax revenues or by an organization that receives public financial support. Authorizes the Federal Communications Commission and local franchising authorities to make determinations concerning the applicability of such prohibition. Sets forth conditions under which a facility is considered to have been constructed, maintained, or renovated with tax revenues. Considers events performed by nonprofit or public organizations that receive tax subsidies to be subject to this Act if the event is sponsored by, or includes the participation of a team that is part of, a tax exempt organization."

Usando essa abordagem, você pode usar inserções como um mecanismo de pesquisa em documentos em uma base de dados de conhecimento. Em seguida, o usuário pode obter o resultado da pesquisa superior e usá-lo para sua tarefa downstream, o que motivou sua consulta inicial.

Solucionando problemas

  • 401/403: Verifique se AZURE_OPENAI_API_KEY está definido e corresponde à sua chave de recurso.
  • 404: Verifique se o AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT corresponde ao nome da sua implantação.
  • URL inválida: verifique se AZURE_OPENAI_ENDPOINT é o ponto de extremidade do recurso, por exemplo https://<resource-name>.openai.azure.com.

Limpar recursos

Se você criou um recurso Azure OpenAI apenas para concluir este tutorial e quiser limpar e remover um recurso Azure OpenAI, precisará excluir seus modelos implantados e excluir o recurso ou o grupo de recursos associado se ele for dedicado ao recurso de teste. Excluir o grupo de recursos também exclui outros recursos associados a ele.

Próximas etapas

Saiba mais sobre os modelos do Azure OpenAI: