포스트

Langchain PDF Chatbot 만들기 - 3 - Embedding

목차

  1. Embedding

Embedding

임베딩이란 텍스트, 소리, 이미지 등의 데이터를 고정 길이의 실수 형태의 벡터로 표현하는 것을 의미합니다.
벡터 값들간의 거리 계산을 통해서 의미적 관계를 유추할 수 있습니다.
→ TensorFlow에서 제공하는 Embedding Projector를 통해서 벡터값을 3차원 공간에 투영한 결과를 확인해볼 수 있습니다.

1) Embedding 모델 종류

1
2
3
4
| 구분                             | 제공                                               | 장점                                                                 | 단점                                |
|----------------------------------|----------------------------------------------------|----------------------------------------------------------------------|-------------------------------------|
| Cloud Service Embedding Model    | OpenAI, Amazon, Anthropic과 같은 플랫폼에서 제공      | 개인의 하드웨어 사양에 대해 크게 신경쓰지 않아도 됨<br>다국어에 대한 임베딩 보장 | 유료                                |
| OpenSource Embedding Model       | HuggingFace에서 제공하는 무료 오픈소스                  | 무료                                                                 | 개인의 하드웨어 사양을 고려<br>다국어에 대한 지원 아쉬움 |


처음에는 HuggingFace에서 Embedding Model을 사용했는데 유사도를 계산할 때 정확하지 못한 것이 많았습니다.
왠만하면 OpenSource로 작업을 하는데 유료 Embedding Model을 사용하기로 결정했습니다.
그 중에서도 OpenAI를 사용하기로 결정했습니다. (나중에 o1, o3 모델 사용 가능성을 위해서)

OpenAI에서는 아래와 같은 임베딩 모델을 제공한다.

1
2
3
4
5
| Model                     | ~ Pages per dollar | Performance on MTEB eval | Max input |
|---------------------------|--------------------|--------------------------|-----------|
| text-embedding-3-small    | 62,500             | 62.3%                    | 8191      |
| text-embedding-3-large    | 9,615              | 64.6%                    | 8191      |
| text-embedding-ada-002    | 12,500             | 61.0%                    | 8191      |

MTEB(Massive Text Embedding Benchmark) 평가에서 모델의 성능을 나타내는 값으로
다양한 텍스트 임베딩 관련 태스크(문장 유사도, 검색, 분류, 클러스터링)를 얼마나 잘 수행하는지에 대한 백분율입니다.
text-embedding-3-large가 제공되는 임베딩 모델 중에서 가장 높은 값을 제공하기 때문에 선택하였습니다.
하지만 1달러로 9,615 페이지를 처리를 해 임베딩 모델 중에서 1달러 대비 처리할 수 있는 페이지 수가 가장 적습니다.
성능과 비용에 대한 트레이드 오프를 잘 비교해서 임베딩 모델을 선택하면 될 것 같습니다.


처음에는 어떻게든 비용을 줄이기 위해서 text-embedding-3-small 모델을 사용했는데
임베딩 결과물의 품질이 만족스럽지가 않아서 결국에는 text-embedding-3-large을 사용하기로 하였습니다.

2) OpenAI Embedding 사용하는 방법

pip install -U langchain langchain_openai로 필요한 라이브러리를 설치합니다.

1
2
from langchain_openai import OpenAIEmbeddings
embedding = OpenAIEmbeddings(model=OPENAI_API_EMBEDDING, openai_api_key=OPENAI_API_KEY)

위의 형태로 사용이 가능합니다.

text-embedding-3-large은 기본 값으로 3072 차원을 가지만 다른 모델은 1536 차원을 가집니다.

1
2
3
4
5
6
7
8
9
10
11
embedding = OpenAIEmbeddings(model="text-embedding-3-large", openai_api_key=OPENAI_API_KEY)
dim = embedding.embed_query('테스트')
print(len(dim))

embedding = OpenAIEmbeddings(model="text-embedding-3-small", openai_api_key=OPENAI_API_KEY)
dim = embedding.embed_query('테스트')
print(len(dim))

embedding = OpenAIEmbeddings(model="text-embedding-ada-002", openai_api_key=OPENAI_API_KEY)
dim = embedding.embed_query('테스트')
print(len(dim))

하지만 dimensions 파라미터를 이용해서 임베딩의 크기를 원하는 값으로 수정할 수 있습니다.

1
2
3
embedding = OpenAIEmbeddings(model="text-embedding-3-large", openai_api_key=OPENAI_API_KEY, dimensions=1536)
dim = embedding.embed_query('테스트')
print(len(dim))


OpenAIEmbeddings.embed_query()를 이용해서 입력된 텍스트를 임베딩 벡터로 반환이 가능하며
OpenAIEmbeddings.embed_documents()를 이용해서 텍스트 문서를 임베딩 벡터로 반환이 가능합니다.

3) Cache Embedding

동일한 내용에 대해서 임베딩을 반복하지 하지 않기 위해서 CacheBackedEmbeddings을 이용해 캐싱을 사용할 수 있습니다.
CacheBackedEmbeddings의 주요 파라미터는 아래와 같습니다.

  • underlying_embeddings : 임베딩을 위해 사용되는 embedder
  • document_embedding_cache : 문서 임베딩을 캐싱하기 위한 ByteStore
  • namespace : 동일한 텍스트가 다른 임베딩 모델을 사용하여 임베딩될 때 충돌을 피하기 위해 설정

1) 영구적인 Cache Embedding

1
2
3
4
5
6
7
8
from langchain.embeddings import CacheBackedEmbeddings
from langchain_openai import OpenAIEmbeddings
from langchain.storage import LocalFileStore

embedding = OpenAIEmbeddings(model=OPENAI_API_EMBEDDING, openai_api_key=OPENAI_API_KEY)
cached_embedding = CacheBackedEmbeddings.from_bytes_store(underlying_embeddings=embedding,
                                                          document_embedding_cache=LocalFileStore(EMBEDDING_CACHE_FOLDER),
                                                          namespace=embedding.model)

2) 비영구적인 Cache Embedding

1
2
3
4
5
6
7
8
9
from langchain.embeddings import CacheBackedEmbeddings
from langchain_openai import OpenAIEmbeddings
from langchain.storage import InMemoryByteStore


embedding = OpenAIEmbeddings(model=OPENAI_API_EMBEDDING, openai_api_key=OPENAI_API_KEY)
cached_embedding = CacheBackedEmbeddings.from_bytes_store(underlying_embeddings=embedding,
                                                          document_embedding_cache=InMemoryByteStore(),
                                                          namespace=embedding.model)

이렇게 설정할 경우 동일한 내용에 대해서는 재계산을 하지 않기 때문에 임베딩에 사용하는 비용을 소폭 줄일 수 있습니다.