このチュートリアルでは、Azure OpenAI embeddings API を使用して、document search を実行して、最も関連性の高いドキュメントを検索するナレッジ ベースを検索します。
このチュートリアルでは、次の方法について説明します。
- サンプル データセットをダウンロードし、分析用に準備します。
- リソース エンドポイントと API キーの環境変数を作成します。
- text-embedding-ada-002 (バージョン 2)、text-embedding-3-large、text-embedding-3-small モデルのいずれかを使用します。
- コサインの類似性を使用して検索結果をランク付けします。
前提 条件
- Azure サブスクリプション - 無料で作成します
- text-embedding-ada-002 (バージョン 2) モデルがデプロイされた Microsoft Foundry または Azure OpenAI リソース。 このモデルは現在、特定の リージョンでのみ使用できます。
- Python 3.10 以降のバージョン
- 次のPythonライブラリ:
openai、num2words、matplotlib、plotly、scipy、scikit-learn、pandas、tiktoken。 - Jupyter Notebooks
セットアップ
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 文字の長さの中規模の法律に焦点を当てています。 プロジェクトと、このデータセットの派生元の学術論文の詳細については、
このチュートリアルでは、GitHub サンプル データからダウンロードできる bill_sum_data.csv ファイルを使用します。
ローカル コンピューターで次のコマンドを実行して、サンプル データをダウンロードすることもできます。
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 と key が必要です。
| 変数名 | 値 |
|---|---|
ENDPOINT |
サービス エンドポイントは、Azure ポータルでリソースを確認する際に キーとエンドポイント セクションで見つけることができます。 または、Microsoft Foundry ポータルのDeploymentsページでエンドポイントを見つけることができます。 エンドポイントの例は、 https://docs-test-001.openai.azure.com/です。 |
API-KEY |
この値は、Azure ポータルでリソースを調べたときに、KEY1またはKEY2を使用できます。 |
Azure ポータルでリソースに移動します。 [ キーとエンドポイント] セクションは、[ リソース管理 ] セクションにあります。 API 呼び出しを認証するために両方が必要になるので、エンドポイントとアクセス キーをコピーします。
KEY1またはKEY2を使用できます。 常に 2 つのキーを使用すると、サービスの中断を引き起こさずに、キーを安全にローテーションおよび再生成できます。
環境変数
API キーの永続的な環境変数を作成して割り当てます。
重要
API キーは慎重に使用してください。 API キーをコードに直接含めず、パブリックに投稿しないでください。 API キーを使用する場合は、Azure Key Vaultに安全に格納します。 アプリで API キーを安全に使用する方法の詳細については、「Azure Key Vault を使用した
AI サービスのセキュリティの詳細については、「Authenticate requests to Azure AI サービス」を参照してください。
setx AZURE_OPENAI_API_KEY "REPLACE_WITH_YOUR_KEY_VALUE_HERE"
環境変数を設定した後、環境変数にアクセスできるようにするには、Jupyter Notebook または使用している IDE を閉じてから再度開く必要がある場合があります。 Jupyter Notebook を使用することを強くお勧めしますが、何らかの理由で pandas データフレームを返すコードを変更する必要がない場合は、コード ブロックの最後で行われるようにprint(dataframe_name)を直接呼び出すのではなく、dataframe_nameを使用して変更する必要があります。
好みのPython IDE で次のコードを実行します。
ライブラリのインポート
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
出力:
初期テーブルには、必要以上に多くの列があり、df_bills、text、summaryの列のみを含む、titleと呼ばれる新しい小さな DataFrame を作成します。
df_bills = df[['text', 'summary', 'title']]
df_bills
出力:
次に、冗長な空白を削除し、句読点をクリーンアップしてトークン化用のデータを準備することで、軽いデータクリーニングを実行します。
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))
トークンの制限に対して長すぎる請求書 (8,192 トークン) を削除する必要があります。
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
メモ
この場合、すべての請求書は埋め込みモデル入力トークンの制限の下にありますが、上記の手法を使用して、埋め込みに失敗する可能性があるエントリを削除できます。 埋め込み制限を超えるコンテンツに直面した場合は、コンテンツをより小さな部分にチャンクし、一度に 1 つずつチャンクを埋め込むこともできます。
もう一度 df_billsを調べます。
df_bills
出力:
n_tokens列とテキストが最終的にどのようにトークン化されるかをもう少し理解するには、次のコードを実行すると便利です。
sample_encode = tokenizer.encode(df_bills.text[0])
decode = tokenizer.decode_tokens_bytes(sample_encode)
decode
このドキュメントでは、意図的に出力を省略していますが、環境内でこのコマンドを実行すると、フルテキストがインデックス0からトークン化されてチャンクに分割されて返されます。 単語全体が 1 つのトークンで表される場合もあれば、単語の一部が複数のトークンに分割されている場合もあります。
[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
トークン化のしくみの詳細を理解したので、埋め込みに進むことができます。 ドキュメントを実際にトークン化していないことに注意することが重要です。
n_tokens列は、トークン化と埋め込みのためにモデルに渡すデータが入力トークンの制限である 8,192 を超えないようにする単なる方法です。 ドキュメントを埋め込みモデルに渡すと、ドキュメントは上記の例と似た (必ずしも同じではないが) トークンに分割され、トークンをベクター検索でアクセスできる一連の浮動小数点数に変換されます。 これらの埋め込みをローカルまたは Azure Database に格納して、ベクター検索をサポートできます。 その結果、各請求書には、DataFrame の右側にある新しい ada_v2 列に、対応する独自の埋め込みベクターが含まれます。
次の例では、埋め込む項目ごとに 1 回埋め込みモデルを呼び出しています。 大規模な埋め込みプロジェクトを使用する場合は、一度に 1 つの入力ではなく、埋め込む入力の配列をモデルに渡すことができます。 モデルに入力の配列を渡すと、埋め込みエンドポイントへの呼び出しあたりの入力項目の最大数は 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
出力:
以下の検索コード ブロックを実行する場合は、同じ 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)
出力:
最後に、ナレッジ ベース全体に対するユーザー クエリに基づいて、ドキュメント検索の上位の結果を表示します。 これは、"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."
この方法を使用すると、ナレッジ ベース内のドキュメント間の検索メカニズムとして埋め込みを使用できます。 その後、ユーザーは上位の検索結果を取得し、ダウンストリーム タスクに使用して、最初のクエリを求めることができます。
トラブルシューティング
-
401/403:
AZURE_OPENAI_API_KEYが設定され、リソース キーと一致するかどうかを確認します。 -
404:
AZURE_OPENAI_EMBEDDINGS_DEPLOYMENTがデプロイ名と一致するかどうかを確認します。 -
無効な URL:
AZURE_OPENAI_ENDPOINTなど、https://<resource-name>.openai.azure.comがリソース エンドポイントであることを確認します。
リソースのクリーンアップ
このチュートリアルを完了するためだけに Azure OpenAI リソースを作成し、Azure OpenAI リソースをクリーンアップおよび削除する場合は、デプロイされたモデルを削除し、テスト リソース専用のリソースまたは関連付けられているリソース グループを削除する必要があります。 リソース グループを削除すると、それに関連付けられている他のリソースも削除されます。
次の手順
Azure OpenAI のモデルについて詳しく知るには、以下をご覧ください。
- 任意のAzureサービスを使用して、埋め込みを格納し、ベクトル (類似性) 検索を実行します。