문서 유사도?
기본적으로 문서가 얼마나 유사하는가에 대한 계산은 Cosine 유사도로 판단하게 된다.
그 이전에 사용하는 개념이 tf-idf인데 그 설명은 위키피디아에 다음과 같이 설명되어 있다.
TF-IDF(Term Frequency - Inverse Document Frequency)는 정보 검색과 텍스트 마이닝에서 이용하는 가중치로, 여러 문서로 이루어진 문서군이 있을 때 어떤 단어가 특정 문서 내에서 얼마나 중요한 것인지를 나타내는 통계적 수치이다. 문서의 핵심어를 추출하거나, 검색 엔진에서 검색 결과의 순위를 결정하거나, 문서들 사이의 비슷한 정도를 구하는 등의 용도로 사용할 수 있다.
TF(단어 빈도, term frequency)는 특정한 단어가 문서 내에 얼마나 자주 등장하는지를 나타내는 값으로, 이 값이 높을수록 문서에서 중요하다고 생각할 수 있다. 하지만 단어 자체가 문서군 내에서 자주 사용 되는 경우, 이것은 그 단어가 흔하게 등장한다는 것을 의미한다. 이것을 DF(문서 빈도, document frequency)라고 하며, 이 값의 역수를 IDF(역문서 빈도, inverse document frequency)라고 한다. TF-IDF는 TF와 IDF를 곱한 값이다.
IDF 값은 문서군의 성격에 따라 결정된다. 예를 들어 '원자'라는 낱말은 일반적인 문서들 사이에서는 잘 나오지 않기 때문에 IDF 값이 높아지고 문서의 핵심어가 될 수 있지만, 원자에 대한 문서를 모아놓은 문서군의 경우 이 낱말은 상투어가 되어 각 문서들을 세분화하여 구분할 수 있는 다른 낱말들이 높은 가중치를 얻게 된다.
말그대로 특정 단어가 문서내에서 얼마나 자주 등장하는지와 너무 자주 등장하는 단어의 경우 그 문서의 핵심 단어라고 말할 수 없는(예를들어 조사같은 것들은 모든 문서에 공통적인 것이기 떄문에 중요도가 높다고 할 수 없다.
이 두가지를 동시에 반영(곱한다)한 것이 tf-idf인데 자세한 수학적 공식은 구글링을 통해 스스로 알아보도록 하자.(어차피 밑에 코드에서 나오기는 하지만..)
아무튼 이러한 것들을 elasticsearch에서는 script를 직접 작성할 수 있게 지원하기 때문에 similarity를 평가하는데에 기준을 tf-idf로 둔다. 그 코드는 다음과 같다.(elasticsearch 공식 홈페이지에 있는 내용을 가지고 왔다.)
유사도 검색을 위한 Elasticsearch index 설정
"scripted_tfidf": {
"type": "scripted",
"script": {
"source": "double tf = Math.sqrt(doc.freq); double idf = Math.log((field.docCount+1.0)/(term.docFreq+1.0)) + 1.0; double norm = 1/Math.sqrt(doc.length); return query.boost * tf * idf * norm;"
}
}
scipted_tfidf라고 짜놓은 스크립트 이름을 지정해주고 source에 그 식을 적어준다.
double tf = Math.sqrt(doc.freq); //tf계산식이다.
double idf = Math.log((field.docCount+1.0)//(term.docFreq+1.0)) + 1.0; //idf계산식이다.
double norm = 1/Math.sqrt(doc.length); //정규화를 위한 식이다.
return query.boost * tf * idf * norm; //이렇게 모든 값을 source에 반영한다.
아무튼.. 이렇게 tf-idf로 기준값을 줬으니 다음에는 유사도를 계산할 차례이다.
cosine유사도를 우리가 직접 구현할 필요는 없다. 왜냐면 이미 elasticsearch에 similarity가 이미 구현되어있기 때문이다.
또한 elasticsearch에 입력되는 문서들을 넣을때 단순히 text또는 keyword로 넣어버리면 그 개수를 알 수 없기때문에
"analysis": {
"analyzer": {
"nori_analyzer": {
"tokenizer": "nori_my_dict_tokenizer"
}
},
"tokenizer": {
"nori_my_dict_tokenizer": {
"type": "nori_tokenizer",
"decompound_mode": "mixed"
}
}
}
tokenizer를 지정해준다.
기본적으로 elasticsearch에서 지원해주는 nori_tokenizer를 사용한다.
그 후 rdbms에서 table과 같은 index를 지정해준다.
여기선 테스트이기 때문에 단순히 하나의 필드만 지정한다.
"mappings": {
"properties": {
"field": {
"type": "text",
"similarity": "scripted_tfidf",
"analyzer":"nori_analyzer"
}
}
}
추가로 similarity도 어떠한 기준을 이용할것인지 정해줘야한다.
이것으로 기본적인 유사도를 위한 elasticsearch의 설정은 완료되었다.
쿼리스트링을 이용해서 검색하면 매우 간단하지만 서버를 구축하기 위해 Spring boot(Java)를 이용한 구현을 다음장에 마져 포스팅하겠다.
'ELK' 카테고리의 다른 글
Elasticsearch를 이용한 문서 유사도 검색과 Springboot를 통한 구현 (설정편)(2) (0) | 2020.12.02 |
---|---|
logstash deactivating (stop-sigterm) 해결방법 (0) | 2020.09.04 |
elasticsearch java high level rest client에서 analyze 사용 (0) | 2020.08.05 |
Elasticsearch Type에 대해서 (0) | 2020.07.27 |
Elasticsearch spring REST Client unrecognized parameter 오류 (0) | 2020.07.22 |