실제 GPT 서비스들을 이용해 보면서 LLM에 관심이 생겼고, 실제 운영환경에서는 다양한 기법들을 접목해서 사용하는 것을 알았다.
그 중 하나가 RAG라는 벡터 임베딩을 활용한 유사도 검색을 통해서 LLM의 환각을 막는 것인데, 이 RAG를 직접 LLM 시스템에 붙여서 사용해보고자 한다.
Vector DB
벡터 데이터베이스를 GPT에게 물어보니 다양한 것들이 있었고, VectorDB가 내장되어 사용가능한 라이브러리도 여러가지가 있었다. 그 중에서 상업적으로도 무료로 이용가능하고, 로컬 환경에서 벡터 디비를 활용해 안전한 Qdrant를 사용해보고자 한다.
Qdrant
Qdrant는 설치할 필요도 없이 도커를 통해 손쉽게 로컬 환경에서 구동해줄 수 있다. mysql 설치처럼 간단하게 사용가능할 것 같다.
docker pull qdrant/qdrant
docker run -p 6333:6333 qdrant/qdrant
이후에는 파이썬 코드를 통해 서버와 상호작용한다.
pip install qdrant-client
Qdrant 서버에 접속하기
도커로 실행되고 있는 qdrant서버에 접속하기 위해 qdrant-client 패키지를 사용합니다. 다음 코드를 이용해 qdrant서버에 접속해줍니다.
from qdrant_client import QdrantClient
from qdrant_client.http.models import Distance, VectorParams, PointStruct, PointInsertOperations
qdrant = QdrantClient(host="localhost", port=6333)
Qdrant Collection 만들기
qdrant.create_collection(
collection_name="klue_mrc_contexts",
vectors_config=VectorParams(size=384, distance=Distance.COSINE)
)
콜렉션 이름과, 백터에 대한 환경변수를 지정해 콜렉션을 만들어 줄 수 있다.
위에서는 간단하게 테스트용도로 벡터 크기를 768, 벡터간 비교 방법을 코사인 유사도를 사용한다.
그외 다른 설정에 대해서는 아래 공식 문서를 참고한다.
Create a collection | Qdrant | API Reference
api.qdrant.tech
데이터 셋과 토큰 임베딩을 위한 토크나이저
from datasets import load_dataset
dataset = load_dataset('klue', 'mrc', split='train')
print(dataset[0])
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2')
# 문맥(context)만 추출
contexts = list(set(example['context'] for example in dataset)) # 중복 제거
context_embeddings = model.encode(contexts, show_progress_bar=True)
테스트를 위해 klue 데이터셋을 불러오고, 해당 데이터셋을 토큰화하기 위해 버트 모델을 사용한다.
qdrant에 저장하기
qdrant.upsert(
collection_name="klue_mrc_contexts",
points=[
{
"id": idx,
"vector": vec.tolist(),
"payload": {"context": contexts[idx]}
}
for idx, vec in enumerate(context_embeddings)
]
)
from tqdm import tqdm
batch_size = 100
for i in tqdm(range(0, len(context_embeddings), batch_size)):
batch_vectors = context_embeddings[i:i+batch_size]
batch_payloads = contexts[i:i+batch_size]
client.upsert(
collection_name="klue_mrc_contexts",
points=[
{
"id": i + j,
"vector": vec.tolist(),
"payload": {"context": batch_payloads[j]}
}
for j, vec in enumerate(batch_vectors)
]
)
오류 발생 시 qdrant 연결 설정에서 timeout시간을 늘려줍니다. 그래도 시간이 부족하다면 업로드 상 배치 사이즈를 정해 데이터를 나눠 업로드 하는 방법을 고려합니다.
qdrant 검색
query = "KT는 무슨 회사야?"
query_vec = model.encode(query)
results = qdrant.query_points(
collection_name="klue_mrc_contexts",
limit=5,
with_payload=True,
query=query_vec.tolist(),
)
for result in results:
for i, re in enumerate(result[1], 1):
print(f"🔹 [결과 {i}]")
print(f"🆔 ID: {re.id}")
print(f"📊 점수: {re.score:.4f}")
print(f"내용: {re.payload['context'][:100]}")
print()
쿼리에 대한 문맥을 반환합니다. 이 문맥을 LLM 프롬프트에 결합시켜서 사용하면 환각을 막을 수 있습니다.
유사도 점수도 같이 나와서, 특정 점수 이상의 문맥만 포함시키는게 가능.
통합방법 ?
1. 사용자가 prompt로 질문을 한다.
2. 벡터 DB에서 사용자 prompt에 대한 맥락을 찾아 LLM 모델에 들어갈 prompt에 추가한다. (환각 방지)
3. 사용자의 질문을 새로 벡터화 해 DB에 저장한다. ?
'인공지능 > LLM 거대모델' 카테고리의 다른 글
파인튜닝 (0) | 2025.03.06 |
---|---|
딥시크(DeepSeek)로 로컬 LLM 구축하기: 쉽고 빠른 AI 모델 활용법 (0) | 2025.03.02 |
오퍼레이터 설계모델 (0) | 2025.02.24 |
LLM을 파인튜닝하다 (0) | 2025.02.17 |
NLP 과거와 오늘, GPT (0) | 2025.02.16 |