LangChain과 LLM 모델을 사용하여 애플리케이션을 만들 때 필요에 따라서 여러개의 템플릿(templates)을 미리 작성해 놓고, 사용자의 질문과 가장 유사한 (most similar, most relavant) 템플릿을 선택해서 LLM 모델로 질문과 템플릿을 인풋으로 보낼 수 있다면 보다 맞춤형의 답변을 생성할 수 있을 것입니다. 

 

LangChain - Routing templates dynamically by semantic search

 

 

시맨틱 검색은 임베딩과 코사인 유사도를 사용하는 것은 자연어 처리 및 정보 검색에서 검색 결과의 관련성과 정확성을 향상시키기 위해 쿼리의 의미에 기반하여 사용되는 방법입니다.  

1. 시맨틱 검색 (Semantic Search): 전통적인 검색 엔진은 종종 정확한 키워드 일치에 의존합니다. 반면에 시맨틱 검색은 검색 쿼리의 의도와 맥락적 의미를 이해하려고 합니다. 동의어, 관련 용어 및 전체 맥락과 같은 언어의 뉘앙스를 해석하여 더욱 관련성 높은 결과를 제공하려고 합니다.  

2. 임베딩 (Embeddings): 시맨틱 이해를 달성하기 위해 시스템은 임베딩을 사용합니다. 임베딩은 단어, 구, 문장 또는 전체 문서를 고차원 공간에서 수치적으로 표현한 것입니다. 이러한 표현은 대규모 데이터셋에서 학습되며 단어 간의 의미 관계를 포착합니다. 예를 들어, 잘 훈련된 임베딩 공간에서는 유사한 의미를 가진 단어들이 서로 가까이 위치합니다.  

3. 코사인 유사도 (Cosine Similarity): 텍스트(검색 쿼리와 검색 가능한 문서 모두)를 임베딩으로 변환한 후, 시스템은 얼마나 유사한지 측정할 방법이 필요합니다. 이를 위해 흔히 사용되는 척도는 코사인 유사도입니다. 코사인 유사도는 두 벡터(이 경우, 쿼리와 문서의 임베딩) 사이의 각도의 코사인을 측정합니다. 코사인 유사도가 1에 가까우면 매우 작은 각도를 나타내고, 따라서 높은 유사도를 의미합니다. 반대로, 코사인 유사도가 0에 가까우면 큰 각도와 낮은 유사도를 나타냅니다.  

4. 검색에서의 적용 (Application in Search): 사용자가 쿼리를 제출하면, 시스템은 이를 임베딩으로 변환한 다음 이 임베딩과 데이터베이스의 다양한 문서들의 임베딩 간의 코사인 유사도를 계산합니다. 코사인 유사도 점수가 더 높은 문서는 쿼리와 더 관련이 있다고 간주되어 검색 결과에서 더 높은 순위를 차지합니다.  

이러한 접근 방식은 시스템이 사용자 쿼리의 의미적 내용을 더 잘 포착하고 응답할 수 있게 하여, 단순히 특정 단어를 일치시키는 것보다 더 미묘하고 효과적인 검색 경험을 가능하게 합니다. 

 

 

[ 코사인 유사도와 시맨틱 검색 ]

Cosine Similarity and Text Semantic Search

 

 

 

LangChain과 ChatGPT LLM 모델을 사용해서 여러개의 템플릿을 미리 만들어놓고 질문에 가장 유사한 템플릿을 선택해서 대답을 생성하는 간단한 챗봇을 만들어보겠습니다. 

 

먼저, 터미널에서 langchain, openai, tiktoken 모듈을 pip install을 사용해서 설치합니다. 

 

! pip install -q langchain openai tiktoken

 

 

OpenAI의 ChatGPT를 LLM 모델로 사용할 것이므로 OpenAI API Key를 등록해줍니다. 

 

import os

os.environ["OPENAI_API_KEY"] = "sk-xxxx..."

 

 

필요한 모듈을 import 하고, Database PostgreSQL 과 관련된 template 1, 그리고 Cloud Kubernetes Docker 관련된 template 2 를 각각 정의해 줍니다. 

 

from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import PromptTemplate
from langchain.utils.math import cosine_similarity
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda, RunnablePassthrough

# Template 1: Database PostgreSQL
database_template = """You are an expert on database, especially open source PostgreSQL. 
You are good at answering questions about database in a concise manner. 

Here is a question:
{query}"""


# Template 2: Colud, Kubernetes, Docker
cloud_template = """You are an expert on cloud platform. 
You are good at answering questions especially on kubernetes and docker. 
The user is from Korean. Answer the question in Korean.

Here is a question:
{query}
"""

 

 

Text Embedding 모델로는 OpenAI의 1,536 차원을 가진 "text-embedding-ada-002" 를 사용하겠습니다. 

  - 여러개의 텍스트에 대해서는 embeddings.embed_documents(),

  - 하나의 query에 대해서는 embeddings.embed_query()

메소드를 사용해서 텍스트를 임베딩으로 변환합니다. 

 

# OpenAI Embeddings: https://platform.openai.com/docs/guides/embeddings
embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")

prompt_templates = [database_template, cloud_template]
prompt_embeddings = embeddings.embed_documents(prompt_templates)


query_embedding = embeddings.embed_query("What is PostgreSQL database?")

len(query_embedding)
# 1536

len(prompt_embeddings[0])
# 1536

query_embedding[:10]
# [0.011614119106739642,
#  -0.011661718070044624,
#  -0.011185729368317337,
#  -0.02718574244037603,
#  -0.043219754372800054,
#  0.02449300773999799,
#  -0.006510842295925382,
#  0.00851339435668751,
#  -0.021011491283506004,
#  -0.018604350362957857]

 

 

임베딩과 시맨틱 검색 (semantic search) 이 잘 작동하는지를 확인하기 위해 아래에 PostgreSQL Database, K8s and Docker 관련된 질문을 각각 해봤습니다. 코사인(Cosine Similarity)는 -1~1 사이의 값을 가지면 1에 가까울 수록 서로 유사하다고 판단할 수 있는데요, 첫번째 PostgreSQL 관련 질문에는 database_template 이 유사하다고 나왔고, 두번째 K8s and Docker 관련 짊누에서는 cloud_template 이 더 유사한 것으로 나왔으니 의도한 대로 잘 작동하네요. 

 

# Question 1: PostgreSQL database
query_embedding = embeddings.embed_query("What is PostgreSQL database?")
similarity = cosine_similarity([query_embedding], prompt_embeddings)[0]
print(similarity)
# [0.8684732  0.74399373]


# Question 2: K8s, Docker
query_embedding = embeddings.embed_query("What is Kubernetes and Docker?")
similarity = cosine_similarity([query_embedding], prompt_embeddings)[0]
print(similarity)
# array([0.75842006, 0.83432307])

 

 

 

템플릿들(templates)과 사용자 질문(user query) 텍스트를 임베딩으로 변환

--> cosine_similarity()  메소드로 템플릿과 질문 간 임베딩 간 코사인 유사도 계산 

--> 질문과 가장 유사한 템플릿 선택하여 반환

하는 과정을 prompt_router(input) 사용자 정의 함수로 정의해줍니다.

 

그리고 langchain_core.runnables.RunnableLambda(prompt_router) 를 사용해서 

 

그리고 '|'를 사용해서 

템플릿들(templates)과 사용자 질문(user query) 텍스트를 임베딩으로 변환

--> 템플릿과 질문 간 임베딩 간 코사인 유사도 계산 (calculate cosine similarity)

--> 질문과 가장 유사한 템플릿 선택하여 반환

--> 질문과 가장 유사한 템플릿을 사용하여 ChatGPT 모델로 답변 생성

--> ChatGPT 답변을 String으로 파싱하여 반환

 

하는 일련의 과정을 chaining 해주었습니다. 

 

# Define a UDF for routing prompts dynamically
def prompt_router(input):
    query_embedding = embeddings.embed_query(input["query"])
    similarity = cosine_similarity([query_embedding], prompt_embeddings)[0]
    most_similar = prompt_templates[similarity.argmax()]
    print("Using Database" if most_similar == database_template else "Using Cloud")
    return PromptTemplate.from_template(most_similar)
    

# Chaining
chain = (
    {"query": RunnablePassthrough()}
    | RunnableLambda(prompt_router)
    | ChatOpenAI()
    | StrOutputParser()
)

 

 

 

앞에서 정의한 chain을 invoke() 메소드를 사용해서 실행시켜줍니다.  

 

chain.invoke("What is PostgreSQL database?")

# Using Database
# 'PostgreSQL is an open-source relational database management system (RDBMS) 
# that offers advanced features and strong data integrity. It is widely known 
# for its robustness, scalability, and SQL compliance. 
# PostgreSQL supports various data types, including structured, semi-structured, 
# and unstructured data, making it suitable for a wide range of applications. 
# It offers features like ACID compliance, MVCC, JSON support, full-text search, 
# and extensive extensibility through procedural languages and extensions. 
# PostgreSQL is highly customizable and can be used for small to large-scale applications, 
# making it a popular choice among developers and enterprises.'

 

 

 

의도한 대로, 그리고 템플릿에 지시문을 작성한 대로 잘 작동하네요. 

(cloud_template 에는 사용자가 한국인이므로 한글로 답변을 생성하라고 지시문을 추가했었습니다.)

 

chain.invoke("What is Kubernetes and Docker?")

# Using Cloud
# 'Kubernetes와 Docker는 모두 컨테이너화된 애플리케이션을 관리하기 위한 클라우드 플랫폼 도구입니다.
# Docker는 컨테이너 기반 가상화 기술을 제공하는 플랫폼입니다. 
# 컨테이너는 애플리케이션을 격리된 환경에서 실행하고, 이식성과 확장성을 높여줍니다. 
# Docker는 애플리케이션의 종속성을 패키징하고 배포할 수 있도록 도와주며, 
# 애플리케이션을 컨테이너 이미지로 만들어 관리합니다.
# Kubernetes는 컨테이너 오케스트레이션 플랫폼으로, 도커 컨테이너의 배포, 확장, 관리를 
# 자동화합니다. Kubernetes는 여러 호스트에 걸쳐 컨테이너를 스케줄링하고, 서비스 디스커버리, 
# 로드 밸런싱, 자가 치유 등의 기능을 제공하여 애플리케이션 운영을 단순화합니다. 
# 또한 Kubernetes는 컨테이너의 상태를 모니터링하고 필요한 조치를 취할 수 있어 
# 안정적인 운영을 지원합니다.\n\n요약하자면, Docker는 컨테이너 기반 가상화 기술을 제공하고, 
# Kubernetes는 컨테이너 오케스트레이션 플랫폼으로 
# 컨테이너화된 애플리케이션의 배포와 관리를 용이하게 해주는 도구입니다.'

 

 

[ Reference ]

* LangChain - Routing by semantic similarity: 

https://python.langchain.com/docs/expression_language/cookbook/embedding_router

* OpenAI Embeddings: https://platform.openai.com/docs/guides/embeddings/what-are-embeddings

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요.  :-)

 

728x90
반응형
Posted by Rfriend
,

토큰화, 단어 임베딩, 텍스트 임베딩은 자연어 처리(NLP) 및 기계 학습에서 텍스트 데이터를 표현하고 처리하는 데 사용되는 개념입니다. 이러한 개념들은 감정 분석, 기계 번역, 텍스트 분류와 같은 NLP 작업에서 중요하며, 기계 학습 모델이 이해하고 학습할 수 있는 형태로 텍스트 정보를 표현하고 처리하는 데 사용됩니다.

 

이번 포스팅에서는 토근화(Tokenization), 단어 임베딩 (Word Embedding), 텍스트 임베딩 (Text Embedding)에 대해서 소개하겠습니다. 그리고 transformers 와 PyTorch 를 이용하여 실습을 해보겠습니다. 

 

tokenization, word embedding, text embedding

 


1. 토큰화 (Tokenization)

 

- 정의: 토큰화는 텍스트를 토큰이라 불리는 더 작은 단위로 분해하는 과정입니다. 이러한 토큰은 단어, 서브워드 또는 문자일 수 있으며 원하는 정밀도 수준에 따라 다릅니다.

- 목적: 토큰화는 NLP에서 중요한 전처리 단계로, 기계가 텍스트의 개별 요소를 이해하고 처리할 수 있게 합니다. 토큰은 후속 분석을 위한 구성 요소로 작용합니다.

 

-- Install transformers, torch, sentence-transformers at terminal at first 

pip install -q transformers
pip install -q torch
pip install -q -U sentence-transformers

 

# 1. Tokenization 
# : Tokenization is the process of breaking down a text into smaller units called tokens. 
# : These tokens can be words, subwords, or characters, depending on the level of granularity desired.

from transformers import AutoTokenizer

# Load pre-trained DistilBERT tokenizer
tokenizer = AutoTokenizer.from_pretrained('distilbert-base-uncased')

# Example text
text = "Love is wanting to be loved"

# Tokenize the text
input_ids = tokenizer.encode(text)
tokens = tokenizer.tokenize(tokenizer.decode(input_ids))

print("Input IDs:", input_ids)
print("-------" * 10)
print("Tokens:", tokens)

# Input IDs: [101, 2293, 2003, 5782, 2000, 2022, 3866, 102]
# ----------------------------------------------------------------------
# Tokens: ['[CLS]', 'love', 'is', 'wanting', 'to', 'be', 'loved', '[SEP]']

 

 


2. 단어 임베딩 (Word Embedding)

 

- 정의: 단어 임베딩은 단어를 연속적인 벡터 공간의 실수로 이루어진 밀도 있는 벡터로 나타내는 기술입니다. 각 단어는 벡터로 매핑되며 이러한 벡터 간의 거리와 방향이 의미를 가집니다.

- 목적: 단어 임베딩은 단어 간의 의미적 관계를 포착하여 기계가 주어진 텍스트의 맥락과 의미를 이해할 수 있게 합니다. 단어가 맥락과 의미가 유사한 경우 단어 임베딩 벡터가 서로 가깝게 위치하는 특성이 있어 semantic search에 유용하게 활용됩니다. 유명한 단어 임베딩 모델로는 Word2Vec, GloVe, FastText가 있습니다. 

 

# 2. Word Embedding
# : Word embedding is a technique that represents words 
#   as dense vectors of real numbers in a continuous vector space. 
# : Each word is mapped to a vector, and the distances and directions 
#   between these vectors carry semantic meaning.

from transformers import AutoTokenizer, AutoModel
import torch

# Load pre-trained DistilBERT model
tokenizer = AutoTokenizer.from_pretrained('distilbert-base-uncased')
model = AutoModel.from_pretrained('distilbert-base-uncased')

# Encode the tokens to obtain word embeddings
input_ids = tokenizer.encode(text, return_tensors='pt')
with torch.no_grad():
    word_embeddings = model(input_ids)[0]

print("Tensor Size:", word_embeddings.size())
print("------" * 10)
print("Word Embeddings:")
print(word_embeddings)
# Tensor Size: torch.Size([1, 8, 768])
# ------------------------------------------------------------
# Word Embeddings: 
#   tensor([[
#          [-0.1067, -0.0101, -0.0897,  ..., -0.1721,  0.2134,  0.3274],
#          [ 0.8698,  0.1002,  0.2687,  ..., -0.3276,  0.4205,  0.1344],
#          [-0.1877, -0.0777,  0.1905,  ..., -0.0220,  0.0024,  0.5138],
#          ...,
#          [ 0.5998, -0.2141,  0.3774,  ..., -0.3945, -0.1149,  0.1245],
#          [ 1.1550, -0.1050,  0.3570,  ..., -0.4063, -0.0489, -0.0717],
#          [ 1.0036,  0.1886, -0.4508,  ...,  0.0999, -0.5486, -0.3076]]])

 


3. 텍스트 임베딩 (Text Embedding)

 

- 정의: 텍스트 임베딩은 문장(sentences)이나 문서(documents) 전체를 밀도 있는 벡터로 나타내어 전체 텍스트의 전반적인 의미를 캡처하는 기술입니다. 단어 임베딩을 집계하거나 요약하여 전체 텍스트의 표현을 만듭니다. 

- 목적: 텍스트 임베딩은 전체 문서나 문장을 의미적 공간에서 비교하고 분석할 수 있게 합니다. 텍스트 임베딩을 생성하는 방법에는 단어 임베딩의 평균(averaging word embeddings)을 사용하거나 순환 신경망(RNN), 장·단기 기억 네트워크(LSTM), 트랜스포머(Transformers)를 사용하는 방법 등이 있습니다. 

 

텍스트 임베딩은 

 - 텍스트 분류 (Text Classification),

 - 감성분석 (Sentiment Analysis),

 - 검색 엔진 랭킹 (Search Engine Ranking),

 - 군집 및 유사도 분석 (Clustering and Similarity Analysis),

 - 질의 응답 (Question Answering, 질문과 답변을 임베딩으로 표현함으로써 시스템은 쿼리의 의미 콘텐츠와 데이터셋의 관련 정보를 정확하게 검색할 수 있음)

 

등에 활용할 수 있습니다. 

text embedding 을 하는 방법에는 (1) transformers 를 이용한 방법과, (2) sentence-transfomers 모델을 이용하는 방법이 있습니다. sentence-transformers 모델을 이용하는 방법이 코드가 간결합니다. 

 

 

3-1. transformers 를 이용한 text embedding

# 3. Text Embedding
# : Text embedding represents entire sentences or documents as dense vectors, 
#   capturing the overall meaning of the text. 
# : It involves aggregating or summarizing word embeddings 
#   to create a representation for the entire text.

from transformers import AutoTokenizer, AutoModel
import torch
import torch.nn.functional as F

#Mean Pooling - Take attention mask into account for correct averaging
def mean_pooling(model_output, attention_mask):
    token_embeddings = model_output[0] #First element of model_output contains all token embeddings
    input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
    return torch.sum(token_embeddings * input_mask_expanded, 1) / torch.clamp(input_mask_expanded.sum(1), min=1e-9)


# Sentences we want sentence embeddings for
# Example sentence
text = "Love is wanting to be loved"

# Load model from HuggingFace Hub
tokenizer = AutoTokenizer.from_pretrained('distilbert-base-uncased')
model = AutoModel.from_pretrained('distilbert-base-uncased')

# Tokenize sentences
encoded_input = tokenizer(text, padding=True, truncation=True, return_tensors='pt')

# Compute token embeddings
with torch.no_grad():
    model_output = model(**encoded_input)

# Perform pooling
sentence_embeddings = mean_pooling(model_output, encoded_input['attention_mask'])

# Normalize embeddings
sentence_embeddings = F.normalize(sentence_embeddings, p=2, dim=1)

print("Tensor Size", sentence_embeddings.size())
print("--------" * 10)
print("Sentence embeddings:")
print(sentence_embeddings)

# Tensor Size torch.Size([1, 768])
# --------------------------------------------------------------------------------
# Sentence embeddings:
# tensor([[ 7.1203e-02, -9.0556e-03,  2.0404e-02, -5.4984e-03,  5.3534e-02,
#          -2.7214e-02,  2.7177e-02,  5.1983e-02, -1.1366e-02, -4.1719e-02,
#              :             :           :           :             :
#           2.5733e-02, -2.1918e-02,  5.2480e-03, -5.7470e-04,  1.4644e-02,
#          -1.8896e-02, -4.5600e-03,  1.4625e-02]])

 

 

 

3-2. sentence-transformers 모델을 이용한 text embedding

: 'all-mpnet-base-v2' 모델을 HuggingFace에서 다운로드 하여 사용

 

sentence-transformers 모델에 대한 설명은 https://huggingface.co/sentence-transformers 를 참고하세요. 

 

# ! pip install -q -U sentence-transformers


# Text embedding by sentence-transformers model
# reference: https://huggingface.co/sentence-transformers/all-mpnet-base-v2
from sentence_transformers import SentenceTransformer

text = "Love is wanting to be loved"

model = SentenceTransformer('sentence-transformers/all-mpnet-base-v2')
text_embedding = model.encode(text)

print("Tensor Shape:", text_embedding.shape)
print("------" * 10)
print("Text Embedding:")
print(text_embedding)

# Tensor Shape: (768,)
# ------------------------------------------------------------
# Text Embedding:
# [ 7.25340098e-02 -3.43943425e-02  3.50002125e-02 -1.46801360e-02
#   1.79491900e-02  5.70273288e-02 -9.77409929e-02  2.65225749e-02
#      :               :               :               :
#   -6.68407883e-03  1.09006204e-02 -1.37606068e-02 -4.93713543e-02
#   3.27182375e-02  5.77081088e-03  4.32791896e-02 -1.55460704e-02]

 

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,