프로젝트/여행지 오픈 API

[ElasticSearch] Nori 분석기, 오타 보정(fuzzy), 로그스태시(logStash)

블랑v 2023. 10. 30. 17:32

노리 분석기

레퍼런스1 : 

https://esbook.kimjmin.net/06-text-analysis/6.7-stemming/6.7.2-nori

 

6.7.2 노리 (nori) 한글 형태소 분석기 - Elastic 가이드북

이번 장에서는 elasticsearch가 데이터를 저장하는 색인 과정에서 처리하는 수많은 작업들에 대해 알아보았습니다. 텍스트 분석 및 텀의 개념과, 데이터 분석에 사용되는 애널라이저, 토크나이저,

esbook.kimjmin.net

 

잘 설명한 글

https://hanamon.kr/elasticsearch-%EA%B2%80%EC%83%89%EC%97%94%EC%A7%84-nori-%ED%98%95%ED%83%9C%EC%86%8C-%EB%B6%84%EC%84%9D%EA%B8%B0-%EA%B2%80%EC%83%89-%EA%B3%A0%EB%8F%84%ED%99%94-%EB%B0%A9%EB%B2%95/

 

Elasticsearch를 검색 엔진으로 사용하기(1): Nori 한글 형태소 분석기로 검색 고도화 하기 - 하나몬

올 4월에는 Elasticsearch(엘라스틱서치) 검색엔진을 이용한 회사 웹사이트의 검색  품질을 개선하는 작업을 했습니다. 기존의 검색 기능은 MySQL의 ‘Like’ 기능을 이용한 방식을 사용했는데, 이는

hanamon.kr

 

nori 설치

: /usr/share/elasticsearch 홈 경로에서, bin/elasticsearch-plugin install analysis-nori를 통해 설치해주자.

 

GET _analyze
{
  "analyzer": "nori",
  "text": "노리 분석기를 통해 한글이 형태소 분리된다."
}

///결과

{
  "tokens" : [
    {
      "token" : "노리",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "분석",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "기",
      "start_offset" : 5,
      "end_offset" : 6,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "통하",
      "start_offset" : 8,
      "end_offset" : 10,
      "type" : "word",
      "position" : 4
	}
    
    .. 이하 생략
  ]
}

 

실제 숙박업소 샘플 데이터를 노리 분석기를 적용해보았다.

 

PUT new_accommodation
{
  "settings": {
    "analysis": {
      "analyzer": {
        "korean": {
          "type": "custom",
          "tokenizer": "nori_tokenizer",
          "filter": ["nori_readingform"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "accommodation_addr" : {
        "type": "text",
        "analyzer": "korean",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "accommodation_lat" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
      },
      "accommodation_lng" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "accommodation_name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "accommodation_pic" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "accommodation_price" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "accommodation_score" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "accommodation_type" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "id" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
    }
  }
}

POST _reindex
{
    "source": {
        "index": "accommodation"
    },
    "dest": {
        "index": "new_accommodation"
    }
}

//테스트 코드
POST /new_accommodation/_analyze
{
  "analyzer": "korean",
  "text": "여수 밤바다는 대한민국의 유명한 바다이다."
}

 

Fuzzy 함수

Nori를 통해 한글 어법에 맞게 형태소를 분리해도, 특정 오타가 있을 경우 검색 결과가 나오지 않는 일이 있었다.

예를 들어 나는 '바다' 를 검색하고 싶은데 '비다' 나 '바디'로 검색할 경우 용어에 매칭되지 않아 결과가 나오지 않는다.

이를 Fuzzy를 사용하면 오타를 보정할 수 있다.

 

단, 이는 keyword 검색보다는 토큰화된 텍스트 전문 검색에 유용하니 유의할 것. 

GET accommodation/_search
{
  "_source": ["accommodation_name"], 
  "query": {
    "fuzzy": {
      "accommodation_name": {
        "value": "펜",
        "fuzziness": 1
      }
    }
  }
}

'펜션' 과 오타 보정 범위가 1인 단어가 검색된다. '펭션', '팬션', 펜셩' 역시도 마찬가지

match 쿼리 fuzziness 설정

  • Levenshtein Edit Distance 계산을 통해 계산된 거리만큼 보정된다.
    • 0 : 검색어 일치일 경우 검색된다.
    • AUTO : 길이에 따라 자동으로 검색된다.
    • 혹은 위 예제처럼 특정 거리를 주입할 수도 있다

Fuzzy 쿼리의 작동 방식

  • fuzzy 쿼리는 Levenshtein Edit Distance 알고리즘을 기반으로 한다. 이는 두 문자열 사이의 "거리"를 측정하는 방법으로, 한 문자열을 다른 문자열로 변환하기 위해 필요한 단일 문자 삽입, 삭제, 교체의 최소 횟수를 나타낸다.
  • fuzziness 매개변수는 이 거리를 정의한다. 예를 들어, fuzziness가 2이면 최대 두 번의 문자 변경(삽입, 삭제, 교체)을 허용한다.
  • fuzzy 쿼리는 text 필드에서 각 토큰에 대해 이러한 "fuzzy" 매칭을 수행한다. keyword 필드의 경우, 전체 문자열에 대해 fuzzy 매칭을 시도하지만, 이는 덜 효과적이다.

신뢰성과 한계

  • fuzzy 쿼리는 작은 오타나 철자 오류에 대해 유용하지만, 단어의 큰 변형이나 완전히 다른 단어에 대해서는 신뢰성이 떨어질 수 있다.
  • 특히 fuzzy 쿼리는 단어의 유사성만을 고려하고, 단어의 의미나 맥락은 고려하지 않는다.
  • 긴 문자열에 대한 fuzzy 검색(keyword 필드)은 부정확할 가능성이 있고 잘못된 값을 반환할 수 있다.

 

데이터 넣기 이슈, 로그스태시

지금껏 csv파일을 Kibana Console을 통해서 넣었었다.

그런데, 100MB가 넘는 Data는 들어가지 않는다. 그렇기에 이번에는 로그스태시를 활용해보려 한다.

API 요청이 들어올때마다 사용자 데이터 역시 수집해야 하기 때문이다.

 

먼저 로그스태시를 설치하기 이전, JDK 설치가 필요하다. AWS에 11버전이 깔려있으므로 여기서는 생략한다.

 

https://www.elastic.co/guide/en/logstash/7.11/installing-logstash.html#_apt

 

Installing Logstash | Logstash Reference [7.11] | Elastic

Use the echo method described above to add the Logstash repository. Do not use add-apt-repository as it will add a deb-src entry as well, but we do not provide a source package. If you have added the deb-src entry, you will see an error like the following:

www.elastic.co

해당 레퍼런스를 따라가면 에러가 나온다. 7.11.x의 리스트가 없다는 에러인 것 같다.

 

중복 저장소 항목 제거:
sudo nano /etc/apt/sources.list.d/elastic-7.x.list

저장소 업데이트 후 로그스태시 설치:
sudo apt-get update
sudo apt-get install logstash

 

과정을 수행해주자.

이후 과정은 추가 게시글로 포스팅하겠다.