10화에서 매칭의 원리를 설명했습니다. 파싱으로 뽑아낸 원재료 이름을, 표준 사전의 항목과 연결하는 작업이라고요.
이제 그 작업을 실제로 돌려볼 차례였습니다.
제품이 약 104만 개. 각 제품에 평균적으로 원재료가 여러 개씩 들어가 있으니, 연결해야 할 건수는 훨씬 많습니다.
전부 돌리고 나서 세어보니, 총 매칭 건수가 700만 건을 넘었습니다.
솔직히 이 숫자를 처음 봤을 때는 좀 놀랐어요. 700만이라는 숫자가 피부에 와닿지 않았거든요.
쉽게 말하면 이런 겁니다. 700만 장의 카드가 있고, 각 카드에 적힌 이름을 보고 4만 3천 개의 항목이 있는 명부에서 맞는 것을 찾아 연결하는 작업.
이 작업만 해도 사람이 하면 아마 몇 년은 걸릴 거예요. 컴퓨터가 하니까 비교적 빠른 시간 안에 끝나긴 했지만, 그래도 한 번 돌리는 데 적지 않은 시간이 걸렸습니다.
결과를 확인했습니다.
전체 매칭 건수: 약 725만 건. 매칭 성공률: 99.5%.
99.5%라는 숫자는 꽤 높아 보입니다. 처음 이 결과를 봤을 때, 저는 상당히 안심했어요. “거의 다 연결됐구나.”
하지만 0.5%가 남아 있었습니다. 약 3만 8천 건.
3만 8천 건이 매칭되지 않았다는 뜻은, 그만큼의 원재료가 표준 사전에서 대응하는 항목을 찾지 못했다는 의미입니다.
매칭되지 않은 것들을 살펴봤습니다.
여러 유형이 있었어요.
제조사가 만든 독자적인 이름. “A사 특제 시즈닝”이나 “B사 자체 개발 소스” 같은 것들. 이건 표준 사전에 있을 리가 없습니다.
화학 물질의 복잡한 이름. 합성향료에 들어가는 화학 명칭이 길고 복잡해서, 사전의 어떤 항목과도 일치하지 않는 경우.
오타나 특이한 표기. “탈지 분유”(띄어쓰기 위치가 다른), “비타민 C”(반각/전각 차이) 같은 것들.
전부를 해결할 수는 없었지만, 하나씩 패턴을 분석하면서 규칙을 추가했습니다.
띄어쓰기를 무시하고 비교하는 규칙. 전각 문자를 반각으로 바꾸고 비교하는 규칙. 접미사(“분말”, “추출물”, “농축액” 등)를 떼어내고 비교하는 규칙.
이런 규칙을 하나 추가할 때마다 매칭률이 조금씩 올라갔습니다.
매칭 방법별로 통계를 내보니 이렇게 나왔습니다.
정확 일치가 가장 많았습니다. 제조사가 표준 사전과 같은 이름을 사용한 경우. 전체의 약 93%가 이 방법으로 연결됐어요.
그 다음이 포함 매칭. 이름의 일부가 사전 항목에 포함되어 있어서 연결된 경우.
그리고 괄호 제거 후 매칭, 정규화 후 매칭 등이 나머지를 차지했습니다.
숫자만 보면 “93%가 정확 일치니까 괜찮네” 싶을 수 있지만, 나머지 7%에 해당하는 수십만 건에서 미세한 오류가 발생할 수 있다는 뜻이기도 했습니다.
특히 조심해야 했던 건 포함 매칭이었습니다.
10화에서 “고추냉이”와 “고추” 이야기를 했죠. 이런 식의 오매칭 가능성이 포함 매칭에는 항상 존재합니다.
그래서 포함 매칭으로 연결된 건에는 신뢰도 점수를 낮게 매기고, 나중에 검토할 수 있도록 별도로 표시해뒀습니다.
실제로 포함 매칭에서 발생한 오매칭을 잡아내기 위해 여러 차례 검수 작업을 했어요.
합성향료의 영문 화학명이 짧은 영어 단어와 매칭되는 문제. 예를 들어 어떤 화학 물질 이름에 “oil”이라는 글자가 포함되어 있어서 “oil”이라는 사전 항목과 매칭되어버리는 식.
이런 것들을 하나씩 발견하고 예외 규칙을 추가하는 과정이 매칭 작업의 절반 이상이었습니다.
그리고 한 가지 아쉬운 발견이 있었습니다.
공공데이터포털에 식품 원재료 정보를 제공하는 API가 하나 더 있었는데, 이 데이터가 정말 잘 정리되어 있었어요. 원재료마다 표준 코드가 붙어 있고, 어떤 원재료가 어떤 원재료의 하위 성분인지 계층 구조까지 있었습니다.
이 데이터만 있으면 파싱도 매칭도 훨씬 쉬워질 텐데…
문제는, 이 API가 사용하는 제품 번호 체계가 우리가 가진 제품 데이터의 번호 체계와 달랐다는 겁니다.
114만 건의 데이터가 있었는데, 거기서 고유한 원재료 정보는 약 1만 7천 건이었어요. 나머지는 같은 원재료가 제품별로 반복되어 있었고요.
그 1만 7천 건의 좋은 데이터를 제품과 연결하려고 번호를 대조해봤는데, 매칭되는 것이 없었습니다. 번호 자릿수부터 달랐어요.
같은 제품을 가리키고 있을 텐데 연결할 수 없다는 건, 정말 아까운 일이었습니다.
그리고 한 가지 더 중요한 문제가 있었습니다.
사전 자체의 문제였어요.
4만 3천 개의 원재료 사전에서, 이름이 겹치는 항목들이 있었습니다.
같은 이름이 두 개의 다른 코드로 등록되어 있는 경우. 또는 어떤 항목의 이명이 다른 항목의 표준 이름과 같은 경우.
예를 들어 “물”이라는 이름이 식품원료로서의 “물”과 위생용품으로서의 “물” 양쪽에 다 있으면, 제조사가 “물”이라고 적었을 때 어느 쪽으로 연결해야 하죠?
이런 충돌을 해결하기 위해 우선순위 규칙을 만들었습니다. 식품 원료가 첨가물보다 우선하고, 첨가물이 위생용품보다 우선하는 식으로.
이렇게 매칭 엔진을 만들고, 돌리고, 결과를 검수하고, 규칙을 다듬고, 다시 돌리는 과정을 반복했습니다.
매칭률은 점점 올라갔고, 99.5%라는 숫자에 도달했을 때 저는 꽤 만족스러웠습니다.
“이 정도면 됐다. 이제 프론트엔드를 만들 수 있겠다.”
그런데 그 만족감은 오래가지 않았습니다.
매칭률이 높다는 것과 매칭이 정확하다는 것은 다른 문제였거든요.
그 이야기는 다음 화에서 하겠습니다. 새우깡 이야기입니다.