共用方式為


教學:探索 Microsoft Foundry 模型中的 Azure OpenAI 嵌入與文件搜尋

本教學課程將逐步引導您使用 Azure OpenAI 內嵌 API 來執行文件搜尋,您將在其中查詢知識庫以尋找最相關的文件。

在本教學課程中,您將瞭解如何:

  • 下載樣本資料集並準備進行分析。
  • 為您的資源端點和 API 金鑰建立環境變數。
  • 使用下列其中一個模型: text-embedding-ada-002 (版本 2),text-embedding-3-large,text-embedding-3-small 模型。
  • 使用餘弦相似性來排名搜尋結果。

先決條件

設定

Python 程式庫

如果您尚未安裝,您需要安裝下列程式庫:

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

下載 BillSum 資料集

BillSum 是美國國會和加利福尼亞州法案的資料集。 出於說明目的,我們只會查看美國的法案。 主體包含來自第 103 屆到第 115 屆 (1993-2018 年)國會議會的法案。 資料分割成 18,949 個訓練法案和 3,269 個測試法案。 BillSum 主體專注在長度從 5,000 到 20,000 個字元的中長度法規。 您可以在 BillSum 專案的 GitHub 存放庫找到此資料集衍生自的專案和原始學術檔的詳細資訊

本教學課程會使用 bill_sum_data.csv 檔案,您可以從 GitHub 範例資料下載這個檔案。

您也可以在本機電腦上執行下列命令來下載範例資料:

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

備註

目前,基於 Microsoft Entra ID 的驗證不支援使用 v1 API 的嵌入功能。

擷取金鑰和端點

若要成功對 Azure OpenAI 進行呼叫,您需要端點金鑰

變數名稱 價值觀
ENDPOINT 檢查來自 Azure 入口網站 的資源時,可以在 [金鑰與端點] 區段中找到服務端點。 或者,你也可以透過 Microsoft Foundry 入口網站的 部署 頁面找到該端點。 範例端點為:https://docs-test-001.openai.azure.com/
API-KEY 從 Azure 入口網站查看您的資源時,可以在 [金鑰與端點] 區段中找到此值。 您可以使用 KEY1KEY2

移至您在 Azure 入口網站中的資源。 您可以在 [資源管理] 區段中找到 [金鑰和端點] 區段。 複製您的端點和存取金鑰,因為您需要這兩者才能驗證 API 呼叫。 您可以使用 KEY1KEY2。 隨時持有兩個金鑰可讓您安全地輪替和重新產生金鑰,而不會造成服務中斷。

這是一個 Azure 入口網站中 Azure OpenAI 資源的總覽使用者介面截圖,其中端點和存取金鑰的位置以紅色圓圈標示。

環境變數

為您的 API 金鑰建立及指派永續性環境變數。

這很重要

請謹慎使用 API 金鑰。 請勿在程式碼中直接包含 API 金鑰,且切勿公開張貼金鑰。 如果您使用 API 金鑰,請安全地將其儲存在 Azure 金鑰保存庫。 如需在應用程式中安全地使用 API 金鑰的詳細資訊,請參閱 使用 Azure Key Vault 的 API 金鑰

如需 AI 服務安全性的詳細資訊,請參閱驗證對 Azure AI 服務的要求

setx AZURE_OPENAI_API_KEY "REPLACE_WITH_YOUR_KEY_VALUE_HERE" 

設定環境變數之後,您可能需要關閉並重新開啟 Jupyter 筆記本,或您使用的任何整合式開發環境 (IDE),才能存取環境變數。 雖然我們強烈建議使用 Jupyter 筆記本,但如果您因為某些原因而無法修改任何使用 print(dataframe_name) 傳回 Pandas DataFrame 的程式碼,而不是像在程式碼區塊結尾一樣,直接呼叫 dataframe_name

在慣用的 Python 整合式開發環境中執行下列程式碼:

匯入程式庫

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

現在我們需要讀取 CSV 檔案,並建立 pandas DataFrame。 建立初始 DataFrame 之後,我們可以執行 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

輸出:

螢幕擷取畫面顯示來自 CSV 檔案的初始 DataFrame 資料表結果。

初始資料表的資料行數目超過我們需要的資料行,我們會建立名為 df_bills 的新小型 DataFrame,其只會包含 textsummarytitle 資料行。

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

輸出:

只有顯示文字、摘要和標題資料行的小型 DataFrame 資料表結果的螢幕擷取畫面。

接下來,我們將藉由移除備援空白字元並清除標點符號來準備資料以進行 Token 化,以執行一些簡單的資料清除。

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

現在,我們需要移除超過語彙基元限制 (8192 個語彙基元) 的任何法案。

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

備註

在此情況下,所有法案都在內嵌模型輸入語彙基元限制下,但您可以使用上述技術來移除會導致內嵌失敗的專案。 當面臨超過內嵌限制的內容時,您也可以將內容區塊化成較小的片段,然後一次內嵌一個區塊。

我們會再次檢查 df_bills

df_bills

輸出:

具有名為 n_tokens 之新資料行的 DataFrame 的螢幕擷取畫面。

若要深入了解 n_tokens 資料行,以及文字最終如何 Token 化,執行下列程式碼會很有幫助:

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

針對我們的文件,我們會刻意截斷輸出,但在您的環境中執行此命令,將會從索引零 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',

如果您接著檢查 decode 變數的長度,您會發現它符合n_tokens 資料行中的第一個數字。

len(decode)
1466

既然我們已深入了解 Token 化的運作方式,我們可以繼續進行內嵌。 請務必注意,我們尚未實際將文件權杖化。 n_tokens 資料行只是一種確保我們傳遞至模型以進行 Token 化和內嵌的資料超出了輸入語彙基元限制 8,192 的方法。 當我們將文件傳遞至內嵌模型時,它會將文件分成類似上述範例的語彙基元 (但不一定完全相同),然後將語彙基元轉換成可透過向量搜尋存取的一系列浮點數。 這些內嵌可以儲存在本機或 Azure 資料庫,以支援向量搜尋。 因此,每個法案在 DataFrame 右側的新 ada_v2 資料行中都會有自己的對應內嵌向量。

在下列範例,我們會根據想要內嵌的每個項目呼叫一次內嵌模型。 處理大型內嵌專案時,您也可以向模型傳遞一個陣列要內嵌的輸入內容,而不是一次內嵌一個。 向模型傳遞一個陣列的輸入內容時,每次呼叫至內嵌端點的輸入項目數目上限為 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

輸出:

來自 df_bills 命令的格式化結果的螢幕擷取畫面。

當我們執行下方的搜尋程式碼區塊時,我們將內嵌搜尋查詢 「我是否可以取得有線電視公司稅收收入的相關資訊?」,並使用相同的 text-embedding-ada-002 (第 2 版) 模型。 接下來,我們會在依餘弦相似性排名的查詢中找到最接近新內嵌文字的法案內嵌。

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)

輸出

執行搜尋查詢之後,res 格式化結果的螢幕擷取畫面。

最後,我們會根據針對整個知識庫的使用者查詢,顯示文件搜尋的首選結果。 這會傳回「1993 年納稅人檢視權法案」的首選結果。本文件在查詢與文件之間具有 0.76 的餘弦相似性分數:

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

使用這種方法,您可以使用內嵌作為知識庫中文件之間的搜尋機制。 然後,使用者可以取得首選的搜尋結果,並將其用於自己的下游工作,以提示其初始查詢。

清理資源

如果建立 Azure OpenAI 資源單純是為了完成本教學課程,而且想清除並移除 Azure OpenAI 資源,您必須刪除已部署的模型,然後在測試資源專用時刪除資源或相關聯的資源群組。 刪除資源群組也會刪除與其相關聯的任何其他資源。

後續步驟

深入了解 Azure OpenAI 模型: