임베딩 검색 정확도를 37% 향상시킨 LLM 트릭 3가지
TMT최근 저는 OpenAI에서 제공하는 임베딩을 중심으로 많은 시간을 보냈습니다. 사실 저는 이전까지 임베딩이나 벡터 데이터베이스에 대한 경험이 전혀 없었는데, YCombinator에 들어간 이후 제품 방향을 전환하면서 코드베이스 검색, 이해, 보완 기능을 갖춘 새로운 제품을 만들기 위해 급하게 공부하게 되었습니다.
이 과정에서 다양한 텍스트 매체, 특히 산문 텍스트와 코드 간의 검색을 다루며 많은 것을 배웠습니다. 특히 다른 창업자들과 이야기를 나누면서 느낀 점은, 많은 사람들이 임베딩을 오해하고 그로 인해 잘못 사용하는 경우가 많다는 것이었습니다. 많은 분들이 검색 쿼리를 단순히 임베딩한 후, 임베딩된 답변 공간을 대상으로 검색하는 방식으로 구현하는데, 간단한 경우에는 괜찮지만 대부분의 응용에는 잘 맞지 않습니다.
아래는 제가 얻은 주요 인사이트들입니다.
질문을 답변처럼 만들어라
임베딩에 대한 오해 중 하나는 임베딩이 본질적으로 검색 도구라고 생각하는 점입니다. 제 생각에는 임베딩은 ‘유사성 도구’에 더 가깝습니다. 두 임베딩 벡터 간의 코사인 유사도는 많은 것을 알려주지만, 검색 시 쿼리와 원하는 답변이 전혀 다를 수 있습니다. 이는 특히 구조적인 형식(예: 코드, CSV 등)에 대해 텍스트로 검색할 때 두드러집니다. 이 경우, 입력한 문자열은 실제 답변과 전혀 닮지 않았을 가능성이 높습니다.
최근 HyDe(Hypothetical Document Embeddings) 논문이 큰 주목을 받았고, 이 문제를 해결하는 데 매우 유용했습니다. 이 논문의 핵심은 질문과 답변 공간이 형태상 전혀 다를 경우, LLM을 이용해 질문으로부터 '예상되는 답변'을 생성한 후, 그것을 임베딩해서 검색하는 것이 더 좋다는 것입니다. 예측한 답변이 완벽하지 않더라도, 원래 쿼리보다 실제 답변에 더 가까울 가능성이 높기 때문에 유사도 점수가 향상됩니다.
저희 제품에서도 사용자의 검색 쿼리를 이러한 방식으로 번역하기 위해 파인튜닝된 모델을 사용하고 있으며, 실제로 코드 검색 정확도가 크게 향상되었습니다. 이 방법을 도입한 이후 검색의 F1 점수가 37%나 상승했습니다. 재밌는 점은, 저는 이 논문이 발표되기 전부터 비슷한 방식으로 구현을 시도했었는데, 질문에서 답변을 예측하는 대신 답변에서 질문을 예측하는 방식으로 해봤고, 그 결과는 훨씬 좋지 않았습니다.
메타 특성 기반 검색
저희가 원했던 기능 중 하나는, 코드에 직접적으로 명시되지 않은 특성들을 검색할 수 있는 기능이었습니다. 예를 들어, 사용자가 “일반적인 재귀 함수”를 검색하고 싶어할 수 있는데, 이는 일반적인 문자열 매칭이나 정규식으로는 거의 불가능하며, 단순한 임베딩 기반 접근으로도 제대로 동작하지 않습니다.
이 개념은 코드 외에도 적용할 수 있습니다. 예를 들어, 찰스 디킨스의 글을 임베딩한 말뭉치에서 “올리버 트위스트가 자기 반성을 하는 장면”을 찾고자 한다면, 일반적인 임베딩 방식으로는 거의 불가능합니다.
저희 Buildt에서는 각 코드 스니펫마다 코드의 메타 특성을 설명하는 텍스트를 생성하고 이를 임베딩합니다. 이 설명은 파인튜닝된 LLM을 통해 생성되며, 원본 코드와 함께 저장됩니다. 이렇게 하면 단순한 코드뿐만 아니라 코드의 동작 특성에 대해서도 검색할 수 있어, 저희가 말하는 “코드가 무엇을 하는지에 대한 검색”이 가능해집니다.
이 접근 방식은 매우 효과적이며, 이를 적용하지 않으면 기능에 대한 질문은 거의 제대로 된 결과를 반환하지 못합니다. 물론 이 방식은 단순히 원본 말뭉치만 임베딩하는 것보다 비용이 훨씬 많이 들고, 임베딩 처리에 걸리는 시간도 길어집니다. 그래서 모든 상황에 적합하진 않지만, 저희에게는 이 ‘마법 같은 검색 경험’을 제공하기 위한 가치 있는 트레이드오프입니다.
탄력적인 임베딩
또 하나 흥미롭게 느꼈던 점은, 임베딩이 생각보다 탄력적이라는 것입니다. 저희는 매우 방대한 양의 정보를 인덱싱해야 하기 때문에 최적화를 고려할 수밖에 없습니다. 그래서 초기에 한 가지 실험을 해봤습니다. 코드 임베딩 시 전체의 40%만 임베딩하도록 한 것입니다. 이는 줄 길이를 기준으로 아주 단순하게 잘라낸 것이었고, 의도적으로 성능이 얼마나 유지되는지를 보기 위함이었습니다.
놀랍게도 F1 점수의 감소는 정보의 60%가 줄어든 것에 비해 훨씬 적었습니다. 실제로 점수는 9% 정도만 떨어졌습니다. 생각해보면 이것도 어느 정도 직관적으로 이해가 됩니다. 임베딩이 유사성을 기반으로 작동하기 때문에, LLM이 생성한 코드 스니펫과 잘린 코드 스니펫 간에도 여전히 유사성이 존재할 수 있기 때문입니다. 이 접근은 코드 외의 다른 영역에서도 유용할 수 있으며, 특히 앞서 설명한 메타 특성 검색이 보완적으로 작용한다면 더욱 탄력적인 검색이 가능해집니다.
요약
저는 임베딩과 관련해서 앞으로도 더 많은 실험을 해보고 싶습니다:
- 쿼리에 대한 극단적인 양/음성 예제를 제공하여 임베딩 모델을 파인튜닝하기
- HyDe 방식에서 예측되는 답변을 생성할 때, 답변 공간의 요약 정보를 함께 제공하여 결과를 더 잘 맞추기
- 검색 결과를 클러스터링하여 관련성과 무관한 결과를 더 명확히 구분하기
이 주제에 대해 언제든 이야기하고 싶습니다. Twitter에서 저를 찾아주세요: [@AlistairPullen](https://twitter.com/AlistairPullen)