Jak mam wywodzić słowa lub lematyzować?


111

Wypróbowałem PorterStemmer i Snowball, ale oba nie działają na wszystkich słowach, brakuje niektórych bardzo popularnych.

Moje słowa testowe to: „ koty biegają w społecznościach kaktusów kaktusów i kaktusów ” i obie mają mniej niż połowę racji.

Zobacz też:


28
Czy to nie powinny być kaktusy?
MSalters

3
Wystarczy nawiązać do pierwotnego pytania opublikowanego na Reddicie: Jak programowo wyprowadzać? (np. „jeść”, „jeść”, „kaktusy”, „kaktus”) Publikowanie tego tutaj, ponieważ komentarze zawierają przydatne informacje.
Renaud Bompuis

Odpowiedzi:


143

Jeśli znasz Pythona, zestaw narzędzi języka naturalnego (NLTK) ma bardzo potężny lematyzator, który korzysta z WordNet .

Zwróć uwagę, że jeśli używasz tego lemmatyzatora po raz pierwszy, musisz pobrać korpus przed jego użyciem. Można to zrobić poprzez:

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

Musisz to zrobić tylko raz. Zakładając, że pobrałeś teraz korpus, działa to tak:

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

W module nltk.stem są inne lematyzatory , ale sam ich nie wypróbowałem.


11
O smutno ... zanim zdążyłem szukać, więc zaimplementowałem własne!
Chris Pfohl,

12
Nie zapomnij zainstalować korpusu przed pierwszym użyciem nltk ! velvetcache.org/2010/03/01/...
Mathieu Rodic

1
Cóż, ten używa niedeterministycznego algorytmu, takiego jak Porter Stemmer, ponieważ jeśli spróbujesz go z dies, daje dyzamiast die. Czy nie ma jakiegoś zakodowanego na stałe słownika stemmera?
SexyBeast

3
masz pojęcie, jakie słowa WordNetLemmatizerbłędnie lematyzują?
alvas

21
nltk WordNetLemmatizer wymaga znacznika pos jako argumentu. Domyślnie jest to „n” (oznaczające rzeczownik). Więc nie będzie działać poprawnie dla czasowników. Jeśli tagi POS nie są dostępne, prostym (ale ad-hoc) podejściem jest dwukrotne wykonanie lematyzacji, jeden dla „n”, a drugi dla „v” (oznaczającego czasownik) i wybranie wyniku innego niż oryginalne słowo (zwykle krótsze, ale „bieg” i „bieg” mają tę samą długość). Wygląda na to, że nie musimy się martwić o „adj”, „adv”, „prep” itp., Ponieważ w pewnym sensie są one już w oryginalnej formie.
Fashandge

29

Używam Stanford nlp do wykonywania lematyzacji. W ciągu ostatnich kilku dni miałem podobny problem. Wszystko dzięki stackoverflow, które pomogło mi rozwiązać problem.

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

Dobrym pomysłem może być również użycie pomijanych słów, aby zminimalizować lematy wyjściowe, jeśli są używane później w klasyfikatorze. Proszę spojrzeć na rozszerzenie coreNlp napisane przez Johna Conwella.


przepraszam za spóźnioną odpowiedź .. ten problem został rozwiązany dopiero teraz! :)
CTsiddharth

1
Linia „pipeline = new ...” nie jest kompilowana. Jeśli zmienię go na „StanfordCoreNLP pipelne = new ...”, kompiluje się. Czy to prawda?
Adam_G,

Tak, najpierw musisz zadeklarować potok var. Stanford NLP może być również używany z wiersza poleceń, więc nie musisz programować, po prostu tworzysz plik właściwości i zasilasz nim pliki wykonywalne. Przeczytaj dokumenty: nlp.stanford.edu/software/corenlp.shtml
Jindra Helcl

24

Wypróbowałem twoją listę terminów w tej witrynie demonstracyjnej o śnieżkach i wyniki wyglądają w porządku ...

  • koty -> kot
  • bieganie -> bieganie
  • run -> run
  • kaktus -> kaktus
  • kaktusy -> kaktusy
  • społeczność -> communiti
  • społeczności -> communiti

Stymulator ma sprowadzić odmienione formy słów do jakiegoś wspólnego rdzenia. Tak naprawdę nie jest zadaniem stempla, aby uczynić z tego korzenia „właściwe” słowo słownikowe. W tym celu należy spojrzeć na analizatory morfologiczne / ortograficzne .

Myślę, że to pytanie dotyczy mniej więcej tego samego, a odpowiedź Kaarela na to pytanie brzmi, skąd wziąłem drugie łącze.


6
Chodzi o to, że stem ("updates") == stem ("update"), co robi (update ->
updat

1
Oprogramowanie może wykonać stem (x) == stem (y), ale to nie odpowiada całkowicie na pytanie
użytkownik

11
Ostrożnie z żargonem, rdzeń nie jest podstawową formą słowa. Jeśli chcesz mieć formę podstawową, potrzebujesz lematyzatora. Temat to największa część słowa, która nie zawiera przedrostków ani przyrostków. Tematem aktualizacji słowa jest rzeczywiście „updat”. Słowa są tworzone z rodów poprzez dodanie końcówek i przyrostków, np. Updat-e lub updat-ing. ( en.wikipedia.org/wiki/Word_stem )
Jindra Helcl,

20

Trwają debaty stemmer vs lemmatizer. To kwestia preferowania precyzji nad wydajnością. Powinieneś lematyzować, aby uzyskać jednostki znaczące językowo i wywodzić się z użycia minimalnego soku obliczeniowego i nadal indeksować słowo i jego odmiany pod tym samym kluczem.

Zobacz Stemmers vs Lemmatizers

Oto przykład z pythonem 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
Jak wspomniano wcześniej, WordNetLemmatizerjest lemmatize()może tag POS. A więc z twojego przykładu: " ".join([wnl.lemmatize(i, pos=VERB) for i in sent.split()])daje 'cat run run cactus cactuses cacti community communities'.
Nick Ruiz

@NickRuiz, myślę, że miałeś na myśli pos=NOUN? BTW: Dawno się nie widzieliśmy, miejmy nadzieję, że wkrótce spotkamy się na konferencji =)
alvas

właściwie nie (mam nadzieję, że „tak” na konferencje). Ponieważ jeśli ustawisz, pos=VERBbędziesz lematyzować tylko czasowniki. Rzeczowniki pozostają takie same. Musiałem tylko napisać własny kod, aby obrócić się wokół rzeczywistych znaczników punktu sprzedaży Penn Treebank, aby zastosować poprawną lematyzację do każdego tokena. Ponadto WordNetLemmatizerśmierdzi lematyzowaniem domyślnego tokenizera nltk. Tak więc przykłady takie jak does n'tnie lematyzują do not.
Nick Ruiz

ale, ale port.stem("this")produkuje thii port.stem("was") wa, nawet jeśli dla każdego zapewniona jest właściwa pozycja.
Lerner Zhang

Stemmer nie zwraca dźwięków językowych. Chodzi tylko o to, aby tekst był bardziej „gęsty” (tj. Zawierał mniej słownictwa). Zobacz stackoverflow.com/questions/17317418/stemmers-vs-lemmatizers i stackoverflow.com/questions/51943811/…
alvas

8

Oficjalna strona Martina Portera zawiera Porter Stemmer w PHP, a także w innych językach .

Jeśli naprawdę poważnie myślisz o dobrym pochodzeniu, ale będziesz musiał zacząć od czegoś takiego jak Algorytm Portera, udoskonal go, dodając reguły, aby naprawić nieprawidłowe przypadki typowe dla zbioru danych, a na koniec dodaj wiele wyjątków od reguł . Można to łatwo zaimplementować za pomocą par klucz / wartość (dbm / hash / dictionaries), w których klucz jest słowem do wyszukania, a wartością jest słowo macierzyste, które ma zastąpić oryginał. Komercyjna wyszukiwarka, nad którą kiedyś pracowałem, zakończyła się 800 wyjątkami od zmodyfikowanego algorytmu Portera.


Idealnym rozwiązaniem byłoby automatyczne rozpoznanie tych oczekiwań. Czy miałeś jakieś doświadczenia z takim systemem?
Malcolm,

Nie. W naszym przypadku indeksowane dokumenty stanowiły kodeks i przepisy z określonej dziedziny prawa, a dziesiątki (ludzkich) redaktorów analizowało indeksy pod kątem nieprawidłowych tematów.
Van Gale,


5

Opierając się na różnych odpowiedziach na Stack Overflow i blogach, z którymi się spotkałem, używam tej metody i wydaje się, że całkiem dobrze zwraca prawdziwe słowa. Chodzi o to, aby podzielić przychodzący tekst na tablicę słów (użyj dowolnej metody), a następnie znaleźć części mowy (POS) dla tych słów i użyć ich, aby pomóc wyprowadzić i lematyzować słowa.

Twoja próbka powyżej nie działa zbyt dobrze, ponieważ nie można określić POS. Jeśli jednak użyjemy prawdziwego zdania, wszystko działa znacznie lepiej.

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

Przyjrzyj się LemmaGen - bibliotece open source napisanej w C # 3.0.

Wyniki dla słów testowych ( http://lemmatise.ijs.si/Services )

  • koty -> kot
  • bieganie
  • run -> run
  • kaktus
  • kaktusy -> kaktusy
  • kaktusy -> kaktusy
  • społeczność
  • wspólnoty -> społeczność

2

Pakiety top python (w żadnym określonym porządku) dla lemmatization to: spacy, nltk, gensim, pattern, CoreNLPi TextBlob. Preferuję implementację spaCy i gensim (opartą na wzorcu), ponieważ identyfikują one tag POS słowa i automatycznie przypisują odpowiedni lemat. Daje bardziej trafne lematy, zachowując znaczenie nietknięte.

Jeśli planujesz używać nltk lub TextBlob, musisz zadbać o ręczne znalezienie odpowiedniego tagu POS i znalezienie odpowiedniego lematu.

Przykład lematyzacji za pomocą 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'

Przykład lematyzacji z 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']

Powyższe przykłady zostały zapożyczone z tej strony lematyzacyjnej .


1

Poszukaj Lucene, nie jestem pewien, czy istnieje port PHP, ale wiem, że Lucene jest dostępny na wiele platform. Lucene to biblioteka do indeksowania i wyszukiwania OSS (z Apache). Oczywiście to i dodatki społeczności mogą mieć coś interesującego do obejrzenia. Możesz przynajmniej nauczyć się, jak to się robi w jednym języku, aby przetłumaczyć „pomysł” na PHP


1

Jeśli mogę zacytować moją odpowiedź na pytanie, o którym wspomniał StompChicken:

Podstawowym problemem jest to, że algorytmy rdzenia działają na podstawie fonetycznej bez faktycznego zrozumienia języka, z którym pracują.

Ponieważ nie rozumieją języka i nie korzystają ze słownika terminów, nie mają możliwości rozpoznania i odpowiedniego zareagowania na nieregularne przypadki, takie jak „run” / „run”.

Jeśli chcesz poradzić sobie z nieregularnymi przypadkami, musisz albo wybrać inne podejście, albo rozszerzyć swoje wyprowadzanie za pomocą własnego niestandardowego słownika poprawek, aby uruchomić po tym, jak stemmer zrobi swoje.



1

Możesz użyć łodygi Morpha. UW przesłał program sterujący morfą do Maven Central, jeśli planujesz używać go z aplikacji Java. Jest opakowanie, które znacznie ułatwia korzystanie. Wystarczy dodać ją jako zależność i użyć edu.washington.cs.knowitall.morpha.MorphaStemmerklasy. Instancje są wątkowo bezpieczne (oryginalny JFlex miał niepotrzebnie pola klas dla zmiennych lokalnych). Utwórz instancję klasy i uruchom morphaoraz słowo, które chcesz macierzyste.

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

0

.Net Lucene ma wbudowaną łodygę portera. Możesz tego spróbować. Należy jednak pamiętać, że wyprowadzanie portera nie bierze pod uwagę kontekstu słów podczas wyprowadzania lematu. (Przejdź przez algorytm i jego implementację, a zobaczysz, jak to działa)


0

Martin Porter napisał Snowball (język do algorytmów rymowania) i przepisał „English Stemmer” w Snowball. Istnieje angielski Stemmer dla C i Java.

Wyraźnie stwierdza, że ​​Porter Stemmer został ponownie zaimplementowany tylko z powodów historycznych, więc testowanie wynikającej poprawności w porównaniu z Porter Stemmer da wyniki, które (powinieneś) już znać.

Z http://tartarus.org/~martin/PorterStemmer/index.html (moje wyróżnienie)

Lody Porter należy traktować jako „ zamrożone ”, to znaczy ściśle zdefiniowane i niepodlegające dalszym modyfikacjom. Jako stemmer jest nieco gorszy od stemmera Snowball English lub Porter2, który pochodzi od niego i który jest poddawany okazjonalnym ulepszeniom. Dlatego do praktycznej pracy zalecany jest nowy mostek Snowball. Łodyga Portera jest odpowiednia do prac badawczych w podczerwieni polegających na wyprowadzaniu, gdzie eksperymenty muszą być dokładnie powtarzalne.

Dr Porter sugeruje użycie stempli angielskich lub Porter2 zamiast młotka Porter. Angielski stemmer jest tym, co faktycznie jest używane w witrynie demonstracyjnej, na co @StompChicken odpowiedział wcześniej.


0

W Javie używam tartargus-snowball do słów kluczowych

Maven:

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

Przykładowy kod:

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

Spróbuj tego tutaj: http://www.twinword.com/lemmatizer.php

Wprowadziłem twoje zapytanie w demo "cats running ran cactus cactuses cacti community communities"i otrzymałem ["cat", "running", "run", "cactus", "cactus", "cactus", "community", "community"]z opcjonalną flagą ALL_TOKENS.

Przykładowy kod

To jest API, więc możesz się z nim łączyć z dowolnego środowiska. Oto, jak może wyglądać wywołanie 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

Gorąco polecam używanie Spacy (bazowe parsowanie i tagowanie tekstu) i Textacy (przetwarzanie tekstu wyższego poziomu oparte na Spacy).

Lemmatyzowane słowa są domyślnie dostępne w Spacy jako .lemma_atrybut tokena, a tekst może być lematyzowany podczas wykonywania wielu innych operacji wstępnego przetwarzania tekstu z tekstem. Na przykład podczas tworzenia zbioru terminów lub słów lub ogólnie tuż przed wykonaniem przetwarzania, które tego wymaga.

Zachęcam do zapoznania się z obydwoma przed napisaniem kodu, ponieważ może to zaoszczędzić sporo czasu!


-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')))
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.