LLM의 막강한 자연어 처리 기능에 자신만의 데이터를 추가하여 서비스를 구성하기 위해서 prompt에 사용자 데이터를 함께 보내주는 방식을 통해 이를 해결하고자 하는 다양한 시도들이 진행 되고 있습니다.
물론 LangChain도 쉽게 사용자 데이터를 처리하는 여러가지 방법을 제공하고 있습니다.
LangChain에서 사용자데이터를 다루기 위해서는 기본적으로 다음과 같은 절차가 필요합니다.
- 사용자 데이터 확보(pdf, web, csv.... 등등)
- documeent load를 통해 사용자 데이터 로드
- LLM에서 처리 가능한 크기로 데이터 분할
- 문자나 워드 등을 모델이 분석 가능한 vector data로 임베딩
- 임베딩벡터와 문자열을 verctor store에 저장
- 임베딩된 사용자 데이터와 문자열과 질문을 LLM 모델에 전달
- 결과 응답
chatGPT 3.5를 학습시킬때 없었던 2022년 발생한 우크라이나-러시아 전쟁의 내용을 설명한 wikipedia 문서의 web url을 이용하여 위와 같은 절차를 수행하는 과정을 구현해 보겠습니다.
1. chat 모델 지정
import os
import apikeys #user defind, apikeys 숨기기
from langchain.chat_models import ChatOpenAI
OPENAI_API_KEY = apikeys.OPENAI_API_KEY
#환경 변수에 넣어준다
os.environ[ "OPENAI_API_KEY" ] = OPENAI_API_KEY
#LLM 모델 지정
chatAI = ChatOpenAI(model_name= 'gpt-3.5-turbo' , temperature= 0.1 )
2. 사용자 데이터 소스 지정
2022년 발생한 우크라이나-러시아 전쟁의 내용을 설명한 wikipedia 문서를 지정
#사용자의 데이터 소스 지정 (지원타입확인)
#웹페이지에서 가져오는 loader 지정
from langchain.document_loaders import WebBaseLoader
#위키피디아 러시아-우크라이나 전쟁에대해 설명한 페이지.
#2022년 발생했기 때문에 gpt-3.5는 이 내용을 알지 못한다.
documents = loader.load()
3. 문서 분할과 문자열 embedding 도구 선택
문자열을 임베딩하는 도구는 여러가지가 있으나 openAI것을 사용하면 변환시에도 토큰을 사용하기 때문에 비용이 발생하게 됩니다.
여기서 처리하는 정도는 무료로 제공하는 Hugging face에서 제공하는 임베딩 도구를 사용해도 문제가 없기 때문에 무료 embedding 도구를 사용하도록 하겠습니다.
다음 패키지 설치 (HuggingFace Embedding 도구 사용시 필요)
> pip install sentence_transformers
#문자열을 vector embbedding하기
#vector embedding 도구는 여러가지가 있으니 적당한 것 사용하면 됨
#Embedding : LLM에서 text를 float 특징 벡터로 변환하는것
#의미상 비슷한 input이 들어갔을때, 나오는 특징 벡터가 유사하다
#from langchain.embeddings import OpenAIEmbeddings #유료
#embeddings = OpenAIEmbeddings()
#여기서는 HuggingFace를 사용한다.
#pip install sentence_transformers # HuggingFace Embedding 사용 위해서 필요
from langchain.embeddings import HuggingFaceEmbeddings #무료
embeddings = HuggingFaceEmbeddings()
'''
Downloading (…)a8e1d/.gitattributes: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████| 1.18k/1.18k [00:00<00:00, 294kB/s]
Downloading (…)_Pooling/config.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 190/190 [00:00<00:00, 75.6kB/s]
Downloading (…)b20bca8e1d/README.md: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████| 10.6k/10.6k [00:00<00:00, 3.52MB/s]
Downloading (…)0bca8e1d/config.json: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 571/571 [00:00<00:00, 191kB/s]
Downloading (…)ce_transformers.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 116/116 [00:00<00:00, 27.8kB/s]
Downloading (…)e1d/data_config.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████| 39.3k/39.3k [00:00<00:00, 9.67MB/s]
Downloading pytorch_model.bin: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████| 438M/438M [00:10<00:00, 42.2MB/s]
Downloading (…)nce_bert_config.json: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████| 53.0/53.0 [00:00<00:00, 17.7kB/s]
Downloading (…)cial_tokens_map.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 239/239 [00:00<00:00, 79.5kB/s]
Downloading (…)a8e1d/tokenizer.json: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████| 466k/466k [00:01<00:00, 360kB/s]
Downloading (…)okenizer_config.json: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 363/363 [00:00<00:00, 121kB/s]
Downloading (…)8e1d/train_script.py: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████| 13.1k/13.1k [00:00<00:00, 5.24MB/s]
Downloading (…)b20bca8e1d/vocab.txt: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████| 232k/232k [00:00<00:00, 660kB/s]
Downloading (…)bca8e1d/modules.json: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████| 349/349 [00:00<00:00, 173kB/s]
'''
#text splitter 설정
#문서의 양이 많기 때문에 여러개의 서브문서로 분할 하는데 사용
#text splitter 중 CharacterTextSplitter를 사용
from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(chunk_size= 1500 , chunk_overlap= 0 )
text splitter에서 사용하는 chunk_size는 문서를 분할 하는데 얼마만큼의 크기로 자를것인지를 지정하는 것이며 임의의 값으로 설정하면 됩니다.
이후에 FAISS같은 유사도 검색도구를 통해서 질문과 유사한 내용을 포함하는 문서들(1500으로 잘린 배열들)을 찾아서 LLM에 함께 전달하게 됩니다.
4. VectorStore 설정
document loader로 읽어들인 데이터를 text_splitter를 통해 분할하고 FAISS를 통해 유사도 검색을 할 수있도록 vectorstore를 설정 하는 과정입니다.
"#유사도 검색 FAISS 동작 시험" 주석으로 처리한 부분은 FAISS도구를 통해서 유사도 검색을 했을때 분할된 문서들에서 유사한 내용을 포함한 문서만 추출해서 잘 알려주는지를 확인하기 위해 테스트해본 것입니다.
실제로는 내부에서 알아서 동작하기 때문에 VectorstoreIndexCreator 설정만 해주면 됩니다.
#vectorstore 설정
#vectorstore는 Embedding 벡터와 텍스트를 저장하는 DB
#여기서 사용하는 FAISS는 유사도 검색모델 중 하나로 단어나 문장의 의미가 비슷한 것을 찾을 수 있다.
#pip install faiss-cpu 설치 필요. FAISS를 사용하기 위해서
#pip install flask-sqlalchemy 필요
from langchain.indexes import VectorstoreIndexCreator
from langchain.vectorstores import FAISS #무료
#유사도 검색 FAISS 동작 시험
#전체 데이터를 분할해놓은 서브문서들 중에서 질문과 유사한 내용이 있는 문서들을 찾아낸다.
#docs = text_splitter.split_documents(documents)
#db = FAISS.from_documents(docs, embeddings)
#result = db.similarity_search("우크라이나 전쟁이 일어난 년도는?")
#print(result)
#loader로 읽어들인 데이터를 text_splitter를 통해 분할하고 FAISS를 통해 유사도 검색을 할 수있도록 vectorstore를 설정
index = VectorstoreIndexCreator(
vectorstore_cls=FAISS,
embedding=embeddings,
text_splitter=text_splitter
).from_loaders([loader])
5. VectorStore DB 저장 및 질의 응답 해보기
DB를 저장하면 저장명의 폴더가 생성되고 index.faiss, index.pkl 파일이 저장됩니다.
이후에 다시 사용할 때는 embedding 과정 없이 바로 사용할 수 있습니다.
# 이후 재사용을 위해서 vector db를 파일로 저장
# 재사용 시에는 embedding과정 필요없음
index.vectorstore.save_local( "faiss-rus-ukr" )
#faiss-rus-ukr 폴더가 생성되고 하위에
#index.faiss, index.pkl 파일이 저장됨
#전체 데이터를 분할해놓은 서브문서들 중에서 질문과 유사한 내용이 있는 문서들을 찾아내서
#chat모델에 전달하고 응답을 받음
result = index.query( "2022년 이후 러시아 우크라이나 전쟁의 상황에 대해 설명해줘" , llm=chatAI, verbose= True )
print (result)
2022이후의 상황에 대해서 설명을 잘 해주는 것을 보니 사용자 데이터의 내용을 이용해서 응답이 잘 생성된 것 같습니다.
'''
2022년 이후 러시아-우크라이나 전쟁은 여러 전선에서 치열하게 전개되고 있습니다. 러시아는 우크라이나의 다양한 지역을 타격하고 점령하려는 시도를 하고 있으며, 우크라이나는 이에 대항하여 저항하고 반격을 시도하고 있습니다.
전쟁은 2022년 2월 24일에 러시아의 침공으로 시작되었습니다. 러시아는 키이우, 하르키우, 크림반도, 도네츠크, 루한스크 등 다양한 지역에서 공격을 진행하였습니다. 우크라이나는 강력한 저항을 펼쳐 러시아군에 큰 손실을 입히고, 일 부 지역에서는 후퇴시키기도 했습니다. 그러나 러시아는 계속해서 지상 공격과 미사일 공습을 이어가고 있습니다.
전쟁은 국제적으로 침략 전쟁으로 비난받고 있으며, 유엔 총회와 국제사법재판소 등에서 러시아의 군사 작전을 중단하라는 요구가 이루어지고 있습니다. 여러 국가들은 러시아에게 경제 제재를 가하고 우크라이나에게 인도적, 군사적 지원을 제공하고 있습니다.
전쟁은 아직 진행 중이기 때문에 상황은 계속 변하고 있습니다. 따라서 최신 상황을 확인하기 위해서는 관련 뉴스나 신뢰할 수 있는 정보원을 참고하는 것이 좋습니다.
'''
참고적으로 다음은 다양한 vector embedding 도구들과 DB 들입니다.
'''
다양한 vector embedding 도구들
__all__ = [
"OpenAIEmbeddings",
"HuggingFaceEmbeddings",
"CohereEmbeddings",
"ClarifaiEmbeddings",
"ElasticsearchEmbeddings",
"JinaEmbeddings",
"LlamaCppEmbeddings",
"HuggingFaceHubEmbeddings",
"MlflowAIGatewayEmbeddings",
"ModelScopeEmbeddings",
"TensorflowHubEmbeddings",
"SagemakerEndpointEmbeddings",
"HuggingFaceInstructEmbeddings",
"MosaicMLInstructorEmbeddings",
"SelfHostedEmbeddings",
"SelfHostedHuggingFaceEmbeddings",
"SelfHostedHuggingFaceInstructEmbeddings",
"FakeEmbeddings",
"AlephAlphaAsymmetricSemanticEmbedding",
"AlephAlphaSymmetricSemanticEmbedding",
"SentenceTransformerEmbeddings",
"GooglePalmEmbeddings",
"MiniMaxEmbeddings",
"VertexAIEmbeddings",
"BedrockEmbeddings",
"DeepInfraEmbeddings",
"DashScopeEmbeddings",
"EmbaasEmbeddings",
"OctoAIEmbeddings",
"SpacyEmbeddings",
"NLPCloudEmbeddings",
"GPT4AllEmbeddings",
]
'''
'''
다양한 vector db 들
__all__ = [
"AlibabaCloudOpenSearch",
"AlibabaCloudOpenSearchSettings",
"AnalyticDB",
"Annoy",
"AtlasDB",
"AwaDB",
"AzureSearch",
"Cassandra",
"Chroma",
"Clickhouse",
"ClickhouseSettings",
"DeepLake",
"DocArrayHnswSearch",
"DocArrayInMemorySearch",
"ElasticVectorSearch",
"ElasticKnnSearch",
"FAISS",
"PGEmbedding",
"Hologres",
"LanceDB",
"MatchingEngine",
"Marqo",
"Milvus",
"Zilliz",
"SingleStoreDB",
"Chroma",
"Clarifai",
"OpenSearchVectorSearch",
"AtlasDB",
"DeepLake",
"Annoy",
"MongoDBAtlasVectorSearch",
"MyScale",
"MyScaleSettings",
"OpenSearchVectorSearch",
"Pinecone",
"Qdrant",
"Redis",
"Rockset",
"SKLearnVectorStore",
"SingleStoreDB",
"StarRocks",
"SupabaseVectorStore",
"Tair",
"Tigris",
"Typesense",
"Vectara",
"VectorStore",
"Weaviate",
"Zilliz",
"PGVector",
]
'''