나는 자바로 웹 SI 프로젝트만 해 본 개발자다. 게시판, 관리자 화면, DB 조회.. 그런 거. 그런데 요즘 생성형 AI로 검색 서비스를 만드는 일을 하고 있다. AI 쪽은 솔직히 초짜다. ㅡ,.ㅡ;
검색 대상은 크롤링한 메타정보와 문서, 사내 시스템 기술문서 3만 건 정도, 그리고 RDB에 있는 각종 정보들이다. 처음에는 키워드 검색으로 시작했고, 지금은 하이브리드 검색을 쓴다. 예전에 sLLM 구축 글에서 "진짜 문제는 검색이었다"고 적었는데, 이번에 제대로 공부해 보려고 시리즈로 정리한다.
'머신러닝'으로 찾으면 나오고 '기계학습'으로 찾으면 안 나온다
키워드 검색만 쓸 때 가장 큰 문제가 이거였다. 사람은 '머신러닝'과 '기계학습'을 같은 말로 생각하고 같은 결과를 기대한다. 그런데 키워드 검색은 전혀 다른 결과를 보여 줬다. 질문을 문장으로 던져도 사용자의 의도를 알아채지 못했다.
정말 그런지 작게 실험해 봤다. 가짜 사내 문서 5개를 만들고, 파이썬으로 키워드 검색(BM25)을 돌렸다. 한국어는 형태소 분석기(Kiwi)로 단어를 쪼갰다.
같은 뜻인데 1위가 다르다. '머신러닝'으로 찾으면 기계학습 교육 문서는 0점이다. 눈여겨볼 건 2위다. '머신러닝'이 '머신'과 '러닝'으로 쪼개지면서 '딥러닝' 문서가 따라 올라왔다. '기계학습' 쪽에서는 '학습'이라는 글자 하나 때문에 GPU 서버 문서가 걸렸다. 뜻이 아니라 글자로 찾기 때문에 생기는 일이다.
키워드 검색: 글자가 같아야 찾는다
키워드 검색은 책 뒤에 있는 '찾아보기(색인)'와 비슷하다. 문서마다 어떤 단어가 들어 있는지 미리 목록을 만들어 두고(역색인), 검색어와 같은 단어가 든 문서를 꺼낸다. 순위는 보통 BM25라는 공식으로 매긴다. 검색어가 자주 나올수록, 그리고 흔하지 않은 단어일수록 점수가 높다.
SI 하면서 쓰던 SQL LIKE '%검색어%'를 떠올리면 쉽다. 그걸 훨씬 빠르고 똑똑하게 만든 게 검색엔진이다. 많이 쓰는 Elasticsearch도 자바로 만든 Lucene 위에서 돌아가고, 기본 점수 공식이 BM25다.
- 강한 곳: 문서번호, 제품명, 사람 이름, 에러 코드처럼 글자가 정확히 맞아야 하는 검색. 왜 이 문서가 나왔는지 설명하기도 쉽다.
- 약한 곳: 같은 뜻 다른 말(머신러닝 = 기계학습, AI = 인공지능), 문장으로 묻는 질문, 오타.
벡터 검색: 뜻이 가까우면 찾는다
벡터 검색은 문장을 숫자 목록(벡터)으로 바꿔서 찾는다. 이 숫자 목록을 만드는 걸 '임베딩'이라고 한다. 뜻이 비슷한 문장은 비슷한 숫자가 나오도록 학습된 모델을 쓰기 때문에, '머신러닝'과 '기계학습'은 가까운 자리에 놓인다. 검색할 때는 질문도 숫자로 바꾼 뒤 가장 가까운 문서를 고른다.
- 강한 곳: 같은 뜻 다른 말, "AI로 문서를 자동으로 나눠 주는 시스템 있어?" 같은 문장 질문.
- 약한 곳: 문서번호, 버전(R1, R2), 이름처럼 글자가 딱 맞아야 하는 것. 뜻이 '비슷한' 엉뚱한 문서도 같이 끌려온다. 왜 이 문서가 나왔는지 설명하기 어렵다.
나도 생성형 AI에게 질문할 때 벡터 검색을 써 봤다. 그런데 원하는 답변을 잘 받지는 못했다. 그렇다고 검색 서버의 검색 전체를 벡터로만 바꾸기에는 한계가 있었다. 키워드 검색이 잘하던 걸 벡터 검색이 못 하기 때문이다.
그래서 하이브리드: 둘 다 돌리고 순위를 합친다
하이브리드 검색은 키워드 검색과 벡터 검색을 같이 돌린 뒤, 두 결과를 하나의 순위로 합친다. 문제는 두 점수의 단위가 다르다는 것이다. BM25 점수는 1.27, 2.06 같은 값이고, 벡터 검색은 0.83 같은 유사도다. 그냥 더하면 안 된다.
그래서 많이 쓰는 게 RRF(Reciprocal Rank Fusion)다. 점수 대신 순위만 본다. 각 결과에서 1 ÷ (60 + 순위)를 구해서 더한다. 60은 흔히 쓰는 기본값이다.
| 문서 | 키워드 순위 | 벡터 순위 | RRF 점수 | 최종 |
|---|---|---|---|---|
| A | 1위 | 3위 | 1/61 + 1/63 = 0.0323 | 2위 |
| B | 2위 | 1위 | 1/62 + 1/61 = 0.0325 | 1위 |
| C | - | 2위 | 1/62 = 0.0161 | 3위 |
| D | 3위 | - | 1/63 = 0.0159 | 4위 |
양쪽에서 모두 상위권인 문서가 위로 올라온다. 한쪽에서만 나온 문서도 버리지 않는다. 나는 여기에 재정렬(reranker)을 하나 더 붙였다. 합쳐진 결과를 질문과 얼마나 맞는지 다시 따져서 순위를 한 번 더 매기는 단계다.
한눈에 비교, 그리고 이 시리즈에서 다룰 것
| 키워드 검색 | 벡터 검색 | 하이브리드 검색 | |
|---|---|---|---|
| 찾는 기준 | 글자가 같은가 | 뜻이 가까운가 | 둘 다 |
| 잘하는 것 | 문서번호, 이름, 코드 | 동의어, 문장 질문 | 둘의 장점 |
| 못하는 것 | 동의어, 의도 파악 | 정확한 글자 매칭 | 구성과 튜닝이 복잡 |
| 대표 도구 | Elasticsearch, OpenSearch | FAISS, Qdrant, pgvector 등 | 위 도구 조합, RRF |
다음 편부터는 하나씩 깊게 들어간다.
- 2편 키워드 검색: 역색인, BM25, 한국어 형태소 분석, 직접 구현
- 3편 벡터 검색: 임베딩, 문서 쪼개기(청킹), 벡터 DB, 직접 구현
- 4편 하이브리드 검색: RRF, 재정렬(reranker), 최신 버전만 고르는 필터
- 5편 검색 품질 평가: 바꾼 게 정말 나아졌는지 숫자로 재는 법
정리하고 보니, 내가 키워드 검색에서 겪은 문제는 벡터 검색이 잘하는 것이고, 벡터 검색에서 겪은 문제는 키워드 검색이 잘하는 것이었다. 결국 하이브리드로 간 것도 이 때문이다. 나도 공부하면서 쓰는 시리즈라 차근차근 가 보련다.
이상 키워드·벡터·하이브리드 검색 개념 정리 끝!


댓글
댓글 쓰기