はてなブックマークの関連エントリー機能開発、PFI さんとの合宿

はてなブックマーク関連エントリーを配信する機能を追加しました。詳しくは 告知日記で。

この関連エントリーは、株式会社プリファードインフラストラクチャー (以下 PFI) の技術者のみなさんと一緒に開発しました。週末に2泊3日で京都で合宿をしてコア部分を作り、その後京都と東京に分かれてオンラインで連絡を取りながら2週間ほど作り込みをして、今日リリースです。

この合宿では何チームかに分かれて、今回の関連エントリーの機能以外の開発も行っています。その辺の成果はまた後日にリリースできるのではないかと思います。

はてなブックマークの一つの問題として、昔のエントリーがデータベースに埋もれてしまうという点がありました。その問題の解決策としての類似記事抽出、それから検索機能の強化を以前から考えていました。PFI のメンバーのみなさんは情報検索技術のスペシャリストです。アカデミックな研究の成果を製品化を通じて市場に出していきたいという思いが PFI のみなさんの柱にあります。PFI さんの技術力と、はてなのサービス開発力を併せれば考えていたことが実現できるのではないかと思い、合宿ではそこをテーマにしました。

現在はてなブックマークの新しいバージョンを開発しています。合宿の成果物は新バージョンでリリースしようかとも思っていたのですが、関連エントリー機能は思いのほか開発がスムーズに進み精度も良く出たので、現行バージョンに搭載してリリースしました。

システム開発の裏話

関連エントリーは、PFI の CTO の太田さん (kzk さん)、吉田さんと自分の 3人のチームで開発しました。吉田さんがインデクシングと類似記事検索のコア部分。太田さんは吉田さんをサポートしつつ、C++ で書かれたエンジンをサーバーに載せて API 化する中間部分、自分ははてなブックマークからデータを抽出加工して出す部分、アプリケーション部分を担当しました。

データの加工には、以前に作った Hadoop Streaming 用のライブラリが役に立ちました。Hadoop は今回は利用していないのですが、MySQL から抽出したデータは転置インデックスのインデクサへ渡す形式に変換される必要があり、Hadoop (MapReduce) はもともとその目的のために作られていることもあって、運良くライブラリだけを流用することができました。MySQL からのデータ抽出は MySQL の C API でシンプルな SQL でストリームで引き出すようにし、ストリームをこのライブラリに渡して Perl で加工処理します。

抽出したデータを、吉田さんが開発したインデクサに入力すると類似記事抽出用の転置インデックスができあがります。このインデックスを使って検索をすると類似記事が得られます。アルゴリズムの解説は、もしかしたら吉田さん本人がしてくれるかもしれません。

C++ で書かれたエンジンと、はてなブックマークPerl のコードの結びつけには太田さんの提案で Thrift を利用しました。Thrift は Facebook が開発し公開している多言語 RPC フレームワークです。Thrift を使うと C++ で書かれたドメインロジックを TCP/IP の RPC 経由で Perl から呼び出すことができます。いわゆる分散オブジェクト技術を多言語に展開したフレームワークです。(Thrift については次回の WEB+DB PRESS の連載で解説してみたいと思っています。)

Thrift でこさえたマルチスレッドな検索サーバーに類似記事検索エンジンを載せて、Perl から入力を与え RPC で結果を得ます。得られた結果を MySQL 上のデータと付き合わせて最終的な出力として加工し、ページに出します。

基本的には、こんなシステムです。この手順を確立するまでにはそんなに時間はかからなかったのですが、問題は精度を出すところでした。はてなブックマークの色々なデータを出したり、エンジン側のアルゴリズムも調整しながらの試行錯誤でした。合宿最終日の午前中に、ついに満足のいくパターンが分かって、サービス化の目処が立ちました。少し暗雲立ちこめる雰囲気だったこともあり、非常に嬉しい瞬間でした。

関連エントリ―機能の今後

ひとまずリリースはできましたが、課題もあります。今のところ考えているのは以下のようなものです。

  • すでに消去されてしまっているページなども抽出されてしまうので、定期的にページの生存チェックを行う必要があります。特にニュース記事などは、一定時間以上過去の記事は参照できないものも多く空振りが目立ちます。
  • リリースの感想として、ページの下に配置して欲しいという声を多くいただいています。検討したいと思います。ただ、新しい機能の開発直後は毎度のことなのですが、これまでの使い方の慣性が残っていて自分も客観的に判断しづらいという点があります。一週間程度様子を見た上、レイアウトを検討しようと思います。
  • 精度の向上。ブックマークがたくさんついているエントリーは、かなり高い精度で関連したエントリーを抽出することができています。問題はブックマークデータが少ない場合です。ブックマークが少ないと関連記事を出すための入力が足りずに精度が落ちてしまいます。ある程度データが少なくても精度が高くなるような工夫は施しているのですが、まだまだ改善が必要そうです。
  • API の提供。関連エントリー機能は API として提供し、外部プログラムからも利用できるようにしたいと思っています。パフォーマンスや精度などをしばらく様子見して問題なさそうであれば、RSS フィードや JSON での出力を提供できればと思っています。

この辺りは新システムの開発と並行して継続して取り組んでいきたいと思います。

類似記事抽出用のエンジンは汎用的に作られているので、このエンジンを利用して他の機能を作る事もできそうです。幾つか試していますが、結構面白い物が出てきていますので、楽しみにしていてください。

PFI のみなさんとのお仕事

PFI のみなさんとの合宿はとても刺激的でした。ICPC 世界大会出場レベルのプログラマのみなさんのコーディングスピードの速さや正確さ、問題解決能力の力量にはただただ驚かされるばかりでした。個人的には、数学的に問題を解くためのアプローチ、確立された理論を使って問題を細かく細分化していく手順、既存の理論にとらわれずに新しい発想で理論自体を作り替えて検証していく考え方など、なるほどこうやって進めるのだな、というのが肌で感じられたのは非常に大きな収穫でした。

晩ご飯でお酒を飲んでいる間もアルゴリズムの改善の話題や、数学の話題が飛び交っていました。更に驚いたことに、みんなお酒を飲んで良い気分になっているにも関わらずそのままオフィスにもどってコードを書いていました。みんな本当にプログラミングが好きなんだなあと、見ている自分も嬉しくなりました。そのテンションに当てられて、自分も酔っぱらったまま朝四時まで開発したりもしました。

PFI ではペアプログラミングでの開発が基本のようで、チームははてなスタッフ1名 + PFIスタッフ2名という3名のチームをいくつか作って進めました。PFI の社長の西川さんが "ICPC で学んだことは、よくできたチームはコミュニケーションがコストにならずに生産性を加速させること。PFI はこれを会社でも実現することを目標にし、妥協しない。" ということを以前におっしゃっていたのですが、まさにそういうスタイルでの開発だったように思います。途中何かしらのアルゴリズムなどで開発が行き詰まると、もう一人のスタッフが理論を提示し議論が始まり、議論の中から改善案が生まれて、すぐにそれがコードに反映されます。その繰り返しです。

ペアプログラミングがお互いの生産性を高めるというのは自分も経験済みでしたが、共通の成功体験と高度な共通知識がある二人でのペアプログラミングは、バグを指摘してもらえるとか、書いたコードを一番少ない手間で共有できるといった一般的なペアプログラミングのメリットを遙かに超えた爆発的な生産性を生み出すのだなと感心しました。

PFI さんとの合宿を経験して自分の成長目標や、はてなの開発レベルの目標をずっと高い位置に引き上げることができましたし、そのイメージもはっきりと具体的になったように思います。そういう意味でも、非常に収穫の多い合宿でした。

今後について

今回 PFI さんとは戦略的提携を交わしています。今回の関連エントリー機能だけでなく、お互いの長所を活かした新機能、新サービスの開発を一緒に進めていきたいと思っています。