単語の語幹解釈または見出し語化を行うにはどうすればよいですか?


111

私はPorterStemmerとSnowballを試してみましたが、どちらもすべての単語に対して機能するわけではなく、非常に一般的な単語がいくつかありません。

私のテストの言葉は、「走っているサボテンサボテンサボテンコミュニティコミュニティを実行している猫」で、どちらも半分以下しか正しくありません。

以下も参照してください。


28
サボテンじゃないの?
MSalters 2009

3
Redditに投稿された元の質問を循環参照するためだけに: プログラムでステミングを行うにはどうすればよいですか?(例:「食べる」から「食べる」、「サボテン」から「サボテン」) コメントには有用な情報が含まれているため、ここに投稿してください。
ルノーボンプイ2009

回答:


143

Pythonを知っている場合、The Natural Language Toolkit(NLTK)にWordNetを利用する非常に強力な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モジュールには他のレマタイザがありますが、私はそれらを自分で試していません。


11
ああ悲しい...私が検索することを知る前に、私は自分で実装しました!
Chris Pfohl、2010

12
初めてnltkを使用する前に、コーパスをインストールすることを忘れないでください!velvetcache.org/2010/03/01/...
マチューRodic

1
あなたがそれをしようとした場合のためにさて、この1は、ポーターステマーのようないくつかの非決定論的なアルゴリズムを使用していますdies、それはあなたを与えるdy代わりにdie。何らかのハードコードされたステマー辞書はありませんか?
SexyBeast 2013年

3
WordNetLemmatizer誤って見出し語化する単語は何ですか?
alvas 2013年

21
nltk WordNetLemmatizerには、引数としてposタグが必要です。デフォルトでは 'n'(名詞を表す)です。したがって、動詞では正しく機能しません。POSタグが利用できない場合、シンプルな(ただしアドホックな)アプローチは、1回は 'n'に対して、もう1回は 'v'(動詞を表す)に対して見出し語化を行い、結果とは異なる結果を選択することです。元の単語(通常は長さが短いが、「ran」と「run」は同じ長さ)。「adj」、「adv」、「prep」などは、なんらかの形ですでに元の形式になっているため、心配する必要はないようです。
Fashandge 2014年

29

スタンマト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 2013年

はい、パイプライン変数を最初に宣言する必要があります。Stanford NLPはコマンドラインからも使用できるため、プログラミングを行う必要はありません。プロパティファイルを作成し、実行可能ファイルをフィードするだけです。ドキュメントを読む:nlp.stanford.edu/software/corenlp.shtml
Jindra Helcl

24

私はこのスノーボールデモサイトで用語のリストを試してみましたが、結果は問題ありません。

  • 猫->猫
  • 実行中->実行中
  • 実行->実行
  • サボテン->サボテン
  • サボテン->サボテン
  • コミュニティ->コミュニティ
  • コミュニティ->コミュニティ

ステマーは、活用形の単語をいくつかの一般的な語根に変えることになっています。そのルートを「適切な」辞書の単語にすることは、ステマーの仕事ではありません。そのためには、形態学的/正書法アナライザーを調べる必要があります

この質問は多かれ少なかれ同じことに関するものだと私は思います。その質問に対するカーレルの答えは、私が2番目のリンクを取得した場所です。


6
重要なのは、stem( "updates")== stem( "update")を実行することです(
update-

1
ソフトウェアはstem(x)== stem(y)を実行できますが、それは質問に完全に答えているわけではありません
ユーザー14

11
語幹に注意して、語幹は単語の基本形ではありません。基本フォームが必要な場合は、lemmatizerが必要です。語幹は、接頭辞または接尾辞を含まない単語の最大の部分です。単語の更新の語幹は確かに「updat」です。単語は、語尾とサフィックスを追加することにより語幹から作成されます(例:updat-e、updat-ing)。(en.wikipedia.org/wiki/Word_stem
Jindra Helcl

20

ステマー対レンマタイザの議論は続いています。効率よりも精度を優先することが問題です。言語学的に意味のある単位を達成するために見出し語を付け、最小限の計算能力を使用し、同じキーの下で単語とそのバリエーションを索引付けするようにステムする必要があります。

ステマーvsレマタイザを参照

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
前に述べたように、WordNetLemmatizerさんは、lemmatize()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か?ところで:久しぶりです。うまくいけば、すぐに会議でお互いに会う予定です=)
alvas

実際には、いいえ(できれば、会議には「はい」です)。なぜなら、設定したpos=VERB場合、動詞の見出し語化だけを行うからです。名詞は変わりません。実際のペンツリーバンクのPOSタグを軸にして独自のコードを記述し、各トークンに正しい見出し語を適用する必要がありました。また、WordNetLemmatizernltkのデフォルトのトークナイザーの扱いを悪用しています。したがって、のような例does n'tはにlemmatizeしませんdo not
Nick Ruiz、2015年

しかし、しかし、port.stem("this")生産thiport.stem("was") wa右のPOSは、それぞれのために提供された場合でも、。
Lerner Zhang

ステマーは、言語的に健全な出力を返しません。それは単にテキストをより「密に」する(つまり、語彙を少なくする)ためです。stackoverflow.com/questions/17317418/stemmers-vs-lemmatizersおよびstackoverflow.com/questions/51943811/…を
alvas '29

8

Martin Porterの公式ページには、PHP他の言語のPorter Stemmerが含まれています

ポーターアルゴリズムのようなものから始める必要があるが、適切なステミングに真剣に取り組んでいる場合は、ルールを追加してデータセットに共通の誤ったケースを修正し、最後に多くの例外をルールに追加する。これは、キーと値のペア(dbm / hash / dictionaries)で簡単に実装できます。キーは検索する単語で、値は元の単語を置き換える語幹の単語です。かつて私が取り組んだ商用検索エンジンは、変更されたポーターアルゴリズムに対して800の例外をいくつか抱えていました。


理想的なソリューションは、これらの期待を自動的に学習することです。そのようなシステムについての経験はありますか?
マルコム

いいえ。私たちの場合、インデックスに登録されるドキュメントは特定の法律分野のコードと規制であり、悪い幹のインデックスを分析する何十人もの(人間の)編集者がいました。
Van Gale


5

Stack Overflowや私が出会ったブログに関するさまざまな回答に基づいて、これは私が使用している方法であり、実際の単語を非常によく返すようです。着信テキストを単語の配列に分割し(どの方法を使用してもかまいません)、それらの単語の品詞(POS)を見つけ、それを使用して単語の語幹と見出し語を作成します。

上記のサンプルは、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

これは興味深いようです:MIT Java WordnetStemmer:http ://projects.csail.mit.edu/jwi/api/edu/mit/jwi/morph/WordnetStemmer.html


3
SOへようこそ。投稿+1をありがとうございます。このステマーの使用法やパフォーマンスなどについていくつかコメントを付けていただければ幸いです。リンクは通常、非常に良い回答とは見なされません。
jogojapan 2012年

2

LemmaGen -C#3.0で記述されたオープンソースライブラリをご覧ください。

テストワードの結果(http://lemmatise.ijs.si/Services

  • 猫->猫
  • ランニング
  • 実行->実行
  • カクタス
  • サボテン->サボテン
  • サボテン->サボテン
  • コミュニティ
  • コミュニティ->コミュニティ

2

:見出し語処理のためのトップのpythonパッケージ(なし特定の順序では)あるspacynltkgensimpatternCoreNLPTextBlob。spaCyとgensimの実装(パターンに基づく)を好むのは、単語のPOSタグを識別し、適切な補題を自動的に割り当てるからです。は意味を損なわずに、より適切な補題を提供します。

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

上記の例は、この見出し付けページで借用したものです。


1

Luceneの検索を行います。PHPの移植版があるかどうかはわかりませんが、Luceneが多くのプラットフォームで利用できることは知っています。Luceneは、(Apacheからの)OSSインデックス作成および検索ライブラリです。当然のことながら、それとコミュニティのエキストラには注目すべき興味深いものがあるかもしれません。少なくとも、1つの言語でその方法を学ぶことができるため、「アイデア」をPHPに翻訳できます。


1

StompChickenが述べた質問への私の答えを引用する場合:

ここでの中心的な問題は、ステミングアルゴリズムが音声ベースで動作し、使用している言語を実際に理解していないことです。

彼らは言語を理解しておらず、用語の辞書から実行されないため、「実行」/「実行」などの不規則なケースを認識して適切に対応する方法がありません。

不規則なケースを処理する必要がある場合は、別のアプローチを選択するか、ステマーが処理を行った後に実行する独自のカスタム修正辞書でステミングを拡張する必要があります。



1

Morphaステマーを使用できます。UWは、モーファステマーを Javaアプリケーションから使用する場合、Mavenセントラルにアップロードしました。使いやすくするラッパーがあります。依存関係として追加し、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には英語のステマーがあります。

彼は、Porter Stemmerが歴史的な理由でのみ再実装されたと明確に述べているため、Porter Stemmerに対してステミングの正しさをテストすると、すでに知っているはずの結果が得られます。

http://tartarus.org/~martin/PorterStemmer/index.html(強調鉱山)

ポーターステマーは「冷凍」、つまり厳密に定義されたものと見なされ、それ以上の変更はできません。ステマーとしては、Snowball EnglishまたはPorter2ステマーよりわずかに劣っています。これは、それから派生し、時折改善されます。したがって、実際の作業では、新しいSnowballステマーが推奨されます。Porterステマーは、実験を正確に繰り返す必要があるステミングを含むIR研究作業に適しています。

ポーター博士は、ポーターステマーの代わりに英語またはポーター2ステマーを使用することを提案しています。英語のステマーは、@ StompChickenが以前に回答したように、デモサイトで実際に使用されているものです。


0

Javaでは、tartargus-snowballを使用して単語をステミングします

Maven:

<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の上に構築された高レベルのテキスト処理)を使用することを強くお勧めします。

レンマ化された単語は、デフォルトでトークンの.lemma_属性としてSpacyで利用可能であり、テキストは、textacyで他の多くのテキスト前処理を行っている間にレンマ化できます。たとえば、用語 や単語のバッグを作成しているとき、または一般的にそれを必要とする処理を実行する直前。

コードを書く前に両方をチェックすることをお勧めします。これにより、多くの時間を節約できる可能性があります。


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