Bagaimana cara melakukan Word Stemming atau Lemmatization?


111

Saya sudah mencoba PorterStemmer dan Snowball tetapi keduanya tidak bekerja pada semua kata, kehilangan beberapa kata yang sangat umum.

Kata-kata pengujian saya adalah: " kucing menjalankan komunitas komunitas komunitas kaktus kaktus kaktus ", dan keduanya mendapatkan kurang dari setengah benar.

Lihat juga:


28
Bukankah itu kaktus?
MSalters

3
Hanya untuk membuat referensi melingkar ke pertanyaan asli yang diposting di Reddit: Bagaimana saya secara terprogram melakukan stemming? (mis. "makan" hingga "makan", "kaktus" hingga "kaktus") Memposting di sini karena di dalam komentar terdapat informasi yang berguna.
Renaud Bompuis

Jawaban:


143

Jika Anda tahu Python, The Natural Language Toolkit (NLTK) memiliki lemmatizer yang sangat kuat yang menggunakan WordNet .

Perhatikan bahwa jika Anda menggunakan lemmatizer ini untuk pertama kali, Anda harus mengunduh korpus sebelum menggunakannya. Ini dapat dilakukan dengan:

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

Anda hanya perlu melakukan ini sekali. Dengan asumsi Anda sekarang telah mengunduh korpus, ini berfungsi seperti ini:

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

Ada lemmatizer lain di modul nltk.stem , tapi saya belum mencobanya sendiri.


11
Oh sedih ... sebelum saya tahu untuk mencari JADI saya menerapkan milik saya sendiri!
Chris Pfohl

12
Jangan lupa install corpusnya sebelum menggunakan nltk untuk pertama kali! velvetcache.org/2010/03/01/…
Mathieu Rodic

1
Nah, yang satu ini menggunakan beberapa algoritma non-deterministik seperti Porter Stemmer, karena jika Anda mencobanya dies, itu memberi Anda dyalih-alih die. Bukankah ada semacam kamus stemmer hardcode?
SexyBeast

3
tahu apa kata-kata yang WordNetLemmatizersalah lemmatize?
alvas

21
nltk WordNetLemmatizer membutuhkan tag pos sebagai argumen. Secara default adalah 'n' (singkatan dari kata benda). Jadi itu tidak akan berfungsi dengan benar untuk kata kerja. Jika tag POS tidak tersedia, pendekatan sederhana (tetapi ad-hoc) adalah melakukan lemmatisasi dua kali, satu untuk 'n', dan yang lainnya untuk 'v' (singkatan dari kata kerja), dan memilih hasil yang berbeda dari kata aslinya (biasanya lebih pendek, tetapi 'lari' dan 'lari' memiliki panjang yang sama). Sepertinya kita tidak perlu khawatir tentang 'adj', 'adv', 'prep', dll, karena mereka sudah dalam bentuk aslinya dalam arti tertentu.
Fashandge

29

Saya menggunakan stanford nlp untuk melakukan lemmatisasi. Saya mengalami masalah serupa dalam beberapa hari terakhir. Semua berkat stackoverflow untuk membantu saya menyelesaikan masalah ini.

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

Mungkin juga merupakan ide yang baik untuk menggunakan stopwords untuk meminimalkan lemma keluaran jika digunakan nanti di pengklasifikasi. Silakan lihat ekstensi coreNlp yang ditulis oleh John Conwell.


maaf atas jawaban yang terlambat .. saya baru menyelesaikan masalah ini sekarang! :)
CTsiddharth

1
Baris 'pipeline = new ...' tidak dapat dikompilasi untuk saya. Jika saya mengubahnya menjadi 'StanfordCoreNLP pipelne = new ...' itu akan dikompilasi. Apakah ini benar?
Adam_G

Ya, Anda harus mendeklarasikan var pipeline terlebih dahulu. Stanford NLP dapat digunakan dari baris perintah juga sehingga Anda tidak perlu melakukan pemrograman apa pun, Anda cukup membuat file properti dan memberi makan file yang dapat dieksekusi dengannya. Baca dokumentasinya: nlp.stanford.edu/software/corenlp.shtml
Jindra Helcl

24

Saya mencoba daftar istilah Anda di situs demo bola salju ini dan hasilnya terlihat oke ....

  • kucing -> kucing
  • lari -> lari
  • lari -> lari
  • kaktus -> kaktus
  • kaktus -> kaktus
  • komunitas -> komuniti
  • komunitas -> komuniti

Sebuah stemmer seharusnya mengubah bentuk kata-kata yang berubah menjadi beberapa akar yang sama. Bukan tugas stemmer untuk membuat root itu menjadi kata kamus yang 'tepat'. Untuk itu Anda perlu mencermati penganalisis morfologi / ortografi .

Saya pikir pertanyaan ini kurang lebih sama, dan jawaban Kaarel untuk pertanyaan itu adalah dari mana saya mengambil tautan kedua.


6
Intinya adalah bahwa stem ("update") == stem ("update"), yang dilakukannya (update ->
updat

1
Perangkat lunak dapat melakukan stem (x) == stem (y) tetapi itu tidak menjawab pertanyaan sepenuhnya
pengguna

11
Hati-hati dengan istilahnya, kata dasar bukanlah bentuk dasar kata. Jika Anda menginginkan bentuk dasar, Anda membutuhkan lemmatizer. Batang adalah bagian terbesar dari sebuah kata yang tidak mengandung prefiks atau sufiks. Batang dari pembaruan kata memang "updat". Kata-kata dibuat dari batang dengan menambahkan akhiran dan sufiks, misalnya updat-e, atau updat-ing. ( en.wikipedia.org/wiki/Word_stem )
Jindra Helcl

20

Perdebatan tentang stemmer vs lemmatizer terus berlanjut. Ini masalah mengutamakan presisi daripada efisiensi. Anda harus berhati-hati untuk mencapai satuan yang bermakna secara linguistik dan tetap menggunakan jus komputasi minimal dan tetap mengindeks sebuah kata dan variasinya di bawah kunci yang sama.

Lihat Stemmers vs Lemmatizers

Berikut contoh dengan 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
Seperti disebutkan sebelumnya, WordNetLemmatizer's lemmatize()dapat mengambil tag POS. Jadi dari contoh Anda: " ".join([wnl.lemmatize(i, pos=VERB) for i in sent.split()])memberi 'cat run run cactus cactuses cacti community communities'.
Nick Ruiz

@NickRuiz, saya pikir maksud Anda pos=NOUN? BTW: Lama tidak bertemu, semoga kita akan bertemu satu sama lain di konferensi segera =)
alvas

sebenarnya, tidak (mudah-mudahan 'ya' untuk konferensi). Karena jika Anda mengatur pos=VERBAnda hanya melakukan lemmatisasi pada kata kerja. Kata benda tetap sama. Saya hanya perlu menulis beberapa kode saya sendiri untuk berputar di sekitar tag POS Penn Treebank yang sebenarnya untuk menerapkan lemmatisasi yang benar ke setiap token. Juga, WordNetLemmatizerbau di tokenizer default nltk lemmatizing. Jadi contoh seperti does n'tjangan lemmatize do not.
Nick Ruiz

tetapi, tetapi port.stem("this")menghasilkan thidan port.stem("was") wa, bahkan ketika posisi yang tepat disediakan untuk masing-masing.
Lerner Zhang

Stemmer tidak mengembalikan keluaran suara secara linguistik. Ini hanya untuk membuat teks lebih "padat" (yaitu mengandung lebih sedikit vocab). Lihat stackoverflow.com/questions/17317418/stemmers-vs-lemmatizers dan stackoverflow.com/questions/51943811/…
alvas

8

Halaman resmi Martin Porter berisi Porter Stemmer dalam PHP serta bahasa lainnya .

Jika Anda benar-benar serius tentang stemming yang baik meskipun Anda akan perlu memulai dengan sesuatu seperti Algoritma Porter, perbaiki dengan menambahkan aturan untuk memperbaiki kasus salah yang umum terjadi pada dataset Anda, dan akhirnya menambahkan banyak pengecualian pada aturan tersebut . Ini dapat dengan mudah diimplementasikan dengan pasangan kunci / nilai (dbm / hash / kamus) di mana kuncinya adalah kata yang akan dicari dan nilainya adalah kata bertangkai untuk menggantikan aslinya. Mesin pencari komersial yang saya kerjakan pernah mendapatkan 800 pengecualian untuk algoritme Porter yang dimodifikasi.


Solusi ideal akan mempelajari ekspektasi ini secara otomatis. Apakah Anda pernah memiliki pengalaman dengan sistem seperti itu?
Malcolm

Tidak. Dalam kasus kami, dokumen yang diindeks adalah kode & peraturan untuk bidang hukum tertentu dan ada lusinan (manusia) editor yang menganalisis indeks untuk menemukan akar yang buruk.
Van Gale


5

Berdasarkan berbagai jawaban di Stack Overflow dan blog yang saya temui, ini adalah metode yang saya gunakan, dan tampaknya mengembalikan kata-kata nyata dengan cukup baik. Idenya adalah untuk membagi teks yang masuk menjadi array kata (gunakan metode mana pun yang Anda suka), dan kemudian temukan parts of speech (POS) untuk kata-kata itu dan gunakan itu untuk membantu membendung dan menyesuaikan kata-kata.

Contoh Anda di atas tidak berfungsi dengan baik, karena POS tidak dapat ditentukan. Namun, jika kita menggunakan kalimat nyata, segalanya bekerja lebih baik.

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

Lihatlah LemmaGen - perpustakaan sumber terbuka yang ditulis dalam C # 3.0.

Hasil untuk kata uji Anda ( http://lemmatise.ijs.si/Services )

  • kucing -> kucing
  • berlari
  • lari -> lari
  • kaktus
  • kaktus -> kaktus
  • kaktus -> kaktus
  • masyarakat
  • komunitas -> komunitas

2

Paket atas python (tanpa urutan tertentu) untuk lemmatization adalah: spacy, nltk, gensim, pattern, CoreNLPdan TextBlob. Saya lebih suka implementasi spaCy dan gensim (berdasarkan pola) karena mereka mengidentifikasi tag POS dari kata tersebut dan memberikan lemma yang sesuai secara otomatis. Itu memberi lemma yang lebih relevan, menjaga maknanya tetap utuh.

Jika Anda berencana menggunakan nltk atau TextBlob, Anda harus berhati-hati dalam menemukan tag POS yang tepat secara manual dan menemukan lemma yang tepat.

Contoh Lemmatisasi dengan 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'

Contoh Lemmatisasi Dengan 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']

Contoh di atas dipinjam dari halaman lemmatisasi ini .


1

Lakukan pencarian untuk Lucene, saya tidak yakin apakah ada port PHP tetapi saya tahu Lucene tersedia untuk banyak platform. Lucene adalah pustaka pengindeksan dan pencarian OSS (dari Apache). Secara alami itu dan ekstra komunitas mungkin memiliki sesuatu yang menarik untuk dilihat. Setidaknya Anda bisa mempelajari bagaimana melakukannya dalam satu bahasa sehingga Anda bisa menerjemahkan "ide" ke dalam PHP


1

Jika saya dapat mengutip jawaban saya untuk pertanyaan yang disebutkan StompChicken:

Masalah inti di sini adalah bahwa algoritma stemming beroperasi pada basis fonetik tanpa pemahaman aktual tentang bahasa yang mereka gunakan.

Karena mereka tidak memiliki pemahaman tentang bahasa dan tidak lari dari kamus istilah, mereka tidak memiliki cara untuk mengenali dan menanggapi dengan tepat kasus yang tidak teratur, seperti "run" / "ran".

Jika Anda perlu menangani kasus yang tidak teratur, Anda harus memilih pendekatan yang berbeda atau menambah stemming Anda dengan kamus koreksi kustom Anda sendiri untuk dijalankan setelah stemmer menyelesaikan tugasnya.



1

Anda bisa menggunakan batang Morpha. UW telah mengunggah morpha stemmer ke pusat Maven jika Anda berencana untuk menggunakannya dari aplikasi Java. Ada pembungkus yang membuatnya lebih mudah digunakan. Anda hanya perlu menambahkannya sebagai dependensi dan menggunakan edu.washington.cs.knowitall.morpha.MorphaStemmerkelasnya. Instance aman untuk thread (JFlex asli memiliki kolom kelas untuk variabel lokal yang tidak perlu). Buat instance kelas dan jalankan morphaserta kata yang ingin Anda akhiri.

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

0

.Net Lucene memiliki porter stemmer bawaan. Anda bisa mencobanya. Tetapi perhatikan bahwa kata dasar porter tidak mempertimbangkan konteks kata saat menurunkan lemma. (Telusuri algoritme dan implementasinya dan Anda akan melihat cara kerjanya)


0

Martin Porter menulis Snowball (bahasa untuk algoritma stemming) dan menulis ulang "English Stemmer" di Snowball. Ada Stemmer Bahasa Inggris untuk C dan Java.

Dia secara eksplisit menyatakan bahwa Porter Stemmer telah diimplementasikan kembali hanya karena alasan historis, jadi pengujian kebenaran stemming terhadap Porter Stemmer akan memberi Anda hasil yang (seharusnya) sudah Anda ketahui.

Dari http://tartarus.org/~martin/PorterStemmer/index.html (penekanan milik saya)

Porter stemmer harus dianggap sebagai ' beku ', yang didefinisikan secara ketat, dan tidak dapat menerima modifikasi lebih lanjut. Sebagai stemmer, ia sedikit lebih rendah daripada stemmer Snowball English atau Porter2, yang berasal darinya, dan yang sesekali mengalami perbaikan. Oleh karena itu, untuk kerja praktek, disarankan menggunakan batang batang Bola Salju yang baru. Porter stemmer sesuai untuk pekerjaan penelitian IR yang melibatkan stemming di mana eksperimen harus benar-benar dapat diulang.

Dr. Porter menyarankan untuk menggunakan stemmer Inggris atau Porter2, bukan stemmer Porter. Stemmer bahasa Inggris adalah apa yang sebenarnya digunakan di situs demo seperti yang telah dijawab @StompChicken sebelumnya.


0

Di Jawa, saya menggunakan tartargus-snowball untuk kata dasar

Maven:

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

Kode sampel:

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

Coba yang ini di sini: http://www.twinword.com/lemmatizer.php

Saya memasukkan kueri Anda di demo "cats running ran cactus cactuses cacti community communities"dan mendapatkan ["cat", "running", "run", "cactus", "cactus", "cactus", "community", "community"]dengan bendera opsional ALL_TOKENS.

Kode sampel

Ini adalah API sehingga Anda dapat menyambungkannya dari lingkungan mana pun. Seperti inilah tampilan panggilan REST PHP.

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

Saya sangat merekomendasikan menggunakan Spacy (parsing & penandaan teks dasar) dan Textacy (pemrosesan teks tingkat tinggi yang dibangun di atas Spacy).

Kata - kata lemmatized tersedia secara default di Spacy sebagai .lemma_atribut token dan teks dapat lemmatized saat melakukan banyak prapemrosesan teks lainnya dengan textacy. Misalnya saat membuat sekantong istilah atau kata atau umumnya tepat sebelum melakukan beberapa pemrosesan yang membutuhkannya.

Saya mendorong Anda untuk memeriksa keduanya sebelum menulis kode apa pun, karena ini dapat menghemat banyak waktu Anda!


-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')))
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.