このチュートリアルでは、Azure OpenAI 埋め込み API を使ってドキュメント検索を実行し、ナレッジ ベースにクエリを実行して最も関連性の高いドキュメントを見つける方法について説明します。
このチュートリアルでは、以下の内容を学習します。
- サンプル データセットをダウンロードして、分析用に準備する。
- リソース エンドポイントと 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 Notebook
セットアップ
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からダウンロードできる ファイルを使います。
ローカル コンピューターで次のコマンドを実行して、サンプル データをダウンロードすることもできます。
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 portal でリソースを調べるときに、[キーとエンドポイント] セクションで確認できます。 または、Microsoft Foundry ポータルの [デプロイ] ページからエンドポイントを見つけることができます。 エンドポイントの例: https://docs-test-001.openai.azure.com/。 |
API-KEY |
この値は、Azure portal からリソースを確認する際に、 [Keys & Endpoint](キーとエンドポイント) セクションで確認することができます。
KEY1 または KEY2 を使用できます。 |
Azure portal でリソースに移動します。
[キーとエンドポイント] セクションは、[リソース管理] セクションにあります。 エンドポイントとアクセス キーをコピーします。これらは、API 呼び出しを認証するために両方とも必要です。
KEY1 または KEY2 を使用できます。 常に 2 つのキーを用意しておくと、サービスを中断させることなく、キーのローテーションと再生成を安全に行うことができます。
環境変数
API キーの永続的な環境変数を作成して割り当てます。
Von Bedeutung
API キーは慎重に使用してください。 API キーは、コード内に直接含めないようにし、絶対に公開しないでください。 API キーを使用する場合は、Azure Key Vault に安全に保存します。 アプリで API キーを安全に使用する方法の詳細については、Azure Key Vault を使用した API キーに関する記事を参照してください。
AI サービスのセキュリティの詳細については、「 Azure AI サービスへの要求を認証する」を参照してください。
setx AZURE_OPENAI_API_KEY "REPLACE_WITH_YOUR_KEY_VALUE_HERE"
環境変数を設定した後、その環境変数にアクセスできるようにするには、Jupyter Notebook や使用している IDE を閉じてから再度開く操作が必要になる場合があります。 Jupyter Notebooks を使用することを強くお勧めしますが、何らかの理由で使用できない場合は、コード ブロックの末尾で通常行われるように、print(dataframe_name) を直接呼び出すのではなく dataframe_name を使用して pandas データフレームを返すコードを変更する必要があります。
任意の 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
このドキュメントでは意図的に出力を切り捨てていますが、実際の環境でこのコマンドを実行すると、ゼロのインデックスからトークン化されてチャンクになったフル テキストが返されます。 単語全体が 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 データベースに保存してベクトル検索をサポートできます。 その結果、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)
出力:
最後に、ナレッジ ベース全体に対するユーザーのクエリに基づいて、ドキュメント検索の上位結果が表示されます。 ここでは、"Taxpayer's Right to View Act of 1993" (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 のモデルの詳細をご覧ください。
- 任意の Azure サービスを使用して、埋め込みを格納し、ベクトル (類似性) 検索を実行します。