자습서: Microsoft Foundry 모델 포함 및 문서 검색에서 Azure OpenAI 살펴보기

이 자습서에서는 Azure OpenAI embeddings 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에서 다운로드할 수 있는 파일을 사용합니다.

로컬 컴퓨터에서 다음 명령을 실행하여 샘플 데이터를 다운로드할 수도 있습니다.

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에 대해 성공적으로 호출하려면 엔드포인트key 필요합니다.

변수 이름
ENDPOINT 서비스 엔드포인트는 Azure 포털에서 리소스를 검사할 때 키 및 엔드포인트 섹션에서 찾을 수 있습니다. 또는 Microsoft Foundry 포털의 배포 페이지를 통해 엔드포인트를 찾을 수 있습니다. 엔드포인트의 예는 다음과 https://docs-test-001.openai.azure.com/같습니다.
API-KEY 이 값은 Azure 포털에서 리소스를 검사할 때 키 및 엔드포인트 섹션에서 찾을 수 있습니다. 또는 KEY1KEY2.를 사용할 수 있습니다.

Azure 포털에서 리소스로 이동합니다. 키 및 엔드포인트 섹션은 리소스 관리 섹션에서 찾을 수 있습니다. API 호출을 인증하는 데 모두 필요하므로 엔드포인트 및 액세스 키를 복사합니다. 또는 KEY1KEY2.를 사용할 수 있습니다. 항상 두 개의 키를 사용하면 서비스 중단 없이 키를 안전하게 회전하고 다시 생성할 수 있습니다.

Azure Portal의 Azure OpenAI 리소스 개요 UI의 스크린샷으로, 엔드포인트 및 액세스 키 위치가 빨간색으로 표시되어 있습니다.

환경 변수

API 키에 대한 영구 환경 변수를 만들고 할당합니다.

중요

주의해서 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 Notebook을 사용하는 것이 강력히 권장되지만, 어떤 이유로든 사용하지 못하는 경우, 코드 블록의 끝에서 자주 직접 호출되는 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

출력:

csv 파일의 초기 DataFrame 테이블 결과 스크린샷

초기 테이블에는 필요한 것보다 더 많은 열이 있습니다. 우리는 df_bills, text, 그리고 summary 열만 포함하는 더 작은 DataFrame인 title를 새로 만들 것입니다.

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

출력:

텍스트, 요약 및 제목 열만 표시된 작은 DataFrame 테이블 결과의 스크린샷.

다음으로 중복 공백을 제거하고 문장 부호를 정리하여 토큰화를 위해 데이터를 준비하여 간단한 데이터 정리를 수행합니다.

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

참고

이 경우 모든 청구서가 포함 모델 입력 토큰 제한에 포함되지만 위의 기술을 사용하여 포함이 실패하는 항목을 제거할 수 있습니다. 포함 한도를 초과하는 콘텐츠에 직면하면 콘텐츠를 더 작은 조각으로 청크한 다음 청크를 한 번에 하나씩 포함할 수도 있습니다.

df_bills 다시 한 번 살펴보겠습니다.

df_bills

출력:

n_tokens이라는 새 열이 있는 DataFrame의 스크린샷

텍스트가 궁극적으로 토큰화되는 방법뿐만 아니라 n_tokens 열을 좀 더 잘 이해하려면 다음 코드를 실행하는 것이 도움이 될 수 있습니다.

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

문서의 경우 의도적으로 출력을 잘라내지만 사용자 환경에서 이 명령을 실행하면 토큰화된 인덱스 0의 전체 텍스트가 청크로 반환됩니다. 경우에 따라 전체 단어가 단일 토큰으로 표현되는 반면, 다른 부분에서는 단어의 일부가 여러 토큰으로 분할되는 것을 볼 수 있습니다.

[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를 초과하지 않도록 하는 방법입니다. 문서를 embeddings 모델에 전달하면 위의 예제와 유사한 토큰으로 문서를 분리한 다음 토큰을 벡터 검색을 통해 액세스할 수 있는 일련의 부동 소수점 숫자로 변환합니다. 이러한 포함은 벡터 검색 지원하도록 로컬 또는 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 명령의 서식이 지정된 결과의 스크린샷.

아래의 검색 코드 블록을 실행할 때 동일한 텍스트 포함-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."

이 방법을 사용하면 기술 자료의 문서에서 임베딩을 검색 메커니즘으로 사용할 수 있습니다. 그런 다음 사용자는 상위 검색 결과를 가져와 다운스트림 작업에 사용할 수 있으며, 이로 인해 초기 쿼리가 표시됩니다.

문제 해결

  • 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 리소스를 정리하고 제거하려는 경우 배포된 모델을 삭제한 다음 테스트 리소스 전용인 경우 리소스 또는 관련 리소스 그룹을 삭제해야 합니다. 리소스 그룹을 삭제하면 연결된 다른 리소스도 삭제됩니다.

다음 단계

OpenAI의 Azure 모델에 대해 자세히 알아보세요.