본문 바로가기
📚도서 공부/LangChain으로 구현하는 LLM

5-1(챗봇이란?, 임베딩) 챗봇 만들기

by Majestyblue 2024. 5. 2.

LLM 기반 챗봇들은 고객 서비스와 같은 대화영 작업에서는 유창하나. 세계 지식이 부족하기 때문에 주제별 질문에는 잘 답변을 못한다. 이를 극복하기 위해 RAG를 통해 어떻게 해결할 수 있는지 탐구해 본다.

핵심은 문서를 벡터 임베딩으로 말뭉치(corpus)를 인코딩해 신속한 의미 검색을 가능하게 하고, 검색 결과를 챗봇의 프롬프트에 통합하는 것이다.

주요 주제(Topic)

  • 챗봇이란 무엇인가?
  • 검색과 벡터의 이해
  • LangChain에서의 로딩 및 검색
  • 챗봇 구현
  • 응답 중재

1. 챗봇이란 무엇인가?

챗봇이란, 텍스트 또는 음성을 통한 대화 상호 작용 시뮬레이션 AI 프로그램이다. GPT-3와 같은 LLM 출현은 ChatGPT(2022)와 같은 더 인간적인 챗봇 시스템을 가능하게 하였으나, 그 능력은 여전히 상당히 제한적이다. 진정한 인간의 대화에는 복잡한 추론, 화용론(상황과 맥락에 따른 의미를 연구하는 것, 예를 들어 엄마한테 “배고파” 라고 하는 것은 ‘밥 좀 달라’ 라는 뜻으로 이해하는 것? 맞나?), 상식과 폭넓은 맥락 지식이 필요하다.

챗봇의 몇 가지 추가적인 사용례는 다음과 같다.

  • 일정 예약: 약속을 잡고 예약을 예약하며 캘린더를 관리하는데 도움을 줄 수 있다.
  • 정보 검색: 날씨 업데이트, 뉴스 기사 또는 주식 가격과 같은 구체적인 정보를 제공할 수 있다.
  • 가상 비서: 개인 비서 역할을 할 수 있으며 사용자의 알림 설정, 메시지 보내기 또는 전화 걸기와 같은 작업을 지원한다.
  • 언어 학습: 상호 작용 대화와 언어 연습을 제공해 언어 학습을 지원할 수 있다.
  • 정신 건강 지원: 정서적 지원을 제공하고 자원을 제공하며 정신 건강 목적으로 치료적인 대화에 참여할 수 있다.
  • 교육: 가상 튜터로 활용돼 학생들이 학습하고 지식을 평가하며 질문에 답하며 개인화된 학습 경험을 제공하는데 사용된다.
  • 인사 및 채용: 후보자를 스크리닝하고 면접 일정을 예약하며 채용 정보를 제공함으로써 채용 프로세스를 지원할 수 있다.
  • 엔터테이먼트: 사용자를 대화영 게임, 퀴즈 및 스토리텔링 체험에 참여시키는데 사용될 수 있다.
  • 법률: 기본 법률 정보를 제공하고 일반적인 법률 질문에 답하며 법률 연구를 지원하고 사용자가 법률 절차를 탐색하는데 도움을 줄 수 있다. 또한 계약 작성이나 법적 서류 작성과 같은 문서 작성을 지원할 수 있다.
  • 의학: 증상 확인, 기본 의료 조언 제공 및 정신 건강 지원을 지원할 수 있다. 챗봇은 관련 정보와 건강 전문가에게 권장 사항을 제공함으로써 임상 결정을 개선할 수 있다.

챗봇은 특정 사용자 요청과 의도를 직접 이해하고 충족시키는 점을 넘어 이전 상호 작용과 문맥적 단서를 기반으로 요구 사항과 선호도를 예측해 잠재적인 사용자 질문을 선제적으로 해결해야 한다.

2. 검색과 벡터의 이해

RAG는 외부 지식을 검색하고 통합함으로써 텍스트 생성을 향상시키는 기술이다. 이는 인코딩된 기존 지식에만 의존하는 것이 아니라 사실적인 정보에 근거해 출력물을 구성한다. 검색 증강 언어 모델(RALM, Retrieval-Augmented Language Models)은 훈련과 추론 과정에서 검색을 통합하는 검색 증강 언어 모델을 의미한다.

RALM은 문맥 검색 알고리즘을 사용해 외부 말뭉치에서 관련있는 문맥을 먼저 검색(쿼리 요청, Query란 데이터베이스에 질의를 보내고 결과를 얻는 것을 의미)한다. 이를 위해 일반적으로 문서를 벡터 임베딩으로 색인화해 근사 최근접 이웃 검색을 통해 빠른 유사성 조회를 가능하게 해야 한다. 검색된 증거는 언어 모델을 더 정확하고 맥락적으로 관련성 있는 텍스트를 생성하도록 조건화한다. 불확실한 부분은 다시 생성해 명확한 지식을 가져온다.

벡터 검색은 쿼리 벡터와의 유사성을 기반으로 벡터(또는 임베딩)을 검색하고 추출하는 기술로서, 추천 시스템, 이미지 및 텍스트 검색, 이상 징후 감지와 같은 애플리케이션에서 일반적으로 사용된다.

가. 임베딩

임베딩은 기계가 처리하고 이해할 수 있는 방식으로 내용을 수치적으로 표현한 것이다. 임베딩은 데이터를 다차원 벡터 공간으로 매핑하며 두 임베딩 간의 거리는 해당 개념간의 의미적 유사성을 나타낸다.

OpenAI의 임베딩의 경우 임베딩은 텍스트를 나타내는 1,536개의 부동소수점 숫자로 이루어지나 벡터이다. 만약 이 공간이 3차원인 경우, 고양이와 개에 대한 벡터를 각각 [0.5, 0.2, -0.1]과 [0.8, -0.3, 0.6]과 같을 수 있다. 이러한 벡터들은 다른 단어들과의 관계에 대한 정보를 인코딩한다. (예를 들어 고양이와 개라는 개념은 컴퓨터 보다는 동물에 더 가까울 것이다.)

1) 단어 주머니(bag-of-words)

단어 수 세서 문서-단어 행렬 만들기, CountVectorizer

 

단어 수 세서 문서-단어 행렬 만들기, CountVectorizer

텍스트 데이터를 분석할 때 가장 흔히 사용하는 방법이 단어 수를 세는 것입니다. 각각의 텍스트에 등장한 단어 수를 알면 이를 기반으로 키워드를 추출한다거나, 표절 검사, 문서 분류 등 다양

radish-greens.tistory.com

단어 주머니에서 각 단어는 텍스트에서 나타나는 횟수로 표현하는 방법이다. 사이킷런(Scikit-learn)의 CountVectorizer로 구현할 수 있다. 위 사이트 참고

2) Word2vec

문장 내에서 다른 주변 단어를 기반으로 단어를 예측해 선형 모델에서 단어 순서를 무시하고 임베딩을 학습하는 것이다. 아래 사이트를 참고해서 HuggingFace Embeddings 모델을 사용하기로 함.

2-4-2. HuggingFaceEmbeddings

 

2-4-2. HuggingFaceEmbeddings

`sentence-transformers` 라이브러리를 사용하면 HuggingFace 모델에서 사용된 사전 훈련된 임베딩 모델을 다운로드 받아서 적용할 수 있습니다. OpenAI…

wikidocs.net

jinaai/jina-embeddings-v2-small-en · Hugging Face

 

jinaai/jina-embeddings-v2-small-en · Hugging Face

The text embedding set trained by Jina AI. Quick Start The easiest way to starting using jina-embeddings-v2-small-en is to use Jina AI's Embedding API. Intended Usage & Model Info jina-embeddings-v2-small-en is an English, monolingual embedding model suppo

huggingface.co

# pip install -U sentence-transformers
from langchain_community.embeddings import HuggingFaceEmbeddings

embeddings_model = HuggingFaceEmbeddings(
    model_name='jinaai/jina-embeddings-v2-small-en',
    model_kwargs={'device':'cpu'},
    encode_kwargs={'normalize_embeddings':True},
)

embeddings_model
Some weights of BertModel were not initialized from the model checkpoint at jinaai/jina-embeddings-v2-small-en and are newly initialized: ['embeddings.position_embeddings.weight', 'encoder.layer.0.intermediate.dense.bias', 'encoder.layer.0.intermediate.dense.weight', 'encoder.layer.0.output.LayerNorm.bias', 'encoder.layer.0.output.LayerNorm.weight', 'encoder.layer.0.output.dense.bias', 'encoder.layer.0.output.dense.weight', 'encoder.layer.1.intermediate.dense.bias', 'encoder.layer.1.intermediate.dense.weight', 'encoder.layer.1.output.LayerNorm.bias', 'encoder.layer.1.output.LayerNorm.weight', 'encoder.layer.1.output.dense.bias', 'encoder.layer.1.output.dense.weight', 'encoder.layer.2.intermediate.dense.bias', 'encoder.layer.2.intermediate.dense.weight', 'encoder.layer.2.output.LayerNorm.bias', 'encoder.layer.2.output.LayerNorm.weight', 'encoder.layer.2.output.dense.bias', 'encoder.layer.2.output.dense.weight', 'encoder.layer.3.intermediate.dense.bias', 'encoder.layer.3.intermediate.dense.weight', 'encoder.layer.3.output.LayerNorm.bias', 'encoder.layer.3.output.LayerNorm.weight', 'encoder.layer.3.output.dense.bias', 'encoder.layer.3.output.dense.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.

HuggingFaceEmbeddings(client=SentenceTransformer(
  (0): Transformer({'max_seq_length': 8192, 'do_lower_case': False}) with Transformer model: BertModel 
  (1): Pooling({'word_embedding_dimension': 512, 'pooling_mode_cls_token': False, 'pooling_mode_mean_tokens': True, 'pooling_mode_max_tokens': False, 'pooling_mode_mean_sqrt_len_tokens': False, 'pooling_mode_weightedmean_tokens': False, 'pooling_mode_lasttoken': False, 'include_prompt': True})
), model_name='jinaai/jina-embeddings-v2-small-en', cache_folder=None, model_kwargs={'device': 'cpu'}, encode_kwargs={'normalize_embeddings': True}, multi_process=False, show_progress=False)
  • model_name='jinaai/jina-embeddings-v2-small-en' : 사용할 모델을 지정한다. Jina AI가 학습한 텍스트 임베딩 모델인 jina-embeddings-v2-small-en 모델을 사용한다.
  • model_kwargs={'device':'cpu'} : 모델이 CPU에서 실행되도록 설정한다. GPU를 사용할 수 있는 환경이라면 'cuda'로 설정할 수도 있다. → 근데 왜 안됨? ㅠㅠ
  • encode_kwargs={'normalize_embeddings':True} : 임베딩을 정규화하여 모든 벡터가 같은 범위의 값을 갖도록 한다. 이는 유사도 계산 시 일관성을 높여준다.
  • embeddings_model 을 출력해보면 Pooling 레이어의 word_embedding_dimension 값에서 임베딩 벡터의 크기를 확인할 수 있다. 512차원의 벡터라는 것을 알 수 있다.
text = "This is a sample query."
query_result = embeddings_model.embed_query(text)
print(query_result)
print(len(query_result))
[0.003712376346811652, -0.022395601496100426, ... 0.06151425838470459, 0.017576472833752632]

512

embed_query 메서드에 단일 문자열 입력을 전달하고 해당 텍스트 임베딩을 검색한다. 결과는 query_result 변수에 저장된다. 임베딩의 길이(차원 수)는 len() 함수를 사용해 얻을 수 있다.

embed_documents() 메서드를 이용하면 여러 문서 입력에 대한 임베딩을 얻을 수 있다.

words = ["cat", "dog", "computer", "animal"]
doc_vectors = embeddings_model.embed_documents(words)

단어 간의 유클리드 거리를 계산하고 도식화 해 보자.

from scipy.spatial.distance import pdist, squareform
import numpy as np
import pandas as pd
X = np.array(doc_vectors)
dists = squareform(pdist(X))
print(dists)
[[0.         0.40634945 0.62752575 0.45617031]
 [0.40634945 0.         0.62912312 0.45818382]
 [0.62752575 0.62912312 0.         0.60961599]
 [0.45617031 0.45818382 0.60961599 0.        ]]
df = pd.DataFrame(
    data = dists,
    index=words,
    columns=words)

df.style.background_gradient(cmap='coolwarm')

 

고양이와 개는 실제로 컴퓨터보다는 동물에 더 가깝다.