繩鋸木斷水滴石穿/Python

[IR] 유사 키워드 매칭하기: python rapidfuzz 사용하기

stranger95 2024. 9. 29. 16:13

들어가며

 

RAG 프로세스를 구상하던 중이었습니다. 사용자가 작성한 질문에서 A'라고 물어봤는데, 이거를 A로 단어를 바꾸어서 검색할 수 없나? A는 미리 정해놓은 범위 내에서 찾게 하고 싶은데 어떻게 바꿀 수 있을까..? 

 

문제 상황은, 서비스 사용자들이 A를 A' 또는 A''라고 쓰기도 하고, AB라고 작성하기도 한다는 것입니다. 실제로 검색 대상인 문서에는 A라고 명확하게 정의된 데 반해, 사용자들은 각자 편하게 부르고 싶은 대로 작성하기도 하고 오타로 인해 잘못 작성하기도 한다는 것이죠.  

 

오타를 내서 검색한 경우

 

사용자마다 가지각색으로 작성된 단어들을 표준화된 단어로 변경해주기 위해서는 사전 검색이 필요했습니다. 다만 사용자들이 어떤 식으로 작성할지를 구상해서 미리 모아두는 것이 실제로는 어렵죠. 때문에 어떤 단어가 들어와도 우리가 미리 정의해둔 단어 목록 중 하나 또는 그 이상 개수로 매칭할 수 있어야 합니다.

 

RAG 서비스의 경우, 사용자가 사람에게 물어보듯 문장을 구상해서 물어보기 때문에, 문장 내에서 관심 키워드를 추출해야 합니다. 이 후에 비교해야 하는 키워드 목록과 대조하면서 그 중 유사한 키워드를 찾아낸다고 할 때 이 키워드 매칭을 적용할 수 있을 것입니다.

 

예를 들어, "고객이 지금 가입한 보험에는 암 보장이 좀 부족한 것 같은데, 암을 보장하는 특약 추천해줘" 라고 질문한다고 합시다. 여기서 질의에 적혀 있으면서 서비스가 관심 대상으로하는 키워드 내지 구문은 "암을 보장하는 특약"입니다. 그리고 이걸 우리가 보유하고 있는 특약 목록인 ["종합암진단특약", "4대암특약", "여성암보장특약", "종합병원수술특약", "장기요양입원어쩌고특약", ..., "로봇수술전용특약"] 과 비교해서 유사한 특약명과 매칭을 해주는 거죠. "암을 보장하는 특약"은 "종합암진단특약", "4대암특약", "여성암보장특약"과 매칭하고, 해당 특약들로 범위를 한정하여 검색을 하는 방향으로 프로세스를 설계할 수 있을 것입니다.

 

 

 

rapidfuzz 소개

 

rapidfuzzLevenshtein Distance 알고리즘을 사용하여 유사도를 계산해 제공하는 파이썬 라이브러리입니다. 

 

Levenshtein Distance 는 유사도를 문자열 A를 문자열 B로 변경하기 위해 얼마나 많은 변경이 필요한가로 정의합니다. 변경 연산에는 문자 삽입(Insertion), 삭제(Deletion), 대체(Substitution)가 포함되고, 당연히 변경하는 사항이 적을수록 두 문자열의 유사도가 높다고 계산되겠죠. Levenshtein Distance는 Edit Distance Algorithm이라고도 불린다고 합니다.

 

출처: https://devopedia.org/levenshtein-distance

 

rapidfuzz는 동일한 기능을 제공하는 fuzzywuzzy 라이브러리를 속도 측면에서 개선하였습니다. 속도 개선을 위해 C++로 작성했고 문자열 매칭을 빠르게 수행할 수 있도록 내부의 알고리즘들도 함께 개선했다고 하네요.

 

 

 

파이썬 적용하기

 

유사도 계산하기

 

파이썬: 3.10.0
rapidfuzz: 3.10.0
from rapidfuzz import fuzz

print(fuzz.ratio("암을 보장하는 특약", "고액암특약"))
print(fuzz.ratio("암을 보장하는 특약", "종합암진단특약"))
40.0
35.29411764705882
print(fuzz.partial_ratio("암을 보장하는 특약", "고액암특약"))
print(fuzz.partial_ratio("암을 보장하는 특약", "종합암진단특약"))
57.14285714285714
44.44444444444444

 

fuzz 모듈은 ratio, partial_ratio, token_sort_ratio, token_set_ratio 등등 다양한 유사도 계산 알고리즘을 제공합니다. 

ratio는 levenshtein distance를 그대로 사용하여 두 문자열이 얼마나 유사한가를 계산하며,

partial_ratio는 하위 문자열 매칭을 수행하여, 가장 짧은 문자열을 기준으로 하위 문자열과 얼마나 일치하는지를 계산합니다. 하나의 문자열이 다른 문자열에 완전히 포함되어 있다면 100점이 나옵니다.

각 함수의 설명과 fuzzywuzzy와의 속도 비교는 github를 참고하세요.

 

 

유사한 키워드 얻기

 

from rapidfuzz import process

query = "암을 보장하는 특약"
choices = ["고액암진단특약", "정기사망특약", "암특정치료비특약(종합병원)", "항암약물치료특약", "뇌질환진단특약", "암직접치료입원특약(요양병원제외)", "다빈치로봇수술특약"]

print(process.extract(query, choices, scorer=fuzz.ratio))
print(process.extractOne(query, choices, scorer=fuzz.ratio))
[('고액암진단특약', 35.29411764705882, 0), ('항암약물치료특약', 33.333333333333336, 3), ('정기사망특약', 25.0, 1), ('암특정치료비특약(종합병원)', 25.0, 2), ('뇌질환진단특약', 23.529411764705888, 4)]
('고액암진단특약', 35.29411764705882, 0)

 

process 모듈은 검색할 키워드와 비교 대상이 되는 키워드 목록들을 대상으로 어떤 것이 가장 유사한지 결과를 반환하는 함수들을 제공합니다. scorer 파라미터를 이용해 fuzz에서 제공하는 유사도 알고리즘들을 적용할 수 있습니다.

만약 가장 유사한 키워드 하나만 추출하고 싶다면 extractOne을 사용하세요.

extract는 모든 비교 후보들을 대상으로 유사도를 함께 제공해주며, "30점 이상인 키워드만 추출하기"와 같은 작업을 수행하고 싶다면 score_cutoff 파라미터를 사용해 기준을 설정할 수 있습니다.

cutoff 기준을 설정하기 어렵다면, 유사도 점수가 급격하게 감소하는 지점을 찾아 그 직전까지만 유사한 키워드로 가정하고 추출하는 것도 적용해볼수 있겠습니다.

 

 

 

마치며

 

본 포스트에서는 유사도 매칭이 필요한 상황과 한국어 유사도 매칭을 위해 파이썬에서 제공하는 rapidfuzz 사용법에 대해 알아보았습니다.

사실 levenshtein distance가 한국어 비교에 아주 적절하다고 생각하지는 않습니다. 한국어는 영어와 다르게 음절 단위로 문자가 조합되기 때문에, 자모 단위로 유사성을 고려하지 못한다는 것이죠.

임베딩 모델을 적용해 유사한 키워드를 매칭하는 쪽이 더 정확도가 높을 수 있습니다. 다만 임베딩 모델을 올리는 것에 부담이 있거나 빠른 연산 결과가 필요한 경우에는 해당 라이브러리도 적절하게 사용될 수도 있을 것 같습니다. 업무에 참고하시길 바랍니다. 🙇‍♀️🙇‍♀️

 

 

참고

 

https://www.lgcns.com/blog/cns-tech/aws-ambassador/52009/

 

‘Fuzzy String Matching’ 노하우를 공개합니다. - LG CNS

Intro 기업 내 다양한 시스템 안에서 고객사, 파트너사, 벤더사 등의 회사명을 표기할 때, 동일한 회사명이 조금씩 다르게 표기된 것을 보신 적이 있을 것입니다. 예를 들어 ‘LG CNS’라는 기업명

www.lgcns.com

https://rapidfuzz.github.io/RapidFuzz/index.html

 

RapidFuzz 3.10.0 documentation

Next Installation

rapidfuzz.github.io

https://medium.com/@kasperjuunge/rapidfuzz-explained-c26e93b6012d

 

Rapidfuzz Explained

Rapidfuzz is a powerful Python library for string matching, offering a suite of functions designed for various fuzzy comparison tasks…

medium.com

https://devopedia.org/levenshtein-distance

 

Levenshtein Distance

Given two words, we can ask how similar are the two words. We can also ask this question of two sentences or string sequences. To quantify the similarity, we need a measure. Levenshtein Distance is such a measure.

devopedia.org