ChatGPT 등 LLM을 바탕으로 프롬프트를 작성하고, 결과를 도출하려고 하면 hallucination 현상이 발생하곤 한다. LLM들이 질의응답이나, 정보 검색용으로 사용되고 있는 최근의 추세를 생각해 봤을 때, 위의 현상은 위험하다고 볼 수 있다.
그래서 개발자들은 개발 과정에서 해당 현상이 발생하는 것을 방지하고, LLM들이 자사 데이터들을 기반으로 더 정확한 답을 할 수 있도록 하기 위해 LangChain과 같은 프레임워크를 사용하고, 다양한 문서들을 참조할 수 있다.
이에 따라 해당 문서에서는 다양한 형태의 문서(Document)들을 참조해, LangChain으로 작업하는 방법을 정리하려고 한다.
다음 방법의 순서는 5가지로 다음과 같다.
해당 문서에서는 우선적으로 위의 3개에 대해 정리하려고 한다.
from langchain.document_loaders import TextLoader
loader = TextLoader("reference.txt",encoding="utf-8")
loader.load()
해당 방식으로 불러오면 Document 객체에 텍스트가 담겨있는 모습을 확인할 수 있다.
여기서는 주의할 점은 인코딩인데, 인코딩에 유의해서 불러오면 문제는 없다.
일단 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와 같은 기능들이 제공되고, 안에 있는 내용을 다 확인해 볼 수 있다.
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 기술을 바탕으로 텍스트를 만들어낸다.
사실 이 말고도 굉장히 많은 데이터들을 가져와서 텍스트화 시키고 참조 문서로 만들 수 있다.
가져와진 문서들은 Vector Store에 저장되기 전에 청킹되어야한다. 청킹되는 과정은 의미상으로 유의미한 관계를 갖도록 청킹해야 하기 때문에 중요하다.
LangChain의 텍스트 분할기에는 두가지 방법이 있다.
두 방법 모두 동일한 논리를 이용하지만, 하나는 텍스트 목록을 입력으로 받고, 다른 하나는 문서 목록을 입력으로 받는다.
또 LangChain에서 제공 받는 텍스트 분할기들은 청킹하는 방식부터, 토큰화 되었을 때 길이를 측정하는 방식 등등이 다양하다.
대략적인 사용법은 다음과 같다.
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))
from langchain.text_splitter import CharacterTextSplitter
c_splitter = CharacterTextSplitter(
chunk_size=450,
chunk_overlap=0,
separator = ' '
)
print(c_splitter.split_text(some_text))
context라고 쓰기는 했는데 의미상의 문맥 같은 것이 아니다.
우리가 수집한 문서가 마크 다운 문서나, 그런 것이라면 특정한 구조 속에 포함되어 있을 수 있는데, 그런 구조를 바탕으로 한 텍스트들의 분석을 의미한다.
다음 모듈들을 통해 해당 기능들을 수행할 수 있다.
from langchain.document_loaders import NotionDirectoryLoader
from langchain.text_splitter import MarkdownHeaderTextSplitter
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개를 확인할 수 있다.