Como faço palavra Stemming ou Lemmatization?

111

Eu tentei PorterStemmer e Snowball, mas ambos não funcionam em todas as palavras, perdendo algumas muito comuns.

Minhas palavras de teste são: " gatos administrando comunidades de cactos cactos cactos ", e ambas acertam menos da metade.

Veja também:

Manixrock
fonte
28
Não deveriam ser cactos?
MSalters em
3
Apenas para fazer uma referência circular à pergunta original postada no Reddit: Como faço para programar a lematização? (por exemplo, "comer" para "comer", "cactos" para "cactos") Poste aqui porque os comentários incluem informações úteis.
Renaud Bompuis

Respostas:

143

Se você conhece Python, o The Natural Language Toolkit (NLTK) tem um lematizador muito poderoso que usa WordNet .

Observe que se você estiver usando este lematizador pela primeira vez, você deve baixar o corpus antes de usá-lo. Isso pode ser feito por:

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

Você só tem que fazer isso uma vez. Supondo que você já tenha baixado o corpus, funciona assim:

>>> 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'

Existem outros lematizadores no módulo nltk.stem , mas não os tentei sozinho.

eles chamam de morte
fonte
11
Que pena ... antes de saber como pesquisar, implementei o meu!
Chris Pfohl
12
Não se esqueça de instalar o corpus antes de usar o nltk pela primeira vez! velvetcache.org/2010/03/01/…
Mathieu Rodic
1
Bem, este usa algum algoritmo não determinístico como Porter Stemmer, pois se você tentar com dies, ele lhe dará ao dyinvés de die. Não existe algum tipo de dicionário de lematizador codificado?
SexyBeast
3
alguma ideia quais são as palavras que WordNetLemmatizererroneamente lematizam?
alvas
21
nltk WordNetLemmatizer requer uma tag pos como argumento. Por padrão, é 'n' (que significa substantivo). Portanto, não funcionará corretamente para verbos. Se as tags POS não estiverem disponíveis, uma abordagem simples (mas ad-hoc) é fazer a lematização duas vezes, uma para 'n' e outra para 'v' (que significa verbo) e escolher o resultado que é diferente do palavra original (geralmente mais curta em comprimento, mas 'correu' e 'correu' têm o mesmo comprimento). Parece que não precisamos nos preocupar com 'adj', 'adv', 'prep', etc, pois eles já estão na forma original em algum sentido.
Fashandge
29

Eu uso Stanford nlp para fazer lematização. Tenho enfrentado um problema semelhante nos últimos dias. Tudo graças ao stackoverflow para me ajudar a resolver o problema.

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);
            }
        }
    }
}

Também pode ser uma boa ideia usar palavras irrelevantes para minimizar lemas de saída se forem usadas posteriormente no classificador. Por favor, dê uma olhada na extensão coreNlp escrita por John Conwell.

CTsiddharth
fonte
desculpe pela resposta tardia .. eu tenho esse problema resolvido apenas agora! :)
CTsiddharth
1
A linha 'pipeline = new ...' não compila para mim. Se eu alterar para 'StanfordCoreNLP pipelne = new ...', ele compila. Está correto?
Adam_G
Sim, você deve declarar o pipeline var primeiro. O Stanford NLP também pode ser usado a partir da linha de comando, então você não precisa fazer nenhuma programação, basta criar o arquivo de propriedades e alimentar os executáveis ​​com ele. Leia os documentos: nlp.stanford.edu/software/corenlp.shtml
Jindra Helcl
24

Tentei sua lista de termos neste site de demonstração de bola de neve e os resultados parecem bons ...

  • gatos -> gato
  • correndo -> corra
  • correu -> correu
  • cacto -> cacto
  • cactos -> cacto
  • comunidade -> comunidade
  • comunidades -> comunidades

Um lematizador deve transformar as formas flexionadas das palavras em alguma raiz comum. Não é realmente o trabalho de um lematizador fazer dessa raiz uma palavra de dicionário "adequada". Para isso, você precisa olhar para analisadores morfológicos / ortográficos .

Acho que essa pergunta é mais ou menos a mesma coisa, e a resposta de Kaarel a essa pergunta é de onde peguei o segundo link.

Stompchicken
fonte
6
O ponto é esse radical ("updates") == stem ("update"), o que ele faz (update -> updat)
Stompchicken
1
O software pode fazer stem (x) == stem (y), mas isso não está respondendo a pergunta completamente
usuário
11
Cuidado com a linguagem, um radical não é a forma básica de uma palavra. Se você quer um formulário básico, você precisa de um lematizador. Um radical é a maior parte de uma palavra que não contém prefixos ou sufixos. O radical de uma palavra atualização é de fato "atualizar". As palavras são criadas a partir de radicais, adicionando terminações e sufixos, por exemplo, updat-e ou atualizando ( en.wikipedia.org/wiki/Word_stem )
Jindra Helcl
20

Os debates lematizador vs lematizador continuam. É uma questão de preferir a precisão à eficiência. Você deve lematizar para obter unidades e radical lingüisticamente significativas para usar o mínimo de energia de computação e ainda indexar uma palavra e suas variações sob a mesma chave.

Vejo Stemmers vs Lemmatizers

Aqui está um exemplo com 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'
Alvas
fonte
3
Como mencionado antes, WordNetLemmatizer's lemmatize()pode ter um tag POS. Então, do seu exemplo: " ".join([wnl.lemmatize(i, pos=VERB) for i in sent.split()])'cat run run cactus cactuses cacti community communities'.
Nick Ruiz
@NickRuiz, acho que você quis dizer pos=NOUN? BTW: Há quanto tempo, espero que nos encontremos na conferência em breve =)
alvas
na verdade, não (espero que sim para conferências, no entanto). Porque se você definir pos=VERBvocê só faz a lematização dos verbos. Os substantivos permanecem os mesmos. Eu só tive que escrever um pouco do meu próprio código para girar em torno das tags POS Penn Treebank reais para aplicar a lematização correta a cada token. Além disso, WordNetLemmatizerfede em lematizar o tokenizer padrão do nltk. Portanto, exemplos como does n'tnão lematizar para do not.
Nick Ruiz
mas, mas port.stem("this")produz thie port.stem("was") wa, mesmo quando a posição certa é fornecida para cada um.
Lerner Zhang
Um lematizador não retorna saídas de som linguisticamente. É apenas para tornar o texto mais "denso" (ou seja, conter menos vocabulário). Consulte stackoverflow.com/questions/17317418/stemmers-vs-lemmatizers e stackoverflow.com/questions/51943811/…
alvas
8

A página oficial de Martin Porter contém um Porter Stemmer em PHP e outras linguagens .

Se você realmente quer uma boa lematização, vai precisar começar com algo como o Algoritmo de Porter, refine-o adicionando regras para corrigir casos incorretos comuns ao seu conjunto de dados e, finalmente, adicione muitas exceções às regras . Isso pode ser facilmente implementado com pares de chave / valor (dbm / hash / dicionários) onde a chave é a palavra a ser pesquisada e o valor é a palavra raiz para substituir o original. Um mecanismo de pesquisa comercial em que trabalhei uma vez acabou com 800 algumas exceções a um algoritmo de Porter modificado.

Van Gale
fonte
Uma solução ideal aprenderia essas expectativas automaticamente. Você já teve alguma experiência com esse sistema?
Malcolm
Não. No nosso caso, os documentos sendo indexados eram o código e os regulamentos de uma área específica do direito e havia dezenas de editores (humanos) analisando os índices em busca de hastes ruins.
Van Gale
5

Com base em várias respostas no Stack Overflow e em blogs que encontrei, este é o método que estou usando e parece retornar palavras reais muito bem. A ideia é dividir o texto recebido em uma série de palavras (use o método que desejar) e, em seguida, encontrar as classes gramaticais (POS) para essas palavras e usá-las para ajudar a drenar e lematizar as palavras.

Sua amostra acima não funciona muito bem, porque o POS não pode ser determinado. No entanto, se usarmos uma frase real, as coisas funcionam muito melhor.

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', '.']
cjbarth
fonte
3

Procure no WordNet, um grande banco de dados lexical para o idioma inglês:

http://wordnet.princeton.edu/

Existem APIs para acessá-lo em vários idiomas.

Ricardo J. Méndez
fonte
2

Dê uma olhada no LemmaGen - biblioteca de código aberto escrita em C # 3.0.

Resultados para suas palavras de teste ( http://lemmatise.ijs.si/Services )

  • gatos -> gato
  • corrida
  • correu -> corre
  • cacto
  • cactos -> cacto
  • cactos -> cactos
  • comunidade
  • comunidades -> comunidade
Alex
fonte
2

Os pacotes de topo Python (em nenhuma ordem específica) para lematização são: spacy, nltk, gensim, pattern, CoreNLPe TextBlob. Eu prefiro a implementação de spaCy e gensim (com base no padrão) porque eles identificam a marca POS da palavra e atribuem o lema apropriado automaticamente. O fornece lemas mais relevantes, mantendo o significado intacto.

Se você planeja usar nltk ou TextBlob, você precisa se preocupar em encontrar a tag POS certa manualmente e encontrar o lema certo.

Exemplo de lematização com 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'

Exemplo de lematização com 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']

Os exemplos acima foram emprestados desta página de lematização .

Selva
fonte
1

Faça uma pesquisa por Lucene, não tenho certeza se existe uma porta PHP, mas sei que Lucene está disponível para muitas plataformas. Lucene é uma biblioteca de indexação e pesquisa OSS (da Apache). Naturalmente, ele e os extras da comunidade podem ter algo interessante para observar. No mínimo você pode aprender como é feito em uma linguagem para que possa traduzir a "ideia" para PHP

mP.
fonte
1

Se eu puder citar minha resposta à pergunta que StompChicken mencionou:

A questão central aqui é que os algoritmos de lematização operam em uma base fonética, sem nenhuma compreensão real da linguagem com a qual estão trabalhando.

Como não entendem a língua e não fogem de um dicionário de termos, não têm como reconhecer e responder adequadamente aos casos irregulares, como "correu" / "correu".

Se precisar lidar com casos irregulares, você precisará escolher uma abordagem diferente ou aumentar sua lematização com seu próprio dicionário personalizado de correções para executar depois que o lematizador tiver feito seu trabalho.

Dave Sherohman
fonte
1

Você pode usar o lematizador Morpha. UW carregou morpha stemmer no Maven central se você planeja usá-lo em um aplicativo Java. Existe um invólucro que o torna muito mais fácil de usar. Você só precisa adicioná-lo como uma dependência e usar a edu.washington.cs.knowitall.morpha.MorphaStemmerclasse. As instâncias são threadsafe (o JFlex original tinha campos de classe para variáveis ​​locais desnecessariamente). Instancie uma classe e execute morphaa palavra que deseja derivar.

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

.Net lucene tem um lematizador de Porter embutido. Você pode tentar isso. Mas observe que o radical de Porter não considera o contexto da palavra ao derivar o lema. (Passe pelo algoritmo e sua implementação e você verá como funciona)

Erik
fonte
0

Martin Porter escreveu Snowball (uma linguagem para algoritmos de lematização) e reescreveu o "English Stemmer" em Snowball. Existe um Stemmer em inglês para C e Java.

Ele afirma explicitamente que o Porter Stemmer foi reimplementado apenas por razões históricas, portanto, testar a correção de stemming com o Porter Stemmer obterá resultados que você (deveria) já conhecer.

De http://tartarus.org/~martin/PorterStemmer/index.html (ênfase minha)

O lematizador Porter deve ser considerado ' congelado ', isto é, estritamente definido e não passível de modificações posteriores. Como lematizador, é ligeiramente inferior ao lematizador Snowball English ou Porter2, que deriva dele e que está sujeito a melhorias ocasionais. Para o trabalho prático, portanto, a nova haste do Snowball é recomendada. O stemmer Porter é apropriado para trabalho de pesquisa de IR envolvendo stemming onde os experimentos precisam ser exatamente repetidos.

Dr. Porter sugere usar o lematizador inglês ou Porter2 em vez do lematizador Porter. O lematizador em inglês é o que realmente é usado no site de demonstração, como @StompChicken respondeu anteriormente.

Oerd
fonte
0

Em Java, eu uso tartargus-snowball para derivar palavras

Maven:

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

Código de amostra:

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
}
Tho
fonte
0

Experimente este aqui: http://www.twinword.com/lemmatizer.php

Entrei sua consulta no demo "cats running ran cactus cactuses cacti community communities"e obtive ["cat", "running", "run", "cactus", "cactus", "cactus", "community", "community"]com o flag opcional ALL_TOKENS.

Código de amostra

Esta é uma API para que você possa se conectar a ela de qualquer ambiente. Esta é a aparência da chamada 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"
  )
);
Joseph Shih
fonte
0

Eu recomendo fortemente o uso de Spacy (análise e marcação de texto base) e Textacy (processamento de texto de nível superior construído em cima do Spacy).

Palavras lematizadas estão disponíveis por padrão no Spacy como um .lemma_atributo de token e o texto pode ser lematizado ao fazer muitos outros pré-processamento de texto com textacy. Por exemplo, ao criar um pacote de termos ou palavras ou, geralmente, antes de executar algum processamento que o exija.

Recomendo que você verifique os dois antes de escrever qualquer código, pois isso pode economizar muito tempo!

Coletivo de QA
fonte
-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')))
avi
fonte