EXCELSIOR

Chap03 -1 : Creating Custom Corpora(corpus, chunk) 본문

NLP/NLTK

Chap03 -1 : Creating Custom Corpora(corpus, chunk)

Excelsior-JH 2017. 1. 6. 18:27

1. Creating a wordlist corpus

corpus는 우리말로는 말뭉치라고 하며, 자연어 연구를 위해 특정한 목적을 가지고 언어의 표본을 추출한 집합이다. corpora는 corpus의 복수형이다. corpus는 라틴어가 어원이다. 특정한 언어 영역 내에서 언어 규칙 발생의 검사와 그 규칙의 정당성 입증에 사용된다. 

NLTK 모듈의 WordListCorpusReader 클래스는 가장 단순한 CorpusReader 클래스 중 하나이다. WordListCorpusReader클래스는 한 줄에 한 단어로 리스팅 되어있는 파일에 사용할 수 있다. 우선, WordListCorpusReader를 사용하기 위해 아래와 같이 'wordlist.txt' 라는 파일을 생성한 다음 WordListCorpusReader를 적용한 예이다. wordlist.txt 파일이 Python console과 같은 디렉터리에 있으면 ' . ' 으로 표현 가능하다.

[wordlist.txt]
nltk
corpus
corpora
wordnet
from nltk.corpus.reader.wordlist import WordListCorpusReader
reader = WordListCorpusReader('.', ['wordlist.txt'])
print(reader.words())
print(reader.fileids())
#결과
['nltk', 'corpus', 'corpora', 'wordnet']
['wordlist.txt']

WordListCorpusReader 클래스는 CorpusReader로 부터 상속받으며, CorpusReader는 읽을 파일을 식별하고, WordListCorpusReader는 파일안의 각 라인을 읽어 토큰화 한다.

WordListCorpusReader의 words( ) 삼수를 호출할 때, nltk.tokenize.line_tokenize( )를 호출하여 토큰화 한다. 위의 예제를 다음과 같이 나타낼 수 있다.

from nltk.corpus.reader.wordlist import WordListCorpusReader
from nltk.tokenize.simple import line_tokenize
reader = WordListCorpusReader('.', ['wordlist.txt'])
print(reader.raw())
print(line_tokenize(reader.raw()))
#결과
nltk

corpus

corpora

wordnet
['nltk', 'corpus', 'corpora', 'wordnet']


2. Creating a part-of-speech tagged word corpus

Part-Of-Speech tagging은 앞의 포스팅에서도 설명했다 시피 단어의 품사를 식별하여 품사의 태그를 달아 주는것을 말한다. 다음은 NLTK의 TaggedCorpusReader를 사용한 예이다.  brown.pos의 파일이 먼저 생성 되어 있어야 한다.

from nltk.corpus.reader.tagged import TaggedCorpusReader
reader = TaggedCorpusReader('.', r'.*\.pos')
print(reader.words())
print(reader.tagged_words())
print(reader.sents())
print(reader.tagged_sents())
print(reader.paras())
print(reader.tagged_paras())
#결과
['The', 'expense', 'and', 'time', 'involved', 'are', ...]
[('The', 'AT-TL'), ('expense', 'NN'), ('and', 'CC'), ...]
[['The', 'expense', 'and', 'time', 'involved', 'are', 'astronomical', '.']]
[[('The', 'AT-TL'), ('expense', 'NN'), ('and', 'CC'), ('time', 'NN'), ('involved', 'VBN'), ('are', 'BER'), ('astronomical', 'JJ'), ('.', '.')]]
[[['The', 'expense', 'and', 'time', 'involved', 'are', 'astronomical', '.']]]
[[[('The', 'AT-TL'), ('expense', 'NN'), ('and', 'CC'), ('time', 'NN'), ('involved', 'VBN'), ('are', 'BER'), ('astronomical', 'JJ'), ('.', '.')]]]

위의 예제는 정규표현 r'.*\.pos'를 사용하여 파일명이 .pos인것을 매칭했다. TaggedCorpusReader 클래스는 corpus로 부터 텍스트를 추출해내는 아래와 같은 여러가지 함수들을 제공한다.

1) Customizing the word tokenizer

기본(default) word tokenizer는 공백 단위로 토큰화 하는 nltk.tokenize.WhitespaceTokenizer 이다. 다른 tokenizer들을 사용하고 싶다면 아래의 예제에서 'word_tokenizer =' 뒤에 Tokenizer를 입력해 주면 된다. 

from nltk.corpus.reader.tagged import TaggedCorpusReader
from nltk.tokenize.simple import SpaceTokenizer
reader = TaggedCorpusReader('.', r'.*\.pos', word_tokenizer=SpaceTokenizer())
print(reader.words())
#결과
['The', 'expense', 'and', 'time', 'involved', 'are', ...]

2) Customizing the sentence tokenizer

기본(default) sentence tokenizer는 nltk.tokenize.RegexpTokenize 이며, '\n' 단위로 식별한다. 마찬가지로 customize하기 위해서는 'sent_tokenizer=' 뒤에 다른 tokenizer를 입력하면 된다.

from nltk.corpus.reader.tagged import TaggedCorpusReader
from nltk.tokenize.simple import LineTokenizer
reader = TaggedCorpusReader('.', r'.*\.pos', sent_tokenizer=LineTokenizer())
print(reader.sents())
#결과
[['The', 'expense', 'and', 'time', 'involved', 'are', 'astronomical', '.']]

3) Converting tags to a universal tagset

NLTK 3.0은 태그들을 범용 태그(Universal tagset)으로 변환하는 기능을 제공한다. tagset은 단순히 Part-of-speech의 리스트들이다. universal tagset은 아래의 표와 같이 12개의 POS tag들로 단순하고, 압축된 형태로 이루어져 있다.

Universal tag

Description 

 설명

VERB

All verbs 

 동사

NOUN

Common and proper nouns 

 명사

PRON

Pronouns 

 대명사

ADJ

Adjectives 

 형용사

ADV

Adverbs 

 부사 

ADP

Prepositions and postpositions 

 전치사

CONJ

Conjunctions 

 접속사

DET

Determines 

 ?

NUM

Cardinal numbers 

 숫자

PRT

participles 

 분사

X

Other 

 

.

Punctuation

 구두점


아래의 예제는 known tagset과 universal tagset을 비교한 예제이다. 

from nltk.corpus.reader.tagged import TaggedCorpusReader
from nltk.tokenize.simple import LineTokenizer
reader = TaggedCorpusReader('.', r'.*\.pos', tagset='en-brown')
print(reader.tagged_sents())
print(reader.tagged_sents(tagset='universal'))
#결과
[[('The', 'AT-TL'), ('expense', 'NN'), ('and', 'CC'), ('time', 'NN'), ('involved', 'VBN'), ('are', 'BER'), ('astronomical', 'JJ'), ('.', '.')]]
[[('The', 'DET'), ('expense', 'NOUN'), ('and', 'CONJ'), ('time', 'NOUN'), ('involved', 'VERB'), ('are', 'VERB'), ('astronomical', 'ADJ'), ('.', '.')]]


2. Creating a chunked phrase corpus

chunk는 문장 내에 구나 절을 의미한다. 아래의 그림은 명사구(NP, Noun Phrase) chunk의 예제이다.


아래의 예제는 ChunkedCorpusReader( )예제 이다.  treebank.chunk파일이 먼저 생성되어 있어야 한다.

from nltk.corpus.reader.chunked import ChunkedCorpusReader
reader = ChunkedCorpusReader('.', r'.*\.chunk')
print(reader.chunked_words())
print(reader.chunked_sents())
print(reader.chunked_paras())
#결과
[Tree('NP', [('Earlier', 'JJR'), ('staff-reduction', 'NN'), ('moves', 'NNS')]), ('have', 'VBP'), ...]
[Tree('S', [Tree('NP', [('Earlier', 'JJR'), ('staff-reduction', 'NN'), ('moves', 'NNS')]), ('have', 'VBP'), ('trimmed', 'VBN'), ('about', 'IN'), Tree('NP', [('300', 'CD'), ('jobs', 'NNS')]), (',', ','), Tree('NP', [('the', 'DT'), ('spokesman', 'NN')]), ('said', 'VBD'), ('.', '.')])]
[[Tree('S', [Tree('NP', [('Earlier', 'JJR'), ('staff-reduction', 'NN'), ('moves', 'NNS')]), ('have', 'VBP'), ('trimmed', 'VBN'), ('about', 'IN'), Tree('NP', [('300', 'CD'), ('jobs', 'NNS')]), (',', ','), Tree('NP', [('the', 'DT'), ('spokesman', 'NN')]), ('said', 'VBD'), ('.', '.')])]]

ChunkedCorpusReader 클래스는 TaggedCorpusReader와 마찬가지로 tagging된 토큰들을 제공한다. 각각의 chunk는 nltk.tree.Tree로 나타내어진다. 문장(Sentence) level은 Tree('S', [...])과 같은 형태로 나타나며, 명사구(Noun phrase)는 Tee('NP', [...])과 같은 형태로 나타난다.




1) IOB tags

IOB tag는 POS tag와 비슷하지만, inside, outside 그리고 chunk의 시작점(beginning)을 제공한다. IOB tag는 단순히 명사구(NP)만 제공하는것이 아니라 다양한 phrase 타입을 제공한다. 아래는 IOB tag의 예제이다. B-NP는 명사구의 시작 부분을 의마한다. I-NP는 명사구 내부를 의미한다.

Mr. NNP B-NP

Meador NNP I-NP

had VBD B-VP

been VBN I-VP

executive JJ B-NP

vice NN I-NP

president NN I-NP

of IN B-PP

Balcor NNP B-NP

. . O


from nltk.corpus.reader.conll import ConllChunkCorpusReader
conllreader = ConllChunkCorpusReader('.', r'.*\.iob', ('NP', 'VP', 'PP'))
print(conllreader.chunked_words())
print(conllreader.chunked_sents())
print(conllreader.iob_words())
print(conllreader.iob_sents())
#결과
[Tree('NP', [('Mr.', 'NNP'), ('Meador', 'NNP')]), Tree('VP', [('had', 'VBD'), ('been', 'VBN')]), ...]
[Tree('S', [Tree('NP', [('Mr.', 'NNP'), ('Meador', 'NNP')]), Tree('VP', [('had', 'VBD'), ('been', 'VBN')]), Tree('NP', [('executive', 'JJ'), ('vice', 'NN'), ('president', 'NN')]), Tree('PP', [('of', 'IN')]), Tree('NP', [('Balcor', 'NNP')]), ('.', '.')])]
[('Mr.', 'NNP', 'B-NP'), ('Meador', 'NNP', 'I-NP'), ...]
[[('Mr.', 'NNP', 'B-NP'), ('Meador', 'NNP', 'I-NP'), ('had', 'VBD', 'B-VP'), ('been', 'VBN', 'I-VP'), ('executive', 'JJ', 'B-NP'), ('vice', 'NN', 'I-NP'), ('president', 'NN', 'I-NP'), ('of', 'IN', 'B-PP'), ('Balcor', 'NNP', 'B-NP'), ('.', '.', 'O')]]

2) Tree leaves

chunk는 tree로 나타나며, tagging된 토큰들은 leaves로 나타나게 된다.

from nltk.corpus.reader.chunked import ChunkedCorpusReader
reader = ChunkedCorpusReader('.', r'.*\.chunk')
print(reader.chunked_words()[0].leaves())
print(reader.chunked_sents()[0].leaves())
print(reader.chunked_paras()[0][0].leaves())
#결과
[('Earlier', 'JJR'), ('staff-reduction', 'NN'), ('moves', 'NNS')]
[('Earlier', 'JJR'), ('staff-reduction', 'NN'), ('moves', 'NNS'), ('have', 'VBP'), ('trimmed', 'VBN'), ('about', 'IN'), ('300', 'CD'), ('jobs', 'NNS'), (',', ','), ('the', 'DT'), ('spokesman', 'NN'), ('said', 'VBD'), ('.', '.')]
[('Earlier', 'JJR'), ('staff-reduction', 'NN'), ('moves', 'NNS'), ('have', 'VBP'), ('trimmed', 'VBN'), ('about', 'IN'), ('300', 'CD'), ('jobs', 'NNS'), (',', ','), ('the', 'DT'), ('spokesman', 'NN'), ('said', 'VBD'), ('.', '.')]


3 Comments
  • 프로필사진 NaeHyeon^^ 2019.10.04 17:42 안녕하세요! 너무 좋은 글 잘보고갑니다.!!
    그런데 파이썬 입문자로서 하나 질문이 있습니다.
    제가 사실 한글 corpus를 구축하고 싶은데 배경지식이 없어서 어디서 부터 시작해야할 지 모르겠네요ㅠㅠ
  • 프로필사진 Favicon of https://excelsior-cjh.tistory.com Excelsior-JH 2019.10.05 18:55 신고 안녕하세요 :-) 도움이 되셨다니 뿌듯하네요 ㅎㅎ

    한글 코퍼스(corpus)를 구축하는 일련의 프로세스는 다음과 같이 나타낼 수 있는데,

    1. 한글 데이터 수집: 크롤링, 다운로드 등
    2. 토큰화(Tokenization):
    - Python 모듈인 KoNLPy를 이용
    - 목적에 맞는 토큰 추출
    - 예를 들어, POS-tagging을 이용한 명사 단위 추출 등

    2 번 토큰화에 대한 부분은 아래의 링크를 참고 하시면 될 듯합니다.

    https://konlpy-ko.readthedocs.io/ko/v0.4.3/
  • 프로필사진 NaeHyeon^^ 2019.10.08 02:57 답변 감사드립니다 :)

    자세히 프로세스까지 써주시다니...ㅎㅎ
    감사합니당 :)
댓글쓰기 폼