LlamaIndex 시작하기

김태훈 2024년 02월 18일 #LLM #RAG #LlamaIndex

시작하기

설치

pip install llama-index

중요: OpenAI 환경 설정

기본적으로 텍스트 생성에는 OpenAI gpt-3.5-turbo 모델을, 검색 및 임베딩에는 text-embedding-ada-002모델을 사용합니다. 이 모델을 사용하려면 환경 변수로 OPENAI_API_KEY가 설정되어 있어야 합니다. API 키는 OpenAI 계정에 로그인하여 새 API 키를 생성하면 얻을 수 있습니다.

$ export OPENAI_API_KEY=<YOUR_OPENAI_API_KEY>

샘플 데이터 다운로드

$ wget https://raw.githubusercontent.com/run-llama/llama_index/main/examples/paul_graham_essay/data/paul_graham_essay.txt
$ mkdir data
$ mv paul_graham_essay.txt data/

데이터 로드 및 인덱싱

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)

작업 디렉토리 구조

├── starter.py
└── data
    └── paul_graham_essay.txt

커스텀 데이터 쿼리

query_engine = index.as_query_engine()
response = query_engine.query("저자는 어렸을 때 무엇을 했나요?")
print(response)

결과

notion image

로깅을 사용하여 쿼리 및 이벤트 보기

starter.py 파일 상단에 아래 코드를 추가 합니다.

import logging
import sys

logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))

자세한 출력을 원하면 로깅 레벨을 DEBUG로 설정하고, 이보다 간략하게 출력하려면 level=logging.INFO를 사용하면 됩니다.

인덱스 저장

기본적으로 방금 로드한 데이터는 일련의 벡터 임베딩으로 메모리에 저장됩니다. 임베딩을 디스크에 저장하면 시간을 절약하고 OpenAI에 대한 요청도 줄일 수 있습니다. 아래 코드를 추가하면 됩니다:

index.storage_context.persist()

기본적으로 데이터를 디렉토리 저장소에 저장하지만 persist_dir 매개변수를 전달하여 이를 변경할 수 있습니다.

물론 데이터를 로드하지 않으면 지속성의 이점을 얻을 수 없습니다. 따라서 색인이 존재하지 않으면 생성하여 저장하고, 존재하면 로드하도록 starter.py를 수정해 보겠습니다:

import os.path
from llama_index import (
    VectorStoreIndex,
    SimpleDirectoryReader,
    StorageContext,
    load_index_from_storage,
)


# 'storage' 폴더가 없으면
if not os.path.exists("./storage"):
    # 문서를 로드하고 색인을 생성합니다.
    documents = SimpleDirectoryReader("data").load_data()
    index = VectorStoreIndex.from_documents(documents)
    # 나중에 사용할 수 있도록 저장
    index.storage_context.persist()
else:
    # 저장된 인덱스 로드
    storage_context = StorageContext.from_defaults(persist_dir="./storage")
    index = load_index_from_storage(storage_context)

# 어느 쪽이든 이제 인덱스를 쿼리할 수 있습니다.
query_engine = index.as_query_engine()
response = query_engine.query("저자는 어렸을 때 무엇을 했나요?")
print(response)

결과

notion image

상위 개념

Retrieval Augmented Generation (RAG)

머신러닝은 방대한 데이터에 대해 학습하지만 사용자 데이터에 대해서는 학습하지 않습니다. 검색 증강 생성(Retrieval Augmented Generation**, RAG**)은 LLM이 이미 액세스하고 있는 데이터에 사용자 데이터를 추가하여 이 문제를 해결합니다. 이 문서에서 RAG에 대한 언급을 자주 보게 될 것입니다.

RAG에서는 데이터가 로드되고 쿼리를 위해 준비되거나 "인덱싱"됩니다. 사용자 쿼리는 인덱스에 작용하여 데이터를 가장 관련성이 높은 컨텍스트로 필터링합니다. 그러면 이 컨텍스트와 쿼리가 프롬프트와 함께 LLM으로 전달되고, LLM은 응답을 제공합니다.

챗봇이나 에이전트를 구축하는 경우에도 데이터를 애플리케이션으로 가져오기 위한 RAG 기술을 알고 싶을 것입니다.

notion image

RAG 단계

RAG에는 다섯 가지 주요 단계가 있으며, 이 단계는 여러분이 구축하는 모든 대규모 애플리케이션의 일부가 될 것입니다. 다음은 다음과 같습니다:

  1. 로딩: 텍스트 파일, PDF, 다른 웹사이트, 데이터베이스, API 등 데이터가 있는 곳에서 파이프라인으로 데이터를 가져오는 것을 말합니다. 라마허브는 선택할 수 있는 수백 개의 커넥터를 제공합니다.

  2. 인덱싱: 이는 데이터를 쿼리할 수 있는 데이터 구조를 만드는 것을 의미합니다. LLM의 경우, 이는 거의 항상 벡터 임베딩, 데이터의 의미를 수치로 표현하는 것, 그리고 맥락에 맞는 데이터를 정확하게 찾을 수 있도록 하는 다양한 메타데이터 전략을 만드는 것을 의미합니다.

  3. 저장: 데이터가 색인된 후에는 거의 항상 색인과 다른 메타데이터를 저장하여 다시 색인할 필요가 없도록 해야 합니다.

  4. 쿼리: 주어진 인덱싱 전략에 따라 하위 쿼리, 다단계 쿼리, 하이브리드 전략 등 다양한 방법으로 LLM과 LlamaIndex 데이터 구조를 활용하여 쿼리할 수 있습니다.

  5. 평가: 모든 파이프라인에서 중요한 단계는 다른 전략과 비교하여 얼마나 효과적인지 또는 언제 변경해야 하는지 확인하는 것입니다. 평가는 쿼리에 대한 응답이 얼마나 정확하고 충실하며 빠른지에 대한 객관적인 척도를 제공합니다.

notion image

각 단계의 중요 개념

또한 이러한 각 단계의 단계를 나타내는 몇 가지 용어가 있습니다.

로드 단계

인덱싱 단계

쿼리 단계

종합 정리

데이터 기반 LLM 애플리케이션의 사용 사례는 무궁무진하지만 크게 세 가지 범주로 분류할 수 있습니다:

커스터마이징 튜토리얼

이 튜토리얼에서는 시작 예제를 위해 작성한 코드부터 시작하여 사용 사례에 맞게 사용자 지정할 수 있는 가장 일반적인 방법을 보여드립니다:

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("What did the author do growing up?")
print(response)

"문서를 더 작은 청크로 파싱하고 싶습니다.”

from llama_index import ServiceContext

service_context = ServiceContext.from_defaults(chunk_size=1000)

ServiceContext는 라마 인덱스 파이프라인에서 사용되는 서비스 및 구성의 번들입니다.

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(
    documents, service_context=service_context
)
query_engine = index.as_query_engine()
response = query_engine.query("What did the author do growing up?")
print(response)

"다른 벡터 저장소를 사용하고 싶습니다.”

import chromadb
from llama_index.vector_stores import ChromaVectorStore
from llama_index import StorageContext

chroma_client = chromadb.PersistentClient()
chroma_collection = chroma_client.create_collection("quickstart")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)

StorageContext는 문서, 임베딩, 인덱스가 저장되는 저장소 백엔드를 정의합니다. 저장소저장소를 사용자 지정하는 방법에 대해 자세히 알아보세요.

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(
    documents, storage_context=storage_context
)
query_engine = index.as_query_engine()
response = query_engine.query("What did the author do growing up?")
print(response)

"쿼리할 때 더 많은 컨텍스트를 검색하고 싶습니다.”

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(similarity_top_k=5)
response = query_engine.query("What did the author do growing up?")
print(response)

as_query_engine은 인덱스 위에 기본 리트리버와 쿼리 엔진을 구축합니다. 키워드 인수를 전달하여 리트리버와 쿼리 엔진을 구성할 수 있습니다. 여기서는 기본값인 2개 대신 가장 유사한 상위 5개 문서를 반환하도록 리트리버를 구성합니다. 리트리버쿼리 엔진에서 자세히 알아보세요.

"다른 LLM을 사용하고 싶습니다.”

from llama_index import ServiceContext
from llama_index.llms import PaLM

service_context = ServiceContext.from_defaults(llm=PaLM())

LLM 커스터마이징에서 자세히 알아볼 수 있습니다.

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(service_context=service_context)
response = query_engine.query("What did the author do growing up?")
print(response)

"다른 응답 모드를 사용하고 싶습니다.”

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(response_mode="tree_summarize")
response = query_engine.query("What did the author do growing up?")
print(response)

쿼리 엔진응답 모드에서 자세히 알아볼 수 있습니다.

"응답을 다시 스트리밍하고 싶습니다.”

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(streaming=True)
response = query_engine.query("What did the author do growing up?")
response.print_response_stream()

응답 스트리밍에서 자세히 알아볼 수 있습니다.

"Q&A 대신 챗봇을 원합니다.”

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_chat_engine()
response = query_engine.chat("What did the author do growing up?")
print(response)

response = query_engine.chat("Oh interesting, tell me more.")
print(response)

채팅 엔진에서 자세히 알아볼 수 있습니다.

LlamaIndex 비디오 시리즈 알아보기

동영상으로 배우는 것을 좋아하신다면 지금이 바로 "라마인덱스 알아보기" 시리즈를 확인해 보시기 바랍니다. 그렇지 않다면 LlamaIndex 이해하기 튜토리얼을 살펴보는 것을 추천합니다.

상향식 개발(라마 문서 봇)

이 문서는 문서 챗봇을 처음부터 구축하는 방법을 보여주는 Discover LlamaIndex의 하위 시리즈입니다.

먼저 데이터 객체인 LLM을 독립적인 모듈로 사용하는 것부터 시작하여 '상향식' 방식으로 구축하는 방법을 보여드립니다. 그런 다음 인덱싱과 고급 검색기/순위 재조정기와 같은 더 높은 수준의 추상화를 점진적으로 추가하세요.

하위 질문 쿼리 엔진 + 10K 분석

이 동영상에서는 복잡한 쿼리를 여러 개의 하위 질문으로 분해하여 재무 문서에 적용하는 데 도움이 되는 SubQuestionQueryEngine과 그 적용 방법에 대해 설명합니다.

Discord 문서 관리

이 동영상에서는 지속적으로 업데이트되는 소스(예: Discord)의 문서를 관리하는 방법과 문서 중복을 방지하고 임베딩 토큰을 저장하는 방법에 대해 설명합니다.

텍스트 to SQL과 시맨틱 검색 통합

이 동영상에서는 SQL과 시맨틱 검색을 하나의 통합 쿼리 인터페이스로 결합하기 위해 LlamaIndex에 내장된 도구에 대해 설명합니다.

참고자료

[LlamaIndex 🦙 v0.10.5

LlamaIndex is a data framework for LLM-based applications which benefit from context augmentation. Such LLM systems have been termed as RAG systems, standing for “Retrieval-Augemented Generation”. LlamaIndex provides the essential abstractions to more easily ingest, structure, and access private or domain-specific data in order to inject these safely and reliably into LLMs for more accurate text generation. It’s available in Python (these docs) and Typescript.

https://docs.llamaindex.ai/en/stable/index.html](https://docs.llamaindex.ai/en/stable/index.html)