Data Analysis & ML/Text Mining

[Text Mining][텍스트마이닝][NLP] NLTK 패키지(형태소, 어간,품사부착,원형복원)

YSY^ 2020. 9. 9. 17:42

형태소 분석

  • 형태소
    • 일정한 의미가 있는 가장 작은 말의 단위
  • 형태소 분석
    • 말뭉치에서 의미있는(분석에 필요한) 형태소들만 추출하는 것
    • 보통 단어로 부터 어근, 접두사, 접미사, 품사등 언어적 속성을 파악하여 처리한다.
  • 형태소 분석을 위한 기법
    • 어간추출(Stemming)
    • 원형(기본형) 복원 (Lemmatization)
    • 품사부착 (POS tagging - Part Of Speech)

어간추출(Stemming)

  • 어간: 활용어에서 변하지 않는 부분
    • painted, paint, painting => 어간: paint
    • 보다, 보니, 보고=>어간 보-
  • 어간 추출
    • 단어에서 어미를 제거하고 어간을 추출하는 작업
  • 목적
    • 같은 의미를 가지는 단어의 여러가지 활용이 있을 경우 다른 단어로 카운트 되는 문제점을 해결한다.
      • flower, flowers 가 두개의 단어로 카운트 되는 것을 flower로 통일한다.
  • nltk의 주요 어간 추출 알고리즘
    • Porter Stemmer
    • Lancaster Stemmer
    • Snowball Stemmer
  • 메소드
    • stemmer객체.stem(단어)
  • stemming의 문제
    • 완벽하지 않다는 것이 문제이다.
      • ex) new와 news는 다른 단어 인데 둘다 new로 처리한다.
    • 처리후 눈으로 직접 확인해야 한다.
text_sample = """Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!"""
 words = [
    'Working', "works", 'worked',
    'painting', 'Painted', 'paints',
    'happy','happier','happiest'
]

from nltk import PorterStemmer, LancasterStemmer, SnowballStemmer


stemmer = PorterStemmer()
stemmer.stem('Working'), stemmer.stem('working')
stem_list = []
stemmer = PorterStemmer()
for word in words:
    stem_list.append(stemmer.stem(word))

print(stem_list)
#==> ['work', 'work', 'work', 'paint', 'paint', 'paint', 'happi', 'happier', 'happiest']

stemmer2 = LancasterStemmer()
print([stemmer2.stem(word) for word in words])
#==> ['work', 'work', 'work', 'paint', 'paint', 'paint', 'happy', 'happy', 'happiest']

stemmer3 = SnowballStemmer("english") # 언어를 지정.
print([stemmer3.stem(word) for word in words])
#==> ['work', 'work', 'work', 'paint', 'paint', 'paint', 'happi', 'happier', 'happiest']

stemmer3.stem('am'),stemmer3.stem('is'), stemmer3.stem('are')

원형(기본형)복원(Lemmatization)

  • 단어의 기본형을 반환한다.
    • ex) am, is, are => be
  • 단어의 품사를 지정하면 정확한 결과를 얻을 수 있다.
  • WordNetLemmatizer객체.lemmatize(단어 [, pos=품사])

어간추출과 원형복원은 문법적 또는 의미적으로 변한 단어의 원형을 찾는 역할은 동일하다.

원형복원은 품사와 같은 문법적요소와 문장내에서의 의미적인 부분을 감안해 찾기 때문에 어간추출 방식보다 더 정교하다.
품사 : v-동사, n-명사, a-형용사, r-부사

nltk.download('wordnet')

words = ['is','are','am', 'have','has','had']


from nltk.stem import WordNetLemmatizer


lemm = WordNetLemmatizer()


for word in words:
    print(lemm.lemmatize(word, pos='v')) #품사: v-동사, n-명사, a-형용사, r-부사

#be
#be
#be
#have
#have
#have

lemm.lemmatize('happier', pos='a'), lemm.lemmatize('happiest', pos='a')
# ('happy', 'happy')

stemmer.stem('meeting'), stemmer.stem('meeting')
# ('meet', 'meet')

lemm.lemmatize('meeting', pos='v'), lemm.lemmatize('meeting', pos='n')
# ('meet', 'meeting')

품사부착-POS Tagging(Part-Of-Speech Tagging)

  • 형태소에 품사를 붙이는 작업.
    • 품사의 구분이나 표현은 언어, 학자마다 다르다.
  • NLTK는 펜 트리뱅크 태그세트(Penn Treebank Tagset) 이용
    • 명사 : N으로 시작 (NN-일반명사, NNP-고유명사)
    • 형용사 : J로 시작(JJ, JJR-비교급, JJS-최상급)
    • 동사: V로 시작 (VB-동사, VBP-현재형 동사)
    • 부사: R로 시작 (RB-부사)
    • nltk.help.upenn_tagset('키워드') : 도움말
  • pos_tag(단어_리스트)
    • 단어와 품사를 튜플로 묶은 리스트를 반환
nltk.download('tagsets')
import nltk
nltk.download('averaged_perceptron_tagger')


word_list = ['Book','car', 'computer', 'go', 'have', 'Korea', 'water']

from nltk.tag import pos_tag
pos_tag(word_list)

[('Book', 'NN'),
('car', 'NN'),
('computer', 'NN'),
('go', 'VBP'),
('have', 'VBP'),
('Korea', 'NNP'),
('water', 'NN')]

# 품사 부착
tokens_pos_list = []
for l in tokens:
    pt = pos_tag(l)
    tokens_pos_list.append(pt)

 

품사부착 + 원형복원

def get_wordnet_pos(pos_tag):
    """
    펜 트리뱅크 품사표기법(pos_tag()의 반환형태)을 받아서 WordNetLemmatizer에서 사용하는 품사표기(v, a, n, r)로 변환하는 함수.
    [매개변수]
        pos_tag: pos_tag()가 반환하는 품사
    [반환값]
        문자열: 동사-"v", 명사-'n', 형용사-'a', 부사-'r', 그외-None
    """
    if pos_tag.startswith('V'):
        return 'v'
    elif pos_tag.startswith('N'):
        return 'n'
    elif pos_tag.startswith('J'):
        return 'a'
    elif pos_tag.startswith('R'):
        return 'r'
    else:
        return None

# pos_tag가 반환한 품사를 원형복원용 품사로 변환
tokens_pos_list2 = []
for token_list in tokens_pos_list:
    temp_list = []
    for token, pos_tag in token_list:
        tag = get_wordnet_pos(pos_tag)
        if tag!=None: #변환된 품사가 None이 아닌 것들만 list에 담는다.
            temp_list.append((token, get_wordnet_pos(pos_tag)))
    tokens_pos_list2.append(temp_list)

 

# 원형복원
from nltk.stem import WordNetLemmatizer

lemm = WordNetLemmatizer()
token_final = []
for token_pos in tokens_pos_list2:
    tmp_list = []
    for token, tag in token_pos:
        tmp_list.append(lemm.lemmatize(token, pos=tag)) # 단어의 원형을 tmp_list에 추가.
    token_final.append(tmp_list)

 

728x90
반응형