From 6fd3f0c56d2781a3f2a469c0aa4c1fc0a6a420be Mon Sep 17 00:00:00 2001 From: lovit Date: Sun, 11 Oct 2020 04:56:16 +0900 Subject: [PATCH 1/6] Implement morpheme corups loader (#103, #122) --- Korpora/korpus_modu_morpheme.py | 106 ++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 Korpora/korpus_modu_morpheme.py diff --git a/Korpora/korpus_modu_morpheme.py b/Korpora/korpus_modu_morpheme.py new file mode 100644 index 0000000..b95b5be --- /dev/null +++ b/Korpora/korpus_modu_morpheme.py @@ -0,0 +1,106 @@ +import json +import os +import re +from dataclasses import dataclass +from glob import glob +from tqdm import tqdm +from typing import List, Tuple +from Korpora.korpora import Korpus, KorpusData + + +description = """ 모두의 말뭉치는 문화체육관광부 산하 국립국어원에서 제공하는 말뭉치로 + 총 13 개의 말뭉치로 이뤄져 있습니다. + 해당 말뭉치를 이용하기 위해서는 국립국어원 홈페이지에 가셔서 "회원가입 > 말뭉치 신청 > 승인"의 + 과정을 거치셔야 합니다. + https://corpus.korean.go.kr/#none + 모두의 말뭉치는 승인 후 다운로드 가능 기간 및 횟수 (3회) 에 제한이 있습니다. + 로그인 기능 및 Korpora 패키지에서의 다운로드 기능을 제공하려 하였지만, + 국립국어원에서 위의 이유로 이에 대한 기능은 제공이 불가함을 확인하였습니다. + Korpora==0.2.0 에서는 "개별 말뭉치 신청 > 승인"이 완료되었다고 가정, + 로컬에 다운로드 된 말뭉치를 손쉽게 로딩하는 기능만 제공할 예정입니다 + (Korpora 개발진 lovit@github, ratsgo@github)""" + +license = """ 모두의 말뭉치의 모든 저작권은 `문화체육관광부 국립국어원 + (National Institute of Korean Language)` 에 귀속됩니다. + 정확한 라이센스는 확인 중 입니다.""" + + +class ModuMorphemeKorpus(Korpus): + def __init__(self, root_dir_or_paths, force_download=False): + super().__init__(description, license) + paths = find_corpus_paths(root_dir_or_paths) + self.train = KorpusData('모두의_형태분석_말뭉치.train', load_modu_ne(paths)) + self.tagmap = { + # TBD + } + + +def find_corpus_paths(root_dir_or_paths): + prefix_pattern = re.compile('[NS]XMP') + def match(path): + prefix = path.split(os.path.sep)[-1][:4] + return prefix_pattern.match(prefix) + + # directory + wildcard + if isinstance(root_dir_or_paths, str): + paths = sorted(glob(f'{root_dir_or_paths}/*.json') + glob(root_dir_or_paths)) + else: + paths = root_dir_or_paths + + paths = [path for path in paths if match(path)] + if not paths: + raise ValueError('Not found corpus files. Check `root_dir_or_paths`') + return paths + + +@dataclass +class MorphemesExample: + sentence_id: str + sentence: str + morphemes: List[str] + tags: List[str] + eojeol_ids: List[int] + + def __str__(self): + return self.__repr__() + + def __repr__(self): + return f"""Morphemes( + id={self.sentence_id}, + sentence={self.sentence}, + tags={self.morphemes}, + positions={self.tags}, + eojeol_id={self.eojeol_ids} +)""" + + +def document_to_examples(document): + examples = [] + sentence = document['sentence'] + for example in sentence: + example_id = example['id'] + form = example['form'] + columns = [(m['form'], m['label'], m['word_id']) for m in example['morpheme']] + morphemes, tags, eojeol_ids = zip(*columns) + eojeol_ids = tuple(idx - 1 for idx in eojeol_ids) + examples.append(MorphemesExample(example_id, form, morphemes, tags, eojeol_ids)) + return examples + + +def load_modu_ne(paths): + examples = [] + for path in paths: + with open(path, encoding='utf-8') as f: + data = json.load(f) + documents = data['document'] + desc = f'Loading ModuMorpheme ({os.path.basename(path)})' + documents_iterator = tqdm(documents, desc=desc, total=len(documents)) + examples += [example for doc in documents_iterator for example in document_to_examples(doc)] + return examples + + +def fetch_modu(): + raise NotImplementedError( + "국립국어원에서 API 기능을 제공해 줄 수 없음을 확인하였습니다." + "\n이에 따라 모두의 말뭉치는 fetch 기능을 제공하지 않습니다" + ) \ No newline at end of file From d26e74a4d8d2e01f2cebbfbe2fac089d3c60bef5 Mon Sep 17 00:00:00 2001 From: lovit Date: Sun, 11 Oct 2020 05:17:07 +0900 Subject: [PATCH 2/6] Skip empty tagged sentence in spoken corpus (#103, #122) --- Korpora/korpus_modu_morpheme.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Korpora/korpus_modu_morpheme.py b/Korpora/korpus_modu_morpheme.py index b95b5be..de7a360 100644 --- a/Korpora/korpus_modu_morpheme.py +++ b/Korpora/korpus_modu_morpheme.py @@ -79,11 +79,14 @@ def document_to_examples(document): sentence = document['sentence'] for example in sentence: example_id = example['id'] - form = example['form'] - columns = [(m['form'], m['label'], m['word_id']) for m in example['morpheme']] - morphemes, tags, eojeol_ids = zip(*columns) - eojeol_ids = tuple(idx - 1 for idx in eojeol_ids) - examples.append(MorphemesExample(example_id, form, morphemes, tags, eojeol_ids)) + try: + form = example['form'] + columns = [(m['form'], m['label'], m['word_id']) for m in example['morpheme']] + morphemes, tags, eojeol_ids = zip(*columns) + eojeol_ids = tuple(idx - 1 for idx in eojeol_ids) + examples.append(MorphemesExample(example_id, form, morphemes, tags, eojeol_ids)) + except: + continue return examples From 717ee9348031d77e991933b256675cf0ca480f69 Mon Sep 17 00:00:00 2001 From: lovit Date: Sun, 11 Oct 2020 05:17:23 +0900 Subject: [PATCH 3/6] Update morpheme corpus usage (#103, #122) --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index 64d9845..3f90fd2 100644 --- a/README.md +++ b/README.md @@ -622,4 +622,36 @@ corpus.train[0] # '요즘처럼 추운 날씨에는 따뜻한 라테 한잔 찾는 분들 많으실 텐데요. 라테 위에 그려진 다양한 라테 아트를 구경하는 것도 ... type(corpus.train[0]) # str +``` + +### 모두의 말뭉치: 형태 분석 말뭉치 (loader) +- author: 국립국어원 +- repository: https://corpus.korean.go.kr/ +- size: + - train: 371,571 examples (tagged sentences) +- example + - 구어 말뭉치의 형태 분석 결과 중에는 형태소가 존재하지 않은 문장이 있기 때문에 이를 제거하며 데이터를 로드 +```python +from Korpora.korpus_modu_morpheme import ModuMorphemeKorpus + +paths_or_dir = 'path/to/NIKL_MP(v1.0)/' +paths_or_dir = 'path/to/NIKL_MP(v1.0)/NXMP1902008040.json' +corpus = ModuMorphemeKorpus(paths_or_dir) + +corpus.train[0] +# Morphemes( +# id=NWRW1800000022.417.1.1, +# sentence=[제주·서울] "세계환경수도 조성위해 10개년 실천계획 만들겠다" 김태환 지사 밝혀, +# tags=('[', '제주', '·', '서울', ']', '"', '세계', '환경', ...), +# positions=('SS', 'NNP', 'SP', 'NNP', 'SS', 'SS', 'NNG', '...), +# eojeol_id=(0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, ...) +# ) +corpus.train[0].sentence +# '[제주·서울] "세계환경수도 조성위해 10개년 실천계획 만들겠다" 김태환 지사 밝혀' +corpus.train[0].morphemes +# ('[', '제주', '·', '서울', ']', '"', '세계', '환경', '수도' ... +corpus.train[0].tags +# ('SS', 'NNP', 'SP', 'NNP', 'SS', 'SS', 'NNG', ...) +corpus.train[0].eojeol_ids +# (0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, ``` \ No newline at end of file From f5d0141f0e881d0ebbd642f009b7efda1df06d32 Mon Sep 17 00:00:00 2001 From: lovit Date: Sun, 11 Oct 2020 05:34:05 +0900 Subject: [PATCH 4/6] Update morpheme tagset (#103, #122) --- Korpora/korpus_modu_morpheme.py | 49 ++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/Korpora/korpus_modu_morpheme.py b/Korpora/korpus_modu_morpheme.py index de7a360..90d151b 100644 --- a/Korpora/korpus_modu_morpheme.py +++ b/Korpora/korpus_modu_morpheme.py @@ -31,7 +31,54 @@ def __init__(self, root_dir_or_paths, force_download=False): paths = find_corpus_paths(root_dir_or_paths) self.train = KorpusData('모두의_형태분석_말뭉치.train', load_modu_ne(paths)) self.tagmap = { - # TBD + 'JKS': '주격조사', + 'JKC': '보격조사', + 'JKG': '관형격조사', + 'JKO': '목적격조사', + 'JKB': '부사격조사', + 'JKV': '호격조사', + 'JKQ': '인용격조사', + 'JX': '보조사', + 'JC': '접속조사', + 'EP': '선어말어미', + 'EF': '종결어미', + 'EC': '연결어미', + 'ETN': '명사형전성어미', + 'ETM': '관형형전성어미', + 'XPN': '체언접두사', + 'XSN': '명사파생접미사', + 'XSV': '동사파생접미사', + 'XSA': '형용사파생접미사', + 'XR': '어근', + 'SF': '마침표, 물음표, 느낌표', + 'SP': '쉼표, 가운뎃점, 콜론, 빗금', + 'SS': '따옴표, 괄호표, 줄표', + 'SE': '줄임표', + 'SO': '붙임표(물결)', + 'SW': '기타 기호', + 'SL': '외국어', + 'SH': '한자', + 'SN': '숫자', + 'NA': '분석불능범주', + 'NF': '명사추정범주', + 'NV': '용언추정범주', + 'NNG': '일반명사', + 'NNP': '고유명사', + 'NNB': '의존명사', + 'NP': '대명사', + 'NR': '수사', + 'VV': '동사', + 'VA': '형용사', + 'VX': '보조용언', + 'VCP': '긍정지정사', + 'VCN': '부정지정사', + 'MMA': '관형사', + 'MMD': '지시 관형사', + 'MMN': '수 관형사', + 'MAG': '일반부사', + 'MAJ': '접속부사', + 'IC': '감탄사', + 'NAP': '이름과 같은 개인정보' } From cbad600f0cb04f54cb514d4ddefd374f4b4158a7 Mon Sep 17 00:00:00 2001 From: "gichang.lee" Date: Mon, 12 Oct 2020 20:34:03 +0900 Subject: [PATCH 5/6] fix typo (#112) --- Korpora/korpus_modu_morpheme.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Korpora/korpus_modu_morpheme.py b/Korpora/korpus_modu_morpheme.py index 90d151b..2c84178 100644 --- a/Korpora/korpus_modu_morpheme.py +++ b/Korpora/korpus_modu_morpheme.py @@ -29,7 +29,7 @@ class ModuMorphemeKorpus(Korpus): def __init__(self, root_dir_or_paths, force_download=False): super().__init__(description, license) paths = find_corpus_paths(root_dir_or_paths) - self.train = KorpusData('모두의_형태분석_말뭉치.train', load_modu_ne(paths)) + self.train = KorpusData('모두의_형태분석_말뭉치.train', load_modu_morpheme(paths)) self.tagmap = { 'JKS': '주격조사', 'JKC': '보격조사', @@ -137,7 +137,7 @@ def document_to_examples(document): return examples -def load_modu_ne(paths): +def load_modu_morpheme(paths): examples = [] for path in paths: with open(path, encoding='utf-8') as f: From 4dbb8c296a3a27e38cc64bccbf68f2c7e3c9081c Mon Sep 17 00:00:00 2001 From: "gichang.lee" Date: Mon, 12 Oct 2020 20:58:11 +0900 Subject: [PATCH 6/6] fix tag name (#112) --- Korpora/korpus_modu_morpheme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Korpora/korpus_modu_morpheme.py b/Korpora/korpus_modu_morpheme.py index 2c84178..5b1b2ad 100644 --- a/Korpora/korpus_modu_morpheme.py +++ b/Korpora/korpus_modu_morpheme.py @@ -72,7 +72,7 @@ def __init__(self, root_dir_or_paths, force_download=False): 'VX': '보조용언', 'VCP': '긍정지정사', 'VCN': '부정지정사', - 'MMA': '관형사', + 'MMA': '성상 관형사', 'MMD': '지시 관형사', 'MMN': '수 관형사', 'MAG': '일반부사',