LLM 엔지니어링7장 · 자체 임베딩과 벡터DB, 검색 품질11 / 22← 이전목차다음 →MetaContents

7장. 자체 임베딩과 벡터DB, 검색 품질

재정렬

서류 심사로 지원자 열 명을 추린 뒤, 면접으로 한 사람씩 자세히 보고 순서를 다시 정합니다. 수백 명을 모두 면접할 수는 없으니 빠른 심사로 줄이고, 줄인 후보만 꼼꼼히 봅니다. 재정렬이 이 면접입니다.

질문과 문서를 따로 임베딩하는 바이 인코더와 한꺼번에 읽는 크로스 인코더를 견준 두 카드

왼쪽 바이 인코더(이 장의 e5)는 문서 임베딩을 미리 만들어 두고 질문만 새로 임베딩하므로 빠릅니다. 오른쪽 크로스 인코더는 질문과 문서를 한 덩어리로 읽어 관련 점수를 내므로, 둘 사이의 낱말 관계를 꼼꼼히 봅니다. 대신 후보마다 모델을 한 번씩 돌려야 해 느립니다. 여기서 서류 심사가 바이 인코더, 면접이 크로스 인코더입니다.

두 짜임 견주기

견줄 것바이 인코더크로스 인코더
입력질문, 문서를 따로(질문, 문서) 쌍
미리 해 둘 수 있나문서 임베딩을 미리 만든다못 한다. 질문이 와야 계산한다
속도수만 개도 빠르다후보 수십 개가 알맞다
정확도좋다대체로 더 좋다
쓰는 곳1단계: 후보 찾기2단계: 후보 다시 줄 세우기

크로스 인코더 쓰는 꼴

sentence-transformers 의 CrossEncoder 는 (질문, 문서) 쌍의 목록을 받아 점수 목록을 돌려줍니다. 아래 줄은 꼴만 보이고, 실제로는 실습 6에서 돌립니다.

Python
question = "로그인을 여러 번 실패해서 들어갈 수가 없어요."
candidates = ["## 계정 잠김\n비밀번호를 5번 틀리면 계정이 30분 동안 잠깁니다.",
              "## 2단계 인증\n로그인할 때는 휴대폰 인증 앱으로 2단계 인증을 합니다."]
pairs = [(question, c) for c in candidates]          # 크로스 인코더에는 쌍으로 넣는다
print(len(pairs), "쌍:", pairs[0][1].split("\n")[0], "/", pairs[1][1].split("\n")[0])
# scores = CrossEncoder("cross-encoder/mmarco-mMiniLMv2-L12-H384-v1").predict(pairs)   ← 실습 6

2 쌍: ## 계정 잠김 / ## 2단계 인증 이 나옵니다. 크로스 인코더의 점수는 확률이 아니라 크기만 견주는 수(음수도 나온다)입니다.

코드에 나오는 함수

코드쉬운 뜻
candidates1단계 검색(바이 인코더)이 찾아 온 후보 조각들이다
[(question, c) for c in candidates]질문 하나를 후보마다 짝지어 (질문, 조각) 쌍의 목록을 만든다
pairs[0][1].split("\n")[0]첫 쌍의 둘째 칸(조각)에서 첫 줄(제목)만 꺼낸다
CrossEncoder("cross-encoder/mmarco-mMiniLMv2-L12-H384-v1")재정렬 모델을 불러온다. 이름의 mmarco 는 여러 언어로 옮긴 MS MARCO 검색 데이터, L12 는 층 12개, H384 는 좌표 384개라는 뜻이다. 여기서는 주석으로만 보였다
.predict(pairs)쌍마다 관련 점수 하나를 돌려준다. predict 는 "예측하다"라는 뜻이다

이 코드가 뜻하는 것

면접장에 지원자를 들여보내기 전에 "면접관(질문)과 지원자(조각)"를 한 조씩 짝짓는 단계입니다. 크로스 인코더는 이 한 조를 함께 보고 점수를 매기므로, 질문을 조각마다 하나씩 붙여 넣어야 합니다.

줄하는 일쉽게 말하면
question = ...사용자의 질문면접관의 질문지
candidates = [...]1단계가 찾아 온 후보 둘서류 심사를 통과한 지원자 둘
pairs = [...]질문과 후보를 하나씩 짝짓는다면접 조를 짠다
print(len(pairs), ...)쌍의 수와 각 쌍의 제목을 확인한다면접 순서표를 본다
# scores = ...predict(pairs)쌍마다 점수를 매긴다(실습 6)면접 점수

질문 "로그인을 여러 번 실패해서"와 "비밀번호를 5번 틀리면"은 낱말이 다릅니다. 크로스 인코더는 두 글을 한 덩어리로 읽기 때문에 이런 관계를 바이 인코더보다 꼼꼼히 볼 수 있습니다.

점수로 다시 줄 세우기

predict 가 점수를 돌려준 뒤에 하는 일은 "점수가 큰 순서로 다시 줄 세우기"뿐입니다. 아래 점수는 모델을 돌린 값이 아니라 설명을 위해 손으로 정한 숫자입니다.

Python
first = ["2단계 인증", "계정 잠김", "비밀번호 주기", "보안 인쇄"]    # 1단계(바이 인코더)가 찾은 순서
cross = [1.2, 6.5, 0.4, -3.1]                                       # 쌍마다 크로스 인코더 점수(설명용 숫자)

pairs = sorted(zip(cross, first), reverse=True)                     # 점수가 큰 순서로 다시 줄 세운다
for new_rank, (s, title) in enumerate(pairs, start=1):
    old_rank = first.index(title) + 1
    print(f"{new_rank}등 {title} / 점수 {s} / 전에는 {old_rank}등")
출력
1등 계정 잠김 / 점수 6.5 / 전에는 2등
2등 2단계 인증 / 점수 1.2 / 전에는 1등
3등 비밀번호 주기 / 점수 0.4 / 전에는 3등
4등 보안 인쇄 / 점수 -3.1 / 전에는 4등
결과읽는 법
1등 계정 잠김1단계에서 2등이던 조각이 가장 높은 점수(6.5)를 받아 1등으로 올라섰습니다
2등 2단계 인증1단계의 1등은 점수 1.2 로 한 칸 내려갔습니다
4등 보안 인쇄 점수 -3.1점수가 음수여도 오류가 아닙니다. 다른 후보보다 관련이 적다는 뜻일 뿐입니다

후보 넷은 그대로이고 순서만 바뀌었습니다. 이것이 "재정렬은 후보 안에서만 순서를 바꾼다"는 말의 뜻입니다.

주의할 점

  • 재정렬은 1단계가 찾아 온 후보 안에서만 순서를 바꿉니다. 정답이 후보 10개 안에 없으면 재정렬로도 찾지 못합니다.
  • 후보를 늘리면 정답이 들어올 가능성은 커지지만 그만큼 느려집니다. 이 장은 후보 10개를 다시 줄 세웁니다.
← 이전 절7장 · 하이브리드 검색다음 절 →7장 · 오픈소스 LLM 비교와 선택