Python、機械学習、そして NLTK ライブラリーについて探る

Python、NLTK、機械学習を使用して RSS フィードを分類するアプリケーションを作成する

機械学習は IT、数学、自然言語が組み合わされたものであり、通常はビッグデータ・アプリケーションに使用されます。この記事では Python プログラミング言語と Python の NLTK ライブラリーについて説明し、さらにそれらを機械学習プロジェクトに適用する方法について説明します。

Chris Joakim, Senior Software Engineer, Primedia Inc

Photo of Chris JoakimChris Joakim は Primedia Inc. のシニア・ソフトウェア技術者です。彼は 25 年以上にわたってソフトウェア開発を行っており、今回使用した Python を含め、Clojure、Ruby、Java、Objective-C、JavaScript、CoffeeScript、Flex、Smalltalk、COBOL などの言語を使用してきました。彼はノースカロライナ州の Davidson に住み、余暇にはマラソンをしたり、コードを作成したりしています。



2012年 11月 08日

Python を初めて扱う人へ

この記事は、初めて機械学習を実装しようとしているソフトウェア開発者 (特に Ruby 言語や Java 言語を扱ってきたソフトウェア開発者) を対象にしています。

課題: 機械学習を使用して RSS フィードを分類する

私は最近、ある顧客のために RSS フィードを分類するサブシステムを作成する仕事を与えられました。この仕事の目標は、何十もの、さらには何百もの RSS フィードを読み取り、それらのフィードに含まれる多数の記事を、あらかじめ設定された何十種類かの分野のいずれかに自動的に分類することでした。フィードの自動取得と自動分類は毎日実行され、その結果によって顧客の Web サイトのコンテンツ、ナビゲーション、検索機能が決まります。

その顧客は、最近読んだ Apache Mahout や Hadoop の記事の情報を基に、これらの技術を使った機械学習を利用することを提案してきました。しかし顧客の開発チームも私達の開発チームも、Java 技術よりも Ruby の方が使い慣れていました。この記事では、この仕事を進める中で私達が行った、使用技術の選択プロセス、その技術の習得プロセス、そして最終的なソリューションの実装について説明します。

機械学習とは何か?

この仕事を進めるにあたり、まず始めに私の頭には「機械学習とは一体何なのだろう?」という質問が浮かんできました。私は機械学習という言葉は聞いたことがあり、「Jeopardy!」というクイズ番組で比較的最近、IBM のスーパーコンピューター「Watson (ワトソン)」が機械学習を利用して人間の出場者に勝ったというニュースをぼんやりと知っていました。また私はネットで買い物をしたり、ソーシャル・ネットワークに参加したりもしているので、Amazon.com と Facebook が購買者のデータに基づいておすすめ (製品や人など) を紹介することに関して非常に優れた成果を収めていることも知っていました。簡単に言えば、機械学習は IT、数学、自然言語を組み合わせたものです。機械学習には主にこの 3 つのトピックが関係しますが、この顧客に対するソリューションの場合、最終的に以下の課題のうちの最初の 2 つを解決する必要がありました。

  • 分類: 類似の項目から成る一連の訓練データに基づき、あらかじめ設定された任意のカテゴリーに項目を割り当てる。
  • 推奨: 類似の項目を観察することによって複数の項目を推奨する。
  • クラスタリング: 母集団のデータの中でサブグループを特定する。

Mahout と Ruby という回り道

機械学習とは何かを理解できたので、次のステップはどのようにして機械学習を実装するかを決めることでした。顧客の提案にもあったように、Mahout は出発点として適切に思えました。私は Apache から Mahout のコードをダウンロードし、Mahout とその兄弟分である Hadoop を使用した機械学習について学び始めました。しかし残念なことに、Mahout は経験豊富な Java 開発者にとっても短時間の習得が難しいこと、そして実際に動作するサンプル・コードがないことがわかりました。さらには機械学習に使用できるような Ruby ベースのフレームワークや gem も残念ながらありませんでした。

Python と NLTK に気付く

ソリューションの検索を続けたところ、検索結果に「Python」が含まれることが何度もありました。私は Rubyist として、Python が Ruby と同じようにオブジェクト指向でテキスト・ベースの動的なインタープリター型プログラミング言語であることは知っていましたが、それまで Python 言語を学んだことはありませんでした。Ruby と Python にはそうした類似点があるにも関わらず、私は Python は無駄なスキル・セットであると見なし、長年学ばずに来てしまいました。Python は私にとって「盲点」でしたが、この点は私と同じ Rubyist の仲間の多くも同じではないかと思います。

機械学習に関する本を検索し、それらの本の目次を詳細に見ると、これらの機械学習システムの多くが実装言語として Python を使用していること、そして Python と共に NLTK (Natural Language Toolkit) というライブラリーを使用していることがわかりました。さらに検索してみると、Python は私が思ったよりも幅広く使用されていることに気付きました。例えば Google App Engine や YouTube、そして Django フレームワークで作成された Web サイトに Python が使用されています。Python はさらに、私が毎日使用している Mac OS X ワークステーションにもプリインストールされていました。それだけではなく、Python には数学、科学、工学のための興味深い標準ライブラリー (NumPy や SciPy など) も付属していましたが、それも知りませんでした。

私は Python の簡潔なコーディング・サンプルを見つけたので、Python によるソリューションを追求することにしました。例えば、HTTP で RSS フィードを読み取り、そのコンテンツを出力するために必要なコードは以下の 1 行のみです。

    print feedparser.parse("http://feeds.nytimes.com/nyt/rss/Technology")

Python について学ぶ

新しいプログラミング言語を学ぶ場合、言語そのものの習得は容易であることが多いものです。難しいのは、そのエコシステムを習得すること、つまりインストール、ライブラリーの追加、コードの作成、コード・ファイルの構造、実行、デバッグ、ユニット・テストの作成などの方法を理解することです。このセクションでは、これらのトピックについて簡単に紹介します。「参考文献」に挙げたリンクを必ずチェックし、詳しい情報を得るようにしてください。

pip

pip (Python Package Index) は Python の標準パッケージ・マネージャーです。pip はシステムにライブラリーを追加するために使用されるプログラムです。pip は Ruby ライブラリーの gem のようなものです。システムに NLTK ライブラリーを追加するには、以下のコマンドを入力します。

  $ pip install nltk

システムにインストールされた Python ライブラリーの一覧を表示するには、以下のコマンドを実行します。

  $ pip freeze

プログラムを実行する

Python プログラムは実行も簡単です。locomotive_main.py というプログラムと 3 つのプログラム引数がある場合、以下のように python コマンドを使用してそのプログラムをコンパイルし、実行することができます。

  $ python locomotive_main.py arg1 arg2 arg3

Python は、ファイル自体がコマンドラインから実行されるのか、あるいは単に他のコードによってファイルがインポートされるのかを、リスト 1if __name__ == "__main__": 構文を使用して判断します。ファイルを実行可能にするには、以下のように "__main__" を検出するコードを追加します。

リスト 1. main を検出するコード
import sys
import time
import locomotive

if __name__ == "__main__":
    start_time = time.time()
    if len(sys.argv) > 1:
        app = locomotive.app.Application()
        ... additional logic ...

virtualenv

ほとんどの Rubyist は、システム全体でライブラリー (つまり gem) を使用することの問題を理解しています。つまり、複数のプロジェクトが存在する中で、そのうちの 1 つがある特定のライブラリーのバージョン 1.0.0 に依存し、別のプロジェクトがバージョン 1.2.7 に依存している可能性があるため、一般にシステム全体で 1 つのライブラリー・セットを使用するのは望ましいことではありません。同様に、Java 開発者もシステム全体で 1 つの CLASSPATH を使用することの問題を認識しています。rvm ツールを使用している Ruby コミュニティーのように、Python コミュニティーでは virtualenv ツール (「参考文献」のリンクを参照) を使用して別の実行環境を作成します (これらの環境には特定のバージョンの Python とライブラリー・セットが含まれています)。リスト 2 のコマンドは p1 プロジェクトのための p1_env という仮想環境を作成する方法を示しており、この環境に含まれるライブラリーは feedparsernumpyscipynltk です。

リスト 2. virualenv を使用して仮想環境を作成するためのコマンド
  $ sudo pip install virtualenv
  $ cd ~
  $ mkdir p1
  $ cd p1
  $ virtualenv p1_env --distribute
  $ source p1_env/bin/activate 
  (p1_env)[~/p1]$ pip install feedparser
  (p1_env)[~/p1]$ pip install numpy
  (p1_env)[~/p1]$ pip install scipy
  (p1_env)[~/p1]$ pip install nltk
  (p1_env)[~/p1]$ pip freeze

シェル・ウィンドウでプロジェクトを扱う場合、そのプロジェクトの仮想環境を有効にするスクリプトを source コマンドによって毎回指定する必要があります。有効化スクリプトがソースとして入力されると、シェル・プロンプトが変わることに注意してください。システム上でシェル・ウィンドウを作成して使用する場合、プロジェクトのディレクトリーに容易にナビゲートしてそのプロジェクトの仮想環境を有効化できるように、以下のようなエントリーを ~/.bash_profile ファイルに追加する必要があるかもしれません。

$ alias p1="cd ~/p1 ; source p1_env/bin/activate"

コード・ベースの構造

単純な 1 つのファイルによる「Hello World」プログラムを卒業すると、Python 開発者はディレクトリーやファイル名に関するコード・ベースを適切に構造化する方法を理解する必要があります。この点に関して、Java 言語にも Ruby 言語にも独自の要件があり、Python も例外ではありません。簡単に言えば、Python はパッケージという概念を使用して関連コードをグループ化し、曖昧さのない名前空間を提供します。この記事では説明のために、コードは指定プロジェクトのルート・ディレクトリーにあるものとします (例えば ~/p1 など)。このディレクトリーの中に、locomotive という Python パッケージのための locomotive ディレクトリーがあります。このディレクトリー構造を示したものがリスト 3 です。

リスト 3. ディレクトリー構造の例
locomotive_main.py
locomotive_tests.py

locomotive/
    __init__.py
    app.py
    capture.py
    category_associations.py
    classify.py
    news.py
    recommend.py
    rss.py

locomotive_tests/
    __init__.py
    app_test.py
    category_associations_test.py
    feed_item_test.pyc
    rss_item_test.py

__init__.py という奇妙な名前のファイルに注意してください。これらのファイルは Python に対し、パッケージに必要なライブラリーをロードするように、またそれらと同じディレクトリーに存在するその特定のアプリケーションのコード・ファイルをロードするように指示します。リスト 4 は locomotive/__init__.py ファイルの内容を示しています。

リスト 4. locomotive/__init__.py
    # system imports; loads installed packages
    import codecs
    import locale
    import sys

    # application imports; these load your specific *.py files
    import app
    import capture
    import category_associations
    import classify
    import rss
    import news
    import recommend

リスト 4 のように構成された locomotive パッケージを使用すると、このプロジェクトのルート・ディレクトリーにある main プログラムがこのパッケージをインポートして使用することができます。例えば、locomotive_main.py ファイルには以下のような import 文が含まれています。

    import sys         # >-- system library
    import time        # >-- system library
    import locomotive  # >-- custom application code library in the "locomotive" directory

テスト

Python の unittest 標準ライブラリーはテストのための優れたソリューションです。JUnit を理解している Java 開発者や Test::Unit フレームワークを理解している Ruby プログラマーは、リスト 5 に示す Python の unittest コードを容易に理解できるはずです。

リスト 5. Python の unittest
  class AppTest(unittest.TestCase):

      def setUp(self):
          self.app = locomotive.app.Application()

      def tearDown(self):
          pass

      def test_development_feeds_list(self):
          feeds_list = self.app.development_feeds_list()
          self.assertTrue(len(feeds_list) == 15)
          self.assertTrue('feed://news.yahoo.com/rss/stock-markets' in feeds_list)

リスト 5 のコードは Python 独特の特徴も示しています。つまりすべてのコードは一貫した形式でインデントする必要があり、そうしないとコンパイルに成功しません。tearDown(self) メソッドは初めて見ると奇妙に思えるかもしれません。なぜテストに必ずパスするようにハードコーディングされているのか不思議に思う人がいるかもしれませんが、実際にはそうではありません。これは単に Python で空のメソッドをコーディングしているにすぎないのです。

ツール

私が Python の学習を支援するツールとして何としても欲しいと思ったのは、構文強調機能やコード補完機能、さらにはブレークポイントを使ったデバッグ機能を備えた統合開発環境 (Integrated Development Environment: IDE) でした。Java 開発に Eclipse IDE を使用する私が次に検討したツールは pyeclipse プラグインでした。このプラグインは動作が遅くなることが時々ありましたが、かなり良好な動作をしました。最終的に、私は自分にとっての IDE 要件をすべて満たす PyCharm IDE に投資しました。

Python とそのエコシステムの基本を理解できたので、いよいよ機械学習ソリューションの実装を開始することになりました。


Python と NLTK を使用して分類を実装する

このソリューションを実装するには、シミュレートされた RSS フィードの取得、フィードからのテキストの抽出、NaiveBayesClassifier の使い方、そして kNN アルゴリズムを使用したカテゴリー分類が必要になります。これらのアクションのそれぞれについて、ここで説明します。

フィードの取得と構文解析

このプロジェクトは特に困難でした。というのも、この顧客は対象とする RSS フィードのリストをまだ明確にしていなかったため、「訓練データ」もなかったのです。従って、開発の最初の段階でフィードと訓練データをシミュレートする必要がありました。

サンプルのフィード・データを取得するために私が使用した最初の方法は、単純にテキスト・ファイルで指定された RSS フィードのリストを取得する方法でした。Python には RSS フィードを構文解析するための feedparser という便利なライブラリーがあり、このライブラリーは多種多様な RSS フォーマットと Atom フォーマットの間の違いを抽象化してくれます。もう 1 つの便利なライブラリーは、単純にテキスト・ベースでオブジェクトをシリアライズする、pickle という面白い名前のライブラリーです。リスト 6 のコードは、この 2 つのライブラリーを両方とも使用しており、各 RSS フィードを取得し、後で使用できるようにオブジェクト・ファイルとして pickle (保存) しています。これを見るとわかるように、Python コードは簡潔で強力です。

リスト 6. CaptureFeeds クラス
import feedparser
import pickle

class CaptureFeeds:

    def __init__(self):
        for (i, url) in enumerate(self.rss_feeds_list()):
            self.capture_as_pickled_feed(url.strip(), i)

    def rss_feeds_list(self):
        f = open('feeds_list.txt', 'r')
        list = f.readlines()
        f.close
        return list

    def capture_as_pickled_feed(self, url, feed_index):
        feed = feedparser.parse(url)
        f = open('data/feed_' + str(feed_index) + '.pkl', 'w')
        pickle.dump(feed, f)
        f.close()

if __name__ == "__main__":
    cf = CaptureFeeds()

次のステップは予想外に困難でした。サンプルのフィード・データが得られたので、訓練データとして使用できるように、そのデータを分類する必要がありました。訓練データは分類アルゴリズムに与えられるデータセットであり、分類アルゴリズムはそのデータセットを基に学習します。

例えば、私が使用したサンプル・フィードには ESPN というスポーツ・ネットワークが含まれていました。フィード項目の 1 つは、アメリカン・フットボール (NFL) チームのデンバー・ブロンコス (Denver Broncos) に所属する Tim Tebow がニューヨーク・ジェッツ (New York Jets) にトレードされ、同時にブロンコスが新しいクォーターバック (quarterback) として Peyton Manning と契約した、という内容でした。取得したフィードに含まれていた別の項目は、ボーイング (Boeing) 社と同社の新しいジェット機 (jet) に関する内容でした。そこで質問です。最初のニュース記事に対し、カテゴリーの値として具体的に何を割り当てるべきなのでしょう。値として、tebowbroncosmanningjetsquarterbacktradenfl はどれも適切です。しかし、訓練データのカテゴリーとして指定できる値は 1 つのみです。同様に、2 番目のニュース記事の場合、カテゴリーは boeing でしょうか jet でしょうか。こうした詳細が難しいのです。アルゴリズムが正確な結果を生成するには、大量の訓練データセットを手作業で正確にカテゴリー分けすることが不可欠です。この作業の時間を軽く考えてはなりません。

まもなく、もっと多くのデータが必要なこと、そしてそれらのデータがあらかじめ正確にカテゴリー分けされている必要があることが明らかになりました。どこでそうしたデータを見つければよいのでしょう。そこで Python NLTK が登場します。Python NLTK は言語テキスト処理のための極めて優れたライブラリーであるのみならず、ダウンロード可能なサンプル・データ・セット (NLTK の用語では「corpus (コーパス)」) や、そのダウンロードされたデータに容易にアクセスするための API まで含んでいます。Reuters (ロイター) のコーパスをインストールするには、以下のコマンドを実行します。すると 10,000 件を超えるニュース記事が ~/nltk_data/corpora/reuters/ ディレクトリーにダウンロードされます。RSS フィード項目の場合と同様、Reuters の各ニュース記事にはタイトルと本文が含まれているため、あらかじめカテゴリーに分けられた NLTK のデータは RSS フィードをシミュレートするには最適です。

$ python               # enter an interactive Python shell
>>> import nltk        # import the nltk library
>>> nltk.download()    # run the NLTK Downloader, then enter 'd' Download
Identifier> reuters    # specify the 'reuters' corpus

特に興味深いものが ~/nltk_data/corpora/reuters/cats.txt です。このファイルには、記事ファイルの名前と各記事ファイルに割り当てられたカテゴリーの一覧が含まれています。このファイルは以下のようなものです。つまり test というサブディレクトリー内にある 14828 という名前のファイルは grain というトピックに関するものです。

test/14826 trade
test/14828 grain

自然言語は無秩序です

RSS フィード分類アルゴリズムに入力される未加工のデータは、当然ながら英語で書かれたテキスト・データで、完全にそのままの英語の文字列です。

英語はもちろん、どの自然言語 (つまり話し言葉または日常言語) も、コンピューター処理の観点から見ると極めて不規則で曖昧です。第 1 に、大文字/小文字の問題があります。「Bronco」という単語は「bronco」と同じなのでしょうか?同じかもしれない、というのが答えです。次に、句読点や空白の問題があります。「bronco.」は「bronco 」や「bronco,」と同じなのでしょうか?まあ同じようなものです。さらに、複数形や綴りの似た単語があります。「run」、「running」、「ran」は同じなのでしょうか?それは状況によりますが、これら 3 つの単語の語幹は同じです。自然言語の言葉が HTML のようなマークアップ言語に埋め込まれた場合にはどうなるのでしょう。その場合には <strong>bronco</strong> のようなテキストを扱う必要があります。最後に、「a」、「and」、「the」など、頻繁に使用されながら基本的に意味を持たない単語の問題があります。これらのいわゆるストップ・ワードが文中に散在しています。自然言語は無秩序であり、処理の前に自然言語を整理する必要があります。

幸いなことに、Python と NLTK を使用することにより、無秩序な自然言語を整理することができます。リスト 7 に示す RssItem クラスの normalized_words メソッドは、こうしたすべての問題を処理してくれます。特に、NLTK のたった 1 行のコードで、未加工の記事のテキストから埋め込まれた HTML マークアップが取り除かれることに注目してください。正規表現を使用して句読点が除去され、個々の単語が分割されて小文字に正規化されています。

リスト 7. RssItem クラス
class RssItem:
    ...
    regex = re.compile('[%s]' % re.escape(string.punctuation))
    ...
    def normalized_words(self, article_text):
        words   = []
        oneline = article_text.replace('\n', ' ')
        cleaned = nltk.clean_html(oneline.strip())
        toks1   = cleaned.split()
        for t1 in toks1:
            translated = self.regex.sub('', t1)
            toks2 = translated.split()
            for t2 in toks2:
                t2s = t2.strip().lower()
                if self.stop_words.has_key(t2s):
                    pass
                else:
                    words.append(t2s)
        return words

ストップ・ワードのリストは、NLTK によって以下の 1 行のコードで取得されます。英語以外の、他の自然言語もサポートされています。

nltk.corpus.stopwords.words('english')

また NLTK には、さらに単語を正規化するための「語幹抽出 (ステマー)」クラスも含まれています。語幹抽出 (ステミング)、見出し語化 (レマタイズ)、文の構造、文法などの詳細については NLTK のドキュメントを調べてください。

単純ベイズ (Naive Bayes) アルゴリズムによる分類

単純ベイズ・アルゴリズムは nltk.NaiveBayesClassifier クラスとして NTLK の中で広く使用され、実装されています。ベイズ・アルゴリズムは特徴の有無によってデータセット内の項目を分類します。RSS フィード項目の場合には、それぞれの特徴に対し、自然言語による (整理された) 単語が与えられます。このアルゴリズムが「単純」と呼ばれる理由は、さまざまな特徴 (この場合は単語) の間に何も関係がないことを前提にしているためです。

しかし英語には 25 万を超える単語が含まれています。当然ですが、単にアルゴリズムに渡すという目的のために、25 万個のブール値を含むオブジェクトを各 RSS フィード項目に対して作成したくはありません。では、どの単語を使えばよいのでしょう?簡単に言えば、その答えは、訓練データの母集団の中で最も一般的な単語であってストップ・ワードではない単語、ということです。NLTK には nltk.probability.FreqDist という素晴らしいクラスがあります。このクラスを使用すると、ストップ・ワードではない最も一般的な単語の上位に位置する単語を特定することができます。リスト 8collect_all_words メッドは、すべての訓練記事に含まれるすべての単語の配列を返します。

そしてこの配列は identify_top_words メソッドに渡され、最も頻繁に使用されている単語が特定されます。nltk.FreqDist クラスには便利な機能があり、このクラスは基本的にはハッシュですが、このハッシュのキーは対応する値 (つまりカウント) によってソートされます。そのため、[:1000] という Python 構文によって上位 1000 個の単語を容易に取得することができます。

リスト 8. nltk.FreqDist クラスを使用する
  def collect_all_words(self, items):
      all_words = []
      for item in items:
          for w in item.all_words:
              words.append(w)
      return all_words

  def identify_top_words(self, all_words):
      freq_dist = nltk.FreqDist(w.lower() for w in all_words)
      return freq_dist.keys()[:1000]

NLTK に含まれる Reuters の記事データを使用してシミュレートされた RSS フィード項目に対しては、各項目のカテゴリーを特定する必要があります。そのためには先ほど触れた ~/nltk_data/corpora/reuters/cats.txt ファイルを読み取ります。Python では、以下のように容易にファイルを読み取ることができます。

  def read_reuters_metadata(self, cats_file):
      f = open(cats_file, 'r')
      lines = f.readlines()
      f.close()
      return lines

次のステップは各 RSS フィード項目の特徴を取得することです。それをしてくれるのが、以下に示す RssItem クラスの features メソッドです。このメソッドでは、まず始めに記事の中にあるすべての単語の配列 (all_words) から重複した単語を除去して set オブジェクトにします。次に上位の単語 (top_words) を繰り返し処理して set オブジェクトとの比較を行い、対象の単語が set 内に存在するかどうかの判定を行います。1000 個のブール値によるハッシュが、w_ というキーに単語が続く形で返されます。非常に簡潔であり、これこそが Python です。

  def features(self, top_words):
      word_set = set(self.all_words)
      features = {}
      for w in top_words:
          features["w_%s" % w] = (w in word_set)
      return features

次に、RSS フィード項目から成る訓練セットと、そこに含まれる個々の特徴を収集し、アルゴリズムに渡します。それを示したものがリスト 9 のコードです。たった 1 行のコードで classifier を訓練できることに注目してください。

リスト 9. nltk.NaiveBayesClassifier を訓練する
  def classify_reuters(self):
        ...
        training_set = []
        for item in rss_items:
            features = item.features(top_words)
            tup = (features, item.category)  # tup is a 2-element tuple
            featuresets.append(tup)
        classifier = nltk.NaiveBayesClassifier.train(training_set)

これで、実行中の Python プログラムのメモリー内にある NaiveBayesClassifier が訓練されました。今度は、分類対象となる RSS フィード項目セットに対して単純に繰り返し処理を行い、各項目のカテゴリーを推測するように、分類器である classifier に指示します。非常に単純です。

  for item in rss_items_to_classify:
      features = item.features(top_words)
      category = classifier.classify(feat)

「単純」よりも賢くする

先ほど触れたように、このアルゴリズムは個々の特徴の間に何も関係がないことを前提にしています。そのため、例えば「machine learning」と「learning machine」、または「New York Jet」と「jet to New York」のような句は等価です (to はストップ・ワードです)。自然言語のコンテキストでは、これらの単語の間には明らかに関係があります。では、「単純」ベイズよりも賢くなるようにアルゴリズムに学習させ、これらの単語の関係を認識させるようにするには、どのように学習させればよいのでしょう?

1 つの方法として、共通のバイグラム (bigram: 2 つの単語からなるグループ) とトライグラム (trigram: 3 つの単語からなるグループ) を特徴セットに含める方法があります。ここまで読んできた皆さんは、NLTK が nltk.bigrams(...) 関数と nltk.trigrams(...) 関数という形でそれらをサポートしていることにも驚かないはずです。訓練データとしての単語で構成される母集団から上位 n 個の単語を収集したのと同じように、上位のバイグラムとトライグラムも、特徴として特定し、使用することができます。

結果は一定ではありません

データとアルゴリズムの改善は一種の芸術です。例えば語幹抽出 (ステミング) により、さらに単語セットを正規化するべきなのでしょうか?あるいは上位の単語として 1000 単語よりも多くの単語を含めるべきなのでしょうか、それとも1000 単語よりも少ない方が適切なのでしょうか?さらには、もっと大きな訓練データセットを使用するべきなのでしょうか?そして、もっとストップ・ワードや「ストップ・グラム」を追加するべきなのでしょうか?これらはどれも自らに問いかけるべき妥当な質問です。これらを試してみると、試行錯誤の中から、皆さんのデータにとって最適のアルゴリズムにたどり着くはずです。私は分類の成功率が 85 パーセントあれば十分であると思いました。

推奨のアルゴリズムは k 近傍法

私達の顧客は、ある選択されたカテゴリー、つまり類似カテゴリーの中に RSS フィード項目を表示したいと望んでいました。フィード項目の分類は単純ベイズ・アルゴリズムによって実現できたので、顧客の要求の最初の部分は満たすことができました。難しかったのは、「類似カテゴリー」を推奨するという要件です。ここで機械学習の推薦システムが登場します。推薦システムは、ある 1 つの項目を他の項目との類似性に基づいて推薦します。この機能の好例が、Amazon.com でのおすすめの商品と Facebook での知り合いの紹介です。

k 近傍法 (kNN: k-Nearest Neighbors) は最も一般的な推薦アルゴリズムです。考え方としては、アルゴリズムに対し、一連のラベル (つまりカテゴリー) と各ラベルに対応するデータセットを提供します。するとアルゴリズムはデータセットと比較して類似の項目を特定します。このデータセットは数値の配列で構成されており、多くの場合は数値が 0 から 1 の間で正規化されています。次にアルゴリズムはデータセットの中にある類似のラベルを特定します。1 つの結果を生成する単純ベイズとは異なり、kNN はいくつかの (つまり k の値で指定される個数の) 推薦項目のリストをランク付きで生成することができます。

私は推薦アルゴリズムの方が分類アルゴリズムよりも理解も実装も簡単だと思いましたが、そのコードはあまりにも長く、数学的にも複雑なため、この記事に含めることはできませんでした。kNN のコーディング例については Manning から『Machine Learning in Action』という優れた新刊が出ていますので、それを参照してください (「参考文献」のリンクを参照)。RSS フィード項目の実装については、ラベルの値は項目のカテゴリーであり、データセットは上位 1000 個の単語それぞれの値で構成される配列です。この配列をどのように構成するかも、一部は科学、一部は数学、一部は芸術です。この配列内の各単語の値は単純な 0 か 1 のブール値であってもよく、記事内での単語の使用頻度をパーセントで表現した値や、このパーセントを指数で表現した値でもよく、さらには他の値であっても構いません。


まとめ

Python、NLTK、機械学習について新たに学んだことは、興味深く楽しい経験でした。Python 言語は強力で簡潔であり、今や私にとって開発ツールキットの中心となっています。機械学習、自然言語、数学/科学アプリケーションに、Python は非常に適しています。この記事では触れませんでしたが、私はチャートやグラフの作成にも Python が有用なことに気付きました。もし皆さんの場合も Python が盲点となっていたのであれば、ぜひ Python を試してみるようお勧めします。

参考文献

学ぶために

  • ウィキペディアで機械学習について学んでください。
  • Python の公式サイトを訪れてください。
  • Peter Harrington 著の『Machine Learning in Action』(2012年 Manning 刊) を読んでください。
  • Steven Bird、Ewan Klein、Edward Loper の共著による『入門 自然言語処理』(2009年オライリー・ジャパン刊) を読んでください。
  • Implement Bayesian inference using PHP」(Paul Meagher 著、developerWorks、2009年3月から5月) を読んでください。この 3 回連載の記事では、ベイズ推論の概念の強力さと潜在的な可能性を理解するために役立つ興味深いアプリケーションを解説しています。
  • developerWorks の Open Source ゾーンには、オープンソース技術を使用した開発や、IBM 製品でオープンソース技術を使用するためのハウツー情報やツール、プロジェクトの更新情報など、豊富な情報が用意されています。
  • さまざまな IBM 製品や IT 業界のトピックに焦点を絞った developerWorks の Technical events and webcasts で最新情報を入手してください。
  • 無料の developerWorks Live! briefing に参加して、IBM の製品およびツールについての情報や IT 業界の動向についての情報を迅速に把握してください。
  • developerWorks podcasts でソフトウェア開発に関する興味深いインタビューや議論を聞いてください。
  • Twitter で developerWorks をフォローしてください。
  • developerWorks demos をご覧ください。初心者のための製品インストール方法やセットアップのデモから、上級開発者のための高度な機能に至るまで、多様な話題が解説されています。

製品や技術を入手するために

  • NLTK のサイトを訪れ、自然言語のデータを扱う Python プログラムの作成方法を調べてください。
  • Python パッケージのインストールや管理のためのツール、pip をダウンロードし、使い方を学んでください。
  • 隔離された Python 環境を作成するためのツール、virtualenv について学んでください。
  • JUnit の Python 言語版、Python unittest 標準ライブラリーについて調べてください。
  • Eclipse の pyeclipse プラグインについて調べてください。
  • Python でプログラミングするための完全な開発ツール・セット、PyCharm IDE について、また Django フレームワークの機能について調べてください。
  • IBM ソフトウェアの試用版にアクセスし (ダウンロードまたは DVD で入手することができます)、特に開発者のために用意されたソフトウェアを利用して皆さんの次のオープンソース開発プロジェクトを革新してください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source, Linux, Web development
ArticleID=844157
ArticleTitle=Python、機械学習、そして NLTK ライブラリーについて探る
publish-date=11082012