728x90
노리(Nori) 형태소 분석기
-
노리는 Elasticsearch 6.6 버전 부터 공식적으로 개발해서 지원하기 시작했다.
-
노리는 루씬에 있는 일본어 형태소 분석기 Kuromoji 를 재활용한 것이다.
-
MeCab 사전을 활용할 수 있다.
설치
환경은 다음과 같습니다.
Ubuntu 18.04.4 LTS
Elasticsearch 7.8.0
다음 명령어로 플러그인을 설치합니다.
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-nori
-> Installing analysis-nori
-> Downloading analysis-nori from elastic
[=================================================] 100%
-> Installed analysis-nori
플러그인이 설치되었는지 확인합니다.
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin list
analysis-nori
elasticsearch 재시작
sudo systemctl restart elasticsearch
인덱스 설정
파이썬 코드를 통해 형태소 분석기를 지정해서 index 를 생성합니다.
decompound_mode: mixed 는 복합명사를 분리하고 기존 형태도 보존합니다. (다다익선)
setting.json
{
"settings": {
"analysis": {
"analyzer": {
"content": {
"type": "custom",
"tokenizer": "nori_tokenizer",
"decompound_mode": "mixed"
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "content"
},
"cont": {
"type": "text",
"analyzer": "content"
}
}
}
}
import pprint
import json
from elasticsearch import Elasticsearch
def create_index(body=None):
if not es.indices.exists(index=index):
return es.indices.create(index=index, body=body)
def insert(body):
return es.index(index=index, doc_type=doc_type, body=body)
if __name__ == '__main__':
url = '192.168.111.176'
port = '9200'
index = 'news'
doc_type = 'daum'
es = Elasticsearch(f'{url}:{port}')
with open('setting.json', 'r', encoding='utf-8') as f:
setting = json.load(f)
create_index(setting)
pprint.pprint(es.indices.get_settings(index))
{'news': {'settings': {'index': {'analysis': {'analyzer': {'content': {'tokenizer': 'nori_tokenizer',
'type': 'custom'}}},
'creation_date': '1597334588569',
'number_of_replicas': '1',
'number_of_shards': '1',
'provided_name': 'news',
'uuid': '1wKVrq5nSImKkNk3closlQ',
'version': {'created': '7080099'}}}}}
analyze() 함수를 통해 지정한 노리 형태소 분석기로 토크나이징을 테스트할 수 있습니다.
결과는 다음과 같습니다. (index 에 저장되어있는 데이터 아님)
body = {
'analyzer': 'content',
'text': '매일 비가 오네요.'
}
r = es.indices.analyze(index=index, body=body)
pprint.pprint(r)
{'tokens': [{'end_offset': 2,
'position': 0,
'start_offset': 0,
'token': '매일',
'type': 'word'},
{'end_offset': 4,
'position': 1,
'start_offset': 3,
'token': '비',
'type': 'word'},
{'end_offset': 5,
'position': 2,
'start_offset': 4,
'token': '가',
'type': 'word'},
{'end_offset': 7,
'position': 3,
'start_offset': 6,
'token': '오',
'type': 'word'},
{'end_offset': 9,
'position': 4,
'start_offset': 7,
'token': '네요',
'type': 'word'}]}
테스트
노리 형태소 분석기의 사용 전후를 테스트 해보겠습니다.
데이터
[
{
"id": 1,
"title": "큰 비 온다는 경보에도 수영등산 처벌은?",
"cont": "집중호우와 산사태 경보가 내려졌는데도, 입산이 통제된 산에 올라가거나 바다에서 수영을 즐기던 동호회원들이 적발되거나 구조됐습니다."
},
{
"id": 2,
"title": "'다주택 매물 쏟아질 것'..서울 36만 호 공급 어디에?",
"cont": "정부는 집을 여러 채 가지고 있던 법인과 임대사업자들이 시장에 집을 내놓을 것이라면서 그러면 집값이 차츰 안정될 것이라고 강조했습니다. 그러면서 수도권 주택공급 세부 계획도 다시 정리해서 발표했습니다. 자세한 내용은 전형우 기자가 설명해드리겠습니다."
},
{
"id": 3,
"title": "여성 납치 후 경찰과 흉기 대치..남양주 대낮 인질극",
"cont": "오늘(13일) 경기도 남양주에선 대낮에 인질극이 벌어졌습니다. 용의자가 여성을 인질로 잡고 경찰과 대치하는 영상입니다. 금품을 뜯어내려고 이렇게 납치를 했습니다. 용의자는 체포됐고 피해자도 무사한 것으로 확인됐습니다."
}
]
index 생성하는 부분만 스왑해서 테스트합니다.
import pprint
import json
from elasticsearch import Elasticsearch
def create_index(body=None):
if not es.indices.exists(index=index):
if body is None:
r = es.indices.create(index=index)
else:
r = es.indices.create(index=index, body=body)
return r
def delete_index():
if es.indices.exists(index=index):
return es.indices.delete(index=index)
def insert(body):
return es.index(index=index, body=body)
def search(keyword=None):
body = {
"query": {
"multi_match": {
"query": keyword,
"fields": ['title', 'content']
}
}
}
res = es.search(index=index, body=body)
return res
if __name__ == '__main__':
url = '192.168.111.176'
port = '9200'
index = 'news'
doc_type = 'daum-news'
es = Elasticsearch(f'{url}:{port}')
with open('index.json', 'r', encoding='utf-8') as f:
setting = json.load(f)
delete_index()
create_index()
# create_index(setting)
with open('data.json', 'r', encoding='utf-8') as f:
json_data = json.load(f)
for d in json_data:
insert(d)
r = search('수영')
pprint.pprint(r)
# if r:
# for doc in r['hits']['hits']:
# for k, v in doc['_source'].items():
# print(k, v)
"수영" 이라는 키워드를 검색해보았습니다.
r = search('수영')
노리 형태소 분석기 사용 전의 결과
create_index()
{'_shards': {'failed': 0, 'skipped': 0, 'successful': 1, 'total': 1},
'hits': {'hits': [],
'max_score': None,
'total': {'relation': 'eq', 'value': 0}},
'timed_out': False,
'took': 1}
노리 형태소 분석기를 사용 후의 결과
"수영등산" 이라는 복합명사를 분리해서 추출이 되었네요.
create_index(setting)
{'_shards': {'failed': 0, 'skipped': 0, 'successful': 1, 'total': 1},
'hits': {'hits': [{'_id': '0ALV6HMBluDb6jxfa4ju',
'_index': 'news',
'_score': 1.1569381,
'_source': {'cont': '집중호우와 산사태 경보가 내려졌는데도, 입산이 통제된 산에 '
'올라가거나 바다에서 수영을 즐기던 동호회원들이 적발되거나 '
'구조됐습니다.',
'id': 1,
'title': '큰 비 온다는 경보에도 수영등산 처벌은?'},
'_type': '_doc'}],
'max_score': 1.1569381,
'total': {'relation': 'eq', 'value': 1}},
'timed_out': False,
'took': 2}
분석기를 통해 토크나이징 결과를 확인해보겠습니다.
body = {
'analyzer': 'content',
'text': '큰 비 온다는 경보에도 수영등산 처벌은?'
}
r = es.indices.analyze(index=index, body=body)
pprint.pprint(r)
{'tokens': [{'end_offset': 1,
'position': 0,
'start_offset': 0,
'token': '크',
'type': 'word'},
{'end_offset': 1,
'position': 1,
'start_offset': 0,
'token': 'ᆫ',
'type': 'word'},
{'end_offset': 3,
'position': 2,
'start_offset': 2,
'token': '비',
'type': 'word'},
{'end_offset': 7,
'position': 3,
'start_offset': 4,
'token': '오',
'type': 'word'},
{'end_offset': 7,
'position': 4,
'start_offset': 4,
'token': 'ᆫ다는',
'type': 'word'},
{'end_offset': 10,
'position': 5,
'start_offset': 8,
'token': '경보',
'type': 'word'},
{'end_offset': 11,
'position': 6,
'start_offset': 10,
'token': '에',
'type': 'word'},
{'end_offset': 12,
'position': 7,
'start_offset': 11,
'token': '도',
'type': 'word'},
{'end_offset': 15,
'position': 8,
'start_offset': 13,
'token': '수영',
'type': 'word'},
{'end_offset': 17,
'position': 9,
'start_offset': 15,
'token': '등산',
'type': 'word'},
{'end_offset': 20,
'position': 10,
'start_offset': 18,
'token': '처벌',
'type': 'word'},
{'end_offset': 21,
'position': 11,
'start_offset': 20,
'token': '은',
'type': 'word'}]}
'Language > Python' 카테고리의 다른 글
파이썬 단위테스트(unittest) 사용하기 -1 (0) | 2020.08.14 |
---|---|
파이썬 로깅( logging) 사용하기 -1 : 출력 / 파일 / 포맷 (0) | 2020.08.14 |
파이썬 쓰레드(Thread) 알아보기 - 4 : 쓰레드 동작 순서 제어를 위한 Event (0) | 2020.08.12 |
파이썬 쓰레드(Thread) 알아보기 - 3 : 데이터 통신 (0) | 2020.08.12 |
파이썬 쓰레드(Thread) 알아보기 - 2 : 뮤텍스(Lock) (2) | 2020.08.12 |