#1. 설계대로 되면 참 좋을텐데..
지난 글에서 임베딩 검색을 붙이기로 했다.
글을 ## 헤딩 단위로 잘라서 청크를 만들고, 각각 임베딩한 다음 기존 LIKE 검색과 함께 사용하는 방식이었다.
설계까지 끝냈으니 이제 구현만 하면 된다.
실제로 구현 자체는 생각보다 어렵지 않았다.
스키마를 추가하고, 글을 저장할 때 청크를 만들고, Gemini API를 이용해서 임베딩을 생성했다.
임베딩 생성에 실패하더라도 글 자체는 정상적으로 저장되도록 처리했다.
검색할 때는 기존 검색 결과를 먼저 보여주고, 그 뒤에 임베딩 검색 결과를 추가했다.
문제는 테스트 하는 과정에서 발생했다.
#2. 내가 생각한거랑 다른데?
배포된 버전과 로컬에서 임베딩 검색을 붙인 버전을 같은 검색어로 비교해봤다.
첫 번째 검색어는 시맨틱.
기존 검색에서는 본문에 실제로 시맨틱이라는 단어가 들어있는 글 두 개가 나왔다.
그런데 임베딩 검색을 붙인 로컬 버전에서는...
모든 포스팅이 다 조회되었다.
내 블로그 글이 28개였는데 27개가 검색됐다.
시맨틱과 아무 상관없는 글까지 줄줄이 딸려 나왔다.
처음에는 설정값을 잘못 잡아서 그런건가 싶었다.
#3. 임베딩 검색 설정 값 조정기
임베딩 검색 설정중에는 Cosine Distance라는 값이 있다. 두 벡터가 얼마나 떨어져 있는지를 나타낸다.
키워드와 벡터로 저장된 포스팅의 내용의 거리를 측정하는 기준값이다.
해당 설정값을 좀 빡빡하게 설정했다.
이번에는 운동 이라는 키워드로 검색했는데,
거제---야호⭐️! 포스팅이 조회되었다.
#4. 딸깍 조정으로는 안된다..
그래서 테스트용 스크립트를 하나 만들어서 검색어별 결과와 코사인 값을 직접 확인해봤다.
운동 이라는 키워드가 집적 포함된 포스팅의 코사인 값이 0.34 정도인데, 운동과 전혀 관련 없는 글도 코사인 값이 0.35 근처에 있었다.
심한 경우에는 둘 사이 차이가 0.002 정도밖에 나지 않았다.
이 정도면 코사인 값 설정을 어디에 맞춰야 할지 상당히 곤란했다.
0.35로 자르면 관련 없는 글이 들어오고,
0.34로 자르면 이번에는 실제 관련 글까지 잘릴 수 있다.
결국 처음 생각했던 것처럼 설정값 조정으로 해결 할 수 있는 문제가 아닌것 같았다.
#5. 문제의 원인을 찾아보자.
원인을 찾기위해 여러가지 키워드와 조회결과를 찾아보았다.
덤벨 이라는 키워드를 검색했을 때 실제 운동이라는 키워드가 포함된 포스팅 보다 더 가까운 글이 있었다.
그 글은 운동관련 포스팅도 아니었고, 특별한 내용이 있는 글도 아니었다.
매우 짧고, 포스팅 작성기능 테스트용으로 작성한 무의미한 글이었다.
그런데 이상하게 특정 검색어와 꽤 가까운 값이 나왔다.
왜 그런지 찾아봤다.
내용이 짧거나 특별한 특징이 없는 텍스트는 임베딩으로 변환했을 때 특정 주제와 강하게 연결되지 않는다.
그렇다 보니 여러 검색어에 대해 애매하게 가까운 값이 나올 수 있었다.
이걸 보고 나니 0.35라는 숫자를 조금 조정하는 것만으로 해결하기 어렵다는 게 더 명확해졌다.
#6. 눈물을 머금은 롤백
예상되는 원인
- 무의미한 짧은 글의 벡터값 변환 노이즈 데이터
- 포스팅을 벡터값으로 변환시켜주는 tsvector 문제(아닌것 같다. 똑똑맨들이 만들고 많은 사람들이 쓰는데?)
- 내가 설정한 청크단위로 끊는 로직의 문제?
문제를 해결하게 되면 다음 시리즈를 올려보겠다.