Natural Language Processing (NLP) in Python with 8 Projects - Stemming and Lemmatization

Natural Language Processing (NLP) in Python with 8 Projects 목차

Stemming and Lemmatization

  • 어간추출 (stemming)

어간(Stem)을 추출하는 작업을 어간 추출(stemming)이라고 합니다. 어간 추출은 형태학적 분석을 단순화한 버전이라고 볼 수도 있고, 정해진 규칙만 보고 단어의 어미를 자르는 어림짐작의 작업이라고 볼 수도 있습니다. 다시 말해, 이 작업은 섬세한 작업이 아니기 때문에 어간 추출 후에 나오는 결과 단어는 사전에 존재하지 않는 단어일 수도 있습니다.

어간 추출(Stemming) 어간 추출(Stemming)이란 단어로 부터 어간(Stem)을 추출하는 작업을 뜻합니다. 어간 추출은 뒤이어 설명할 표제어 추출보다 성능이 떨어집니다. 즉, 일부 철자가 훼손된 어근 단어를 추출합니다. 단어를 보고 어림짐작하여 어미를 잘라 어간을 추출하기 때문입니다. NLTK의 어간 추출 Stemmer로는 대표적으로 Porter, Lancaster, Snowball Stemmer가 있습니다. Porter Stemmer와 Lancaster Stemmer를 비교해보겠습니다. 더자세한 내용은 밑 링크 참고!

https://bkshin.tistory.com/entry/NLP-4-%EC%96%B4%EA%B0%84-%EC%B6%94%EC%B6%9CStemming%EA%B3%BC-%ED%91%9C%EC%A0%9C%EC%96%B4-%EC%B6%94%EC%B6%9CLemmatization

Playing

Play

Played

=> Play

words = ['run','runner','running','ran','runs','easily','fairly']
import nltk #NLTK는 Porter, Lancaster, Regexp, Snowball Stemmer 클래스를 제공한다.

from nltk.stem.porter import PorterStemmer
from nltk.stem.snowball import  SnowballStemmer
p_stemmer = PorterStemmer()
s_stemmer = SnowballStemmer(language='english')
for word in words:
  print(word + ' ------  ' +p_stemmer.stem(word))
run ------  run
runner ------  runner
running ------  run
ran ------  ran
runs ------  run
easily ------  easili
fairly ------  fairli
for word in words:
  print(word + ' ------  ' +s_stemmer.stem(word))
run ------  run
runner ------  runner
running ------  run
ran ------  ran
runs ------  run
easily ------  easili
fairly ------  fair
# 예제2

import nltk
from nltk.stem import PorterStemmer
from nltk.tokenize import word_tokenize

s = PorterStemmer()
text = "This was not the map we found in Billy Bones's chest, but an accurate copy, complete in all things--names and heights and soundings--with the single exception of the red crosses and the written notes."
words = word_tokenize(text)
print(words)
['This', 'was', 'not', 'the', 'map', 'we', 'found', 'in', 'Billy', 'Bones', "'s", 'chest', ',', 'but', 'an', 'accurate', 'copy', ',', 'complete', 'in', 'all', 'things', '--', 'names', 'and', 'heights', 'and', 'soundings', '--', 'with', 'the', 'single', 'exception', 'of', 'the', 'red', 'crosses', 'and', 'the', 'written', 'notes', '.']
[s.stem(w) for w in words] #어간 추출하기 전에 word_tokenize로 텍스트 분리하기

['thi',
 'wa',
 'not',
 'the',
 'map',
 'we',
 'found',
 'in',
 'billi',
 'bone',
 "'s",
 'chest',
 ',',
 'but',
 'an',
 'accur',
 'copi',
 ',',
 'complet',
 'in',
 'all',
 'thing',
 '--',
 'name',
 'and',
 'height',
 'and',
 'sound',
 '--',
 'with',
 'the',
 'singl',
 'except',
 'of',
 'the',
 'red',
 'cross',
 'and',
 'the',
 'written',
 'note',
 '.']

Lemmatization

표제어 추출

일반적으로 어간 추출보다 표제어 추출이 더 정확히 어근 단어를 찾아줍니다.

표제어 추출은 WordNetLemmatizer를 주로 사용합니다.

표제어 추출을 하는 가장 섬세한 방법은 단어의 형태학적 파싱을 먼저 진행하는 것이다. 형태소란 ‘의미를 가진 가장 작은 단위’를 뜻한다. 그리고 형태학(Morphology)이란, 형태소로부터 단어들을 만들어가는 학문을 뜻한다. 형태소는 두 가지 종류가 있다. 각각 stem(어간)과 affix(접사)이다. stem(어간) : 단어의 의미를 담고 있는 단어의 핵심 부분. affix(접사) : 단어에 추가적인 의미를 주는 부분.

import spacy
nlp = spacy.load('en_core_web_sm')
doc1 = nlp("The striped bats are hanging on their feet for best")
for token in doc1:
  print(token.text, '\t', token.lemma_)
#표제어 추출은 단어들이 서로 다른 형태를 가지더라도, 그 뿌리 단어를 찾아가서 단어의 개수를 줄일 수 있는지를 판단한다. 예를 들어 am,are,is는 서로 다른 스펠링이지만, 그 뿌리 단어는 be이기 때문에 이 단어들의 Lemma는 be라고 한다.
The 	 the
striped 	 striped
bats 	 bat
are 	 be
hanging 	 hang
on 	 on
their 	 their
feet 	 foot
for 	 for
best 	 good
s1 = "The striped bats are hanging on their feet for best"
for word in s1.split():
  print(word + ' ------  ' +p_stemmer.stem(word))
The ------  the
striped ------  stripe
bats ------  bat
are ------  are
hanging ------  hang
on ------  on
their ------  their
feet ------  feet
for ------  for
best ------  best

형태학적 파싱은 두 가지 구성 요소를 분리하는 작업을 뜻한다. 가령, cats라는 단어에 대해 형태학적 파싱을 수행한다면, 형태학적 파싱은 결과로 cat(어간)와 -s(접사)를 분리한다. 물론, 꼭 두 가지로 분리되지 않는 경우도 있다. 가령, 단어 fox는 형태학적 파싱을 한다고 하더라도 더 이상 분리를 할 수 없다. fox는 독립적인 형태소이기 때문이다. NLTK에서는 표제어 추출을 위한 도구인 WordNetLemmatizer를 지원한다.

import nltk
from nltk.stem import WordNetLemmatizer
n = WordNetLemmatizer()
words=['policy', 'doing', 'organization', 'have', 'going', 'love', 'lives','fly', 'dies', 'watched', 'has', 'starting']
[n.lemmatize(w) for w in words]
['policy',
 'doing',
 'organization',
 'have',
 'going',
 'love',
 'life',
 'fly',
 'dy',
 'watched',
 'ha',
 'starting']

표제어 추출은 어간 추출(Stemming)과는 달리(다음에서 다룸) 단어의 형태가 적절히 보존되는 양상을 볼 수있다. 하지만 그럼에도 위의 결과에서는 dy나 ha와 같이 의미를 알 수 없는 적절하지 못한 단어를 출력하고 있다. 이는 표제어 추출기(Lemmatizer)가 본래 단어의 품사 정보를 알아야만 정확한 결과를 얻을 수 있기 때문이다. WordNetLemmatizer는 입력으로 단어의 품사를 지정해줄 수 있다. 즉, dies와 watched, has가 문장에서 동사로 쓰였다는 것을 알려준다면 표제어 추출기는 품사의 정보를 보존하면서 정확한 Lemma를 출력하게 된다.

n.lemmatize('dies','v')

'die'
n.lemmatize('wathced', 'v')

'wathced'
n.lemmatize('has', 'v')

'have'