목차
찾아보기를 만들려면 먼저 정해야 할 게 있다. 어떤 낱말을 목록에 올릴 것인가.
자르는 일에 이름이 있다
본문을 조각으로 자르고, 그 조각을 색인에 올릴 모양으로 다듬는 일을 토큰화라고 부른다. 잘라 나온 조각 하나가 토큰이다.
"주문 후 장바구니를 비웠습니다"
↓ 자른다
[주문] [후] [장바구니] [를] [비우] [었] [습니다]
↓ 다듬는다
[주문] [장바구니] [비우]이 결과가 곧 역색인의 사전에 올라가는 낱말이다. 그래서 여기서 안 만들어진 낱말은 영원히 검색되지 않는다. 색인에 없는 것은 찾을 방법이 없다.
책의 찾아보기를 만드는 사람이 하는 일과 같다. 격리 수준으로 올릴지 격리와 수준으로 따로 올릴지를 정하는 일이고, 그 결정이 독자가 그 항목을 찾을 수 있는지를 정한다.
공백으로 자르면 끝나는 줄 알았다
영어라면 공백에서 자르면 될 것 같다. 실제로 그렇게 시작한다. 그런데 몇 줄만 내려가도 걸린다.
don't- 하나인가 둘인가state-of-the-art- 하이픈에서 자르면art로 검색됐을 때 걸린다C++와C#- 기호를 떼면 둘 다c가 된다192.168.0.1- 점에서 자르면 주소가 사라진다
그래서 영어도 “공백에서 자른다”가 아니라 규칙을 정해 자른다. 기호를 어떻게 다룰지, 숫자를 어떻게 다룰지가 전부 결정 사항이다.
다만 영어는 그래도 공백이 대체로 낱말 경계라는 출발점이 있다. 한국어에는 그 출발점이 없다.
한국어는 공백이 낱말 경계가 아니다
같은 낱말이 문장마다 다르게 붙어 나온다.
장바구니를 비웠다
장바구니에 담았다
장바구니는 비어 있다공백에서 자르면 토큰이 장바구니를, 장바구니에, 장바구니는 셋이 된다. 색인에는 이 셋이 서로 아무 관계 없는 낱말로 올라간다.
이제 사용자가 검색창에 장바구니라고 친다. 색인에 장바구니라는 낱말은 없다. 세 문서 중 아무것도 안 걸린다. 데이터는 멀쩡히 있고 색인도 멀쩡히 돌았는데 결과가 0건이다.
그래서 한국어는 조사를 떼어내야 한다. 이 일을 하는 게 형태소 분석기다. 사전과 규칙을 들고 장바구니를을 장바구니와 를로 쪼갠 뒤 앞엣것만 색인에 올린다.
형태소 분석기도 만능은 아니다. 약한 자리가 뚜렷하다.
- 사전에 없는 말에 약하다. 신조어·제품명·회사 이름이 엉뚱하게 쪼개진다
- 복합명사를 어디까지 붙일지 애매하다.
한국어정보처리를 통째로 둘지 셋으로 쪼갤지에 따라 검색 결과가 달라진다 - 사전을 손으로 채워 넣어야 하고, 그 사전이 운영 중에 계속 자라는 물건이 된다
같은 것을 같게 만든다
자르고 나면 다듬는 단계가 온다. 눈에 같아 보이는 것들을 실제로 같은 토큰으로 만드는 일이다. 이걸 정규화라고 한다.
대소문자를 맞춘다. Search와 search가 다른 낱말이면 곤란하다. 대개 소문자로 통일한다.
유니코드 층에서 같게 만든다. 눈에 똑같이 보이는 글자가 데이터로는 다른 경우가 있다. 가가 완성된 한 글자일 수도, ㄱ과 ㅏ를 조합한 것일 수도 있다. 이걸 한쪽으로 몰아 주지 않으면 육안으로는 같은 낱말인데 검색이 안 되는 일이 생긴다. 이 이야기는 글자 수 세기 글에서 이미 다뤘다. 검색에서는 그 어긋남이 “결과 0건”으로 나타난다는 점만 다르다.
어형을 하나로 모은다. 영어라면 running·ran·runs를 run으로 모은다. 그래야 어느 형태로 검색해도 걸린다. 대신 지나치게 깎으면 뜻이 다른 낱말끼리 뭉쳐서 엉뚱한 결과가 나온다.
뺄 것을 정한다
그리고, 하지만, the, is 같은 낱말은 거의 모든 문서에 나온다. 이런 낱말을 불용어라 하고, 전통적으로는 색인에서 빼 왔다.
이유는 두 가지였다. 포스팅 리스트가 문서 전체만큼 길어져 공간을 먹고, 그 목록을 훑는 게 곧 전체를 훑는 것과 같아 느려지기 때문이다.
그런데 빼면 못 찾는 것이 생긴다.
The Who- 밴드 이름인데 두 낱말이 다 불용어다to be or not to be- 통째로 사라진다IT- 소문자로 바꾸면 대명사it이 되어 빠진다
그래서 요즘은 덜 뺀다. 저장 공간이 싸졌고, 무엇보다 다음 글에서 볼 랭킹이 흔한 낱말의 값을 알아서 낮춰 주기 때문이다. 지우는 대신 순위에서 내리는 쪽이 잃는 게 적다.
자르기를 포기하는 방법
자르는 규칙을 잘 만드는 게 어려우면, 아예 규칙 없이 자르는 길도 있다. 글자를 정해진 개수씩 겹쳐 가며 전부 자르는 방법이고, 두 글자씩이면 이렇게 된다.
장바구니 → [장바] [바구] [구니]사전이 필요 없다. 형태소 분석기가 모르는 신조어·제품명·오타에도 똑같이 동작한다. 조사가 붙어 있어도 앞쪽 조각은 그대로 나오니 장바구니로 검색해도 걸린다.
대가는 둘이다. 색인이 커진다. 낱말 하나가 조각 여러 개를 만드니 포스팅 리스트 수가 몇 배로 는다. 그리고 엉뚱한 문서가 걸린다. 조각이 우연히 겹치기만 해도 후보에 들어오기 때문에, 낱말 경계를 넘어 이어진 조각까지 붙잡는다.
| 형태소 분석 | 글자 조각 | |
|---|---|---|
| 사전이 필요한가 | 필요하다 | 필요 없다 |
| 신조어·제품명 | 약하다 | 강하다 |
| 색인 크기 | 작다 | 크다 |
| 엉뚱한 결과 | 적다 | 많다 |
둘 중 하나만 골라야 하는 것도 아니다. 본문은 형태소로, 제품명 컬럼은 글자 조각으로 색인해 두고 두 결과를 합치는 식으로 섞어 쓴다.
색인할 때와 찾을 때 같은 칼을 써야 한다
여기가 실제 사고가 가장 많이 나는 자리다.
토큰화는 두 번 일어난다. 문서를 색인할 때 한 번, 사용자가 검색어를 넣을 때 또 한 번. 그리고 이 둘은 같은 규칙이어야 한다.
두 판에서 다른 것은 규칙 칸 하나뿐이다. 그 한 칸 때문에 끝의 토큰이 서로 만나지 못한다.
색인할 때는 형태소 분석을 걸어 장바구니를 만들어 두고, 검색할 때는 공백으로만 잘라 장바구니를을 그대로 던지면, 양쪽이 만나지 못한다. 색인에도 데이터가 있고 검색어도 맞는데 결과가 0건이다.
반대 방향도 같다. 색인 규칙을 바꾸고 나서 기존 문서를 다시 색인하지 않으면, 옛 문서는 옛 칼로 잘린 토큰을 들고 있어서 새 검색어와 안 맞는다. 분석 규칙을 바꾸는 일은 언제나 전체 재색인을 부른다.
이 실패는 에러를 안 낸다. 쿼리는 성공하고 응답도 200이고 결과만 비어 있다. 그래서 “검색이 안 된다”는 신고를 받으면 먼저 볼 것은 코드가 아니라 양쪽 토큰이다. 대부분의 검색엔진에 문자열을 넣으면 토큰이 어떻게 나오는지 보여주는 기능이 있다.
실무에서: “검색이 안 돼요”를 받으면
이 신고는 원인이 열 갈래로 보이지만 실제로는 순서대로 짚으면 대개 세 번째 안에서 끝난다.
첫째, 색인에 그 문서가 있는가. 문서 번호로 직접 조회해 본다. 없으면 검색 문제가 아니라 색인 문제다. 아직 안 들어왔을 수도 있는데, 그 이야기는 뒤에서 따로 한다.
둘째, 그 문서의 토큰에 사용자가 친 낱말이 있는가. 본문에 장바구니를이 있어도 토큰이 장바구니를이면 장바구니 검색에 안 걸린다. 여기서 걸리면 분석 규칙 문제다.
셋째, 검색어 쪽 토큰은 어떻게 나오는가. 양쪽을 나란히 찍어 보면 어긋난 지점이 바로 보인다.
세 번째까지 다 맞는데 결과가 이상하다면 그건 찾는 문제가 아니라 순서 문제다. 찾긴 찾았는데 20번째에 있어서 사용자가 못 본 것이다.
그리고 규칙을 고칠 때 지킬 게 하나 있다. 분석 규칙은 색인과 검색 양쪽에 한 벌로 묶어 관리한다. 둘을 따로 설정할 수 있게 되어 있으면 언젠가 반드시 한쪽만 바뀐다.
정리
- 본문을 색인에 올릴 낱말로 자르고 다듬는 일이 토큰화다. 여기서 안 만들어진 낱말은 영원히 검색되지 않는다.
- 한국어는 공백이 낱말 경계가 아니라서 조사를 떼어내야 한다. 안 떼면
장바구니검색에 아무것도 안 걸린다. - 정규화는 눈에 같아 보이는 것을 실제로 같은 토큰으로 만든다. 대소문자·유니코드·어형이 대상이다.
- 불용어를 빼면 못 찾는 것이 생긴다. 요즘은 지우는 대신 순위에서 내리는 쪽을 고른다.
- 규칙 없이 글자를 겹쳐 가며 자르는 길도 있다. 사전이 필요 없는 대신 색인이 커지고 엉뚱한 결과가 는다.
- 가장 흔한 사고는 색인할 때와 검색할 때 다른 규칙을 쓴 것이다. 에러가 안 나고 결과만 0건이다.
여기까지가 “찾는” 이야기였다. 다음 글은 찾고 난 다음이다. 조건에 맞는 문서가 5,000건 나왔을 때 무엇을 맨 위에 둘 것인가. 책의 찾아보기에는 없던 문제인데, 기계가 만든 색인에는 반드시 생긴다.