Elasticsearch Twitter ツィート情報リアルタイム分類

Kunihiko Kido
Hello! Elasticsearch.
7 min readDec 16, 2015

Twitter のツィートテキスト情報をもとに、リアルタイムに分類してみる。

機械学習を使った多項分類では、教師データが必要で、教師データがない場合には、この正解データを作成することがかなり大変です。例えば今回のタイトルにあるように Twitter のツィート情報をもとに任意のカテゴリーに分類したい場合、教師データを作成することも大変ですが、Twitter の特性上、特徴を抽出するためのテキスト情報も少なく、さらにせっかく作成した教師データもすぐに古くなってしまう(使い物にならない)可能性があります。

なので、今回は Elasticsearch のパワーを使って大量のツィート情報をリアルタイムに分類したいと思います。

システム概要

一般に公開されている Twitter Streaming API (statuses/filter)を使って言語を ja のみ、キーワードを twitter にすると、実績値では、ツィート数:約450万件/日、インデックスサイズ:約20GB/日相当のデータが流れてくるので、それなりのシステム構成で実施します。下記はそのシステム概要です。

ツィート情報リアルタイム分類システム概要

ストリーミングしてきたツィート情報を直接 Elasticsearch に流し込んでしまうと、パンクもしくは、Elasticsearch のサーバー台数がたくさん必要になってしまうので、Amazon Kinesis Firehose を使ってバッファリングの調整が出来る構成になっています。

主なプロセスは、過去のツィート情報から分類ルールを作成するための「分類機作成プロセス」と、ストリーミングしてきたツィート情報をリアルタイムに分類及びインデックスする「分類・インデックスプロセス」です。

分類機作成プロセス

分類機作成プロセスでは、過去のツィート情報をもとに、新しいツィート情報を任意のカテゴリーに分類するためのルールを自動的に作成してしまおうという仕組みです。

例えば、「サッカー」と言う分類ルールを作成するには、人が最初に与えるインプットの情報は、「サッカー」と言うキーワードだけです。

あとはこのキーワードをもとに定期的に過去のツィート情報からその特徴を抽出して、その特徴をもとに分類ルールの作成と登録をする処理を実装します。

特徴抽出

まずは、「サッカー」を例に特徴を抽出する方法について見ていきましょう。Elasticsearch の Significant Terms Aggregation を使用して実現します。

以下はそのクエリー例です。※ ちなみに書式は Elasticsearch 2.0 系です。

1)の人が与えるキーワードは、例えば「Jリーグ」とか「なでしこジャパン」など一つの単語でその分類とわかるシンプルな単語を列挙しましょう。

2)の検索対象フィールドは、ツィートのテキスト情報がインデックスされているフィールドです。

3)の集計期間の絞り込み条件は、なるべく最新のツィート情報からその特徴を抽出したいため、過去1日以内のツィートに絞り込んでいます。

4)の特徴を抽出する対象フィールドは、ツィートのテキスト情報から名詞形品詞のみインデックスしているフィールドです。

このクエリーを実行すると過去1日のツィート情報から「サッカー」を含むツィート情報を対象に特徴的なキーワードをランキング形式で得ることができます。

分類ルール作成

次に、「特徴抽出」人が与えたキーワードと、クエリーで得た、サッカーにを表す特徴キーワードを使って、分類ルールを作成します。分類ルールの作成では、Elasticsearch の Percolator と言う機能を使用します。

※ 参考:Percolator パーコレーター

Percolator に登録するのはドキュメントではなく、クエリーです。ですので、分類ルール==クエリーと理解してもらえれば良いかと思います。

以下はその例です。

このクエリーは、「人が与えたキーワード」または「自動抽出した特徴キーワード」にマッチする条件になっています。

category や type、id、created などは、このクエリーのメタ情報として付けています。

あとは、このクエリーを Percolator API を使って登録すると「分類機作成プロセス」は完了です。

この一連の流れを自動化して、定期的に最新のツィート情報を使用して分類ルールを更新してください。

実際は、精度を上げるため自動抽出したキーワードからたのカテゴリーでも出現するような単語の除外など調整していますが、基本的な考え方は以上になります。

分類・インデックスプロセス

次に、「分類機作成プロセス」で作成した Percolator を使用して、新しく入ってきたツィート分類する仕組みについて説明します。

分類予測

新しくストリーミングされてきたツィートを分類予測するには Elasticsearch の Percolator API を使用します。

以下はその例です。ツィートのテキスト情報を条件にしていることがわかるかと思います。また、マッチした Percolator Query の分類名を取得するためメタ情報として設定した category フィールドで集計しています。

以下の結果例では、あらかじめ「テクノロジー」で作成したクエリーにマッチしたことになります。

インデックス

あとは、Percolator API で得た分類結果をツィート情報のメタ情報として追加し、Elasticsearch へインデックスすれば完了です。

また、このプロセスで新しくインデックスしたツィートデータは分類機作成プロセスでまた使用される仕組みのため、新しいツィート情報にも対応することができます。

リアルタイムに分類されてく様子をKibanaで可視化するとこんな感じ

最後に

ツィートデータ1件1件みると、「なんでこの文章がこのカテゴリーに分類されているんだろう?」と言うのもちらほらあるので、まだまだチューニングの余地はありますが、特定のユーザーで絞り込んで、そのユーザーの傾向を見たり、特定のキーワードで絞り込んでその傾向を見たり、大量のツィートデータから様々な視点でカテゴリー傾向を分析するには十分な感じで実装できたかなと思います。

追記:2016/01/06

特徴抽出するときのクエリーは、Significant Terms Aggregation に percentage オプションを追加して、さらにその Aggregation の結果 score が 70%以上のものを採用すると精度が出やすいです(制御しやすい)。

また、percentage オプションを使用すると、3/3 = 100% になるので、ニッチな特徴がランキングされやすいため、合わせて、min_doc_count と shard_min_doc_count を設定して調整すると良い。

参考:Percentage

{
"query": {
"bool": {
"must": [{
"simple_query_string": {
"query": "\"サッカー\"",
"fields": ["text"]
}
}],
"filter": [{
"range": {
"created_at": {
"gte": "now-2d",
"lte": "now"
}
}
}]
}
},
"aggs": {
"keywords": {
"significant_terms": {
"field": "text.noun",
"size": 300,
"exclude": {
"pattern": ".{1,2}|[a-zA-Z0-9]+|[ぁ-ん]+|[0-9]+.*"
},
"percentage": {},       // これ追加
"min_doc_count": 10 // これで制御

}
}
},
"size": 0
}

--

--