Elasticsearch를 사용하여 analyze를 사용하다가 조사, 형용사 등등을 제외하고 형태소 토크나이즈가 되어야 했다. 그래서 정식 문서를 찾아보더니 nori_part_of_speech라는 필터가 있었다.

우선 저번 시간에 만들었던 wedul_analyzer 인덱스를 이용해서 토크나이즈를 해보자.

{
  "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": 7,
      "end_offset": 9,
      "type": "word",
      "position": 3
    },
    {
      "token": "서",
      "start_offset": 9,
      "end_offset": 10,
      "type": "word",
      "position": 4
    },
    {
      "token": "나",
      "start_offset": 11,
      "end_offset": 12,
      "type": "word",
      "position": 5
    },
    {
      "token": "왔다",
      "start_offset": 12,
      "end_offset": 14,
      "type": "word",
      "position": 6
    }
  ]
}

여기서 '나'와 '왔다'를 없애고 토크나이즈 결과가 나왔으면 좋겠다.

그럼 '나'와 '왔다'의 형태소가 어떻게 되는지 우선 알아보자. analyzer api에 explain: true 옵션을 부여하면 해당 토크나이즈에 분리된 형태소들의 정보가 나온다.

GET _analyze
{
  "analyzer": "nori",
  "explain": true, 
  "text": "바보 위들이 집에서 나왔다"
}

'나'와 '왔다'는 NP와 UNKNOWN이다.  이 두개를 nori_part_of_speech필터를 이용해서 제거해보자.

 {
          "token": "나",
          "start_offset": 11,
          "end_offset": 12,
          "type": "word",
          "position": 6,
          "bytes": "[eb 82 98]",
          "leftPOS": "NP(Pronoun)",
          "morphemes": null,
          "posType": "MORPHEME",
          "positionLength": 1,
          "reading": null,
          "rightPOS": "NP(Pronoun)",
          "termFrequency": 1
        },
        {
          "token": "왔다",
          "start_offset": 12,
          "end_offset": 14,
          "type": "word",
          "position": 7,
          "bytes": "[ec 99 94 eb 8b a4]",
          "leftPOS": "UNKNOWN(Unknown)",
          "morphemes": null,
          "posType": "MORPHEME",
          "positionLength": 1,
          "reading": null,
          "rightPOS": "UNKNOWN(Unknown)",
          "termFrequency": 1
        }

custom analyzer를 만들면서 nori_part_of_speech 필터를 추가해주면된다. 이 필터에서 stoptags 배열에 제거하고 싶은 형태소 요형을 추가하면 해당 형태소를 제거한 결과만 출력된다.

PUT wedul_anaylyzer
{
  "settings": {
    "index" : {
      "analysis" : {
        "tokenizer": {
          "nori_user_dict": {
            "type": "nori_tokenizer",
            "decompound_mode": "none",
            "user_dictionary": "dic/nori_userdict_ko.txt"
          }
        },
        "analyzer" : {
          "custom_analyze" : {
            "type": "custom",
            "tokenizer" : "nori_user_dict",
            "filter": [
              "my_posfilter"
            ]
          }
        },
        "filter": {
          "my_posfilter": {
            "type": "nori_part_of_speech",
            "stoptags": [
              "NP", "UNKNOWN"
            ]
          }
        }
      }
    }
  }
}

이렇게 만든 analyze를 이용해서 다시한번 확인해보자. 

아래 결과 처럼 '나'와 '왔다' 두개의 형태소가 사라진 것을 확인할 수 있다.

{
  "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": 7,
      "end_offset": 9,
      "type": "word",
      "position": 3
    },
    {
      "token": "서",
      "start_offset": 9,
      "end_offset": 10,
      "type": "word",
      "position": 4
    }
  ]
}

 

기본적으로 stoptags를 적용하지 않으면 10몇가지의 형태소 종류가 기본으로 배제된다.

NP, VPC등 형태소들에 대한 용어는 하단 사이트에 잘 정리되어 있다.

https://coding-start.tistory.com/167
http://kkma.snu.ac.kr/documents/?doc=postag

 

꼬꼬마, 한글 형태소 분석기 (Kind Korean Morpheme Analyzer, KKMA)

꼬꼬마 한국어 형태소 분석기 한글 형태소 품사 (Part Of Speech, POS) 태그표 한글 형태소의 품사를 '체언, 용언, 관형사, 부사, 감탄사, 조사, 어미, 접사, 어근, 부호, 한글 이외'와 같이 나누고 각 세부 품사를 구분한다. 대분류 세종 품사 태그 심광섭 품사 태그 KKMA 단일 태그 V 1.0 태그 설명 Class 설명 묶음1 묶음2 태그 설명 확률태그 저장사전 체언 NNG 일반 명사 NN 명사 N NN NNG 보통 명사 NNA no

kkma.snu.ac.kr

 

출처
https://www.elastic.co/guide/en/elasticsearch/plugins/6.4/analysis-nori-speech.html

엘라스틱 서치를 쓰면서 기존에 형태소 분석기를 아리랑, 은전한닢, open korea등을 사용했었다.


근데 이번에 6.4버전이 출시 되면서 Elasticsearch에서 기본으로 제공하는 한글 형태소 분석기가 나왔다. 이름은 nori(노리)이다. 

노리는 놀이라는 뜻에서 가져왔으며 mecab-ko-dic 사전을 이용하지만 사전을 압축하므로 기존 형태소 분석기와 비교하여 메모리를 적게 쓰고 훨씬 빠르다.


그럼 Docker에 엘라스틱서치 6.4와 Kibana 6.4를 설치하고 Nori 플러그인을 설치해서 한글 형태소분석기를 사용해보자.


Docker에 Elasticsearch와 Kibana 설치

1
2
docker run --9200:9200 -9300:9300 --name elastic -"discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.4.0
docker run ---link elastic:elastic-url -"ELASTICSEARCH_URL=http://elastic-url:9200" -5601:5601 --name kibana docker.elastic.co/kibana/kibana:6.4.0
cs

설치를 완료하고 docker process를 확인하여 정상 동작하는지 확인한다.



Elasticsearch에 Nori 플러그인 설치

엘라스틱서치 bash셀에 접근해서 anaysis-nori를 설치한다.

1
2
3
4
5
6
// elasticsearch의 bash셀 접근
docker exec -it elastic /bin/bash
 
 
// 노리 플러그인 설치
bin/elasticsearch-plugin install analysis-nori
cs


설치가 완료되면 elasticsearch 프로세스를 재 시작 한다. 그러면서 프로세스가 올라올때 Nori 플러그인이 정상적으로 올라오는지 확인해보자.

1
[2018-10-13T01:26:05,498][INFO ][o.e.p.PluginsService     ] [dFC4eSy] loaded plugin [analysis-nori]
cs


그리고 Kibana를 사용해서 분석 플러그인 동작을 확인하자

1
2
3
4
5
6
7
GET _analyze
{
  "analyzer": "nori",
  "text" : "안녕 나는 바보 위들이야."
}
 
 
cs




사전 추가하기

위에 결과를 보면 위들이라는건 하나의 대명사로써 내 별칭인데 '들'이라는 조사를 제거하고 보여주느라 위들이라는 단어가 사라졌다. 이를 해결하기위해 사전을 만들어서 위들을 추가해보자.


먼저 사전에 사용될 사전은 mecab-ko-dic을 사용한다. 플러그인 설치했던것처럼 Elasticsearch에 접속하여 config/userdict_ko.txt를 생성한다. 그리고 그 txtx파일에 "위들"을 추가한다.


그리고 해당 사전을 사용하도록 anaylzer를 생성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PUT nori_sample
{
  "settings": {
    "index": {
      "analysis": {
        "tokenizer": {
          "nori_user_dict": {
            "type": "nori_tokenizer",
            "decompound_mode": "mixed",
            "user_dictionary": "userdict_ko.txt"
          }
        },
        "analyzer": {
          "my_analyzer": {
            "type": "custom",
            "tokenizer": "nori_user_dict"
          }
        }
      }
    }
  }
}
cs


그리고 다시한번 조회해보자.

1
2
3
4
5
6
GET nori_sample/_analyze
{
  "analyzer": "my_analyzer",
  "text" : "안녕 나는 바보 위들이야."
}
 
cs

결과를 확인해보면 정상적으로 위들이 형태소분석기에 의해 잘 나누어지는 것을 볼 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
  "tokens": [
    {
      "token": "안녕",
      "start_offset": 0,
      "end_offset": 2,
      "type": "word",
      "position": 0
    },
    {
      "token": "나",
      "start_offset": 3,
      "end_offset": 4,
      "type": "word",
      "position": 1
    },
    {
      "token": "는",
      "start_offset": 4,
      "end_offset": 5,
      "type": "word",
      "position": 2
    },
    {
      "token": "바보",
      "start_offset": 6,
      "end_offset": 8,
      "type": "word",
      "position": 3
    },
    {
      "token": "위들",
      "start_offset": 9,
      "end_offset": 11,
      "type": "word",
      "position": 4
    },
    {
      "token": "이",
      "start_offset": 11,
      "end_offset": 12,
      "type": "word",
      "position": 5
    },
    {
      "token": "야",
      "start_offset": 12,
      "end_offset": 13,
      "type": "word",
      "position": 6
    }
  ]
}
cs



참고

https://www.elastic.co/guide/en/elasticsearch/plugins/6.4/analysis-nori.html

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

https://www.elastic.co/guide/en/elasticsearch/plugins/6.4/analysis-nori-tokenizer.html

Elasticsearch 검색시에 한글 형태소를 사용하지 않으면 term을 단순하게 공백을 이용해서 쪼갠다. 하지만 한글말에는 조사도 구분해야하고 품사도 구분해서 사용해야 정확한 검색을 지원할 수 있다.

한글 형태소 플러그인은 크게 arirang, seunjeon, open korea text가 존재한다. 3개의 성능 비교와 자세한 설명은 엘라스틱 서치 블로그에서 참고하면 된다.

https://www.elastic.co/kr/blog/using-korean-analyzers


open korea text 설치

3가지 플러그인중에 open korea text를 사용해서 기능을 테스트해보자.

우선 docker를 사용중이므로 elasticsearch 내부 bash shell로 접속한 후 elasticsearch-plugin 명령어를 사용하여 설치해보자. 설치 파일은 github 페이지를 참고하면 된다. https://github.com/open-korean-text/elasticsearch-analysis-openkoreantext 

1
2
3
4
5
#bash shell 접속
docker exec -it elastic /bin/bash
 
#설치
bin/elasticsearch-plugin install https://github.com/open-korean-text/elasticsearch-analysis-openkoreantext/releases/download/6.1.1/elasticsearch-analysis-openkoreantext-6.1.1.2-plugin.zip
cs


설치한 후 docker를 재시작 한다. 엘라스틱서치가 실행되면서 로그를 살펴보면 open korea text 모듈이 load 되는 것을 확인할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[2018-09-13T13:41:28,292][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded module [aggs-matrix-stats]
[2018-09-13T13:41:28,292][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded module [analysis-common]
[2018-09-13T13:41:28,292][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded module [ingest-common]
[2018-09-13T13:41:28,292][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded module [lang-expression]
[2018-09-13T13:41:28,292][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded module [lang-mustache]
[2018-09-13T13:41:28,292][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded module [lang-painless]
[2018-09-13T13:41:28,293][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded module [mapper-extras]
[2018-09-13T13:41:28,293][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded module [parent-join]
[2018-09-13T13:41:28,293][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded module [percolator]
[2018-09-13T13:41:28,293][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded module [reindex]
[2018-09-13T13:41:28,293][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded module [repository-url]
[2018-09-13T13:41:28,293][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded module [transport-netty4]
[2018-09-13T13:41:28,293][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded module [tribe]
[2018-09-13T13:41:28,294][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded plugin [analysis-seunjeon]
[2018-09-13T13:41:28,294][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded plugin [elasticsearch-analysis-openkoreantext]
[2018-09-13T13:41:28,294][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded plugin [ingest-geoip]
[2018-09-13T13:41:28,294][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded plugin [ingest-user-agent]
[2018-09-13T13:41:28,294][INFO ][o.e.p.PluginsService     ] [c-CqQL8] loaded plugin [x-pack]
cs


인덱스 생성
간단하게 title만 가지고 있는 인덱스를 생성해보자. 생성할 때 analyzer를 설치한 open korea text를 지정해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
PUT cgv      // 인덱스
{
  "mappings" : {
    "movie" : {   // 타입
      "properties": {
        "title" : { 
          "type" : "text",
          "analyzer": "openkoreantext-analyzer"  // 형태소
        }
      }
    }
  }
}
cs


데이터 삽입

1
2
3
4
5
6
7
8
9
POST cgv/movie/
{
 "title" : "여전히 돈 많은 백수가 되고싶다展"
}
 
POST cgv/movie/
{
 "title" : "뮤지컬 〈점프 (JUMP)〉- 서울전용극장(명보아트홀3F)"
}
cs


조회
비슷한 문자열을 가진 검색어를 통해서 검색을 해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#검색1
GET cgv/movie/_search
{
  "query": {
    "match": {
      "title": "뮤지컬 점프(JUMP)"
    }
  }
}
 
#결과1
{
  "took": 23,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.8630463,
    "hits": [
      {
        "_index": "cgv",
        "_type": "movie",
        "_id": "_update",
        "_score": 0.8630463,
        "_source": {
          "title": "뮤지컬 〈점프 (JUMP)〉- 서울전용극장(명보아트홀3F)"
        }
      }
    ]
  }
}
 
#검색2
GET cgv/movie/_search
{
  "query": {
    "match": {
      "title": "1인권/입장권]여전히 돈 많은 백수가 되고싶다展"
    }
  }
}
 
#결과2
{
  "took": 9,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 4.8969383,
    "hits": [
      {
        "_index": "cgv",
        "_type": "movie",
        "_id": "_update",
        "_score": 4.8969383,
        "_source": {
          "title": "여전히 돈 많은 백수가 되고싶다展"
        }
      }
    ]
  }
}
cs


잘된다. 형태소 분석기 만드시는분들 보면 개인적으로 만드시는데 대단하신분들 인거 같다.



+ Recent posts