Tutorial: Explorar Azure OpenAI en Modelos Foundry de Microsoft, incorporaciones y búsqueda de documentos

En este tutorial se muestra cómo usar la API Azure OpenAI embeddings para realizar document search. Puede consultar una base de conocimiento para encontrar el documento más relevante.

En este tutorial, aprenderá a:

  • Descargue un conjunto de datos de ejemplo y prepárelo para su análisis.
  • Cree variables de entorno para su punto de conexión de recursos y su clave de API.
  • Use uno de los modelos siguientes: text-embedding-ada-002 (versión 2), text-embeding-3-large o text-embedding-3-small.
  • Utiliza la similitud de coseno para clasificar los resultados de la búsqueda.

Requisitos previos

Configurar

bibliotecas de Python

Si aún no lo ha hecho, debe instalar las siguientes bibliotecas:

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

Descarga del conjunto de datos de BillSum

BillSum es un conjunto de datos de proyectos de ley del Congreso de los Estados Unidos y del estado de California. Con fines ilustrativos, solo veremos las facturas estadounidenses. El corpus consta de proyectos de ley de las sesiones 103.ª a 115.ª (1993-2018) del Congreso. Los datos se dividieron en 18.949 facturas de tren y 3.269 facturas de prueba. El corpus de BillSum se centra en la legislación de longitud media de 5000 a 20 000 caracteres de longitud. Puede encontrar más información sobre el proyecto y el documento académico original del que se deriva este conjunto de datos en el repositorio GitHub del proyecto BillSum

En este tutorial se utiliza el archivo bill_sum_data.csv, que se puede descargar de nuestros datos de ejemplo de GitHub.

También puede descargar los datos de ejemplo ejecutando el siguiente comando en el equipo 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

actualmente no se admite la autenticación basada en Microsoft Entra ID para incrustaciones con la API v1.

Recuperar clave y punto de conexión

Para realizar correctamente una llamada en Azure OpenAI, necesita un endpoint y un key.

Nombre de variable Valor
ENDPOINT El punto de conexión de servicio está disponible en la sección Claves y Puntos de conexión al examinar su recurso desde el portal de Azure. Como alternativa, puede encontrar el punto de conexión a través de la página Implementaciones en el portal de Microsoft Foundry. Un punto de conexión de ejemplo es: https://docs-test-001.openai.azure.com/.
API-KEY Este valor se puede encontrar en la sección Claves y Puntos de Conexión al examinar tu recurso en el portal de Azure. Puede usar bien KEY1 o KEY2.

Vaya a su recurso en el portal de Azure. La sección Claves y punto de conexión se puede encontrar en la sección Administración de recursos . Copie el punto de conexión y la clave de acceso, ya que necesitará ambos para autenticar las llamadas API. Puede usar bien KEY1 o KEY2. Tener siempre dos claves le permite rotar y regenerar las claves de forma segura sin provocar una interrupción del servicio.

Captura de pantalla de la interfaz de usuario de información general de un recurso OpenAI de Azure en el portal de Azure, con el punto de conexión y la ubicación de las claves de acceso marcados en rojo.

Variables de entorno

Cree y asigne variables de entorno persistentes para la clave de API.

Importante

Use las claves de API con precaución. No incluya la clave de API directamente en el código y nunca la publique públicamente. Si usa una clave de API, almacénela de forma segura en Azure Key Vault. Para obtener más información sobre el uso de claves de API de forma segura en las aplicaciones, consulte CLAVESAPI con Azure Key Vault.

Para obtener más información sobre la seguridad de los servicios de inteligencia artificial, consulte Authenticate requests to Servicios de Azure AI (Solicitudes de autenticación a Servicios de Azure AI.

setx AZURE_OPENAI_API_KEY "REPLACE_WITH_YOUR_KEY_VALUE_HERE" 

Después de establecer las variables de entorno, es posible que tenga que cerrar y volver a abrir cuadernos de Jupyter Notebook o cualquier IDE que use para que las variables de entorno sean accesibles. Aunque recomendamos encarecidamente usar Jupyter Notebooks, si por algún motivo no puedes utilizarlos, deberás modificar cualquier código que devuelva un dataframe de pandas usando print(dataframe_name) en lugar de llamar directamente a dataframe_name, como se hace a menudo al final de un bloque de código.

Ejecute el código siguiente en el IDE de Python preferido:

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

Ahora es necesario leer el archivo CSV y crear un dataframe de Pandas. Después de crear el dataframe inicial, podemos ver el contenido de la tabla ejecutando 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

Salida:

Captura de pantalla de los resultados iniciales de la tabla DataFrame del archivo CSV.

La tabla inicial tiene más columnas de las que necesitamos crearemos un nuevo DataFrame más pequeño denominado df_bills que contendrá solo las columnas de text, summaryy title.

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

Salida:

Captura de pantalla de los resultados más pequeños de la tabla DataFrame con solo las columnas de texto, resumen y título mostradas.

A continuación, se realizará una limpieza de datos ligeros mediante la eliminación de espacios en blanco redundantes y la limpieza de los signos de puntuación para preparar los datos para la tokenización.

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))

Ahora es necesario quitar las facturas que sean demasiado largas para el límite 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

En este caso, todas las facturas están por debajo del límite de tokens de entrada del modelo de inserción, pero puede usar la técnica anterior para quitar entradas que, de lo contrario, provocarían un error en la inserción. Cuando te enfrentes a contenido que supere el límite de inserción, puedes dividir el contenido en partes más pequeñas y luego insertar cada parte por separado.

Volveremos a examinar df_bills.

df_bills

Salida:

Captura de pantalla del DataFrame con una nueva columna denominada n_tokens.

Para comprender el n_tokens columna un poco más, así como cómo se tokeniza el texto en última instancia, puede resultar útil ejecutar el código siguiente:

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

Para nuestros documentos, truncamos intencionadamente la salida, pero la ejecución de este comando en el entorno devolverá el texto completo del índice cero tokenizado en fragmentos. Puede ver que, en algunos casos, se representa una palabra completa con un solo token, mientras que en otras partes de palabras se dividen entre varios 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',

Si, a continuación, comprueba la longitud de la decode variable, encontrará que coincide con el primer número de la columna n_tokens.

len(decode)
1466

Ahora que entendemos más sobre cómo funciona la tokenización, podemos pasar a la inserción. Es importante tener en cuenta que aún no hemos tokenizado los documentos. La n_tokens columna es simplemente una manera de asegurarse de que ninguno de los datos que pasamos al modelo para la tokenización e incrustación supera el límite de token de entrada de 8192. Cuando pasamos los documentos al modelo de incrustaciones, dividirá los documentos en tokens similares (aunque no necesariamente idénticos) a los ejemplos anteriores y, a continuación, convertirá los tokens en una serie de números de punto flotante que serán accesibles a través de la búsqueda vectorial. Estas inserciones se pueden almacenar localmente o en una base de datos Azure para admitir la búsqueda de vectores. Como resultado, cada factura tendrá su propio vector de inserción correspondiente en la nueva ada_v2 columna del lado derecho del DataFrame.

En el ejemplo siguiente, llamamos al modelo de inserción una vez por cada elemento que queremos insertar. Al trabajar con proyectos de inserción de gran tamaño, también puede pasar al modelo una matriz de entradas para insertar en lugar de una entrada a la vez. Al pasarle al modelo un arreglo de entradas, el número máximo de elementos de entrada por llamada al punto de conexión de incrustación es 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

Salida:

Captura de pantalla de los resultados con formato del comando df_bills.

A medida que ejecutamos el bloque de código de búsqueda siguiente, insertaremos la consulta de búsqueda "¿Puedo obtener información sobre los ingresos fiscales de la compañía de cable?" con el mismo modelo text-embeding-ada-002 (versión 2). A continuación, encontraremos la representación vectorial de la factura más cercana al texto recién representado de nuestra consulta, clasificada por similitud de coseno.

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)

Salida:

Captura de pantalla de los resultados con formato de res una vez que se ha ejecutado la consulta de búsqueda.

Por último, mostraremos el resultado superior de la búsqueda de documentos en función de la consulta de usuario en toda la base de conocimiento. Esto devuelve el resultado principal de la "Ley del Derecho del Contribuyente a Ver de 1993". Este documento tiene una puntuación de similitud por coseno de 0,76 entre la consulta y el 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."

Mediante este enfoque, puede usar incrustaciones como un mecanismo de búsqueda entre documentos de una base de conocimiento. Después, el usuario puede tomar el primer resultado de la búsqueda y usarlo para su tarea subsiguiente, que motivó su consulta inicial.

Solución de problemas

  • 401/403: Verifique que AZURE_OPENAI_API_KEY está configurado y coincide con la clave de recurso.
  • 404: Compruebe que AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT coincide con el nombre de la implementación.
  • URL no válida: Compruebe que AZURE_OPENAI_ENDPOINT sea el extremo del recurso, como https://<resource-name>.openai.azure.com.

Limpieza de recursos

Si ha creado un recurso de Azure OpenAI únicamente para completar este tutorial y desea limpiar y quitar un recurso de OpenAI de Azure, elimine los modelos implementados. A continuación, elimine el recurso o el grupo de recursos asociado si está dedicado al recurso de prueba. Al eliminar el grupo de recursos también se eliminan los demás recursos asociados.

Pasos siguientes

Aprenda más sobre los modelos de Azure OpenAI:

modelos Azure OpenAI