Tutoriel : Explorer les incorporations Azure OpenAI Service et la recherche de documents

Ce tutoriel vous guide tout au long de l’utilisation de l’API Incorporations Azure OpenAI pour effectuer une recherche de documents où vous interrogerez une base de connaissances afin de trouver le document le plus pertinent.

Dans ce tutoriel, vous allez apprendre à :

  • Installez Azure OpenAI.
  • Téléchargez un exemple de jeu de données et préparez-le pour l’analyse.
  • Créez des variables d’environnement pour le point de terminaison et la clé API de vos ressources.
  • Utiliser le modèle text-embedding-ada-002 (version 2)
  • Utilisez la similarité cosinus pour classer les résultats de la recherche.

Prérequis

  • Un abonnement Azure - En créer un gratuitement
  • Accès accordé à Azure OpenAI dans l’abonnement Azure souhaité. L’accès à ce service n’est accordé qu’à l’application. Vous pouvez demander l’accès à Azure OpenAI en complétant le formulaire à l’adresse https://aka.ms/oai/access. Ouvrez un problème sur ce dépôt pour nous contacter si vous rencontrez un problème.
  • Une ressource Azure OpenAI avec le modèle text-embedding-ada-002 (version 2) déployé. Ce modèle n’est actuellement disponible que dans certaines régions. Si vous n’avez pas de ressource, le processus est documenté dans notre guide de déploiement de ressources.
  • Python 3.8 ou version ultérieure
  • Les bibliothèques Python suivantes : openai, num2words, matplotlib, plotly, scipy, scikit-learn, pandas, tiktoken.
  • Blocs-notes Jupyter

Configurer

Bibliothèques Python

Si vous ne l’avez pas déjà fait, vous devez installer les bibliothèques suivantes :

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

Télécharger le jeu de données BillSum

BillSum est un jeu de données des projets de loi du Congrès des États-Unis et de Californie. À des fins d’illustration, nous examinerons uniquement les projets de loi américains. Le corpus se compose de projets de loi des 103ème à 115ème (1993-2018) sessions du Congrès. Les données ont été divisées en 18 949 projets de loi d’entraînement et 3 269 projets de loi de test. Le corpus BillSum se concentre sur les lois d’une longueur moyenne de 5 000 à 20 000 caractères. Pour plus d’informations sur le projet et le document universitaire d’origine d’où ce jeu de données est dérivé, consultez le dépôt GitHub du projet BillSum

Ce tutoriel utilise le fichier bill_sum_data.csv qui peut être téléchargé à partir de nos exemples de données GitHub.

Vous pouvez également télécharger les exemples de données en exécutant la commande suivante sur votre ordinateur 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

Récupérer la clé et le point de terminaison

Pour effectuer correctement un appel sur Azure OpenAI, vous avez besoin d’un point de terminaison et d’une clé.

Nom de la variable Valeur
ENDPOINT Cette valeur se trouve dans la section Clés et point de terminaison quand vous examinez votre ressource à partir du portail Azure. Vous pouvez également trouver la valeur dans l’affichage Azure OpenAI Studio>Playground>Code. Voici un exemple de point de terminaison : https://docs-test-001.openai.azure.com/.
API-KEY Cette valeur se trouve dans la section Clés et point de terminaison quand vous examinez votre ressource à partir du portail Azure. Vous pouvez utiliser soit KEY1, soit KEY2.

Accédez à votre ressource sur le portail Azure. La section Point de terminaison et les clés se trouvent dans la section Gestion des ressources. Copiez votre point de terminaison et votre clé d’accès, car vous aurez besoin de l’authentification de vos appels d’API. Vous pouvez utiliser soit KEY1, soit KEY2. Avoir toujours deux clés vous permet de faire pivoter et de régénérer en toute sécurité les clés sans provoquer d’interruption de service.

Capture d’écran de l’interface utilisateur de vue d’ensemble d’une ressource Azure OpenAI dans le Portail Azure avec l’emplacement du point de terminaison et des clés d’accès entouré en rouge.

Variables d'environnement

setx AZURE_OPENAI_API_KEY "REPLACE_WITH_YOUR_KEY_VALUE_HERE" 
setx AZURE_OPENAI_ENDPOINT "REPLACE_WITH_YOUR_ENDPOINT_HERE" 

Après avoir défini les variables d’environnement, vous allez peut-être devoir fermer et rouvrir les notebooks Jupyter ou tout IDE que vous utilisez pour rendre les variables d’environnement accessibles. Bien que nous vous recommandons vivement d’utiliser Jupyter Notebooks, si pour une raison quelconque cela est impossible, vous devez modifier tout code qui retourne une tramedonnées Pandas en utilisant print(dataframe_name) plutôt que simplement appeler directement dataframe_name comme c’est souvent le cas à la fin d’un bloc de code.

Exécutez le code suivant dans votre IDE Python préféré :

Importer des bibliothèques

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 AzureOpenAI

Nous devons maintenant lire notre fichier CSV et créer un DataFrame pandas. Une fois le DataFrame initial créé, nous pouvons afficher le contenu de la table en exécutant 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

Output:

Capture d’écran des résultats de la table DataFrame initiale provenant du fichier CSV.

La table initiale comporte plus de colonnes que nécessaire. Nous allons créer un DataFrame plus petit appelé df_bills qui contiendra uniquement les colonnes pour text, summary et title.

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

Output:

Capture d’écran des résultats de la table DataFrame plus petite avec uniquement les colonnes de texte, de récapitulatif et de titre affichées.

Nous allons ensuite effectuer un léger nettoyage des données en supprimant les espaces blancs redondants et en nettoyant la ponctuation pour préparer les données à la création de jetons.

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

Nous devons maintenant supprimer toutes les factures qui dépassent la limite de jetons (environ 8 192 jetons).

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

Remarque

Dans ce cas, toutes les factures sont en deçà de la limite de jetons d’entrée du modèle d’incorporation, mais vous pouvez utiliser la technique ci-dessus pour supprimer des entrées qui entraîneraient l’échec de l’incorporation. Lorsque vous êtes confronté à un contenu qui dépasse la limite d’incorporation, vous pouvez également le segmenter en morceaux plus petits, puis les incorporer un par un.

Nous allons à nouveau examiner df_bills.

df_bills

Output:

Capture d’écran du DataFrame avec une nouvelle colonne appelée n_tokens.

Pour comprendre un peu mieux la colonne n_tokens ainsi que la façon dont le texte est segmenté en jetons (tokenisé), il peut être utile d’exécuter le code suivant :

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

Pour nos documents, nous tronquons intentionnellement la sortie, mais l’exécution de cette commande dans votre environnement retournera le texte intégral de l’index zéro tokenisé en blocs. Vous pouvez voir que dans certains cas, un mot entier est représenté avec un seul jeton, tandis que dans d’autres, les parties de mots sont divisées en plusieurs jetons.

[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 vous vérifiez ensuite la longueur de la variable decode, vous constaterez qu’elle correspond au premier nombre figurant dans la colonne n_tokens.

len(decode)
1466

Maintenant que nous en savons plus sur le fonctionnement de la création de jetons, nous pouvons passer à l’incorporation. Il est important de remarquer que nous n’avons pas encore tokenisé les documents. La colonne n_tokens est simplement un moyen de s’assurer qu’aucune des données que nous transmettons au modèle pour la création de jetons et l’incorporation ne dépasse la limite de jeton d’entrée de 8 192. Lorsque nous passons les documents au modèle d’incorporation, il les décompose en jetons similaires (mais pas nécessairement identiques) aux exemples ci-dessus, puis convertit les jetons en une série de nombres à virgule flottante qui seront accessibles via la recherche vectorielle. Ces incorporations peuvent être stockées localement ou dans une base de données Azure pour prendre en charge la recherche vectorielle. Par conséquent, chaque facture possède son propre vecteur d’incorporation correspondant dans la nouvelle colonne ada_v2, à droite du DataFrame.

Dans l’exemple ci-dessous, nous appelons le modèle d’incorporation une fois par élément à incorporer. Lorsque vous travaillez avec des projets d’incorporation volumineux, vous pouvez également transmettre au modèle un tableau d’entrées à incorporer plutôt qu’une entrée à la fois. Lors de la transmission d’un tableau d’entrées au modèle, le nombre maximal d’éléments d’entrée par appel au point de terminaison d’incorporation est de 2 048.

client = AzureOpenAI(
  api_key = os.getenv("AZURE_OPENAI_API_KEY"),  
  api_version = "2024-02-01",
  azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
)

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

Sortie :

Capture d’écran des résultats mis en forme de la commande df_bills.

Lorsque nous exécutons le bloc de code de recherche ci-dessous, nous allons incorporer la requête de recherche « Puis-je obtenir des informations sur les recettes fiscales de la société de câble ? » avec le même modèle text-embedding-ada-002 (version 2). Nous allons ensuite trouver la facture la plus proche incorporée au texte nouvellement incorporé à partir de notre requête classée par similarité cosinus.

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)

Sortie :

Capture d’écran des résultats mis en forme de res une fois la requête de recherche exécutée.

Enfin, nous allons afficher le premier résultat de la recherche de documents en fonction de la requête utilisateur sur l’ensemble de la base de connaissances. Cela retourne le premier résultat de la « loi de 1993 sur le droit des contribuables ». Ce document a un score de similarité cosinus de 0,76 entre la requête et le document :

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

Prérequis

  • Un abonnement Azure - En créer un gratuitement

  • Accès accordé à Azure OpenAI dans l’abonnement Azure souhaité.

    L’accès à ce service n’est accordé qu’à l’application. Vous pouvez demander l’accès à Azure OpenAI en complétant le formulaire à l’adresse https://aka.ms/oai/access. Ouvrez un problème sur ce dépôt pour nous contacter si vous rencontrez un problème.

  • Une ressource Azure OpenAI avec le modèle text-embedding-ada-002 (version 2) déployé.

    Ce modèle n’est actuellement disponible que dans certaines régions. Si vous n’avez pas de ressource, le processus est documenté dans notre guide de déploiement de ressources.

  • PowerShell 7.4

Remarque

De nombreux exemples de ce tutoriel réutilisent des variables d’une étape sur l’autre. Gardez la même session de terminal ouverte tout au long du tutoriel. Si les variables définies à une étape précédente sont perdues en raison de la fermeture du terminal, vous devez recommencer au début.

Récupérer la clé et le point de terminaison

Pour effectuer correctement un appel sur Azure OpenAI, vous avez besoin d’un point de terminaison et d’une clé.

Nom de la variable Valeur
ENDPOINT Cette valeur se trouve dans la section Clés et point de terminaison quand vous examinez votre ressource à partir du portail Azure. Vous pouvez également trouver la valeur dans l’affichage Azure OpenAI Studio>Playground>Code. Voici un exemple de point de terminaison : https://docs-test-001.openai.azure.com/.
API-KEY Cette valeur se trouve dans la section Clés et point de terminaison quand vous examinez votre ressource à partir du portail Azure. Vous pouvez utiliser soit KEY1, soit KEY2.

Accédez à votre ressource sur le portail Azure. La section Point de terminaison et les clés se trouvent dans la section Gestion des ressources. Copiez votre point de terminaison et votre clé d’accès, car vous aurez besoin de l’authentification de vos appels d’API. Vous pouvez utiliser soit KEY1, soit KEY2. Avoir toujours deux clés vous permet de faire pivoter et de régénérer en toute sécurité les clés sans provoquer d’interruption de service.

Capture d’écran de l’interface utilisateur de vue d’ensemble d’une ressource Azure OpenAI dans le Portail Azure avec l’emplacement du point de terminaison et des clés d’accès entouré en rouge.

Créez et affectez des variables d’environnement persistantes pour votre clé et votre point de terminaison.

Variables d'environnement

setx AZURE_OPENAI_API_KEY "REPLACE_WITH_YOUR_KEY_VALUE_HERE" 
setx AZURE_OPENAI_ENDPOINT "REPLACE_WITH_YOUR_ENDPOINT_HERE" 

Pour ce tutoriel, nous utilisons la documentation de référence de PowerShell 7.4 comme exemple de jeu de données bien connu et sûr. Sinon, vous pouvez choisir d’explorer les exemples de jeu de données des outils Microsoft Research.

Créez un dossier où stocker votre projet. Définissez votre emplacement sur le dossier du projet. Téléchargez le jeu de données sur votre machine locale avec la commande Invoke-WebRequest, puis développez l’archive. Enfin, définissez votre emplacement sur le sous-dossier contenant les informations de référence de PowerShell version 7.4.

New-Item '<FILE-PATH-TO-YOUR-PROJECT>' -Type Directory
Set-Location '<FILE-PATH-TO-YOUR-PROJECT>'

$DocsUri = 'https://github.com/MicrosoftDocs/PowerShell-Docs/archive/refs/heads/main.zip'
Invoke-WebRequest $DocsUri -OutFile './PSDocs.zip'

Expand-Archive './PSDocs.zip'
Set-Location './PSDocs/PowerShell-Docs-main/reference/7.4/'

Comme nous utilisons une grande quantité de données dans ce tutoriel, nous choisissons un objet de table de données .NET pour avoir des performances efficaces. La table de données à les colonnes title, content, prep, uri, file, et vectors. La colonne title est la clé primaire.

À l’étape suivante, nous chargeons le contenu de chaque fichier Markdown dans la table de données. Nous utilisons également l’opérateur PowerShell -match pour capturer les lignes de texte connues title: et online version:, et les stocker dans des colonnes distinctes. Certains fichiers ne contiennent pas les lignes de métadonnées de texte, mais comme il s’agit de pages de présentation et non de documents de référence détaillés, nous les excluons de la table de données.

# make sure your location is the project subfolder

$DataTable = New-Object System.Data.DataTable

'title', 'content', 'prep', 'uri', 'file', 'vectors' | ForEach-Object {
    $DataTable.Columns.Add($_)
} | Out-Null
$DataTable.PrimaryKey = $DataTable.Columns['title']

$md = Get-ChildItem -Path . -Include *.md -Recurse

$md | ForEach-Object {
    $file       = $_.FullName
    $content    = Get-Content $file
    $title      = $content | Where-Object { $_ -match 'title: ' }
    $uri        = $content | Where-Object { $_ -match 'online version: ' }
    if ($title -and $uri) {
        $row                = $DataTable.NewRow()
        $row.title          = $title.ToString().Replace('title: ', '')
        $row.content        = $content | Out-String
        $row.prep           = '' # use later in the tutorial
        $row.uri            = $uri.ToString().Replace('online version: ', '')
        $row.file           = $file
        $row.vectors        = '' # use later in the tutorial
        $Datatable.rows.add($row)
    }
}

Consultez les données avec la commande out-gridview (non disponible dans Cloud Shell).

$Datatable | out-gridview

Sortie :

Capture d’écran des résultats DataTable initiaux.

Ensuite, effectuez un nettoyage léger des données en supprimant les caractères supplémentaires, les espaces vides et les autres notations de document, afin de préparer les données pour la tokenisation. L’exemple de fonction Invoke-DocPrep montre comment utiliser l’opérateur PowerShell -replace pour itérer dans une liste de caractères que vous voulez supprimer du contenu.

# sample demonstrates how to use `-replace` to remove characters from text content
function Invoke-DocPrep {
param(
    [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
    [string]$content
)
    # tab, line breaks, empty space
    $replace = @('\t','\r\n','\n','\r')
    # non-UTF8 characters
    $replace += @('[^\x00-\x7F]')
    # html
    $replace += @('<table>','</table>','<tr>','</tr>','<td>','</td>')
    $replace += @('<ul>','</ul>','<li>','</li>')
    $replace += @('<p>','</p>','<br>')
    # docs
    $replace += @('\*\*IMPORTANT:\*\*','\*\*NOTE:\*\*')
    $replace += @('<!','no-loc ','text=')
    $replace += @('<--','-->','---','--',':::')
    # markdown
    $replace += @('###','##','#','```')
    $replace | ForEach-Object {
        $content = $content -replace $_, ' ' -replace '  ',' '
    }
    return $content
}

Après avoir créé la fonction Invoke-DocPrep, utilisez la commande ForEach-Object afin de stocker le contenu préparé dans la colonne prep, pour toutes les lignes de la table de données. Nous utilisons une nouvelle colonne pour que la mise en forme d’origine soit disponible si nous voulons la récupérer par la suite.

$Datatable.rows | ForEach-Object { $_.prep = Invoke-DocPrep $_.content }

Consultez à nouveau la table de données pour voir le changement.

$Datatable | out-gridview

Quand nous passons les documents au modèle d’incorporation, il les encode dans des jetons, puis renvoie une série de nombres à virgule flottante à utiliser dans une recherche de similarité cosinus. Ces incorporations peuvent être stockées localement ou dans un service comme la Recherche vectorielle dans la Recherche Azure AI. Chaque document a son propre vecteur d’incorporation correspondant dans la nouvelle colonne vectors.

L’exemple suivant effectue une boucle dans chaque ligne de la table de données, récupère les vecteurs du contenu prétraité et les stocke dans la colonne vectors. Comme le service OpenAI limite les demandes fréquentes, l’exemple inclut un backoff exponentiel, comme suggéré dans la documentation.

Une fois le script terminé, chaque ligne doit avoir une liste délimitée par des virgules de 1536 vecteurs pour chaque document. Si une erreur se produit et que le code d’état est 400, le chemin de fichier, le titre et le code d’erreur sont ajoutés à une variable nommée $errorDocs pour la résolution des problèmes. L’erreur la plus courante se produit quand le nombre de jetons est supérieur à la limite d’invites pour le modèle.

# Azure OpenAI metadata variables
$openai = @{
    api_key     = $Env:AZURE_OPENAI_API_KEY 
    api_base    = $Env:AZURE_OPENAI_ENDPOINT # should look like 'https://<YOUR_RESOURCE_NAME>.openai.azure.com/'
    api_version = '2024-02-01' # may change in the future
    name        = $Env:AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT # custom name you chose for your deployment
}

$headers = [ordered]@{
    'api-key' = $openai.api_key
}

$url = "$($openai.api_base)/openai/deployments/$($openai.name)/embeddings?api-version=$($openai.api_version)"

$Datatable | ForEach-Object {
    $doc = $_

    $body = [ordered]@{
        input = $doc.prep
    } | ConvertTo-Json

    $retryCount = 0
    $maxRetries = 10
    $delay      = 1
    $docErrors = @()

    do {
        try {
            $params = @{
                Uri         = $url
                Headers     = $headers
                Body        = $body
                Method      = 'Post'
                ContentType = 'application/json'
            }
            $response = Invoke-RestMethod @params
            $Datatable.rows.find($doc.title).vectors = $response.data.embedding -join ','
            break
        } catch {
            if ($_.Exception.Response.StatusCode -eq 429) {
                $retryCount++
                [int]$retryAfter = $_.Exception.Response.Headers |
                    Where-Object key -eq 'Retry-After' |
                    Select-Object -ExpandProperty Value

                # Use delay from error header
                if ($delay -lt $retryAfter) { $delay = $retryAfter++ }
                Start-Sleep -Seconds $delay
                # Exponential back-off
                $delay = [math]::min($delay * 1.5, 300)
            } elseif ($_.Exception.Response.StatusCode -eq 400) {
                if ($docErrors.file -notcontains $doc.file) {
                    $docErrors += [ordered]@{
                        error   = $_.exception.ErrorDetails.Message | ForEach-Object error | ForEach-Object message
                        file    = $doc.file
                        title   = $doc.title
                    }
                }
            } else {
                throw
            }
        }
    } while ($retryCount -lt $maxRetries)
}
if (0 -lt $docErrors.count) {
    Write-Host "$($docErrors.count) documents encountered known errors such as too many tokens.`nReview the `$docErrors variable for details."
}

Vous avez maintenant une table locale de base de données en mémoire de documents de référence PowerShell 7.4.

À partir d’une chaîne de recherche, nous devons calculer un autre ensemble de vecteurs afin que PowerShell puisse classer chaque document par similarité.

Dans l’exemple suivant, les vecteurs sont récupérés pour la chaîne de recherche get a list of running processes.

$searchText = "get a list of running processes"

$body = [ordered]@{
    input = $searchText
} | ConvertTo-Json

$url = "$($openai.api_base)/openai/deployments/$($openai.name)/embeddings?api-version=$($openai.api_version)"

$params = @{
    Uri         = $url
    Headers     = $headers
    Body        = $body
    Method      = 'Post'
    ContentType = 'application/json'
}
$response = Invoke-RestMethod @params
$searchVectors = $response.data.embedding -join ','

Enfin, l’exemple de fonction suivant, qui s’inspire de l’exemple de script Measure-VectorSimilarity écrit par Lee Holmes, effectue un calcul de similarité cosinus, puis classe chaque ligne de la table de données.

# Sample function to calculate cosine similarity
function Get-CosineSimilarity ([float[]]$vector1, [float[]]$vector2) {
    $dot = 0
    $mag1 = 0
    $mag2 = 0

    $allkeys = 0..($vector1.Length-1)

    foreach ($key in $allkeys) {
        $dot  += $vector1[$key]  * $vector2[$key]
        $mag1 += ($vector1[$key] * $vector1[$key])
        $mag2 += ($vector2[$key] * $vector2[$key])
    }

    $mag1 = [Math]::Sqrt($mag1)
    $mag2 = [Math]::Sqrt($mag2)

    return [Math]::Round($dot / ($mag1 * $mag2), 3)
}

Les commandes de l’exemple suivant parcourent toutes les lignes de $Datatable et calculent la similarité cosinus de la chaîne de recherche. Les résultats sont triés, et les trois premiers sont stockés dans une variable nommée $topThree. L’exemple ne renvoie pas de sortie.

# Calculate cosine similarity for each row and select the top 3
$topThree = $Datatable | ForEach-Object {
    [PSCustomObject]@{
        title = $_.title
        similarity = Get-CosineSimilarity $_.vectors.split(',') $searchVectors.split(',')
    }
} | Sort-Object -property similarity -descending | Select-Object -First 3 | ForEach-Object {
    $title = $_.title
    $Datatable | Where-Object { $_.title -eq $title }
}

Passez en revue la sortie de la variable $topThree, uniquement avec les propriétés title et url, en mode grille.

$topThree | Select "title", "uri" | Out-GridView

Sortie :

Capture d’écran des résultats mis en forme une fois la requête de recherche terminée.

La variable $topThree contient toutes les informations des lignes de la table de données. Par exemple, la propriété content contient le format de document d’origine. Utilisez [0] pour indexer le premier élément du tableau.

$topThree[0].content

Consultez le document complet (tronqué dans l’extrait de code de sortie de cette page).

---
external help file: Microsoft.PowerShell.Commands.Management.dll-Help.xml
Locale: en-US
Module Name: Microsoft.PowerShell.Management
ms.date: 07/03/2023
online version: https://learn.microsoft.com/powershell/module/microsoft.powershell.management/get-process?view=powershell-7.4&WT.mc_id=ps-gethelp
schema: 2.0.0
title: Get-Process
---

# Get-Process

## SYNOPSIS
Gets the processes that are running on the local computer.

## SYNTAX

### Name (Default)

Get-Process [[-Name] <String[]>] [-Module] [-FileVersionInfo] [<CommonParameters>]
# truncated example

Enfin, au lieu de regénérer les incorporations chaque fois que vous devez interroger le jeu de données, vous pouvez stocker les données sur disque et les rappeler par la suite. Les méthodes WriteXML() et ReadXML() des types d’objets DataTable dans l’exemple suivant simplifient le processus. Le schéma du fichier XML nécessite que la table de données ait un TableName.

Remplacez <YOUR-FULL-FILE-PATH> par le chemin complet où vous voulez écrire et lire le fichier XML. Le chemin doit se terminer par .xml.

# Set DataTable name
$Datatable.TableName = "MyDataTable"

# Writing DataTable to XML
$Datatable.WriteXml("<YOUR-FULL-FILE-PATH>", [System.Data.XmlWriteMode]::WriteSchema)

# Reading XML back to DataTable
$newDatatable = New-Object System.Data.DataTable
$newDatatable.ReadXml("<YOUR-FULL-FILE-PATH>")

Quand vous réutilisez les données, vous devez obtenir les vecteurs de chaque nouvelle chaîne de recherche (et non l’intégralité de la table de données). Pour vous entraîner, essayez de créer un script PowerShell afin d’automatiser la commande Invoke-RestMethod avec la chaîne de recherche comme paramètre.

À l’aide de cette approche, vous pouvez utiliser des incorporations comme mécanisme de recherche dans des documents d’une base de connaissances. L’utilisateur peut ensuite prendre le premier résultat de recherche et l’utiliser pour sa tâche en aval, qui a demandé sa requête initiale.

Nettoyer les ressources

Si vous avez créé une ressource Azure OpenAI uniquement pour réaliser ce tutoriel et que vous souhaitez nettoyer et supprimer une ressource Azure OpenAI, vous devrez supprimer vos modèles déployés, puis supprimer la ressource ou le groupe de ressources associé s'il est dédié à votre ressource de test. La suppression du groupe de ressources efface également les autres ressources qui y sont associées.

Étapes suivantes

Découvrez-en plus sur les modèles d’Azure OpenAI :