NLTKでの形態素解析器の比較と作成

こんにちは。 この記事では、NLTKライブラリにある既存の形態素解析器と独自の形態素解析器の作成について比較します。



はじめに



NLTKは、Pythonプログラミング言語で記述された自然言語の記号的および統計的処理のためのライブラリとプログラムのパッケージです。 コンピュータ言語学、機械学習、情報検索[1]を勉強している人に最適です。

この記事では、サンプルにPythonコード(バージョン2.7)を添付します。



さあ始めましょう



プロセスを開始する前に、NLTKパッケージ自体をインストールおよび構成する必要があります。



これは、pipを介して実行できます。



pip install nltk
      
      





次に、パッケージを構成します。 これを行うには、Python GUIで次のように入力します。



 >>> import nltk >>> nltk.download()
      
      





NLTKのパッケージをインストールできるウィンドウが開きます。これには、必要なブラウンケースが含まれます。 必要なパッケージをマークして、「ダウンロード」をクリックします。 すべて、セットアップが完了しました。 これで仕事に取りかかることができます。



サンプリングとトレーニング



テストはどうなりますか? アナライザー自体をテストする前に、それをトレーニングする必要があります。 そして、トレーニングは既成のタグ付き単語の助けを借りて行われます。 ブラウンケース、またはむしろ「ニュース」と呼ばれる部分を使用します。これは、ケースではかなり大きなカテゴリの素材であり、主にニューステキストで構成されています。



トレーニングにはサンプル全体の90%が使用され、テストには残りの10%が使用されます。 メソッドを使用して結果を確認します



 tagger.evaluate(test_sents)
      
      





その結果、0から1の値を取得します。これに100を掛けて、パーセンテージを取得できます。



最初に、トレーニングとテスト文を定義します。 ブラウンのケースの90%からオファーの数がわかります。



 >>> training_count = int(len(nltk.corpus.brown.tagged_sents(categories='news')) * 0.9) >>> training_count 4160
      
      





4160は、各アナライザーのトレーニングの数です。 前述のとおり、残りはテストに使用されます。



サンプルを既に定義し、それらを使用します。 文章を追加して、作品を示します。



 >>> training_sents = nltk.corpus.brown.tagged_sents(categories='news')[:training_count] >>> testing_sents = nltk.corpus.brown.tagged_sents(categories='news')[training_count+1:] >>> test_sents_notags = nltk.corpus.brown.sents(categories='news')[training_count+1:]
      
      





既存のアナライザー



NLTKパッケージにはいくつかの形態素解析ツールがあります。 しかし、それらの中で最も人気があるのは、デフォルトアナライザー、ユニグラムアナライザー、N-グラムアナライザー、正規表現アナライザーです。 また、それらに基づいて独自に作成することもできます(後で詳しく説明します)。それぞれについて詳しく見ていきましょう。



  1. デフォルトのアナライザー。



    おそらく、NLTKに存在するすべての中で最も簡単です。 すべての単語に同じタグを自動的に指定します。 このアナライザーは、最も使用されるタグを割り当てる場合に使用できます。 それを見つける:



     >>> tags = [tag for (word, tag) in nltk.corpus.brown.tagged_words(categories='news')] >>> nltk.FreqDist(tags).max() 'NN'
          
          





    その結果、NN(名詞、名詞)が得られます。 上記のリストで、デフォルトのアナライザーを作成します。 また、すぐにその動作を確認します。



     >>> default_tagger = nltk.DefaultTagger('NN') >>> default_tagger.tag(testing_sents_notags[10]) [('The', 'NN'), ('evidence', 'NN'), ('in', 'NN'), ('court', 'NN'), ('was', 'NN'), ('testimony', 'NN'), ('about', 'NN'), ('the', 'NN'), ('interview', 'NN'), (',', 'NN'), ('which', 'NN'), ('for', 'NN'), ('Holmes', 'NN'), ('lasted', 'NN'), ('an', 'NN'), ('hour', 'NN'), (',', 'NN'), ('although', 'NN'), ('at', 'NN'), ('least', 'NN'), ('one', 'NN'), ('white', 'NN'), ('student', 'NN'), ('at', 'NN'), ('Georgia', 'NN'), ('got', 'NN'), ('through', 'NN'), ('this', 'NN'), ('ritual', 'NN'), ('by', 'NN'), ('a', 'NN'), ('simple', 'NN'), ('phone', 'NN'), ('conversation', 'NN'), ('.', 'NN')]
          
          





    前述のように、すべての単語(単語でなく)に1つのタグが付けられます。 このアナライザーは、失礼なので、単独で使用されることはほとんどありません。



    ここで、精度を確認します。



     >>> default_tagger.evaluate(testing_sents) 0.1262832652247583
          
          





    わずか13%の精度は非常に小さな指標です。

    さらに複雑なアナライザーに移りましょう。

  2. 正規表現アナライザー。



    私の意見では、これは非常に興味深いアナライザーです。 テンプレートに基づいてタグを設定します。 -ingが動名詞の場合、 -edで終わるすべての単語が動詞の過去分詞であると想定できるとします。



    アナライザーを作成して、すぐに確認しましょう。



     >>> patterns = [ (r'.*ing$', 'VBG'), # gerunds (r'.*ed$', 'VBD'), # simple past (r'.*es$', 'VBZ'), # 3rd singular present (r'.*ould$', 'MD'), # modals (r'.*\'s$', 'NN$'), # possessive nouns (r'.*s$', 'NNS'), # plural nouns (r'^-?[0-9]+(.[0-9]+)?$', 'CD'), # cardinal numbers (r'.*', 'NN') # nouns (default) ] >>> regexp_tagger = nltk.RegexpTagger(patterns) >>> regexp_tagger.tag(testing_sents_notags[10]) [('The', 'NN'), ('evidence', 'NN'), ('in', 'NN'), ('court', 'NN'), ('was', 'NNS'), ('testimony', 'NN'), ('about', 'NN'), ('the', 'NN'), ('interview', 'NN'), (',', 'NN'), ('which', 'NN'), ('for', 'NN'), ('Holmes', 'VBZ'), ('lasted', 'VBD'), ('an', 'NN'), ('hour', 'NN'), (',', 'NN'), ('although', 'NN'), ('at', 'NN'), ('least', 'NN'), ('one', 'NN'), ('white', 'NN'), ('student', 'NN'), ('at', 'NN'), ('Georgia', 'NN'), ('got', 'NN'), ('through', 'NN'), ('this', 'NNS'), ('ritual', 'NN'), ('by', 'NN'), ('a', 'NN'), ('simple', 'NN'), ('phone', 'NN'), ('conversation', 'NN'), ('.', 'NN')]
          
          





    ご覧のとおり、ほとんどの単語は「デフォルト」タグNNで示されています。 しかし、パターンのおかげで他の人によってマークされているものもあります。



    精度を確認します。



     >>> regexp_tagger.evaluate(testing_sents) 0.2047244094488189
          
          





    20%-このアナライザーは、デフォルトのアナライザーと比較してすでに良い仕事をしています

  3. ユニグラムアナライザー。



    単純な統計的ワードマーキングアルゴリズムを使用します。 各単語(トークン)は、その単語に最も可能性の高いタグでタグ付けされます。



    まず、アナライザーを作成してトレーニングし、作業中に表示します。



     >>> unigram_tagger = nltk.UnigramTagger(training_sents) >>> unigram_tagger.tag(testing_sents_notags[10]) [('The', 'AT'), ('evidence', 'NN'), ('in', 'IN'), ('court', 'NN'), ('was', 'BEDZ'), ('testimony', 'NN'), ('about', 'IN'), ('the', 'AT'), ('interview', 'NN'), (',', ','), ('which', 'WDT'), ('for', 'IN'), ('Holmes', None), ('lasted', None), ('an', 'AT'), ('hour', 'NN'), (',', ','), ('although', 'CS'), ('at', 'IN'), ('least', 'AP'), ('one', 'CD'), ('white', 'JJ'), ('student', 'NN'), ('at', 'IN'), ('Georgia', 'NP-TL'), ('got', 'VBD'), ('through', 'IN'), ('this', 'DT'), ('ritual', None), ('by', 'IN'), ('a', 'AT'), ('simple', 'JJ'), ('phone', 'NN'), ('conversation', 'NN'), ('.', '.')]
          
          





    結果は、デフォルトのアナライザーよりもはるかに優れています。 しかし、結果として、タグ付けされていない(Noneに値する)単語があることがわかります。 これは、これらの単語がトレーニング中に表示されなかったことを意味します。 アナライザーの精度を確認します。



     >>> unigram_tagger.evaluate(testing_sents) 0.8110236220472441
          
          





    〜81%は非常に良い指標です。 単語の19%のみがマークされているか正しくないか、トレーニング中に同じ単語がまったく表示されませんでした。

  4. Nグラム。



    前のアナライザーで、トレーニングで出会った単語に基づいてタグが付けられた場合、そのコンテキストは考慮されませんでした。 たとえば、単語windは、 toまたはtheの前にあるかどうかに関係なく、同じタグでマークされます。 N-gramアナライザーはこの問題を解決します。 これは、前の単語のタグn-1を使用して現在の単語のタグを設定する場合のUnigramアナライザの一般的なケースです。



    次に、BigramTaggerの動作を確認しましょう-nワードとn-1ワードのアナライザー。



     >>> bigram_tagger = nltk.BigramTagger(training_sents) >>> bigram_tagger.tag(testing_sents_notags[10]) [('The', 'AT'), ('evidence', 'NN'), ('in', 'IN'), ('court', 'NN'), ('was', 'BEDZ'), ('testimony', None), ('about', None), ('the', None), ('interview', None), (',', None), ('which', None), ('for', None), ('Holmes', None), ('lasted', None), ('an', None), ('hour', None), (',', None), ('although', None), ('at', None), ('least', None), ('one', None), ('white', None), ('student', None), ('at', None), ('Georgia', None), ('got', None), ('through', None), ('this', None), ('ritual', None), ('by', None), ('a', None), ('simple', None), ('phone', None), ('conversation', None), ('.', None)]
          
          





    そして、ここでアナライザの主な問題がすぐに発生します-マークされていない単語がたくさん。 テキストに新しい単語が見つかるとすぐに、アナライザはその単語にタグを設定できません。 また、次のタグはマークされません。これは、Noneタグの後のテスト中にこの単語が発生しなかったためです。 そして、そのようなマークのない単語のチェーンを得ました。



    この問題のため、このアナライザーの精度はほとんどありません。



     >>> bigram_tagger.evaluate(testing_sents) 0.10216286255357321
          
          





    10%のみが非常に小さな指標です。 単語をマークするこの方法は、精度が低いため、単独では使用されません。 しかし、これはアナライザーの組み合わせを使用する場合に非常に強力なツールです。



    また、Biggramアナライザーと同じ原理で動作するTrigramTaggerもあり、1つではなく2つの以前のタグが分析されます。 ただし、その精度はもちろん低くなります。



異なるアナライザーの組み合わせ



最後に、最も興味深いのは、アナライザーの組み合わせの作成です。 たとえば、Bigramアナライザー、Unigramアナライザー、およびデフォルトアナライザーの結果を組み合わせることができます。 これは、アナライザーの作成時にbackoffパラメーターを使用して行われます。 各アナライザー(デフォルトのアナライザーを除く)は、別のアナライザーを使用してマルチパスアナライザーを作成するためのポインターを持つことができます。



作成しましょう:



 >>> default_tagger = nltk.DefaultTagger('NN') >>> unigram_tagger = nltk.UnigramTagger(training_sents, backoff=default_tagger) >>> bigram_tagger = nltk.BigramTagger(training_sents, backoff=unigram_tagger)
      
      





アナライザーを確認しましょう。



 >>> bigram_tagger.tag(test_sents_notags[10]) [('The', 'AT'), ('evidence', 'NN'), ('in', 'IN'), ('court', 'NN'), ('was', 'BEDZ'), ('testimony', 'NN'), ('about', 'IN'), ('the', 'AT'), ('interview', 'NN'), (',', ','), ('which', 'WDT'), ('for', 'IN'), ('Holmes', 'NN'), ('lasted', 'NN'), ('an', 'AT'), ('hour', 'NN'), (',', ','), ('although', 'CS'), ('at', 'IN'), ('least', 'AP'), ('one', 'CD'), ('white', 'JJ'), ('student', 'NN'), ('at', 'IN'), ('Georgia', 'NP'), ('got', 'VBD'), ('through', 'IN'), ('this', 'DT'), ('ritual', 'NN'), ('by', 'IN'), ('a', 'AT'), ('simple', 'JJ'), ('phone', 'NN'), ('conversation', 'NN'), ('.', '.')]
      
      





ご覧のとおり、すべての単語がマークされています。 次に、このアナライザーの精度を確認します。



 >>> bigram_tagger.evaluate(testing_sents) 0.8447124489185687
      
      





その結果、約84%が得られます。 これは非常に良い指標です。 さまざまなアナライザーを組み合わせて、より多くのトレーニングサンプルを取り、より良い結果を得ることができます。



おわりに



どのような結論を出すことができますか? もちろん、アナライザーの組み合わせを使用することをお勧めします。 しかし、Unigramアナライザーも同様に機能し、それに費やす時間はより少なくなりました。



この記事がアナライザーの選択に役立つことを願っています。 ご清聴ありがとうございました。



参照資料



  1. NLTKパッケージ。 公式サイト
  2. NLTK。 ドキュメント



All Articles