LabHub

LLM 엔지니어링 · 토크나이저와 임베딩 · 실습

TF-IDF 임베딩과 코사인 검색

LabHub 에서 이어서 보기

목표

TF-IDF 임베딩을 numpy 로 직접 계산하고, 코사인 유사도로 문서를 검색하며, 차원 축소의 대가를 숫자로 확인합니다.

왜 중요한가

임베딩 API 를 호출하면 벡터가 나옵니다. 그 안에서 무슨 일이 일어나는지 모른 채로도 쓸 수는 있지만, 검색 결과가 이상할 때 원인을 좁힐 수 없습니다.

TF-IDF 는 신경망 임베딩보다 훨씬 단순하지만 핵심 구조는 같습니다. 텍스트를 벡터로 바꾸고, 길이를 정규화하고, 내적으로 유사도를 잰다는 뼈대가 동일합니다. 그래서 여기서 정규화를 빼먹으면 긴 문서가 모든 질의에 걸리는 현상을 직접 볼 수 있고, 그 경험이 실제 벡터 검색을 디버깅할 때 그대로 쓰입니다.

마지막 단계의 해싱 트릭도 의미가 있습니다. 차원을 줄이면 메모리와 속도를 얻지만 충돌 때문에 정보를 잃습니다. 그 손실이 실제로 얼마나 되는지 숫자로 재 보면, 벡터 차원을 고를 때의 감각이 생깁니다.

단계

작업 디렉터리는 /root/llm 입니다. 대상은 docs 표의 문서 30건입니다.

1. 각 문서를 토큰화해 /root/llm/doc_tokens.tsv 에 저장합니다. docs.id<탭>공백으로 구분한 토큰들 형식이고 30줄입니다. 토큰화 규칙은 소문자로 바꾼 뒤 정규식 [가-힣a-z0-9]+ 로 뽑아내기 입니다.
2. 항별 문서 빈도를 /root/llm/df.tsv항<탭>df 형식으로 저장합니다. 항은 오름차순 정렬이고, 한 문서에 여러 번 나와도 1로 셉니다.
3. IDF 를 /root/llm/idf.tsv항<탭>idf 형식으로 저장합니다. 항 순서는 2번과 같고 값은 소수점 여섯 자리입니다. 공식은 ln((1 + N) / (1 + df)) + 1 이며 N 은 문서 수입니다.
4. TF-IDF 행렬을 /root/llm/tfidf.npy 로 저장합니다. 모양은 (문서 수, 항 수)이고 항의 순서는 2번과 같습니다. 값은 등장 횟수에 idf 를 곱한 뒤 각 행의 L2 길이를 1로 정규화한 것입니다.
5. 각 문서의 최근접 이웃을 /root/llm/sim_top.tsvdocs.id<탭>가장 비슷한 docs.id<탭>점수 로 저장합니다. 자기 자신은 제외하고, 점수는 소수점 여섯 자리이며, 동점이면 docs.id 가 작은 쪽을 고릅니다.
6. 아래 질의 3개에 대해 상위 3개 문서를 /root/llm/query_top3.tsv질의번호<탭>순위<탭>docs.id<탭>점수 로 저장합니다. 총 9줄입니다.

참고

단계 8개

  1. 문서 토큰화하기
  2. 문서 빈도 세기
  3. IDF 계산하기
  4. TF-IDF 행렬 만들기
  5. 최근접 이웃 찾기
  6. 질의로 문서 검색하기
  7. 해싱 트릭으로 차원 줄이기
  8. 두 방식의 결과 비교하기