아리제 피닉스를 사용한 평가
이 가이드에서는 Milvus를 기반으로 구축된 검색 증강 생성(RAG) 파이프라인을 평가하기 위해 Arize Pheonix를 사용하는 방법을 설명합니다.
RAG 시스템은 검색 시스템과 생성 모델을 결합하여 주어진 프롬프트에 따라 새로운 텍스트를 생성합니다. 시스템은 먼저 Milvus를 사용하여 말뭉치에서 관련 문서를 검색한 다음, 생성 모델을 사용하여 검색된 문서를 기반으로 새 텍스트를 생성합니다.
아리제 피오닉스는 RAG 파이프라인을 평가하는 데 도움이 되는 프레임워크입니다. 이러한 파이프라인을 구축하는 데 도움이 되는 기존 도구와 프레임워크가 있지만 이를 평가하고 파이프라인의 성능을 정량화하는 것은 어려울 수 있습니다. 이것이 바로 Arize Pheonix가 필요한 이유입니다.
전제 조건
이 노트북을 실행하기 전에 다음 종속성이 설치되어 있는지 확인하세요:
$ pip install --upgrade pymilvus openai requests tqdm pandas "arize-phoenix>=4.29.0" nest_asyncio
Google Colab을 사용하는 경우 방금 설치한 종속성을 활성화하려면 런타임을 다시 시작해야 할 수 있습니다(화면 상단의 '런타임' 메뉴를 클릭하고 드롭다운 메뉴에서 '세션 다시 시작'을 선택).
이 예제에서는 OpenAI를 LLM으로 사용하겠습니다. 환경 변수로 OPENAI_API_KEY
API 키를 준비해야 합니다.
import os
# os.environ["OPENAI_API_KEY"] = "sk-*****************"
RAG 파이프라인 정의
Milvus를 벡터 저장소로, OpenAI를 LLM으로 사용하는 RAG 클래스를 정의하겠습니다. 이 클래스에는 Milvus에 텍스트 데이터를 로드하는 load
메서드, 주어진 질문과 가장 유사한 텍스트 데이터를 검색하는 retrieve
메서드, 검색된 지식으로 주어진 질문에 답하는 answer
메서드가 포함되어 있습니다.
from typing import List
from tqdm import tqdm
from openai import OpenAI
from pymilvus import MilvusClient
class RAG:
"""
RAG(Retrieval-Augmented Generation) class built upon OpenAI and Milvus.
"""
def __init__(self, openai_client: OpenAI, milvus_client: MilvusClient):
self._prepare_openai(openai_client)
self._prepare_milvus(milvus_client)
def _emb_text(self, text: str) -> List[float]:
return (
self.openai_client.embeddings.create(input=text, model=self.embedding_model)
.data[0]
.embedding
)
def _prepare_openai(
self,
openai_client: OpenAI,
embedding_model: str = "text-embedding-3-small",
llm_model: str = "gpt-4o-mini",
):
self.openai_client = openai_client
self.embedding_model = embedding_model
self.llm_model = llm_model
self.SYSTEM_PROMPT = """
Human: You are an AI assistant. You are able to find answers to the questions from the contextual passage snippets provided.
"""
self.USER_PROMPT = """
Use the following pieces of information enclosed in <context> tags to provide an answer to the question enclosed in <question> tags.
<context>
{context}
</context>
<question>
{question}
</question>
"""
def _prepare_milvus(
self, milvus_client: MilvusClient, collection_name: str = "rag_collection"
):
self.milvus_client = milvus_client
self.collection_name = collection_name
if self.milvus_client.has_collection(self.collection_name):
self.milvus_client.drop_collection(self.collection_name)
embedding_dim = len(self._emb_text("demo"))
self.milvus_client.create_collection(
collection_name=self.collection_name,
dimension=embedding_dim,
metric_type="IP",
consistency_level="Strong",
)
def load(self, texts: List[str]):
"""
Load the text data into Milvus.
"""
data = []
for i, line in enumerate(tqdm(texts, desc="Creating embeddings")):
data.append({"id": i, "vector": self._emb_text(line), "text": line})
self.milvus_client.insert(collection_name=self.collection_name, data=data)
def retrieve(self, question: str, top_k: int = 3) -> List[str]:
"""
Retrieve the most similar text data to the given question.
"""
search_res = self.milvus_client.search(
collection_name=self.collection_name,
data=[self._emb_text(question)],
limit=top_k,
search_params={"metric_type": "IP", "params": {}}, # inner product distance
output_fields=["text"], # Return the text field
)
retrieved_texts = [res["entity"]["text"] for res in search_res[0]]
return retrieved_texts[:top_k]
def answer(
self,
question: str,
retrieval_top_k: int = 3,
return_retrieved_text: bool = False,
):
"""
Answer the given question with the retrieved knowledge.
"""
retrieved_texts = self.retrieve(question, top_k=retrieval_top_k)
user_prompt = self.USER_PROMPT.format(
context="\n".join(retrieved_texts), question=question
)
response = self.openai_client.chat.completions.create(
model=self.llm_model,
messages=[
{"role": "system", "content": self.SYSTEM_PROMPT},
{"role": "user", "content": user_prompt},
],
)
if not return_retrieved_text:
return response.choices[0].message.content
else:
return response.choices[0].message.content, retrieved_texts
OpenAI와 Milvus 클라이언트로 RAG 클래스를 초기화해 보겠습니다.
openai_client = OpenAI()
milvus_client = MilvusClient(uri="./milvus_demo.db")
my_rag = RAG(openai_client=openai_client, milvus_client=milvus_client)
MilvusClient
의 인수는 다음과 같습니다:
uri
를 로컬 파일(예:./milvus.db
)로 설정하는 것이 가장 편리한 방법인데, Milvus Lite를 자동으로 활용하여 모든 데이터를 이 파일에 저장하기 때문입니다.- 데이터 규모가 큰 경우, 도커나 쿠버네티스에 더 고성능의 Milvus 서버를 설정할 수 있습니다. 이 설정에서는 서버 URL(예:
http://localhost:19530
)을uri
으로 사용하세요. - 밀버스의 완전 관리형 클라우드 서비스인 질리즈 클라우드를 사용하려면, 질리즈 클라우드의 퍼블릭 엔드포인트와 API 키에 해당하는
uri
와token
을 조정하세요.
RAG 파이프라인을 실행하고 결과 얻기
Milvus 개발 가이드는 간단한 RAG 파이프라인을 위한 좋은 데이터 소스로서 RAG의 비공개 지식으로 사용합니다.
이 가이드를 다운로드하여 RAG 파이프라인에 로드하세요.
import urllib.request
import os
url = "https://raw.githubusercontent.com/milvus-io/milvus/master/DEVELOPMENT.md"
file_path = "./Milvus_DEVELOPMENT.md"
if not os.path.exists(file_path):
urllib.request.urlretrieve(url, file_path)
with open(file_path, "r") as file:
file_text = file.read()
text_lines = file_text.split("# ")
my_rag.load(text_lines)
Creating embeddings: 100%|██████████| 47/47 [00:12<00:00, 3.84it/s]
개발 가이드 문서의 내용에 대한 쿼리 질문을 정의해 보겠습니다. 그런 다음 answer
메서드를 사용하여 답변과 검색된 컨텍스트 텍스트를 가져옵니다.
question = "what is the hardware requirements specification if I want to build Milvus and run from source code?"
my_rag.answer(question, return_retrieved_text=True)
('The hardware requirements specification to build and run Milvus from source code are:\n\n- 8GB of RAM\n- 50GB of free disk space',
['Hardware Requirements\n\nThe following specification (either physical or virtual machine resources) is recommended for Milvus to build and run from source code.\n\n```\n- 8GB of RAM\n- 50GB of free disk space\n```\n\n##',
'Building Milvus on a local OS/shell environment\n\nThe details below outline the hardware and software requirements for building on Linux and MacOS.\n\n##',
"Software Requirements\n\nAll Linux distributions are available for Milvus development. However a majority of our contributor worked with Ubuntu or CentOS systems, with a small portion of Mac (both x86_64 and Apple Silicon) contributors. If you would like Milvus to build and run on other distributions, you are more than welcome to file an issue and contribute!\n\nHere's a list of verified OS types where Milvus can successfully build and run:\n\n- Debian/Ubuntu\n- Amazon Linux\n- MacOS (x86_64)\n- MacOS (Apple Silicon)\n\n##"])
이제 해당 실측 답변이 포함된 몇 가지 질문을 준비해 보겠습니다. RAG 파이프라인에서 답변과 컨텍스트를 가져옵니다.
from datasets import Dataset
import pandas as pd
question_list = [
"what is the hardware requirements specification if I want to build Milvus and run from source code?",
"What is the programming language used to write Knowhere?",
"What should be ensured before running code coverage?",
]
ground_truth_list = [
"If you want to build Milvus and run from source code, the recommended hardware requirements specification is:\n\n- 8GB of RAM\n- 50GB of free disk space.",
"The programming language used to write Knowhere is C++.",
"Before running code coverage, you should make sure that your code changes are covered by unit tests.",
]
contexts_list = []
answer_list = []
for question in tqdm(question_list, desc="Answering questions"):
answer, contexts = my_rag.answer(question, return_retrieved_text=True)
contexts_list.append(contexts)
answer_list.append(answer)
df = pd.DataFrame(
{
"question": question_list,
"contexts": contexts_list,
"answer": answer_list,
"ground_truth": ground_truth_list,
}
)
rag_results = Dataset.from_pandas(df)
df
/Users/eureka/miniconda3/envs/zilliz/lib/python3.9/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
Answering questions: 100%|██████████| 3/3 [00:03<00:00, 1.04s/it]
question | contexts | answer | ground_truth | |
---|---|---|---|---|
0 | 하드웨어 요구 사양은 어떻게 되나요? | [하드웨어 요구 사항\n\n다음 사양은 ... | 빌드하기 위한 하드웨어 요구 사양은 무엇입니까? | 밀버스를 빌드하고 소스에서 실행하려면... |
1 | 작성에 사용되는 프로그래밍 언어는 무엇인가요? | [CMake & Conan\n\n밀버스의 알고리즘 라이브러리는... | Knowher를 작성하는 데 사용되는 프로그래밍 언어는 무엇인가요? | Knowher를 작성하는 데 사용되는 프로그래밍 언어는 무엇입니까? |
2 | 코드 커버리지를 실행하기 전에 확인해야 할 사항은 무엇인가요? | [코드 커버리지\n\n풀을 제출하기 전에 ... | 코드 커버리지를 실행하기 전에 다음을 확인해야 합니다. | 코드 커버리지를 실행하기 전에 다음을 수행해야 합니다. |
아리즈 피닉스를 사용한 평가
저희는 두 가지 주요 메트릭에 초점을 맞춰 검색 증강 생성(RAG) 파이프라인을 평가하기 위해 Arize Phoenix를 사용합니다:
환각 평가: 콘텐츠가 사실인지 또는 환각(문맥에 근거하지 않은 정보)인지 판단하여 데이터 무결성을 보장합니다.
- 환각 설명: 응답이 사실인지 아닌지 이유를 설명합니다.
QA 평가: 입력 쿼리에 대한 모델 답변의 정확성을 평가합니다.
- QA 설명: 답변이 정답 또는 오답인 이유를 자세히 설명합니다.
Phoenix 추적 개요
Phoenix는 Langchain, LlamaIndex와 같은 프레임워크와 OpenAI 및 Mistral과 같은 SDK를 위한 통합을 통해 LLM 애플리케이션을 위한 OTEL 호환 추적을 제공합니다. 추적은 전체 요청 흐름을 캡처하여 다음에 대한 인사이트를 제공합니다:
- 애플리케이션 지연 시간: 느린 LLM 호출과 컴포넌트 성능을 식별하고 최적화합니다.
- 토큰 사용량: 비용 최적화를 위해 토큰 소비를 세분화합니다.
- 런타임 예외: 속도 제한과 같은 중요한 문제를 캡처하세요.
- 검색된 문서: 문서 검색, 점수, 순서를 분석하세요.
Phoenix의 추적을 활용하여 병목 현상을 파악하고, 리소스를 최적화하고, 다양한 프레임워크와 언어에서 시스템 안정성을 보장할 수 있습니다.
import phoenix as px
from phoenix.trace.openai import OpenAIInstrumentor
# To view traces in Phoenix, you will first have to start a Phoenix server. You can do this by running the following:
session = px.launch_app()
# Initialize OpenAI auto-instrumentation
OpenAIInstrumentor().instrument()
🌍 To view the Phoenix app in your browser, visit http://localhost:6006/
📖 For more information on how to use Phoenix, check out https://docs.arize.com/phoenix
대체 텍스트
import nest_asyncio
from phoenix.evals import HallucinationEvaluator, OpenAIModel, QAEvaluator, run_evals
nest_asyncio.apply() # This is needed for concurrency in notebook environments
# Set your OpenAI API key
eval_model = OpenAIModel(model="gpt-4o")
# Define your evaluators
hallucination_evaluator = HallucinationEvaluator(eval_model)
qa_evaluator = QAEvaluator(eval_model)
# We have to make some minor changes to our dataframe to use the column names expected by our evaluators
# for `hallucination_evaluator` the input df needs to have columns 'output', 'input', 'context'
# for `qa_evaluator` the input df needs to have columns 'output', 'input', 'reference'
df["context"] = df["contexts"]
df["reference"] = df["contexts"]
df.rename(columns={"question": "input", "answer": "output"}, inplace=True)
assert all(
column in df.columns for column in ["output", "input", "context", "reference"]
)
# Run the evaluators, each evaluator will return a dataframe with evaluation results
# We upload the evaluation results to Phoenix in the next step
hallucination_eval_df, qa_eval_df = run_evals(
dataframe=df,
evaluators=[hallucination_evaluator, qa_evaluator],
provide_explanation=True,
)
run_evals |██████████| 6/6 (100.0%) | ⏳ 00:03<00:00 | 1.64it/s
results_df = df.copy()
results_df["hallucination_eval"] = hallucination_eval_df["label"]
results_df["hallucination_explanation"] = hallucination_eval_df["explanation"]
results_df["qa_eval"] = qa_eval_df["label"]
results_df["qa_explanation"] = qa_eval_df["explanation"]
results_df.head()
input | 컨텍스트 | output | ground_truth | 컨텍스트 | 참조 | hallucination_eval | hallucination_explanation | qa_eval | qa_explanation | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 하드웨어 요구 사양은 어떻게 됩니까? | [하드웨어 요구 사항\n\n다음 사양은... | 빌드할 하드웨어 요구 사양은 무엇입니까? | 밀버스를 빌드하고 소스에서 실행하려면 다음과 같은 사양이 필요합니다. | [하드웨어 요구 사항\n\n다음 사양입니다. | [하드웨어 요구 사항\n\n다음 사양입니다... | 사실 | 답변이 사실인지 아니면 허구인지 확인하려면 ... | correct | 정답인지 확인하려면 다음이 필요합니다. |
1 | 이 프로그램을 작성하는 데 사용되는 프로그래밍 언어는 무엇인가요? | [CMake & Conan\n\n밀레의 알고리즘 라이브러리는 ... | Knowher를 작성하는 데 사용되는 프로그래밍 언어는 무엇입니까? | Knowher...를 작성하는 데 사용된 프로그래밍 언어입니다. | [CMake & Conan\n\n밀의 알고리즘 라이브러리입니다. | [CMake & Conan\n\n밀의 알고리즘 라이브러리 | 사실 | 답이 사실인지 허구인지 판단하려면... | correct | 답이 맞는지 판단하려면 다음이 필요합니다. |
2 | 코드 커버리지를 실행하기 전에 확인해야 할 사항은 무엇입니까? | [코드 커버리지\n\n풀을 제출하기 전에 ... | 코드 커버리지를 실행하기 전에 다음을 확인해야 합니다. | 코드 커버리지를 실행하기 전에 다음을 수행해야 합니다. | [코드 커버리지\n\n풀을 제출하기 전에 ... | [코드 커버리지\n\n풀을 제출하기 전에 ... | 사실 | 참조 텍스트는 실행하기 전에 ... | correct | 정답이 맞는지 확인하려면 다음이 필요합니다. |