nori 한국어 형태소 분석기

이동명·2023년 6월 28일
1
post-thumbnail

한국어 형태소 분석기 노리(nori)

https://www.notion.so/gasbugs/e6d8283ea2ed47458adf42ddf457e89b

참고자료

https://www.elastic.co/guide/en/elasticsearch/plugins/6.4

https://coding-start.tistory.com/167

https://wedul.site/517

https://www.elastic.co/kr/blog/nori-the-official-elasticsearch-plugin-for-korean-language-analysis

1 한국어 형태소 분석기 노리


1.1 한글 형태소 분석기 노리(놀이)의 탄생

  • 엘라스틱서치는 모두 한글에는 성능을 발휘하기 쉽지 않은 검색엔진
  • 한글은 다른 언어와 달리 조사나 어미의 접미사가 명사,동사 등과 결합하기 때문에 기본 형태소분석기로는 분석하기 쉽지 어려움
  • 한글 형태소 분석기를 사용하면 한글을 분석해 저장하기 때문에 더 나은 검색이 가능(아버지가 → 아버지, 가)
  • Nori 형태소 분석기는 루씬 프로젝트에서 공식적으로 제공되는 한글 형태소 분석기
  • 엘라스틱서치 6.4버전부터 지원
  • 내부적으로 세종 말뭉치와 mecab-ko-dic 사전을 사용
  • 사전을 모두 압축하여 사용하고 있기 때문에 30%이상 빠르고 메모리 사용량 감소 및 시스템 전반적으로 영향을 주지 않게 최적화
  • 직접 수동으로 플러그인 설치 후 사용

1.2 MeCab

  • 언어에 존재하는 수많은 규칙과 예외사항으로 인해 규칙 기반 시스템을 개발하기란 매우 어려움
  • 성공적인 한국어 형태소 분석기는 대부분 확률적 모델링을 사용합
  • 1998년에 한국 정부가 시작한 21세기 세종 프로젝트를 통해 대규모 한국어 말뭉치 개발
  • 이를 기반으로 한국어 형태소의 확률적 모델링 수행
  • mecab-ko-dic(메카부)는 세종프로젝트를 기반으로 한국어 형태론의 확률적 모델을 학습
  • 원래 일본어를 나누기 위해 작성, 한국어와 일본어의 유일한 차이점은 사용하는 사전뿐
  • 루씬에는 버전 3.6부터 일본어 형태소 분석기가 포함
  • MeCab로 만든 IPADIC 사전을 사용해서 일본어 형태소를 나누고 품사를 표시합니다. 두 사전 모두 같은 도구로 만들었기 때문에, 루씬의 일본어 분석기를 재사용해서 이 한국어 사전을 처리하는 것은 구미가 당기는 일이었습니다
  • 이 방법의 장점은 여러 해에 걸쳐 메모리 사용량과 속도 면에서 최적화된 견고한 분석기를 활용

1.3 한국어 형태소가 없을 때 문제점 테스트

데이터 삽입

POST test/data
{
  "제목":"안녕하세요.",
  "내용":"안녕하세요. 이동명입니다.",
  "날짜":"2019-12-28",
  "비밀번호":"1234"
}

첫번째 데이터 검색 요청

GET _search?q="안녕"

데이터 검색 결과 - 실패

{
  "took" : 11,
  "timed_out" : false,
  "_shards" : {
    "total" : 6,
    "successful" : 6,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}

두 번째 데이터 검색 요청

GET /_search?q="안녕하세요"

데이터 검색 결과 - 성공

{
  "took" : 11,
  "timed_out" : false,
  "_shards" : {
    "total" : 6,
    "successful" : 6,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "test",
        "_type" : "data",
        "_id" : "lxNPSm8BB_pfiKR1HdID",
        "_score" : 0.2876821,
        "_source" : {
          "제목" : "안녕하세요.",
          "내용" : "안녕하세요. 이동명입니다.",
          "날짜" : "2019-12-28",
          "비밀번호" : "1234"
        }
      }
    ]
  }
}

결론: 풀 텍스트 검색 불가능...

해결 방법: 노리를 사용해서 분석한 데이터를 입력하면 검색하도록 만들 수 있음

2 노리 설치


2.1 윈도우 버전 설치하기

PS C:\elk\elasticsearch\bin> ***.\elasticsearch-plugin.bat install analysis-nori***
-> Downloading analysis-nori from elastic
[=================================================] 100%??
-> Installed analysis-nori
PS C:\elk\elasticsearch\bin>

2.2 리눅스 버전 설치하기

$ ***sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-nori***
-> Downloading analysis-nori from elastic
[=================================================] 100%?? 
-> Installed analysis-nori

2.3 도커 버전 설치하기

docker exec -t es01 /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-nori
docker exec -t es02 /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-nori
docker exec -t es03 /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-nori

2.4 기본적인 nori 기능 사용

POST _analyze
{
    "analyzer":"nori",
    "text":"역곡역 주변에 보안프로젝트 사무실이 생겼어요."
}

3 노리 토크나이저와 토큰 필터


노리는 하나의 토크나이저와 두 개의 토큰 필터로 구성됨

  • nori_tokenizer : 토크나이저 (공백, 문자, 구두점 등을 사용)
  • nori_part_of_speech : 토큰필터
  • nori_readingform : 토큰필터

토크나이저: 단어(term또는 token)를 분리

토큰 필터: 단어들을 검색 가능 하도록(searchable) 가공

토크나이저 + 토큰 필터 → 분석기(Analyzer) 한국어의 풀텍스트 서치 가능!

3.1 노리 토크나이저

decompound_mode: 토크 나이저가 복합 토큰을 처리하는 방법을 결정

user_dictionary

  • Nori 토크 나이 저는 기본적으로 mecab-ko-dic 사전을 사용
  • 사용자 지정 명사 (NNG)가있는 user_dictionary가 기본 사전에 추가 가능
  • 사전은 다음 형식이어야합니다. →<토큰> [<토큰 1> ... <토큰 n>]

$ES_HOME/config/userdict_ko.txt

c++     
C샤프
세종
세종시 세종 시
  • 첫 번째 토큰은 필수이며 사전에 추가해야하는 사용자 지정 명사를 나타냅니다. 복합 명사의 경우 사용자 정의 분할은 첫 번째 토큰 ([<토큰 1> ... <토큰 n>]) 후에 제공 될 수 있습니다. 사용자 정의 복합 명사의 분할은 decompound_mode 설정에 의해 제어

3.2 노리 기능을 커스터마이징해 사용

사전 파일 생성 - userdict_test.txt

안녕하세요 안녕 하세 요
이동명입니다 이동명 입니다

사전 파일 배치

docker cp userdict_test.txt es01:/usr/share/elasticsearch/config
docker cp userdict_test.txt es02:/usr/share/elasticsearch/config
docker cp userdict_test.txt es03:/usr/share/elasticsearch/config

가장 먼저 인덱스가 노리의 분석 기능을 사용할 수 있도록 설정이 필요

PUT korean_analyzer1
{
    "settings":{
        "analysis":{
            "tokenizer":{
                "korean_nori_tokenizer":{
                    "type":"nori_tokenizer",
                    "decompound_mode":"mixed",
                    "user_dictionary":"userdict_test.txt"
                }
            },
            "analyzer":{
                "nori_analyzer":{
                    "type":"custom",
                    "tokenizer":"korean_nori_tokenizer"
                }
            }
        }
    }
}
POST korean_analyzer1/_analyze
{
  "analyzer":"nori_analyzer",
  "text":"안녕하세요. 이동명입니다."
}

3.3 nori_part_of_speech

nori_part_of_speech 토큰 필터는 품사 태그 세트와 일치하는 토큰을 제거

지원되는 음성 태그 및 의미의 목록은 여기에서 확인

http://lucene.apache.org/core/7_4_0/analyzers-nori/org/apache/lucene/analysis/ko/POS.Tag.html

숫자에 해당하는 태그는 삭제하도록 설정

PUT nori_sample
{
  "settings": {
    "index": {
      "analysis": {
        "analyzer": {
          "my_analyzer": {
            "tokenizer": "nori_tokenizer",
            "filter": [
              "my_posfilter"
            ]
          }
        },
        "filter": {
          "my_posfilter": {
            "type": "***nori_part_of_speech***",
            "stoptags": [
              "***NR***"   
            ]
          }
        }
      }
    }
  }
}

다음 질의를 하면 NR에 해당하는 내용은 삭제

NR = Numeral 숫자

GET nori_sample/_analyze
{
  "analyzer": "my_analyzer",
  "text": "일곱 강아지가"  
}

스탑 태그의 디폴트 세팅

"stoptags": [
    "E",
    "IC",
    "J",
    "MAG", "MAJ", "MM",
    "SP", "SSC", "SSO", "SC", "SE",
    "XPN", "XSA", "XSN", "XSV",
    "UNA", "NA", "VSV"
]

3.4 nori_readingform

nori_readingform 토큰 필터는 한자로 작성된 토큰을 한글 형식으로 다시 작성

한자 → 한글

PUT hanja
{
    "settings": {
        "index":{
            "analysis":{
                "analyzer" : {
                    "my_analyzer" : {
                        "tokenizer" : "nori_tokenizer",
                        "filter" : ["nori_readingform"]
                    }
                }
            }
        }
    }
}

GET hanja/_analyze
{
  "analyzer": "my_analyzer",
  "text": "崔壹善"      
}

4 인덱스에 적용


매핑 작업

PUT article
{
  "settings": {
    "analysis": {
      "analyzer": {
        "nori": {
          "tokenizer": "nori_tokenizer"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "fields": {
          "nori": {
            "type": "text",
            "analyzer": "nori"
          }
        }
      }
    }
  }
}

PUT article/_bulk
{"index": {}}
{"title":"양산 삼호지구 뉴딜사업 규모 확 늘린다"}
{"index": {}}
{"title":"무궁화 프로젝트, ‘end’가 아닌 ‘and'인 이유"}
{"index": {}}
{"title":"인천시 서구, 가재울마을 도시재생뉴딜사업 본격추진"}
{"index": {}}
{"title":"도시재생으로 활력 키우는 춘천시…인구·관광객 유치 효과 기대"}
{"index": {}}
{"title":"임실군, 농촌맞춤형 도시재생 구현 ‘앞장’"}
{"index": {}}
{"title":"광명시, '소규모주택정비사업' 시민들 많은 관심"}
{"index": {}}
{"title":"금천구 독산동 우시장 일대 도시재생 ‘성과공유회’ 개최"}
{"index": {}}
{"title":"[기고] 지역 전통주, 도시재생사업으로 마을기업 이룬다면"}
{"index": {}}
{"title":"국토부 'LH에 도시재생 직렬 신설 검토 중'"}
{"index": {}}
{"title":"대전 중구, 내년까지 동서대로 1421번길 일원… 40개 건물·65개 업소 간판 개선"}
POST article/_search
{
  "query" : {
    "match" : {
      "station.nori": "홍대"
    }
  }
}
profile
Web Developer

0개의 댓글