본문 바로가기

서비스 제작

[MDQA]1.3 자른 텍스트를 특정 크기의 벡터로 임베딩(embedder 모듈)

MDQA를 위해 “유저가 입력한 웹 페이지 또는 파일을 데이터 베이스에 저장”하는 과정에 대해 다루고 있다. [MDQA]1. 웹 페이지 또는 파일을 데이터 베이스에 저장하기 에서 설명한 것처럼 그 전체 과정은 아래와 같다.

  1. 유저가 웹 페이지의 URL을 입력 또는 PDF 파일을 업로드
  2. 웹 페이지는 크롤링, PDF는 파일을 읽어서 텍스트 데이터를 가져옴(Loader 모듈)
  3. 텍스트 데이터를 chunk로 자른다.(chunker 모듈)
  4. 자른 텍스트를 특정 크기의 벡터로 임베딩한다. (embedder 모듈)
  5. chunk의 중요한 순서를 기록한다. (Text ranker 모듈, 이 부분은 필수적이지 않다.)
  6. 이렇게 처리한 데이터들을 데이터베이스에 저장한다.

이번 포스팅에서는 4번 과정인 “자른 텍스트를 특정 크기의 벡터로 임베딩한다. (embedder 모듈)”를 설명해본다.

Embedder

Embedding이 필요한 이유

데이터 저장 파이프라인에서 embedding 전 단계는 아래와 같다.

  1. 유저가 웹 페이지의 URL을 입력 또는 PDF 파일을 업로드
  2. 웹 페이지는 크롤링, PDF는 파일을 읽어서 텍스트 데이터를 가져옴(Loader 모듈)
  3. 텍스트 데이터를 chunk로 자른다.(chunker 모듈)

지금까지는 데이터를 가져오고, 이를 chunk라는 단위로 나누었다. 그러면 chunk text를 왜 embedding해야할까?

Embedding이 필요한 주된 이유는 검색(Retrieval)을 위해서이다. 사용자의 질문(query)이 입력되면, 해당 질문에 답변하기 위한 적합한 부분을 문서 내에서 찾아야 한다. 이 과정에서 가장 기본적으로 사용되는 방법 중 하나는 cosine similarity를 계산하는 것이다. Cosine similarity를 계산하기 위해서는 문서 내의 특정 부분(Chunk)을 벡터로 변환해야한다. 이 벡터는 해당 chunk의 의미론적 내용을 정확하게 반영해야 하는데, 이를 위해 embedding 모델을 사용하여 chunk의 의미론적 내용을 잘 표현하는 embedding vector를 생성한다.

다양한 Embedding 모델

embedding 모델도 여러가지 모델을 테스트해봤다.


  • 한국어 embedding 모델(korsts, kornli 류의 한글 데이터셋으로 학습된 embedding 모델들)
  • multilingual embedding(huggingface sentence embedding, multilingual-e5-large, openai ada, openai embedding v3 large)

embedding 모델의 비교를 해보면 아래 표와 같다.


한국어 embedding 모델 multilingual-e5-large openai embedding large
생활언어 성능 good good good
전문언어 성능 bad bad soso
한글-영어 일관성 ? bad soso
API 여부(X면 서버에 모델을 구축해야함) X X O
비용 컴퓨터 서버 비용 컴퓨터 서버 비용 API 비용(개인적으로 저렴하다고 생각한다)
한국어에 최적화된 tokenizer X X X

여기서 한국어 만을 위한 embedding은 일반적으로 성능이 안좋았다. 아마 부족한 데이터셋과 학습된 데이터셋의 특성에 따른 결과인듯하다. 생활 용어에서는 embedding의 성능이 괜찮았지만 전문적인 문서에서는 embedding의 성능이 안좋았다. korsts, kornli 데이터셋을 살펴보면 짧은 글과 생활 대화에 대한 데이터들이 많다. 따라서 embedding의 성능이 아쉬운 결과는 이런 한정된 데이터셋에 대해서만 학습했기 때문이라는 생각이 든다. 그래서 korsts, kornli와 같은 벤치마크에서는 성능이 좋게 나와도 실제로 사용해보면 성능이 안좋게 느껴진다. (LLM이나 다른 모델에 대해서도 벤치마크 성능은 좋지만 실제로 사용하면 아쉬운 문제가 발생한다.)


multilingual embedding에서는 성능이 공식 sentence embedding < multilingual e2 < openai ada < openai large의 순서였다. multilingual e2 성능이 아쉬운 이유는 동일한 언어에 대해 cosine similarity가 높게 나왔기 때문이다. 예를 들어 query로 “I like apple”, chunk1은 “나는 오렌지를 좋아한다.”, chunk2는 “I like orange”를 생각해보자. 이 경우 query embedding과 chunk1 embedding, chunk2 embedding의 cosine similarity를 계산했을 때 값이 동일하기를 바란다. 하지만 chunk2에 대한 cosine similarity가 더 크게 나왔다. Openai embedding 모델은 query나 chunk가 한글, 영어를 교차하여 사용해서 사용해도 cosine similarity가 일관성이 있었고, 전문적인 문서도 성능이 괜찮았다.


한국어에 최적화된 tokenizer는 모두 좋지 않다고 생각한다. 한국어 embedding 모델에 대해서는 tokenizer가 적은 텍스트 데이터로부터 생성되었기 때문에 편향되는 성격이 보였다. 예를 들어 대통령들의 이름이 tokenizer에 나타나기도 한다. 대통령의 이름보다도 더 많이 쓰이는 단어들이 더 많을 것인데 이들은 포함되지 않는다. 그리고 multilingual-e5-large는 다국어 모델이기 때문에 중국어, 한국어, 영어, 일본어 등 여러 언어가 token에 포함되기 때문에 한국어의 비중이 적다. 이 때문에 정말 많이 사용되는 단어를 제외하고는 한글자 단어들이 대다수이다. Openai tokenizer도 마찬가지다. Openai embedding은 한 글자도 여러 token으로 분리되기도 한다. 이런 이유로 추후에 domain에 특화되는 embedding 모델을 만들기 위해서는 tokenizer부터 다시 설계가 필요할 듯 하다.

결론

최종적으로 openai embedding large 모델을 embedding 모델로 선택하였다. Openai embedding large모델의 specification은 아래와 같다.

  • vector 크기 종류: 256, 1024, 3072
  • 가격: $0.13/ 1M tokens
  • Max input: 8191