ユーザストーリー

Elasticsearchで簡単にテキスト分類を実行

Elasticsearchは、検索および分析エンジンとして広く使用されており、そのテキストマイニングAPIとしての機能はあまり知られていません。

このブログでは、Elasticsearchでテキスト分類を実行する方法について紹介します。私には、計算言語学に関する知識と、テキストマイニングの分野におけるフリーランサーとしての数年間のビジネス経験があり、さまざまなシナリオにおいて下記のようなテクニックを実装およびテストする機会がありました。

Elasticsearchに初めて出会ったとき、私はその使いやすさとスピード、構成オプションに魅了されました。Elasticsearchを使うたびに、それまで使用していた自然言語処理(NLP)ツールやテクニックよりもさらにシンプルな処理方法が見つかるのです。

私はゼロから実装するように訓練されていましたが、ある時、Elasticsearchのそのままの設定で多くのことを解決できることが分かりました。

ほとんどのNLPタスクは、次のような標準の事前処理パイプラインから開始されます。

  1. データの収集
  2. 生テキストの抽出
  3. 文章の分割
  4. トークン化
  5. 正規化(ステミング、レンマ化
  6. ストップワードの除外
  7. 品詞のタグ付け

構文解析などの一部のNLPタスクには、詳細な言語分析が必要です。

このような種類のタスクに関して、Elasticsearchは、そのままでは最適なアーキテクチャーとデータフォーマットを提供しません。つまり、トークンレベルより後のタスクの場合、全文にアクセスするカスタムのプラグインを記述または使用する必要があります。

しかし、分類、クラスター化、キーワード抽出、類似度の測定などのタスクは、正規化された、または可能であれば重み付けされた、任意のドキュメントのBag of Words表現が必要になります。

ステップ1と2は、ElasticsearchのIngest Attachment Processor Plugin(5.0より前ではMapper Attachments Plugin)で解決できます。

これらのプラグイン用の生テキストの抽出には、最も一般的なデータフォーマット(HTML/PDF/Wordなど)で使用可能なApache Tikaが使用されます。

ステップ4から6は、そのまますぐに使える言語アナライザーで解決できます。

サンプルマッピング:

  {
    "properties":{
       "content":{
          "type":"text",
          "analyzer":"german"
       }
    }
  }

特定のフィールドのマッピングタイプが"text"(5.0より前では"analyzed string")であり、Elasticsearchがネイティブでサポートする言語のうちの1つにアナライザーが設定されている場合、トークン化、ステミング、およびストップワードの除外はインデックス時に自動的に実行されます。

これにより、Apache Tikaがサポートする任意の種類のドキュメントからBag of Words表現に到達するためにカスタムコードやその他のツールは必要ありません。

Elasticsearchが実行されている場合、言語アナライザーはREST APIで呼び出すことも可能です。

curl -XGET "http://localhost:9200/_analyze?analyzer=english" -d'
  {
   "text" :"This is a test."
  }'
  {
    "tokens":[
       {
          "token":"test",
          "start_offset":10,
          "end_offset":14,
          "type":"<ALPHANUM>",
          "position":3
       }
    ]
  }

Elasticsearchを使用しない場合のアプローチは次のようになります。

カスタムコードでのテキスト収集、手動またはTikaライブラリを使用したドキュメント解析、従来のNLPライブラリまたはNLTKOpenNLPStanford NLPSpacyなどのAPIの使用、またはリサーチ部門が開発したその他のツールなどの使用です。ただし、リサーチ部門が開発したツールは通常、エンタープライズのコンテキストではあまり役に立つものではありません。往々にして、データフォーマットはプロプライエタリであり、ツールはコンパイルしてコマンドラインで実行することが必要で、結果は単純に標準出力にパイプするものがほとんどです。REST APIは例外です。

一方、Elasticsearchの言語アナライザーを使用すれば、必要になるのはマッピングの構成とデータのインデックスだけです。インデックス時に事前処理が実行されます。

テキスト分類に対する従来のアプローチ

テキスト分類は従来、教師付き機械学習で解決されてきたタスクです。モデルをトレーニングするための入力は、ラベル付けされたドキュメントのセットです。この最も簡単な表現は、次の 2 個のフィールドを持つ JSON ドキュメントです:

「content」と「category」

従来、テキスト分類はSciKit LearnWekaNLTKApache Mahoutなどのツールで解決しています。

モデルの作成

ほとんどの機械学習アルゴリズムには、データのベクトル空間モデル表現が必要です。この機能空間は通常、特定のデータセットの最も重要な1万語などです。単語の重要性はどのようにして計測するのでしょうか。

通常、TF-IDFを使用します。これは1970年代に発明された式です。TF-IDFは、特定のドキュメント内の用語を、データセットのその他の部分と比較してスコア付けする指標です。つまり、ドキュメント内の用語のTF-IDFスコアが高い場合、その用語は非常に特徴的なキーワードであることになり、この用語によってそのドキュメントは他のすべてのドキュメントから区別されることになります。

ドキュメントのサブセット内で最も高いTF-IDFスコアのキーワードは、トピックを表していると捉えることが可能です。テキスト分類できわめて一般的なのは、機能空間にTF-IDF総合スコアが最も高いn個の単語があることです。

各ドキュメントは機能ベクトルに変換され、その後、各クラス/カテゴリーのすべてのトレーニングインスタンスとともに使用され、モデルが作成されます。これにより、そのモデルに従って新しいドキュメントを分類することが可能になります。そのため、ドキュメントは機能ベクトルに変換される必要があり、それによってすべての類似度が計算されます。ドキュメントは、最も高いスコアのカテゴリーでラベル付けされます。

Elasticsearchでのテキスト分類

Elasticsearch(またはLucene)では、上記のすべてをもっとシンプルな方法で解決できます。

次の4つのステップを実行するだけです。

  1. マッピングを構成する("content" : "text", "category" : "keyword")
  2. ドキュメントをインデックスする
  3. More Like This Query(MLTクエリ)を実行する
  4. そのクエリのヒット数をスコアで集計する小規模なスクリプトを記述する
PUT sample
  POST sample/document/_mapping
  {
    "properties":{
       "content":{
          "type":"text",
          "analyzer":"english"
       },
       "category":{
          "type":"text",
          "analyzer":"english",
          "fields":{
             "raw":{
                "type":"keyword"
             }
          }
       }
    }
  }
  POST sample/document/1
  {
    "category":"Apple (Fruit)",
    "content":"Granny Smith, Royal Gala, Golden Delicious and Pink Lady are just a few of the thousands of different kinds of apple that are grown around the world!You can make dried apple rings at home - ask an adult to help you take out the core, thinly slice the apple and bake the rings in the oven at a low heat."
  }
  POST sample/document/2
  {
    "category":"Apple (Company)",
    "content":"Apple is an American multinational technology company headquartered in Cupertino, California, that designs, develops, and sells consumer electronics, computer software, and online services.Its hardware products include the iPhone smartphone, the iPad tablet computer, the Mac personal computer, the iPod portable media player, the Apple Watch smartwatch, and the Apple TV digital media player.Apple's consumer software includes the macOS and iOS operating systems, the iTunes media player, the Safari web browser, and the iLife and iWork creativity and productivity suites.Its online services include the iTunes Store, the iOS App Store and Mac App Store, Apple Music, and iCloud."
  }

MLTクエリは、テキストマイニングにおいてきわめて重要なクエリです。

これは次のように機能します。任意のテキストを処理し、実際の「モデル」と比較して上位n個のキーワードを抽出し、それらのキーワードでブール一致クエリを実行します。このクエリは類似したドキュメントの収集によく使用されます。

すべてのドキュメントにクラス/カテゴリーラベルがあり、クラス単位で類似した数のトレーニングインスタンスがある場合、これは分類と同等になります。入力ドキュメントをlikeフィールドとして使用してMLTクエリを実行し、ヒット数上位n個のスコアおよびカテゴリーを集計する小規模なスクリプトを記述します。

GET sample/document/_search
  {
    "query":{
       "more_like_this":{
          "fields":[
             "content",
             "category"
          ],
          "like":"The apple tree (Malus pumila, commonly and erroneously called Malus domestica) is a deciduous tree in the rose family best known for its sweet, pomaceous fruit, the apple.It is cultivated worldwide as a fruit tree, and is the most widely grown species in the genus Malus.The tree originated in Central Asia, where its wild ancestor, Malus sieversii, is still found today.Apples have been grown for thousands of years in Asia and Europe, and were brought to North America by European colonists.Apples have religious and mythological significance in many cultures, including Norse, Greek and European Christian traditions.",
          "min_term_freq":1,
          "max_query_terms":20
       }
    }
  }

このサンプルは、ワークフローを示すことのみを意図しています。実際の分類については、もう少し多くのデータが必要になります。そのため、この例で実際に結果を得られなくても心配は不要です。さらにデータを追加するだけで正しく機能します。

以下は、小規模なPythonスクリプトです。応答を処理し、入力ドキュメントに関して最も可能性の高いカテゴリーを返します。

from operator import itemgetter
  def get_best_category(response):
     categories = {}
     for hit in response['hits']['hits']:
         score = hit['_score']
         for category in hit['_source']['category']:
             if category not in categories:
                 categories[category] = score
             else:
                 categories[category] += score
     if len(categories) > 0:
         sortedCategories = sorted(categories.items(), key=itemgetter(1), reverse=True)
         category = sortedCategories[0][0]
     return category

以上が、Elasticsearchでテキスト分類を実行する方法です。

ユースケース

テキストの分類は、実際にNLPのユースケースとして非常に一般的です。Eコマースデータ(製品)について考えてみてください。多くの人が、アフィリエイトリンクのあるEコマースショップを運営しています。データはいくつかのショップから提供され、通常はカテゴリータグが付けられています。ただし、カテゴリータグは各ショップで異なります。そのためカテゴリーシステムを統一する必要があり、新しいカテゴリーツリーに従ってすべてのデータを再分類する必要があります。または、ビジネスインテリジェンスアプリケーションの場合は、企業のセクター(美容院、パン屋など)に従って企業のウェブサイトを分類する必要があります。

評価

私は標準のテキスト分類データセットである20 Newsgroupsデータセットを使用して、このアプローチを評価しました。ドキュメントの12%のみを含めた場合に、高品質スコアのしきい値で最高の適合率(92%の正確なラベル)を達成しました。すべてのドキュメントにラベルを付けると(100%の再現率)、予測の72%が適合していました。

20 Newsgroupsデータセットのテキスト分類に関する最良のアルゴリズムは、通常、SVNおよびnaive Bayesです。これらは、データセット全体に関して、より高い平均適合率を達成します。

このように、より優れたアルゴリズムがあるのに、Elasticsearchの使用を検討するべき理由は何でしょうか。

それにはいくつかの実用的な理由がありますが、そのうちの1つは、SVMモデルのトレーニングには時間がかかることです。特に、スタートアップ企業の場合や、さまざまな顧客またはユースケースに迅速に適応する必要がある場合は、大きな問題になる可能性があります。データが変更されるたびにモデルを再トレーニングできない場合があります。私自身、ドイツの大手銀行でのプロジェクトで経験しています。このため、古いモデルを使用することになり、想定されるような良好なスコアを達成することはありません。

Elasticsearchのアプローチでは、インデックス時にトレーニングが実行され、モデルはアプリケーションのダウンタイムを発生させずにいつでも動的に更新できます。データをElasticsearchに保存した場合、追加のインフラストラクチャーは不要です。通常、10%を超える非常に正確な結果で、最初のページを埋めることができます。これは多くのアプリケーションで良好な第一印象として十分です。

ではなぜ、他のツールがあるのにElasticsearchを使うのでしょうか。

なぜなら、そこにすでにデータがあり、Elasticsearchは基盤となる統計を事前に計算するからです。無料でNLPを手に入れるようなものです。


Saskia Vola

Saskia Volaは、ハイデルベルク大学で計算言語学を修め、2009年よりテキストマイニングの分野でのキャリアを開始しました。ベルリンでスタートアップ企業に数年間勤務した後、フリーランサーとしてのキャリアを歩み始め、デジタルノマドの生活を楽しんでいます。関連プロジェクトの依頼が増え続けているため、NLP/AIフリーランサー用のプラットフォームであるtextminers.ioを立ち上げました。