Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
**/.retrieval-augmented-generation
**/.env
**/__pycache__
**/res
**/res
**/.venv
**/config.py
32 changes: 32 additions & 0 deletions RAGAS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# AI 성능 평가 메트릭스 개요

AI 응용 프로그램의 성능을 평가할 때 적절한 메트릭스를 사용하여 시스템이 얼마나 잘 작동하는지 정확하게 측정하는 것이 중요하다. 이러한 메트릭스는 비교, 최적화 및 의사 결정의 기준을 제공한다.

메트릭스 역할:
- **구성 요소 선택**: 메트릭스는 LLM, 검색기, 에이전트 등 AI 시스템의 다양한 구성 요소를 평가, 자신의 데이터와 비교해서 선택을 할 수 있게 한다.
- **오류 진단 및 디버깅**: 오류나 성능 저하의 원인이 되는 부분을 식별하는 데 도움을 준다. 디버깅과 개선을 쉽게 만든다.
- **지속적인 모니터링**: AI 시스템은 시간이 지나면서 진화하기 때문에, 데이터 변동, 모델 저하, 변화하는 사용자 요구 사항 등과 같은 문제를 발견할 수 있게 도움을 준다.

## 메트릭스 유형

메트릭스는 크게 두 가지 그룹으로 나눌 수 있다

- **LLM 기반 메트릭스**: LLM을 여러 번 호출하여 점수를 산출하는 방식으로, 인간의 평가와 더 유사한 방식으로 성능을 측정한다.
- **비 LLM 기반 메트릭스**: 전통적인 방법(예: 문자열 유사도, BLEU 점수 등)을 활용하는 메트릭스.

## 평가 유형

- **단일 턴 메트릭스**: 사용자가 AI와 상호작용한 한 차례를 기준으로 성능을 평가하는 지표이다.
- **멀티 턴 메트릭스**: 여러 차례의 상호작용을 기준으로 성능을 평가하는 지표이다.

## 메트릭스 설계 원칙

효과적인 메트릭스 설계를 위해 다음과 같은 원칙을 따라야 한다:

1. 단일 측면 집중: 하나의 지표는 하나의 특정 측면에만 집중해야 한다.
2. 직관적이고 해석 가능한: 지표는 이해하기 쉽고 해석할 수 있어야 한다.
3. 효과적인 프롬프트 흐름: LLM을 사용할 때 인간 평가와 밀접하게 연관된 프롬프트 흐름을 사용해야 한다.
4. 강건성: LLM 기반 지표는 원하는 결과를 반영하는 충분한 샘플을 제공해야 한다.
5. 일관된 점수 범위: 지표 점수 값은 특정 범위 내에 정규화되거나 일정 범위(예: 0~1) 내에 있어야 한다.


2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# RAG-1stWeek
11월 7일 실습 및 과제를 위한 레포지토리입니다.

# LLMContextCall
1,102 changes: 65 additions & 1,037 deletions embeddings.ipynb

Large diffs are not rendered by default.

32 changes: 24 additions & 8 deletions intro.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# dotenv, os를 import 하여 \n",
"# .env 파일을 찾아 \"OPENAI_API_KEY\"라는 키를 가져옵니다.\n",
"from dotenv import load_dotenv\n",
"import os\n",
"\n",
Expand All @@ -16,7 +18,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"metadata": {},
"outputs": [
{
Expand All @@ -28,25 +30,29 @@
}
],
"source": [
"# openai 를 import 하여 OpenAI 키값을 넣어\n",
"# 클라이언트를 통해 OpenAI API의 기능을 사용\n",
"from openai import OpenAI\n",
"\n",
"client = OpenAI(api_key=OPENAI_API_KEY)\n",
"\n",
"#OpenAI 모델에 보내는 프롬프트\n",
"prompt=\"내일 서울 날씨는 어때?\"\n",
"\n",
"#GPT 모델에게 메시지를 보내어 응답을 생성\n",
"completion = client.chat.completions.create(\n",
" model=\"gpt-3.5-turbo-0125\",\n",
" messages=[\n",
" {\"role\": \"user\", \"content\": prompt},\n",
" ]\n",
")\n",
"\n",
"#생성된 응답 내용을 출력\n",
"print(completion.choices[0].message.content)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": null,
"metadata": {},
"outputs": [
{
Expand All @@ -58,21 +64,25 @@
}
],
"source": [
"# AI의 능력과 한계점\n",
"# openai 를 import 하여 OpenAI 키값을 넣어\n",
"# 클라이언트를 통해 OpenAI API의 기능을 사용\n",
"curious_prompt = \"어떻게 네가 내일 날씨를 이야기해줄 수 있니? 너는 실시간 정보 업데이트가 안 되지 않니? 기상청 정보는 어떻게 알고 있는 거니?\"\n",
"\n",
"#GPT 모델에게 메시지를 보내어 응답을 생성\n",
"completion = client.chat.completions.create(\n",
" model=\"gpt-3.5-turbo-0125\",\n",
" messages=[\n",
" {\"role\": \"user\", \"content\": curious_prompt},\n",
" ]\n",
")\n",
"\n",
"#생성된 응답 내용을 출력\n",
"print(completion.choices[0].message.content)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": null,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -101,7 +111,7 @@
"- (전라권) 전남해안: 5mm 미만\n",
"- (경상권, 28일) 경북동해안: 5~10mm/ 대구.경북내륙, 부산.울산.경남: 5mm 미만\n",
"- (제주도, 29일) 제주도: 5~40mm\"\"\"\n",
"\n",
"# 정보를 주입 후 날씨 정보 질문 후 응답을 출력\n",
"prompt = \"내일 날씨는 어때?\" + \"\\n\\n\" + weather_info\n",
"\n",
"completion = client.chat.completions.create(\n",
Expand All @@ -117,7 +127,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": null,
"metadata": {},
"outputs": [
{
Expand All @@ -130,22 +140,28 @@
}
],
"source": [
"# 텍스트 두개를 단어별로 분리 후 \n",
"# 교집합을 이용하여 같은 단어의 수를 알아내는 함수\n",
"def count_common_words(text1, text2):\n",
" words1 = text1.split()\n",
" words2 = text2.split()\n",
" common_words = set(words1) & set(words2)\n",
" return len(common_words)\n",
"\n",
"# 비교할 질문 텍스트를 설정\n",
"question = '한국의 전통 음식에 대해 무엇을 알고 계신가요?'\n",
"\n",
"# 비교할 레퍼런스 텍스트들을 설정\n",
"ref_texts = [\n",
" '한국의 전통 음식은 다양한 재료와 조리법으로 유명합니다. 김치, 불고기, 비빔밥 등이 대표적인 예시입니다.',\n",
" '한국의 음식 문화는 건강에 좋은 재료를 사용하는 것으로 알려져 있습니다. 발효 식품인 김치가 대표적입니다.',\n",
" '한국의 역사와 문화는 매우 흥미롭습니다. 고궁과 한복, 그리고 태권도 등이 유명한 문화 요소입니다.'\n",
"]\n",
"\n",
"#질문과 레퍼런스를 지교해서 공통된 단어수를 리스트로 저장\n",
"common_word_counts = [count_common_words(question, ref_text) for ref_text in ref_texts]\n",
"\n",
"# 리스트에서 가장 유사한 텍스트 번호와 겹치는 단의의 수를 출력\n",
"most_similar_index = common_word_counts.index(max(common_word_counts))\n",
"print(f\"가장 유사한 텍스트 번호: {most_similar_index+1}\")\n",
"print(f\"겹치는 단어 수 : {common_word_counts[most_similar_index]}\")"
Expand Down
47 changes: 36 additions & 11 deletions rag_data.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -33,8 +33,11 @@
}
],
"source": [
"#huggingface_hub 라이브러리에서 snapshot_download 함수를 가져와 \n",
"# 특정 리포지토리의 스냅샷을 다운로드하는데 사용\n",
"from huggingface_hub import snapshot_download\n",
"\n",
"# 로컬 디렉토리에서(res) 데이터를 다운로드\n",
"snapshot_download(\n",
" repo_id='allganize/rag-ko',\n",
" repo_type='dataset',\n",
Expand All @@ -47,7 +50,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -273,14 +276,14 @@
"source": [
"import pandas as pd\n",
"\n",
"\n",
"# 지정된 경로에 있는 파일을 읽어와 DataFrame으로 출력\n",
"df = pd.read_parquet('./res/rag-ko/data/test-00000-of-00001.parquet')\n",
"df"
]
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": null,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -339,12 +342,14 @@
}
],
"source": [
"# 사용자의 질문에 대해 답변할 때 준수해야 하는 규칙이 적혀져 \n",
"# 있는 DataFrame df에서 첫 번째 행(iloc 사용)의 'system' 값을 출력\n",
"print(df.iloc[0]['system'])"
]
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": null,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -396,30 +401,42 @@
}
],
"source": [
"# re 라이브러리를 가져와 정규 표현식 기능을 사용\n",
"# 문자열에서 패턴을 찾고 처리\n",
"import re\n",
"\n",
"# 'system_prompt' 내에 포함된 CONTEXT 정보를 추출하는 함수\n",
"def extract_contexts(system_prompt):\n",
" # 정규 표현식을 사용하여 system_prompt에서 'CONTEXT' 내용 전체를 추출\n",
" context_match = re.search(r'CONTEXT=\"\"\"\\n(.*?)\"\"\"', system_prompt, re.DOTALL)\n",
" # 'CONTEXT' 없으면 빈 리스트를 반환\n",
" if not context_match:\n",
" return []\n",
"\n",
" \n",
" # 'CONTEXT' 부분을 텍스트로 저장\n",
" context_text = context_match.group(1)\n",
"\n",
" # 정규 표현식을 사용하여 모든 context를 찾은 후 추출\n",
" contexts = re.findall(r'\\(context \\d+\\)=(.*?)(?=\\n\\(context \\d+\\)=|\\Z)', context_text, re.DOTALL)\n",
"\n",
" # 정리된 context를 저장 함수\n",
" cleaned_contexts = []\n",
" for context in contexts:\n",
" #각 컨텍스트를 행 분리\n",
" lines = context.split('\\n')\n",
" #제목(\"Title:\")으로 시작하는 줄을 제외\n",
" cleaned_lines = [line for line in lines if not line.strip().startswith('Title:')]\n",
" # 나머지 내용을 리스트에 추가\n",
" cleaned_context = '\\n'.join(cleaned_lines).strip()\n",
" cleaned_contexts.append(cleaned_context)\n",
"\n",
" return cleaned_contexts\n",
"\n",
"#system_prompt에서 추출된 context를 출력\n",
"system_prompt = df.iloc[0]['system']\n",
"\n",
"extracted_contexts = extract_contexts(system_prompt)\n",
"\n",
"#추출한 context들을 출력\n",
"for i, context in enumerate(extracted_contexts, 1):\n",
" print(f\"Context {i}:\")\n",
" print(context)\n",
Expand All @@ -428,7 +445,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": null,
"metadata": {},
"outputs": [
{
Expand All @@ -443,15 +460,17 @@
}
],
"source": [
"#'system_prompt'에서 추출된 세 번째 context와 context 요약이 동일한지 비교\n",
"extracted_contexts[2] == df.iloc[0]['answer_context_summary']"
]
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#'df'의 각 행을 순차적으로 처리하는 함수\n",
"contexts = []\n",
"for i in range(len(df)):\n",
" system_prompt = df.iloc[i]['system']\n",
Expand All @@ -461,24 +480,29 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#각 행의 'human'에서 질문을 추출하여 리스트로 저장\n",
"questions = [df.iloc[i]['human'] for i in range(len(df))]\n",
"# 각 행의 'answer_position' 값에서 1을 빼서 텍스트을 추출하여 리스트로 저장\n",
"contexts_answers_idxs = [df.iloc[i]['answer_position'] - 1 for i in range(len(df))]\n",
"#각 행의 'answer_context_summary' 에서 텍스트을 추출하여 리스트로 저장\n",
"contexts_answers = [df.iloc[i]['answer_context_summary'] for i in range(len(df))]\n",
"# 행의 'answer' 컬럼에서 응답을을 추출하여 리스트로 저장\n",
"answers = [df.iloc[i]['answer'] for i in range(len(df))]"
]
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pickle\n",
"\n",
"# 추출한 텍스트들을 딕셔너리에 저장\n",
"rag_data = {\n",
" 'questions': questions,\n",
" 'contexts': contexts,\n",
Expand All @@ -487,6 +511,7 @@
" 'answers': answers\n",
"}\n",
"\n",
"#'rag_data' 딕셔너리를 파일에 저장\n",
"with open('./res/rag_data.pkl', 'wb') as f:\n",
" pickle.dump(rag_data, f)"
]
Expand Down
Loading