はじめに
本稿では、はてなブックマークの10周年記念の第1弾として開発した「トピックページ」の作り方について解説します。トピックページとは、インターネット上で話題となったトピックを閲覧できるページです。
トピックページは、トピックに関連する記事の集合とトピックを表すタイトルから構成されます。
トピックページ生成の流れは以下の通りです。
-
トピック生成
- トピック表すキーワード集合を獲得し、そのキーワードに関連する記事を収集する。
-
トピックタイトル生成
- トピックに関連する記事の情報を利用してトピックを表すタイトルを生成する。
本稿では、Elasticsearchなどの検索技術を活用したトピック生成方法、および、CaboChaなどの自然言語処理技術を活用したトピックタイトル生成方法について説明します。
対象読者
- Elasticsearchを利用している/したい方
- 検索技術、自然言語処理技術に関心のある方
- 大規模データ処理に関心のある方
必要な環境
- Elasticsearch、Cabochaがインストールされている必要があります。
トピックページとは
トピックページとは、話題の記事の中から関連性の高い記事をまとめることにより、インターネット上で話題となったトピックを閲覧できるページです。
はてなブックマークに蓄積された過去10年分の記事データを利用することにより、10年前のトピックから最新のトピックまでを閲覧できます。また、トピックに関する記事を時系列に追うことができるようになっています。
トピックページは以下の要素で構成されます。
- トピックに関する記事の集合
- トピックを表すタイトル
トピックページを作るには、前述の通り、トピックに関する記事の集合を生成する「トピック生成」と、トピックを表すタイトルを生成する「トピックタイトル生成」という過程が必要になります。
これまでにも、はてなではトピックページの自動生成に何度かチャレンジしてきましたが、トピック生成で十分な精度が達成できず、リリースするまでには至りませんでした。
その要因としては、1段階で直接記事を分類していたことにあると考えています。つまり、記事に含まれるキーワードやタグの情報を利用して記事を直接分類するアプローチをとっていました。
これに対して、今回は、まずトピックを表すキーワードを抽出し、次にトピックに関連する記事を収集するという、2段階でトピックを生成するアプローチをとりました。こうすることで、ノイズ除去のためのチューニングが容易になり、トピック生成の精度を高めることができたと考えます。
トピックタイトルの生成は、はてなでもこれまでほとんど取り組まれてきませんでしたが、自然言語処理分野の要約技術を活用することで解決しました。
以降で、トピック生成とトピックタイトル生成について、それぞれ説明します。
トピックの生成
トピック生成では、まず、トピックを以下のように、キーワードの集合で表し、これらを獲得します。
- 官邸、首相、ドローン、落下、カメラ
- 阿久津、AWAKE、将棋、棋士、投了
- 北陸、新幹線、金沢、開業、長野
これらはそれぞれ、首相官邸にドローンが落下した話題、将棋電王戦で阿久津主税 八段とAWAKEが対戦しAWAKEが投了した話題、北陸新幹線が開業した話題に関するトピックです。
次に生成されたトピックに関連する記事、つまり、首相官邸にドローンが落下したことについて書かれたニュース記事やブログなどを収集します。
トピック生成の流れは以下の通りです。
まず、トピックキーワードの集合を、ElasticsearchのSignificant Terms Aggregationという機能を利用して生成します。次に、キーワードのスコアを利用して、関連する記事を取得します。最後に重複するトピックをマージします。
Elasticsearch Significant Terms Aggregation
全文検索システムのElasticsearchには、Significant Terms Aggregation (執筆時点の最新バージョンである1.6の時点では実験的機能)という、重要語や話題語を取得する機能があります。
Significant Terms Aggregationの実行サンプルを以下に示します。なお、レスポンスは一部省略しています。
{ "aggs": { "terms": { "significant_terms": { "field": "title", "size": 5 } } } }
{ "aggregations": { "terms": { "buckets": [ { "key": "シャルリ", "doc_count": 55, "score": 2.50210212027157, "bg_count": 400 }, { "key": "エブド", "doc_count": 39, "score": 2.277874539496356, "bg_count": 221 }, { "key": "スイスフラン", "doc_count": 57, "score": 1.8023504538916841, "bg_count": 596 }, { "key": "ニュース", "doc_count": 1966, "score": 1.6056558085044332, "bg_count": 732518 }, { "key": "ようじ", "doc_count": 48, "score": 1.511414342391317, "bg_count": 504 }, ] } } }
レスポンスに含まれるbuckets
にキーワードが含まれています。key
はキーワード、doc_count
は指定した範囲でのキーワードの出現頻度、score
はキーワードの重要度を表すスコア、bg_count
は文書全体でのキーワードの出現頻度を表します。この例ではキーワードとして、シャルリ、エブド、スイスフラン、ニュース、ようじが取得されています。
このスコアをSignificant Terms Aggregationではどのように算出しているのでしょうか。Elasticsearch 1.6 (執筆時点の最新バージョン)では5つのスコア計算手法 (JLH score、mutual information、Chi square、google normalized distance、Percentage)が用意されています。
これらのうち、本稿ではJLH scoreを用います。
JLH scoreとは出現割合を利用した計算手法で、指定範囲と全体の出現割合を比較してスコアを算出します。指定範囲での出現割合から全体での出現割合を引いた絶対割合変化と、指定範囲での出現割合を全体での出現割合で割った相対割合変化の積がスコアとなります。このスコアが高いほど、話題性の高いキーワードとなります。
JLH = 絶対割合変化 × 相対割合変化
絶対割合変化 = 指定範囲での出現割合 - 全体での出現割合
相対割合変化 = 指定範囲での出現割合 ÷ 全体での出現割合
はてなブックマークに蓄積した記事データに対して、特定の期間を指定してSignificant Terms Aggregationを適用することで、特定の時期に盛り上がった話題が抽出できます。