n-Gram이란 문장의 유사도를 비교하는 방법중 하나로 문장을 쪼개서 비교한다
예를 들어
3-gram으로
'과자중에 제일 맛있는건 새우깡'
'제일 맛있는 과자는 무엇일까'
이 두문장을 비교한다면
각 문장을 3글자씩 자른다.
"과자중에 제일 맛있는건 새우깡"
[
'과자중', '자중에',
'중에 ', '에 제',
' 제일', '제일 ',
'일 맛', ' 맛있',
'맛있는', '있는건',
'는건 ', '건 새',
' 새우', '새우깡'
]
"제일 맛있는 과자는 무엇일까"
[
'제일 ', '일 맛',
' 맛있', '맛있는',
'있는 ', '는 과',
' 과자', '과자는',
'자는 ', '는 무',
' 무엇', '무엇일',
'엇일까'
]
그리고 각 요소를 비교해서 유사도를 측정한다
https://too-march.tistory.com/20
[5] 문장의 유사도 분석하기 - 레벤슈타인 거리, N-gram
레벤슈타인 거리란? 레벤슈타인 거리는 문자열이 얼마나 비슷한 지를 나타내는 것으로 편집 거리라고도 부른다. 비슷한 어구 검색, DNA 배열의 유사성 판단 등 다양한 분야에서 활용된다. 편집할
too-march.tistory.com
해당 블로그를 참고했는데 해당 블로그 코드에는 2가지 큰 문제가 있다
해당 블로그의 설명을 토대로 코드를 짜보면
exports.diff_ngram = (data, search, num) => {
if (search === undefined) return 0;
let splitArrA = ngram(data, num);
let splitArrB = ngram(search, num);
arr = [];
let count = 0;
for (let i = 0; i < splitArrA.length; i++) {
for (let j = 0; j < splitArrB.length; j++) {
if (splitArrA[i] === splitArrB[j]) {
count++;
arr.push(splitArrA[i]);
}
}
}
return count / splitArrA.length;
};
const ngram = (str, num) => {
let arr = [];
const repeat = str.length - num + 1;
for (let i = 0; i < repeat; i++) {
const split = str.slice(i, i + num);
arr.push(split);
}
return arr;
};
이러한데
1번문제점
이중 for문에 break가 없어서
첫 문장 요소와 일치 하는 값을 두번째 문장 요소에서 찾고 count++를 했을때 for문을 탈출하지 않아서
count가 또 오를 수 있다
즉, 1번문장과 2번문장의 길이보다도 count가 더 커질 수 있으므로 (유사도가 100%이상이 나올 수 있으므로)
for문안 if문에 break를 넣어줘야한다
2번문제점
마지막 리턴을 count / splitArrA.length로 줬는데
이러면 data값과 search값의 위치가 바꼈을때 즉, 두 문장의 위치를 바꿨을때 결과가 다르게 나온다
data값이 짧을 경우
data가 '새우깡'이고 search가 '새우깡은 과자다'인 경우
새우깡이 들어가기만 한다면 search가 아무리 길어도 유사도가 최고치인 1이 나온다
그러면 검색을 길게하면 할수록 많은 데이터들이 검색되는, 검색의 문장 성분들이 모두 or검색이 된다
data값이 길 경우
data가 '새우깡은 과자다'이고 search가 '새우깡'이라면 data 길이가 길어질 수록 유사도가 떨어지며
search가 '새우깡'이든 '새우깡임'이든 '새우깡ㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇ'이든 일치하지 않은 문자열이 아무리 길어도 같은 유사도가 나온다
그래서 return해주는 값을
자카드 지수(Jaccard index)를 이용했다
해당 공식을 이용하면 최대 유사도 1, 최저 유사도 0이며
두 문자열의 위치가 바뀌어도 결과값이 똑같고 더욱 더 신뢰성있는 결과를 얻을 수 있다
그래서 최종 코드는 아래와같다
const ngram = (str, num) => {
let arr = [];
const repeat = str.length - num + 1;
for (let i = 0; i < repeat; i++) {
const split = str.slice(i, i + num);
arr.push(split);
}
return arr;
};
exports.diff_ngram = (data, search, num) => {
if (search === undefined) return 0;
let splitArrA = ngram(data, num);
let splitArrB = ngram(search, num);
arr = [];
let count = 0;
for (let i = 0; i < splitArrA.length; i++) {
for (let j = 0; j < splitArrB.length; j++) {
if (splitArrA[i] === splitArrB[j]) {
count++;
arr.push(splitArrA[i]);
break;
}
}
}
return count / (splitArrA.length + splitArrB.length - count);
};
응용할 수 있는 방법으로
1-Gram을 하면 글자의 순서만 뒤엉켰을 경우
'안녕하세요'와 '하녕요세안'과 같은경우 정확도 1이 나와서 검색가능하게 만들 수 있다
n-Gram 개선
n-Gram 개선 및 고찰
자음 모음단위로 n-Gram 저번에 구현한 n-gram https://fullfish.tistory.com/109 n-Gram n-Gram이란 문장의 유사도를 비교하는 방법중 하나로 문장을 쪼개서 비교한다 예를 들어 3-gram으로 '과자중에 제일 맛있..
fullfish.tistory.com
n-gram 적용
17일차 / n-Gram구현 및 개선, 리벤슈타인 거리 시간,공간 복잡도 개선
n-Gram n-gram에 대해 내가 쓴 글들 https://fullfish.tistory.com/109 n-Gram n-Gram이란 문장의 유사도를 비교하는 방법중 하나로 문장을 쪼개서 비교한다 예를 들어 3-gram으로 '과자중에 제일 맛있는건 새우깡..
fullfish.tistory.com
'코딩 공부 > 검색' 카테고리의 다른 글
n-Gram 개선 및 고찰 (0) | 2022.05.15 |
---|---|
레벤슈타인 거리 시간복잡도와 공간복잡도 개선 (0) | 2022.05.12 |
레벤슈타인 거리 (Levenshtein Distance) (0) | 2022.05.12 |
fuzzy검색의 highlight와 가중치 적용 (0) | 2022.05.11 |
퍼지(fuzzy) 검색 (정규표현식이용) (3) | 2022.05.07 |