참조 문서 기반으로 LangChain 사용하기(1)

김지우·2023년 11월 21일

1. 배경

ChatGPT 등 LLM을 바탕으로 프롬프트를 작성하고, 결과를 도출하려고 하면 hallucination 현상이 발생하곤 한다. LLM들이 질의응답이나, 정보 검색용으로 사용되고 있는 최근의 추세를 생각해 봤을 때, 위의 현상은 위험하다고 볼 수 있다.

그래서 개발자들은 개발 과정에서 해당 현상이 발생하는 것을 방지하고, LLM들이 자사 데이터들을 기반으로 더 정확한 답을 할 수 있도록 하기 위해 LangChain과 같은 프레임워크를 사용하고, 다양한 문서들을 참조할 수 있다.

이에 따라 해당 문서에서는 다양한 형태의 문서(Document)들을 참조해, LangChain으로 작업하는 방법을 정리하려고 한다.

다음 방법의 순서는 5가지로 다음과 같다.

  • Document Loading
  • Document Splitting
  • Vector Store and Embedding
  • Retrieval
  • Question Answering

업로드중..

해당 문서에서는 우선적으로 위의 3개에 대해 정리하려고 한다.

2. Document Loading

1) 텍스트 파일

from langchain.document_loaders import TextLoader

loader = TextLoader("reference.txt",encoding="utf-8")
loader.load()

해당 방식으로 불러오면 Document 객체에 텍스트가 담겨있는 모습을 확인할 수 있다.
여기서는 주의할 점은 인코딩인데, 인코딩에 유의해서 불러오면 문제는 없다.

2) PDF 파일

일단 PDF 파일을 읽어오는 작업을 하기 전에 pip install pypdf 로 라이브러리를 설치 해야한다.

from langchain.document_loaders import PyPDFLoader

loader = PyPDFLoader("KCB_imms.pdf")
#Load the document by calling loader.load()
pages = loader.load()

print(len(pages))
print(pages[0].page_content[:])

위와 같은 코드로 pages 라는 리스트를 만들 수 있다. 당연히 append, pop. sort와 같은 기능들이 제공되고, 안에 있는 내용을 다 확인해 볼 수 있다.

3) 유튜브 동영상

from langchain.document_loaders.generic import GenericLoader
from langchain.document_loaders.parsers import OpenAIWhisperParser
from langchain.document_loaders.blob_loaders.youtube_audio import YoutubeAudioLoader

url="https://www.youtube.com/watch?v=jGwO_UgTS7I"
save_dir="docs/youtube/"
loader = GenericLoader(
    YoutubeAudioLoader([url],save_dir),
    OpenAIWhisperParser()
)
docs = loader.load()

print(docs[0].page_content[0:500])

유튜브 영상에 있는 정보들도 문서로 활용할 수 있는데, GenericLoader를 이용해 , OpenAIWhisperParser와 YoutubeAudioLoader를 활용하는 것을 확인할 수 있다.

왜 이건 뭔데 이렇게 많이 쓰냐 싶을 수 있는데, YoutubeAudioLoader가 유튜브에서 영상에서 활용된 오디오 영상을 가져오는 기능이고, OpenAIWhipserParser를 이용해 STT 기술을 바탕으로 텍스트를 만들어낸다.

사실 이 말고도 굉장히 많은 데이터들을 가져와서 텍스트화 시키고 참조 문서로 만들 수 있다.

3. Data splitting

가져와진 문서들은 Vector Store에 저장되기 전에 청킹되어야한다. 청킹되는 과정은 의미상으로 유의미한 관계를 갖도록 청킹해야 하기 때문에 중요하다.

LangChain의 텍스트 분할기에는 두가지 방법이 있다.

  • 문서 생성
  • 문서 분할

두 방법 모두 동일한 논리를 이용하지만, 하나는 텍스트 목록을 입력으로 받고, 다른 하나는 문서 목록을 입력으로 받는다.

또 LangChain에서 제공 받는 텍스트 분할기들은 청킹하는 방식부터, 토큰화 되었을 때 길이를 측정하는 방식 등등이 다양하다.

대략적인 사용법은 다음과 같다.

RecursiveCharacterTextSplitter

from langchain.text_splitter import RecursiveCharacterTextSplitter

some_text = """When writing documents, writers will use document structure to group content. \
This can convey to the reader, which idea's are related. For example, closely related ideas \
are in sentances. Similar ideas are in paragraphs. Paragraphs form a document. \n\n  \
Paragraphs are often delimited with a carriage return or two carriage returns. \
Carriage returns are the "backslash n" you see embedded in this string. \
Sentences have a period at the end, but also, have a space.\
and words are separated by space."""

r_splitter = RecursiveCharacterTextSplitter(
    chunk_size=450,
    chunk_overlap=0, 
    separators=["\n\n", "\n", " ", ""]
)

print(r_splitter.split_text(some_text))

CharacterTextSplitter

from langchain.text_splitter import CharacterTextSplitter
c_splitter = CharacterTextSplitter(
    chunk_size=450,
    chunk_overlap=0,
    separator = ' '
)

print(c_splitter.split_text(some_text))

Context-aware splitting

context라고 쓰기는 했는데 의미상의 문맥 같은 것이 아니다.
우리가 수집한 문서가 마크 다운 문서나, 그런 것이라면 특정한 구조 속에 포함되어 있을 수 있는데, 그런 구조를 바탕으로 한 텍스트들의 분석을 의미한다.

다음 모듈들을 통해 해당 기능들을 수행할 수 있다.

from langchain.document_loaders import NotionDirectoryLoader
from langchain.text_splitter import MarkdownHeaderTextSplitter

4. Vector Store Embeddings

vectorstore에 임베딩한 벡터들을 저장하는 이유는 질의응답시에 빠르게 정보를 찾을 수 있도록 하기 위함이다.

임베딩 작업은 텍스트의 수적 표현을 가능하게 한다. 이를 바탕으로 다른 텍스트와의 유사도를 비교할 수 도 있다.

vector store 는 일종의 데이터베이스로 질문에 관련있는 문서를 찾을 때 유용하다.

from langchain.vectorstores import Chroma
from langchain.embeddings.openai import OpenAIEmbeddings

embedding = OpenAIEmbeddings()

# 앞으로도 활용할 수 있도록 저장될 경로를 설정
persist_directory = ''

#Create the vector store
vectordb = Chroma.from_documents(
    documents =splits,
    embedding=embedding,
    persist_directory=persist_directory
)

# 몇개의 문서가 있는지 확인 
print(vectordb._collection.count())
question = "사회 과학 연구와 비슷한 과목은 어떤게 있을까?"

docs = vectordb.similarity_search(question, k=3)

print(len(docs))

print(docs[0].page_content)

print(vectordb.persist())

vector store를 활용하면 위와 같이 유사도 검사를 할 수 있게된다.
해당 코드에서 가장 질문에 답을 해줄 수 있을 것 같은 문서 3개를 확인할 수 있다.

profile
프로그래밍 기록 + 공부 기록

0개의 댓글