단어 형태소 또는 기본형은 어떻게합니까?


111

PorterStemmer와 Snowball을 사용해 보았지만 둘 다 모든 단어에서 작동하지 않으며 매우 일반적인 단어가 누락되었습니다.

내 테스트 단어는 : " cats running ran cactus cactuses cacti community community "그리고 둘 다 절반 미만의 권리를 얻습니다.

또한보십시오:


28
선인장이어야하지 않나요?
MSalters

3
Reddit에 게시 된 원래 질문에 대한 순환 참조를 만들기 위해 : 어떻게 프로그래밍 방식으로 형태소 분석을 수행합니까? (예 : "먹기"에서 "먹기", "선인장"에서 "선인장") 댓글에 유용한 정보가 포함되어 있으므로 여기에 게시합니다.
Renaud Bompuis

답변:


143

Python을 아는 경우 NLTK (Natural Language Toolkit) 에는 WordNet 을 사용하는 매우 강력한 lemmatizer가 있습니다 .

이 lemmatizer를 처음 사용하는 경우 사용하기 전에 말뭉치를 다운로드해야합니다. 이것은 다음을 통해 수행 할 수 있습니다.

>>> import nltk
>>> nltk.download('wordnet')

이 작업은 한 번만 수행하면됩니다. 이제 코퍼스를 다운로드했다고 가정하면 다음과 같이 작동합니다.

>>> from nltk.stem.wordnet import WordNetLemmatizer
>>> lmtzr = WordNetLemmatizer()
>>> lmtzr.lemmatize('cars')
'car'
>>> lmtzr.lemmatize('feet')
'foot'
>>> lmtzr.lemmatize('people')
'people'
>>> lmtzr.lemmatize('fantasized','v')
'fantasize'

nltk.stem 모듈 에 다른 lemmatizer가 있지만 직접 시도하지는 않았습니다.


11
아 슬프다 ... 검색하기 전에 내가 직접 구현했습니다!
Chris Pfohl 2010

12
nltk 를 처음 사용하기 전에 코퍼스를 설치하는 것을 잊지 마십시오 ! velvetcache.org/2010/03/01/…
Mathieu Rodic

1
글쎄, 이것은 Porter Stemmer와 같은 비 결정적 알고리즘을 사용합니다.으로 시도 dies하면 dy대신 die. 어떤 종류의 하드 코딩 된 형태소 분석기 사전이 없습니까?
SexyBeast

3
WordNetLemmatizer잘못 lemmatize 하는 단어가 무엇인지 아십니까?
alvas 2013-06-27

21
nltk WordNetLemmatizer에는 인수로 pos 태그가 필요합니다. 기본적으로 'n'(명사를 나타냄)입니다. 따라서 동사에서는 제대로 작동하지 않습니다. POS 태그를 사용할 수없는 경우 간단한 (임시) 접근 방식은 'n'에 대해 하나, 'v'(동사를 나타냄)에 대해 두 번 lemmatization을 수행하고 다른 결과를 선택하는 것입니다. 원래 단어 (일반적으로 길이가 더 짧지 만 'ran'과 'run'의 길이는 같습니다). 'adj', 'adv', 'prep'등은 이미 어떤 의미에서는 원래 형태이기 때문에 걱정할 필요가없는 것 같습니다.
Fashandge 2014-06-07

29

나는 lemmatization 을 수행하기 위해 stanford nlp 를 사용합니다. 지난 며칠 동안 비슷한 문제가 발생했습니다. 이 문제를 해결하는 데 도움이되는 stackoverflow 덕분입니다.

import java.util.*; 
import edu.stanford.nlp.pipeline.*;
import edu.stanford.nlp.ling.*; 
import edu.stanford.nlp.ling.CoreAnnotations.*;  

public class example
{
    public static void main(String[] args)
    {
        Properties props = new Properties(); 
        props.put("annotators", "tokenize, ssplit, pos, lemma"); 
        pipeline = new StanfordCoreNLP(props, false);
        String text = /* the string you want */; 
        Annotation document = pipeline.process(text);  

        for(CoreMap sentence: document.get(SentencesAnnotation.class))
        {    
            for(CoreLabel token: sentence.get(TokensAnnotation.class))
            {       
                String word = token.get(TextAnnotation.class);      
                String lemma = token.get(LemmaAnnotation.class); 
                System.out.println("lemmatized version :" + lemma);
            }
        }
    }
}

나중에 분류 자에서 사용되는 경우 불용어를 사용하여 출력 기본형을 최소화하는 것도 좋은 생각 일 수 있습니다. John Conwell이 작성한 coreNlp 확장을 살펴보십시오 .


답장이 늦어서 죄송합니다 ..이 문제는 지금 만 해결되었습니다! :)
CTsiddharth

1
'pipeline = new ...'라인은 나를 위해 컴파일되지 않습니다. 'StanfordCoreNLP pipelne = new ...'로 변경하면 컴파일됩니다. 이게 맞나요?
Adam_G 2010 년

예, 파이프 라인 var를 먼저 선언해야합니다. Stanford NLP는 명령 줄에서도 사용할 수 있으므로 프로그래밍을 할 필요가 없습니다. 속성 파일을 만들고 실행 파일을 제공하기 만하면됩니다. : 워드 프로세서 읽기 nlp.stanford.edu/software/corenlp.shtml
Jindra Helcl

24

이 눈덩이 데모 사이트 에서 용어 목록을 시도했는데 결과가 괜찮아 보입니다 ....

  • 고양이-> 고양이
  • 달리기-> 달리기
  • 실행-> 실행
  • 선인장-> 선인장
  • 선인장-> 선인장
  • 커뮤니티-> 커뮤니티
  • 커뮤니티-> 커뮤니티

형태소 분석기는 변형 된 형태의 단어를 일반적인 어근으로 바꿔야합니다. 그 어근을 '적절한'사전 단어로 만드는 것은 실제로 형태소 분석기의 일이 아닙니다. 이를 위해서는 형태 / 직교 분석기 를 살펴볼 필요가 있습니다 .

나는 이 질문 이 거의 똑같은 것에 관한 것이라고 생각하며, 그 질문 에 대한 Kaarel의 대답은 내가 두 번째 링크를 가져온 곳입니다.


6
요점은 stem ( "updates") == stem ( "update")입니다. (업데이트-> updat)
Stompchicken

1
이 소프트웨어는 줄기 (x)를 == 줄기 (y)를 할 수 있지만 완전히 질문에 대답 아니에요
사용자

11
용어에주의 할 때 어간은 단어의 기본 형태가 아닙니다. 기본 양식을 원하면 lemmatizer가 필요합니다. 어간은 접두사 또는 접미사가없는 단어의 가장 큰 부분입니다. 업데이트라는 단어의 어간은 실제로 "updat"입니다. 단어는 updat-e 또는 updat-ing과 같은 어미와 접미사를 추가하여 어간에서 생성됩니다. ( en.wikipedia.org/wiki/Word_stem )
Jindra Helcl

20

형태소 분석기 대 lemmatizer 논쟁은 계속됩니다. 효율성보다 정밀도를 선호하는 문제입니다. 언어 적으로 의미있는 단위를 얻기 위해 lemmatize하고 최소한의 컴퓨팅 주스를 사용하면서 동일한 키 아래에서 단어와 그 변형을 색인화해야합니다.

참조 Lemmatizers 대 형태소 분석기를

다음은 Python NLTK를 사용한 예입니다.

>>> sent = "cats running ran cactus cactuses cacti community communities"
>>> from nltk.stem import PorterStemmer, WordNetLemmatizer
>>>
>>> port = PorterStemmer()
>>> " ".join([port.stem(i) for i in sent.split()])
'cat run ran cactu cactus cacti commun commun'
>>>
>>> wnl = WordNetLemmatizer()
>>> " ".join([wnl.lemmatize(i) for i in sent.split()])
'cat running ran cactus cactus cactus community community'

3
앞에서 언급했듯이 WordNetLemmatizerlemmatize()는 POS 태그를 사용할 수 있습니다. 따라서 귀하의 예에서 : " ".join([wnl.lemmatize(i, pos=VERB) for i in sent.split()])제공합니다 'cat run run cactus cactuses cacti community communities'.
Nick Ruiz

@NickRuiz, 당신이 의미하는 것 같아요 pos=NOUN? BTW :
오랜만에 만나요.

실제로는 아닙니다 (컨퍼런스에는 '예'입니다). 당신이 설정 pos=VERB하면 동사에 대해서만 lemmatization을 수행하기 때문입니다. 명사는 동일하게 유지됩니다. 실제 Penn Treebank POS 태그를 중심으로 피벗하여 각 토큰에 올바른 lemmatization을 적용하기 위해 자체 코드를 작성해야했습니다. 또한 WordNetLemmatizernltk의 기본 토크 나이저를 lemmatizing 할 때 악취가납니다. 예를 들면 같은 does n't에 lemmatize하지 않습니다 do not.
Nick Ruiz

그러나 각각에 대해 올바른 위치가 제공되는 경우에도 및을 port.stem("this")생성 합니다. thiport.stem("was") wa
Lerner Zhang

형태소 분석기는 언어 적으로 사운드 출력을 반환하지 않습니다. 그것은 단지 텍스트를 더 "조밀"하게 만드는 것입니다 (즉, 더 적은 어휘를 포함). stackoverflow.com/questions/17317418/stemmers-vs-lemmatizersstackoverflow.com/questions/51943811/…
alvas

8

Martin Porter의 공식 페이지에는 PHP다른 언어 로 된 Porter Stemmer가 포함되어 있습니다 .

Porter Algorithm과 같은 것으로 시작해야하지만 좋은 형태소 분석에 대해 정말로 진지한 경우, 규칙을 추가하여 데이터 세트에 공통된 잘못된 사례를 수정 한 다음 마지막으로 규칙에 많은 예외를 추가합니다. . 키가 조회 할 단어이고 값이 원본을 대체 할 어간 단어 인 키 / 값 쌍 (dbm / hash / dictionaries)으로 쉽게 구현할 수 있습니다. 내가 작업 한 상업용 검색 엔진은 수정 된 Porter 알고리즘에 대한 몇 가지 예외로 끝났습니다.


이상적인 솔루션은 이러한 기대치를 자동으로 학습합니다. 그러한 시스템에 대한 경험이 있습니까?
Malcolm

아니요. 우리의 경우 색인이 생성되는 문서는 특정 법률 분야에 대한 코드 및 규정이었으며 불량 어간에 대한 색인을 분석하는 수십 명의 (인간) 편집자가있었습니다.
Van Gale


5

Stack Overflow에 대한 다양한 답변과 제가 만난 블로그에 따르면 이것이 제가 사용하는 방법이며 실제 단어를 꽤 잘 반환하는 것 같습니다. 아이디어는 들어오는 텍스트를 단어 배열로 분할 한 다음 (원하는 방법을 사용하여) 해당 단어의 품사 (POS)를 찾아이를 사용하여 단어의 어간과 lemmatize를 돕는 것입니다.

위의 샘플은 POS를 결정할 수 없기 때문에 너무 잘 작동하지 않습니다. 그러나 실제 문장을 사용하면 상황이 훨씬 더 잘 작동합니다.

import nltk
from nltk.corpus import wordnet

lmtzr = nltk.WordNetLemmatizer().lemmatize


def get_wordnet_pos(treebank_tag):
    if treebank_tag.startswith('J'):
        return wordnet.ADJ
    elif treebank_tag.startswith('V'):
        return wordnet.VERB
    elif treebank_tag.startswith('N'):
        return wordnet.NOUN
    elif treebank_tag.startswith('R'):
        return wordnet.ADV
    else:
        return wordnet.NOUN


def normalize_text(text):
    word_pos = nltk.pos_tag(nltk.word_tokenize(text))
    lemm_words = [lmtzr(sw[0], get_wordnet_pos(sw[1])) for sw in word_pos]

    return [x.lower() for x in lemm_words]

print(normalize_text('cats running ran cactus cactuses cacti community communities'))
# ['cat', 'run', 'ran', 'cactus', 'cactuses', 'cacti', 'community', 'community']

print(normalize_text('The cactus ran to the community to see the cats running around cacti between communities.'))
# ['the', 'cactus', 'run', 'to', 'the', 'community', 'to', 'see', 'the', 'cat', 'run', 'around', 'cactus', 'between', 'community', '.']



2

LemmaGen -C # 3.0으로 작성된 오픈 소스 라이브러리를 살펴보십시오 .

테스트 단어에 대한 결과 ( http://lemmatise.ijs.si/Services )

  • 고양이-> 고양이
  • 달리는
  • 실행-> 실행
  • 선인장
  • 선인장-> 선인장
  • 선인장-> 선인장
  • 커뮤니티
  • 커뮤니티-> 커뮤니티

2

원형 화를위한 (특정 순서) 상위 파이썬 패키지는 다음과 같습니다 : spacy, nltk, gensim, pattern, CoreNLPTextBlob. 나는 단어의 POS 태그를 식별하고 적절한 기본형을 자동으로 할당하기 때문에 spaCy와 gensim의 구현 (패턴 기반)을 선호합니다. 의미를 그대로 유지하면서 더 관련성이 높은 기본형을 제공합니다.

nltk 또는 TextBlob을 사용하려는 경우 올바른 POS 태그를 수동으로 찾고 올바른 기본형을 찾아야합니다.

spaCy를 사용한 기본형 예제 :

# Run below statements in terminal once. 
pip install spacy
spacy download en

import spacy

# Initialize spacy 'en' model
nlp = spacy.load('en', disable=['parser', 'ner'])

sentence = "The striped bats are hanging on their feet for best"

# Parse
doc = nlp(sentence)

# Extract the lemma
" ".join([token.lemma_ for token in doc])
#> 'the strip bat be hang on -PRON- foot for good'

Gensim을 사용한 기본형 예제 :

from gensim.utils import lemmatize
sentence = "The striped bats were hanging on their feet and ate best fishes"
lemmatized_out = [wd.decode('utf-8').split('/')[0] for wd in lemmatize(sentence)]
#> ['striped', 'bat', 'be', 'hang', 'foot', 'eat', 'best', 'fish']

위의 예는이 lemmatization 페이지 에서 차용 한 것입니다 .


1

Lucene 검색을 수행하십시오. PHP 포트가 있는지 확실하지 않지만 Lucene이 많은 플랫폼에서 사용 가능하다는 것을 알고 있습니다. Lucene은 OSS (Apache의) 인덱싱 및 검색 라이브러리입니다. 당연히 그것과 커뮤니티 엑스트라는 흥미로운 것을 볼 수 있습니다. 최소한 "아이디어"를 PHP로 번역 할 수 있도록 한 언어로 어떻게 수행되는지 배울 수 있습니다.


1

StompChicken이 언급 한 질문에 대한 답변을 인용 할 수 있다면 :

여기서 핵심 문제는 형태소 분석 알고리즘이 작업중인 언어에 대한 실제 이해없이 음성 기반으로 작동한다는 것입니다.

그들은 언어를 이해하지 못하고 용어 사전에서 실행되지 않기 때문에 "run"/ "ran"과 같은 불규칙한 경우를 인식하고 적절하게 대응할 방법이 없습니다.

불규칙한 경우를 처리해야하는 경우 다른 접근 방식을 선택하거나 형태소 분석기가 작업을 수행 한 후 실행할 사용자 지정 수정 사전을 사용하여 형태소 분석을 확장해야합니다.



1

Morpha 형태소 분석기를 사용할 수 있습니다. UW는 Java 애플리케이션에서 사용하려는 경우 morpha 형태소 분석기를 Maven central에 업로드 했습니다. 훨씬 쉽게 사용할 수있는 래퍼가 있습니다. 종속성으로 추가하고 edu.washington.cs.knowitall.morpha.MorphaStemmer클래스를 사용하기 만하면 됩니다. 인스턴스는 스레드로부터 안전합니다 (원래 JFlex에는 불필요하게 로컬 변수에 대한 클래스 필드가 있음). 클래스를 인스턴스화하고 morpha줄기를 원하는 단어를 실행 하십시오.

new MorphaStemmer().morpha("climbed") // goes to "climb"

0

.Net lucene에는 내장 포터 스테 머가 있습니다. 시도해 볼 수 있습니다. 그러나 포터 형태소 분석은 기본형을 도출 할 때 단어 컨텍스트를 고려하지 않습니다. (알고리즘과 그 구현을 살펴보면 어떻게 작동하는지 볼 수 있습니다)


0

Martin Porter는 Snowball (형태소 분석 알고리즘 용 언어)을 작성하고 Snowball에서 "English Stemmer"를 다시 작성했습니다. C 및 Java 용 English Stemmer가 있습니다.

그는 Porter Stemmer가 역사적인 이유로 다시 구현되었다고 명시 적으로 언급하므로 Porter Stemmer에 대한 형태소 분석 정확성을 테스트하면 이미 알고 있어야하는 결과를 얻을 수 있습니다.

에서 http://tartarus.org/~martin/PorterStemmer/index.html (강조 광산)

Porter 형태소 분석기는 ' 고정 ', 즉 엄격하게 정의 된 것으로 간주되어야하며 추가 수정이 불가능합니다. 형태소 분석기는 Snowball English 또는 Porter2 형태소 분석기보다 약간 열등합니다.이 형태소는 여기에서 파생되며 때때로 개선됩니다. 따라서 실제 작업을 위해서는 새로운 Snowball 형태소 분석기를 사용하는 것이 좋습니다. Porter 형태소 분석기는 실험이 정확하게 반복되어야하는 형태소 분석과 관련된 IR 연구 작업에 적합합니다.

Porter 박사는 Porter 형태소 분석기 대신 English 또는 Porter2 형태소 분석기를 사용할 것을 제안합니다. 영어 형태소 분석기는 @StompChicken이 이전에 답변했듯이 데모 사이트 에서 실제로 사용되는 것입니다.


0

Java에서는 tartargus-snowball 을 사용 하여 단어를 형태소 분석합니다.

메이븐 :

<dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-snowball</artifactId>
        <version>3.0.3</version>
        <scope>test</scope>
</dependency>

샘플 코드 :

SnowballProgram stemmer = new EnglishStemmer();
String[] words = new String[]{
    "testing",
    "skincare",
    "eyecare",
    "eye",
    "worked",
    "read"
};
for (String word : words) {
    stemmer.setCurrent(word);
    stemmer.stem();
    //debug
    logger.info("Origin: " + word + " > " + stemmer.getCurrent());// result: test, skincar, eyecar, eye, work, read
}

0

여기에서 시도해보세요 : http://www.twinword.com/lemmatizer.php

데모에 쿼리를 입력 하고 선택적 플래그를 "cats running ran cactus cactuses cacti community communities"얻었 ["cat", "running", "run", "cactus", "cactus", "cactus", "community", "community"]습니다 ALL_TOKENS.

샘플 코드

이것은 API이므로 모든 환경에서 연결할 수 있습니다. 다음은 PHP REST 호출의 모습입니다.

// These code snippets use an open-source library. http://unirest.io/php
$response = Unirest\Request::post([ENDPOINT],
  array(
    "X-Mashape-Key" => [API KEY],
    "Content-Type" => "application/x-www-form-urlencoded",
    "Accept" => "application/json"
  ),
  array(
    "text" => "cats running ran cactus cactuses cacti community communities"
  )
);

0

Spacy (기본 텍스트 구문 분석 및 태그 지정)와 Textacy (Spacy 위에 구축 된 고급 텍스트 처리 )를 사용하는 것이 좋습니다 .

기본 화 된 단어 는 기본적으로 Spacy 에서 토큰의 .lemma_속성으로 사용할 수 있으며 텍스트는 텍스트를 사용하여 다른 많은 텍스트 전처리를 수행하는 동안 lemmatized 할 수 있습니다. 예를 들어 용어 또는 단어 모음을 만드는 동안 또는 일반적으로 필요한 일부 처리를 수행하기 직전에.

코드를 작성하기 전에 두 가지를 모두 확인하는 것이 좋습니다. 이렇게하면 많은 시간을 절약 할 수 있습니다!


-1
df_plots = pd.read_excel("Plot Summary.xlsx", index_col = 0)
df_plots
# Printing first sentence of first row and last sentence of last row
nltk.sent_tokenize(df_plots.loc[1].Plot)[0] + nltk.sent_tokenize(df_plots.loc[len(df)].Plot)[-1]

# Calculating length of all plots by words
df_plots["Length"] = df_plots.Plot.apply(lambda x : 
len(nltk.word_tokenize(x)))

print("Longest plot is for season"),
print(df_plots.Length.idxmax())

print("Shortest plot is for season"),
print(df_plots.Length.idxmin())



#What is this show about? (What are the top 3 words used , excluding the #stop words, in all the #seasons combined)

word_sample = list(["struggled", "died"])
word_list = nltk.pos_tag(word_sample)
[wnl.lemmatize(str(word_list[index][0]), pos = word_list[index][1][0].lower()) for index in range(len(word_list))]

# Figure out the stop words
stop = (stopwords.words('english'))

# Tokenize all the plots
df_plots["Tokenized"] = df_plots.Plot.apply(lambda x : nltk.word_tokenize(x.lower()))

# Remove the stop words
df_plots["Filtered"] = df_plots.Tokenized.apply(lambda x : (word for word in x if word not in stop))

# Lemmatize each word
wnl = WordNetLemmatizer()
df_plots["POS"] = df_plots.Filtered.apply(lambda x : nltk.pos_tag(list(x)))
# df_plots["POS"] = df_plots.POS.apply(lambda x : ((word[1] = word[1][0] for word in word_list) for word_list in x))
df_plots["Lemmatized"] = df_plots.POS.apply(lambda x : (wnl.lemmatize(x[index][0], pos = str(x[index][1][0]).lower()) for index in range(len(list(x)))))



#Which Season had the highest screenplay of "Jesse" compared to "Walt" 
#Screenplay of Jesse =(Occurences of "Jesse")/(Occurences of "Jesse"+ #Occurences of "Walt")

df_plots.groupby("Season").Tokenized.sum()

df_plots["Share"] = df_plots.groupby("Season").Tokenized.sum().apply(lambda x : float(x.count("jesse") * 100)/float(x.count("jesse") + x.count("walter") + x.count("walt")))

print("The highest times Jesse was mentioned compared to Walter/Walt was in season"),
print(df_plots["Share"].idxmax())
#float(df_plots.Tokenized.sum().count('jesse')) * 100 / #float((df_plots.Tokenized.sum().count('jesse') + #df_plots.Tokenized.sum().count('walt') + #df_plots.Tokenized.sum().count('walter')))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.