RapidFuzz: Fast string matching library

Gyuri Lee·2025년 1월 16일
0
post-thumbnail

Overview

온라인 쇼핑 사이트에서 브랜드 이름을 검색할 때,
우리는 여러 가지 형태로 동일한 브랜드를 입력할 수 있습니다.

예를 들어:

  • 구찌gucci
  • 메종 키츠네메종키츠네
  • 비버리힐즈폴로클럽, 비버리힐즈 폴로클럽, 비버리힐스폴로클럽

이처럼 동일한 브랜드를 다른 방식으로 입력해 검색하는 경우가 많습니다.

소비자로서
우리는 구찌를 검색하든지 gucci를 검색하든지 간에
동일한 검색 결과가 나오는 것을 기대합니다.

이는 사용자 경험 측면에서 매우 중요하지만,
컴퓨터 로직으로 이를 처리하는 것은 생각보다 복잡한 문제입니다.

브랜드 이름 자체는 특별한 의미를 지니지 않기 때문에,
의미적 유사도로 동일한 브랜드인지 판단하기가 어렵기 때문입니다.

이런 문제를 해결하기 위해 RapidFuzz를 사용하는 것을 추천합니다.
Github 바로가기 🖥️

본론

RapidFuzz란

RapidFuzz는 파이썬(및 C++ 지원)으로 작성된 문자열 매칭 및 유사도 측정 라이브러리입니다. 이 라이브러리는 Levenshtein 거리 알고리즘을 기반으로 하여, 다양한 형태의 문자열 유사도를 계산할 수 있는 기능을 제공합니다. RapidFuzz는 특히 빠르고 효율적이며, 대량의 데이터 처리에도 용이합니다.

Levenshtein 거리의 개념

Levenshtein(레벤슈타인) 거리 알고리즘은 두 문자열 간의 유사성을 측정하기 위한 기준 중 하나로, 두 문자열을 동일하게 만들기 위해 필요한 최소 편집 작업의 수를 계산하는 방법입니다.
편집 작업에는 문자 삽입, 삭제, 교체가 포함됩니다.

  • 삽입 (Insertion): 문자열에 한 문자를 추가하는 작업입니다.
  • 삭제 (Deletion): 문자열에서 한 문자를 제거하는 작업입니다.
  • 교체 (Substitution): 문자열 내의 한 문자를 다른 문자로 변경하는 작업입니다.

예를 들어,
"kitten"과 "sitting"이라는 두 문자열이 있을 때:

  1. "kitten"의 'k'를 's'로 교체합니다. => "sitten"
  2. 'e'를 'i'로 교체합니다. => "sittin"
  3. 마지막으로 'n' 뒤에 'g'를 삽입합니다. => "sitting"

이 경우,
총 3번의 편집 작업이 필요하므로 "kitten"과 "sitting"의 Levenshtein 거리3입니다.

추가 예시로, "flaw"와 "lawn"을 비교할 수 있습니다:

  1. 'f'를 삭제합니다. => "law"
  2. 'a'를 교체합니다. => "lawn"

이 경우,
총 2번의 편집 작업이 필요하므로 "flaw"와 "lawn"의 Levenshtein 거리2입니다.

RapidFuzz 사용법

RapidFuzz를 사용하려면, 우선 라이브러리를 설치해야 합니다.
아래의 명령어로 쉽게 설치할 수 있습니다.

pip install rapidfuzz

사용 예시는 아래와 같습니다:

from rapidfuzz import fuzz

test_str1, test_str2 = "비버리힐즈폴로클럽", "비버리힐스폴로클럽"
score = fuzz.ratio(test_str1, test_str2)
print(f"{test_str1} vs {test_str2} score: {score}")
# 비버리힐즈폴로클럽 vs 비버리힐스폴로클럽 score: 88.88888888888889

test_str1, test_str2 = "구찌", "비버리힐즈폴로클럽"
score = fuzz.ratio(test_str1, test_str2)
print(f"{test_str1} vs {test_str2} score: {score}")
# 구찌 vs 비버리힐즈폴로클럽 score: 0.0

RapidFuzz는 단순한 문자열 유사도 비율 뿐만 아니라, 다양한 기능을 제공합니다.

예를 들어:

  • fuzz.token_set_ratio: 부분 문자열만을 비교할 수 있어, 문자열끼리의 포함관계를 파악할 수 있습니다.
  • processor=utils.default_process: 특수 문자를 무시하고 문자열 유사도를 측정할 수 있는 옵션입니다.

이렇게 다양한 기능을 통해
사용자는 자신의 목적에 맞는 최적의 방법으로 문자열을 비교할 수 있습니다.

from rapidfuzz import fuzz, utils

test_str1, test_str2 = "비버리힐즈 폴로클럽", "비버리힐즈"
score = fuzz.ratio(test_str1, test_str2)
print(f"{test_str1} vs {test_str2} score: {score}")
# 비버리힐즈 폴로클럽 vs 비버리힐즈 score: 66.66666666666667

test_str1, test_str2 = "비버리힐즈 폴로클럽", "비버리힐즈"
score = fuzz.token_set_ratio(test_str1, test_str2)
print(f"{test_str1} vs {test_str2} score: {score}")
# 비버리힐즈 폴로클럽 vs 비버리힐즈 score: 100.0

test_str1, test_str2 = "구찌", "구찌!!"
score = fuzz.ratio(test_str1, test_str2)
print(f"{test_str1} vs {test_str2} score: {score}")
# 구찌 vs 구찌!! score: 66.66666666666667

test_str1, test_str2 = "구찌", "구찌!!"
score = fuzz.ratio(test_str1, test_str2, processor=utils.default_process)
print(f"{test_str1} vs {test_str2} score: {score}")
# 구찌 vs 구찌!! score: 100.0

RapidFuzz 꿀팁

브랜드 이름과 같은 경우,
"gucci"와 "구찌"의 RapidFuzz 점수가 낮게 나올 수 있습니다.

이럴 땐, 구글 번역 API를 활용하여
문자열을 번역한 후에 RapidFuzz로 유사도를 계산하면,
더 높은 유사도 점수를 얻을 수 있습니다.

from googletrans import Translator
from rapidfuzz import fuzz, utils

test_ko_str, test_en_str =  "에르메스","HERMES"
original_score = fuzz.ratio(test_ko_str, test_en_str, processor=utils.default_process)
print(f"original score: {original_score}")
# original score: 0.0

translator = Translator()
translated_en_str = translator.translate(test_ko_str, dest='en').text
translated_en_str_score = fuzz.ratio(translated_en_str, test_en_str, processor=utils.default_process)
print(f"translated score: {translated_en_str_score}")
# translated score: 100.0

테스트 결과

저는 총 8000여 개 브랜드 이름 사이에서 유사도를 구했고,
95점 이상 유사도를 가진 브랜드 이름끼리 묶어보았습니다.

아래는 그 테스트 결과입니다.

한글은 googletrans를 통해 번역후 rapidFuzz 유사도를 구했습니다.

이미지를 보시면
동일 브랜드를 의미하는 단어끼리 잘 묶인 것을 확인할 수 있습니다.

결론

RapidFuzz는 브랜드 이름과 같은 비표준 문자열을 다룰 때 매우 유용한 도구입니다.

이 라이브러리는 효율적이고 직관적인 방법으로 문자열 간의 유사성을 측정할 수 있어,
자연어 처리에서 발생하는 여러 문제를 효과적으로 해결할 수 있습니다.

특히 브랜드 이름과 같은 상황에서의 유사도 측정에서 그 진가가 발휘할 수 있음을 확인했습니다.

0개의 댓글