Kelime Köklemeyi veya Lemmatizasyonu nasıl yaparım?


111

PorterStemmer ve Snowball'u denedim ama her ikisi de tüm kelimeler üzerinde çalışmıyor, çok yaygın olanları kaçırıyor.

Test sözcüklerim: " kaktüs kaktüsleri çalıştıran kediler kaktüsler topluluğu toplulukları " ve her ikisi de yarıdan az hak alıyor.

Ayrıca bakınız:


28
Bunun kaktüs olması gerekmiyor mu?
MSalters

3
Reddit'te yayınlanan orijinal soruya döngüsel bir referans yapmak için: Nasıl programatik olarak kök oluşturmayı yaparım? (ör. "yemek" "yemek", "kaktüsler" ve "kaktüsler") Yorumlar faydalı bilgiler içerdiği için buraya yayınlamak .
Renaud Bompuis

Yanıtlar:


143

Python biliyorsanız, Doğal Dil Toolkit (NLTK) markaları arasında kullandıkları çok güçlü lemmatizer sahiptir WordNet .

Bu tanımlayıcıyı ilk kez kullanıyorsanız, kullanmadan önce külliyatı indirmeniz gerektiğini unutmayın. Bu şu şekilde yapılabilir:

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

Bunu yalnızca bir kez yapmanız gerekir. Derlemeyi indirdiğinizi varsayarsak, şu şekilde çalışır:

>>> 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 modülünde başka tanımlayıcılar da var, ancak onları kendim denemedim.


11
Ah üzücü ... aramayı bilmeden önce SO kendimi uyguladım!
Chris Pfohl

12
Nltk'yi ilk kez kullanmadan önce korpusu kurmayı unutmayın ! velvetcache.org/2010/03/01/…
Mathieu Rodic

1
Bu, Porter Stemmer gibi deterministik olmayan bir algoritma kullanıyor, çünkü eğer denerseniz dies, bunun dyyerine size verir die. Bir tür sabit kodlanmış stemmer sözlüğü yok mu?
SexyBeast

3
WordNetLemmatizerYanlış ifade eden kelimeler hakkında bir fikriniz var mı?
alvas

21
nltk WordNetLemmatizer, bağımsız değişken olarak bir pos etiketi gerektirir. Varsayılan olarak "n" dir (isim yerine geçer). Bu yüzden fiiller için doğru çalışmayacaktır. POS etiketleri mevcut değilse, basit (ancak geçici) bir yaklaşım, biri 'n' ve diğeri 'v' için (fiil yerine) olmak üzere iki kez lemmatizasyon yapmak ve sonuçtan farklı olan sonucu seçmektir. orijinal kelime (genellikle daha kısadır, ancak 'koştu' ve 'koş' aynı uzunluktadır). Görünüşe göre 'adj', 'adv', 'prep' vb. Hakkında endişelenmemize gerek yok, çünkü bunlar zaten bir anlamda orijinal formda.
Fashandge

29

Lemmatizasyon yapmak için stanford nlp kullanıyorum. Son birkaç gündür benzer bir sorunla sıkışıp kaldım. Hepsi sorunu çözmeme yardımcı olan stackoverflow'a teşekkürler.

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

Ayrıca, daha sonra sınıflandırıcıda kullanılırsa, çıktı sözcüklerini en aza indirmek için yok sayılan sözcükler kullanmak iyi bir fikir olabilir. Lütfen John Conwell tarafından yazılan coreNlp uzantısına bir göz atın .


geç cevap verdiğim için özür dilerim .. bu sorunu sadece şimdi çözdüm! :)
CTsiddharth

1
'Pipeline = new ...' satırı benim için derlenmiyor. Bunu 'StanfordCoreNLP pipelne = new ...' olarak değiştirirsem, derler. Bu doğru mu?
Adam_G

Evet, önce boru hattını belirtmelisiniz. Stanford NLP komut satırından da kullanılabilir, böylece herhangi bir programlama yapmanıza gerek kalmaz, sadece özellikler dosyasını oluşturur ve çalıştırılabilir dosyaları onunla beslersiniz. Belgeleri okuyun: nlp.stanford.edu/software/corenlp.shtml
Jindra Helcl

24

Bu kartopu demo sitesinde terimler listenizi denedim ve sonuçlar iyi görünüyor ...

  • kediler -> kedi
  • koşuyor -> koş
  • koştu -> koştu
  • kaktüs -> kaktüs
  • kaktüsler -> kaktüs
  • topluluk -> communiti
  • topluluklar -> communiti

Bir kök tutucunun, çekilmiş sözcük biçimlerini ortak bir köke çevirmesi beklenir. Bu kökü 'uygun' bir sözlük kelimesi haline getirmek gerçekten bir kök uzmanının işi değil. Bunun için morfolojik / ortografik analizörlere bakmanız gerekir .

Sanırım bu soru aşağı yukarı aynı şey hakkında ve Kaarel'in bu soruya cevabı ikinci halkayı nereden aldığım.


6
Mesele şu ki, kök ("güncellemeler") == kök ("güncelleme"), bunu yapar (güncelleme -> güncelleme)
Stompchicken

1
Yazılım, stem (x) == stem (y) yapabilir ancak bu, soruyu tam olarak yanıtlamaz
kullanıcı

11
Dil konusunda dikkatli olun, kelime kökü bir kelimenin temel formu değildir. Temel bir form istiyorsanız, bir anlatıcıya ihtiyacınız var. Kök, bir kelimenin ön ek veya son ek içermeyen en büyük kısmıdır. Bir kelime güncellemesinin kökü gerçekten de "updat" tır. Kelimeler köklerden, sonlar ve son ekler, örneğin updat-e veya updat-ing eklenerek oluşturulur. ( en.wikipedia.org/wiki/Word_stem )
Jindra Helcl

20

Stemmer ve lemmatizer tartışmaları devam ediyor. Verimlilik yerine hassasiyeti tercih etme meselesi. Dilbilimsel olarak anlamlı birimler elde etmek için lemmatize etmeli ve minimum bilgi işlem suyu kullanmak ve yine de bir kelimeyi ve varyasyonlarını aynı anahtar altında indekslemelisiniz.

Bkz Lemmatizers vs köklerini

İşte python NLTK ile bir örnek:

>>> 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
Daha önce belirtildiği gibi, WordNetLemmatizer'in lemmatize()bir POS etiketi alabilir. Yani sizin örneğinizden: " ".join([wnl.lemmatize(i, pos=VERB) for i in sent.split()])verir 'cat run run cactus cactuses cacti community communities'.
Nick Ruiz

@NickRuiz, demek istediğini düşünüyorum pos=NOUN? BTW:
Görüşmeyeli

aslında hayır (Umarım konferanslara 'evet' olur). Çünkü eğer ayarlarsanız pos=VERB, sadece fiiller üzerinde lemmatizasyon yaparsınız. İsimler aynı kalır. Her bir jetona doğru tanımlamayı uygulamak için gerçek Penn Treebank POS etiketleri etrafında dönmek için kendi kodumdan bazılarını yazmam gerekiyordu. Ayrıca, WordNetLemmatizernltk'nin varsayılan belirteçini tanımlamada da kötü kokuyor. Öyleyse, does n'tlemmatize etmeyin gibi örnekler do not.
Nick Ruiz

ama, her biri için doğru konum sağlandığında bile port.stem("this")üretir thive port.stem("was") wa.
Lerner Zhang

Bir kök oluşturucu dilsel olarak sağlam çıktılar döndürmez. Sadece metni daha "yoğun" yapmak için (yani daha az kelime haznesi içerir). Stackoverflow.com/questions/17317418/stemmers-vs-lemmatizers ve stackoverflow.com/questions/51943811/…
alvas

8

Martin Porter'ın resmi sayfası, PHP'de ve diğer dillerde bir Porter Stemmer içerir .

Porter Algoritması gibi bir şeyle başlamanız gerekecek olsa da, iyi saplama konusunda gerçekten ciddiyseniz, veri kümenizde yaygın olan yanlış durumları düzeltmek için kurallar ekleyerek düzeltin ve ardından kurallara birçok istisna ekleyin. . Bu, anahtarın aranacak sözcük olduğu ve değerin orijinalin yerini alacak köklü sözcük olduğu anahtar / değer çiftleriyle (dbm / hash / sözlükler) kolayca uygulanabilir. Bir zamanlar üzerinde çalıştığım ticari bir arama motoru, değiştirilmiş bir Porter algoritmasına 800 bazı istisna ile sonuçlandı.


İdeal bir çözüm, bu beklentileri otomatik olarak öğrenecektir. Böyle bir sistemle herhangi bir deneyiminiz oldu mu?
Malcolm

Hayır. Bizim durumumuzda, indekslenen belgeler belirli bir hukuk alanı için kod ve yönetmeliklerdi ve herhangi bir kötü kaynak için indeksleri analiz eden düzinelerce (insan) editör vardı.
Van Gale


5

Stack Overflow ve karşılaştığım bloglardaki çeşitli cevaplara dayanarak, kullandığım yöntem bu ve gerçek kelimeleri oldukça iyi döndürüyor gibi görünüyor. Buradaki fikir, gelen metni bir dizi kelimeye bölmek (istediğiniz yöntemi kullanın) ve ardından bu kelimeler için konuşma bölümlerini (POS) bulmak ve bunu kelimelerin kökleşmesine ve anlamlandırılmasına yardımcı olmak için kullanmaktır.

Yukarıdaki örneğiniz çok iyi çalışmıyor çünkü POS belirlenemiyor. Ancak gerçek bir cümle kullanırsak işler çok daha iyi sonuç verir.

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'e bir göz atın - C # 3.0 ile yazılmış açık kaynak kitaplığı.

Test kelimelerinizin sonuçları ( http://lemmatise.ijs.si/Services )

  • kediler -> kedi
  • çalışan
  • koştu -> koş
  • kaktüs
  • kaktüsler -> kaktüs
  • kaktüsler -> kaktüs
  • topluluk
  • topluluklar -> topluluk

2

Lemmatization için (belirli bir sırayla) üst piton paketleri şunlardır: spacy, nltk, gensim, pattern, CoreNLPveTextBlob . Ben spaCy ve gensim uygulamasını tercih ediyorum (kalıba göre) çünkü kelimenin POS etiketini tanımlıyorlar ve uygun lemmayı otomatik olarak atıyorlar. , Anlamı olduğu gibi koruyarak daha alakalı lemmalar verir.

Nltk veya TextBlob'u kullanmayı planlıyorsanız, doğru POS etiketini manuel olarak ve doğru lemmayı bulmaya dikkat etmeniz gerekir.

SpaCy ile Lemmatizasyon Örneği:

# 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 ile Lemmatizasyon Örneği:

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

Yukarıdaki örnekler bu tanıma sayfasından ödünç alınmıştır .


1

Lucene için bir arama yapın, bir PHP bağlantı noktası olup olmadığından emin değilim, ancak Lucene'nin birçok platform için kullanılabilir olduğunu biliyorum. Lucene, bir OSS (Apache'den) indeksleme ve arama kütüphanesidir. Doğal olarak o ve topluluk ekstralarının bakması ilginç bir şey olabilir. En azından tek bir dilde nasıl yapıldığını öğrenebilir, böylece "fikri" PHP'ye çevirebilirsiniz.


1

StompChicken'ın bahsettiği soruya cevabımı aktarabilirsem:

Buradaki temel sorun, kök belirleme algoritmalarının, üzerinde çalıştıkları dile ilişkin gerçek bir anlayış olmadan fonetik bir temelde çalışmasıdır.

Dili anlamadıklarından ve bir terimler sözlüğünden çalıştırılmadıklarından, "koşma" / "koşma" gibi düzensiz durumları tanıma ve bunlara uygun şekilde yanıt vermenin hiçbir yolu yoktur.

Düzensiz vakalarla ilgilenmeniz gerekiyorsa, ya farklı bir yaklaşım seçmeniz ya da kök oluşturucu işini yaptıktan sonra çalıştırmak için kendi özel düzeltme sözlüğünüzle kök oluşturmanızı genişletmeniz gerekir.



1

Morpha stemmer'ı kullanabilirsiniz. Bir Java uygulamasından kullanmayı planlıyorsanız UW, morpha stemmer'ı Maven merkezine yükledi . Kullanımı çok daha kolay hale getiren bir ambalaj var. Bunu bir bağımlılık olarak eklemeniz ve edu.washington.cs.knowitall.morpha.MorphaStemmersınıfı kullanmanız yeterlidir . Örnekler iş parçacıklıdır (orijinal JFlex, yerel değişkenler için gereksiz yere sınıf alanlarına sahipti). Bir sınıfın örneğini oluşturun ve koşmak morphaistediğiniz kelimeyi oluşturun.

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

0

.Net lucene'de dahili bir taşıyıcı saplayıcı vardır. Bunu deneyebilirsin. Ancak hamal kök bulmanın lemmayı türetirken kelime bağlamını dikkate almadığını unutmayın. (Algoritmayı ve uygulamasını gözden geçirin ve nasıl çalıştığını göreceksiniz)


0

Martin Porter, Snowball'u (kök belirleme algoritmaları için bir dil) yazdı ve Snowball'daki "English Stemmer" ı yeniden yazdı. C ve Java için bir İngiliz Stemmer var.

Porter Stemmer'ın yalnızca tarihsel nedenlerle yeniden uygulandığını açıkça belirtiyor , bu nedenle Porter Stemmer'e karşı köklü doğruluğu test etmek size zaten bilmeniz gereken (yapmanız gereken) sonuçlar verecektir.

Http://tartarus.org/~martin/PorterStemmer/index.html adresinden (vurgu benim)

Porter stemmer, ' donmuş ', yani kesinlikle tanımlanmış ve daha fazla değişikliğe tabi tutulamaz olarak görülmelidir . Bir kök oluşturucu olarak, ondan türetilen ve ara sıra iyileştirmelere tabi tutulan Snowball English veya Porter2 stemmer'dan biraz daha düşüktür. Pratik çalışma için, bu nedenle, yeni Kartopu saplayıcı tavsiye edilir. Porter stemmer, deneylerin tam olarak tekrarlanabilir olması gereken yerlerde kök belirlemeyi içeren IR araştırma çalışmaları için uygundur.

Dr. Porter, Porter kök tutucusu yerine English veya Porter2 saplarını kullanmayı önerir. @StompChicken daha önce cevapladığı gibi , demo sitesinde aslında kullanılan İngilizce stemmer'tir.


0

Java'da kelimelerin kökünü oluşturmak için tartargus-kartopu kullanıyorum

Uzman:

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

Basit 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

Bunu burada deneyin: http://www.twinword.com/lemmatizer.php

Sorgunuzu demoya "cats running ran cactus cactuses cacti community communities"girdim ["cat", "running", "run", "cactus", "cactus", "cactus", "community", "community"]ve isteğe bağlı bayrağı aldım ALL_TOKENS.

Basit kod

Bu bir API'dir, böylece ona herhangi bir ortamdan bağlanabilirsiniz. PHP REST çağrısı şöyle görünebilir.

// 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 (temel metin ayrıştırma ve etiketleme) ve Textacy kullanmanızı şiddetle tavsiye ederim (Spacy üzerine inşa edilmiş daha yüksek seviyeli metin işleme .

Ön tanımlı sözcükler , bir belirteç .lemma_özelliği olarak Spacy'de varsayılan olarak mevcuttur ve metin, metinle diğer birçok metin önişlemesi yapılırken metne aktarılabilir. Örneğin , bir dizi terim veya kelime oluştururken veya genellikle onu gerektiren bazı işlemleri gerçekleştirmeden hemen önce.

Herhangi bir kod yazmadan önce her ikisini de kontrol etmenizi öneririm, çünkü bu size çok zaman kazandırabilir!


-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')))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.