Självstudie: Utforska Azure OpenAI med embeddingar i Microsoft Foundry-modeller och dokumentsökning

Den här självstudien beskriver hur du använder API:et för Inbäddning av Azure OpenAI för att utföra dokumentsökning där du frågar en kunskapsbas för att hitta det mest relevanta dokumentet.

I den här tutorialen lär du dig följande:

  • Ladda ned en exempeldatauppsättning och förbered den för analys.
  • Skapa miljövariabler för dina resursers slutpunkt och API-nyckel.
  • Använd någon av följande modeller: text-embedding-ada-002 (version 2), text-embedding-3-large, text-embedding-3-small models.
  • Använd cosinélikhet för att rangordna sökresultat.

Förutsättningar

Ställ in

Python-bibliotek

Om du inte redan har gjort det måste du installera följande bibliotek:

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

Ladda ned BillSum-datauppsättningen

BillSum är en datamängd av USA kongress- och Kaliforniens delstatsräkningar. I illustrationssyfte tittar vi bara på de amerikanska räkningarna. Corpus består av räkningar från kongressens 103:e-115:e (1993-2018). Uppgifterna delades upp i 18 949 tågräkningar och 3 269 testräkningar. BillSum corpus fokuserar på mellanlängdslagstiftning från 5 000 till 20 000 tecken. Mer information om projektet och den ursprungliga akademiska uppsatsen där den här datamängden härleds från finns på BillSum-projektets GitHub-lagringsplats

I den här självstudien bill_sum_data.csv används filen som kan laddas ned från våra GitHub-exempeldata.

Du kan också ladda ned exempeldata genom att köra följande kommando på den lokala datorn:

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

Anmärkning

Microsoft Entra ID-baserad autentisering stöds för närvarande inte för inbäddningar med v1-API:et.

Hämta nyckel och slutpunkt

Om du vill göra ett anrop mot Azure OpenAI behöver du en slutpunkt och en nyckel.

Variabelnamn Värde
ENDPOINT Tjänstslutpunkten finns i avsnittet Nycklar och slutpunkter när du undersöker resursen från Azure Portal. Du kan också hitta slutpunkten via sidan Distributioner i Microsoft Foundry-portalen. En exempelslutpunkt är: https://docs-test-001.openai.azure.com/.
API-KEY Det här värdet finns i avsnittet Nycklar och slutpunkt när du undersöker resursen från Azure-portalen. Du kan använda antingen KEY1 eller KEY2.

Gå till resursen i Azure Portal. Avsnittet Nycklar och slutpunkter finns i avsnittet Resurshantering . Kopiera slutpunkten och åtkomstnyckeln eftersom du behöver båda för att autentisera dina API-anrop. Du kan använda antingen KEY1 eller KEY2. Om du alltid har två nycklar kan du rotera och återskapa nycklar på ett säkert sätt utan att orsaka avbrott i tjänsten.

Skärmbild av översiktsgränssnittet för en Azure OpenAI-resurs i Azure Portal med slutpunkten och åtkomstnycklarnas plats inringad i rött.

Miljövariabler

Skapa och tilldela beständiga miljövariabler för din API-nyckel.

Viktigt!

Använd API-nycklar med försiktighet. Inkludera inte API-nyckeln direkt i koden och publicera den aldrig offentligt. Om du använder en API-nyckel lagrar du den på ett säkert sätt i Azure Key Vault. Mer information om hur du använder API-nycklar på ett säkert sätt i dina appar finns i API-nycklar med Azure Key Vault.

Mer information om säkerhet för AI-tjänster finns i Autentisera begäranden till Azure AI-tjänster.

setx AZURE_OPENAI_API_KEY "REPLACE_WITH_YOUR_KEY_VALUE_HERE" 

När du har angett miljövariablerna kan du behöva stänga och öppna Jupyter Notebooks igen eller den IDE du använder för att miljövariablerna ska vara tillgängliga. Vi rekommenderar starkt att du använder Jupyter Notebooks, men om du av någon anledning inte kan behöva ändra någon kod som returnerar en Pandas-dataram med hjälp print(dataframe_name) av i stället för att bara anropa dataframe_name direkt som ofta görs i slutet av ett kodblock.

Kör följande kod i din föredragna Python IDE:

Importera bibliotek

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

Nu måste vi läsa csv-filen och skapa en Pandas DataFrame. När den första DataFrame har skapats kan vi visa innehållet i tabellen genom att köra 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

Resultat:

Skärmbild av de första DataFrame-tabellresultaten från csv-filen.

Den inledande tabellen har fler kolumner än vi behöver, vi skapar en ny mindre DataFrame med namnet df_bills som endast innehåller kolumnerna för text, summaryoch title.

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

Resultat:

Skärmbild av de mindre DataFrame-tabellresultaten med endast text-, sammanfattnings- och rubrikkolumner som visas.

Nu ska vi utföra viss rensning av lätta data genom att ta bort redundant tomt utrymme och rensa skiljetecken för att förbereda data för tokenisering.

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

Nu måste vi ta bort alla fakturor som är för långa för tokengränsen (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

Anmärkning

I det här fallet ligger alla fakturor under inbäddningsmodellens indatatokengräns, men du kan använda metoden ovan för att ta bort poster som annars skulle leda till att inbäddningen misslyckas. När du ställs inför innehåll som överskrider inbäddningsgränsen kan du även segmentera innehållet i mindre delar och sedan bädda in segmenten en i taget.

Vi ska än en gång undersöka df_bills.

df_bills

Resultat:

Skärmbild av DataFrame med en ny kolumn med namnet n_tokens.

För att förstå n_tokens kolumnen lite mer och hur texten i slutändan tokeniseras kan det vara bra att köra följande kod:

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

För våra dokument trunkerar vi avsiktligt utdata, men om du kör det här kommandot i din miljö returneras den fullständiga texten från indexet noll tokeniserat till segment. Du kan se att ett helt ord i vissa fall representeras med en enda token medan delar av ord i andra delar delas upp i flera token.

[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',

Om du sedan kontrollerar längden på variabeln decode ser du att den matchar det första talet i kolumnen n_tokens.

len(decode)
1466

Nu när vi förstår mer om hur tokenisering fungerar kan vi gå vidare till inbäddning. Det är viktigt att notera att vi inte har tokeniserat dokumenten ännu. Kolumnen n_tokens är helt enkelt ett sätt att se till att ingen av de data som vi skickar till modellen för tokenisering och inbäddning överskrider gränsen för indatatoken på 8 192. När vi skickar dokumenten till inbäddningsmodellen delas dokumenten upp i token som liknar (men inte nödvändigtvis identiska) med exemplen ovan och konverterar sedan token till en serie flyttalsnummer som kan nås via vektorsökning. Dessa inbäddningar kan lagras lokalt eller i en Azure Database för att stödja Vector Search. Därför har varje faktura sin egen motsvarande inbäddningsvektor i den nya ada_v2 kolumnen till höger om DataFrame.

I exemplet nedan anropar vi inbäddningsmodellen en gång per varje objekt som vi vill bädda in. När du arbetar med stora inbäddningsprojekt kan du också skicka modellen en matris med indata för att bädda in i stället för en indata i taget. När du skickar modellen är en matris med indata det maximala antalet indataobjekt per anrop till inbäddningsslutpunkten 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

Resultat:

Skärmbild av de formaterade resultaten från kommandot df_bills.

När vi kör sökkodsblocket nedan bäddar vi in sökfrågan "Kan jag få information om skatteintäkter från kabelföretag?" med samma modell för textinbäddning-ada-002 (version 2). Härnäst hittar vi den närmaste fakturainbäddningen till den nyligen inbäddade texten från vår fråga rangordnad efter cosinna likhet.

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)

Utdata:

Skärmbild av formaterade resultat av res när sökfrågan har körts.

Slutligen visar vi det översta resultatet från dokumentsökning baserat på användarfråga mot hela kunskapsbas. Detta returnerar det högsta resultatet av "Skattebetalarnas rätt att se lagen från 1993.". Det här dokumentet har en samexisteringspoäng på 0,76 mellan frågan och dokumentet:

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."

Med den här metoden kan du använda inbäddningar som en sökmekanism i dokument i en kunskapsbas. Användaren kan sedan ta det översta sökresultatet och använda det för sin underordnade uppgift, vilket föranledde deras första fråga.

Rensa resurser

Om du har skapat en Azure OpenAI-resurs enbart för att slutföra den här självstudien och vill rensa och ta bort en Azure OpenAI-resurs måste du ta bort dina distribuerade modeller och sedan ta bort resursen eller den associerade resursgruppen om den är dedikerad till testresursen. Att ta bort resursgruppen innebär också att alla andra resurser som är kopplade till den tas bort.

Nästa steg

Läs mer om Azure OpenAI:s modeller: