以下はあまり整理されていない製作の記録です.
英語論文執筆用の例文検索サービス
英語での論文執筆の際に,専門用語を含む例文や言い回しのパターンを知りたいことが多々あります.有用なサービスとしては- ライフサイエンス辞書のコーパス検索
- Springer Exemplar (2018/2/1頃に終了)
がありますが,
- データがライフサイエンス系の論文に限られている(ライフサイエンス辞書)
- ソートの基準が頻度順ではないため典型的な例文が上位にこない
- ストップワードに近い頻出語を検索した際の
- 検索が重い(Springer Exemplar)
- 表示可能な検索結果が偏る(ライフサイエンス辞書)
という不満点があったので,並行して個人的な資料から検索を行うプログラムを作って使っていました.
しかし,個人的に収集した英語論文ではやはりデータベースが小さく,例文検索エンジンとしてはいまいちだなーと思っていました.
arXiv Bulk Data Access
何がきっかけだったか忘れましたが,去年の年末あたりに arXiv が Bulk Data Access という全論文のデータを一括ダウンロードできるサービスを提供していることに気が付きました.データの公開は Amazon S3 で Requester Pays Buckets として行われており,700GBくらいのデータなので,全部ダウンロードすると8000円くらいかかります(多分それくらい請求されていたと思います).
これをダウンロードして例文検索エンジンを作れば冬休みの間楽しめそうだなと思ったので,今回の冬休みのPCと人間の空き時間をこの課題に投入することにしました.ちなみにダウンロード可能なデータには
- LaTeXソース+図表のファイルのアーカイブ
コーパスへの変換
ダウンロードしてきたデータはLaTeXのソースを含んだアーカイブなので,まずこれを展開して文章を抽出し,コーパスとして出力するプログラムを書く必要があります.LaTeXからプレーンテキストへの変換方法にはざっと調べた感じといった選択肢がありましたが,もとのデータ量がそこそこ多く,ヘッダ,図表,参考文献リスト,箇条書きなどの,そもそもコーパスに入れたくない情報はカットしておきたかったので,構文解析後のAST(抽象構文木)にフィルターを掛けられる Pandoc で変換を行うことにしました.
Pythonで書いたスクリプトですべてのファイルを処理し,得られたテキストデータのコーパスのサイズは約24GBでした.
FM-index に基づく検索エンジンの作成
検索のたびに24GBを普通に総当たりすると耐え難い速度になりますので,
検索用のインデックスを作成する必要があります.
10年ほど前に小規模なデータで同じことをやった時には接尾辞配列を使ったのですが,ざっくり元データの5倍程度のサイズのインデックスが必要で,24GBのコーパスを使うと100GB以上のインデックスをメモリ上に置くことになります.SSD上にインデックスを置くことも考えたのですが,雑な実装では耐え難い遅さだったので,データ構造の改善を考えることにしました.
本筋とは全く関係ないですが,このあたりまでは新年なので普段使わない言語を使おうと思い,C言語なみのパフォーマンスが出せそうな言語という基準で Go言語を使って書いていました.しかしWindows環境で32GB以上のメモリを使えないという仕様にげんなりしたので,やめにしました.代替として Rust を使おうかとも思ったのですが,残り時間が心もとなかったので,久しぶりにC++で書くことにしました.C++11以降の C++ は以前とはまるで別言語のようで,新しい言語を学ぶのと同程度に楽しかったです.
データ構造についての話にもどります.圧縮接尾辞配列を使えば元のデータよりも小さいインデックスで検索が可能という話を小耳に挟んでいたので,これについて勉強することにしました.このあたりの話題については,ネット上にわかりやすい資料がたくさんあって助かりました.
一通り調べて勉強した結果,今現在では FM-index というのを使うのがベストで実装としては Succinct Data Structure Library を使うのが楽ちんという結論に達しました.
WSL由来のiostreamの実装が4GB以上のファイルを読むと死ぬ問題など,色々と大きいデータに絡む問題がこの後も続きましたが,24GBのコーパスを5GB程度のインデックスに圧縮でき,スニペット200個を1秒程度で返せる速さになったので,この辺で満足してつぎの段階に進むことにしました.
WSL由来のiostreamの実装が4GB以上のファイルを読むと死ぬ問題など,色々と大きいデータに絡む問題がこの後も続きましたが,24GBのコーパスを5GB程度のインデックスに圧縮でき,スニペット200個を1秒程度で返せる速さになったので,この辺で満足してつぎの段階に進むことにしました.
Crow + Vue.js によるWebフロントエンド
Webブラウザから検索を行えるようにしたかったので,まず,軽量なC++用のフレームワークである Crow を利用し,REST API の形で検索を提供するアプリケーションサーバーを実装しました.Crowは今どきのC++の機能を活用した使いやすいフレームワークで,Python における Flask と同じような簡単さで書くことができます.
適当なテンプレートエンジンを持ってきてC++のアプリケーションサーバーに HTTP/HTTPS アクセスを受けさせようかとも思いましたが,
Viewの部分には最近流行っていて使うのが簡単そうな Vue.js を使うことにしました.
5年ほど前に一度 AngularJS を使った時にも使いやすさに感動しましたが,Vue.js の方がすんなり使えたような気がします.とはいえ,どちらのフレームワークについても大したことをやらなかったので,明確な差を感じる部分はなかったです.
適当なテンプレートエンジンを持ってきてC++のアプリケーションサーバーに HTTP/HTTPS アクセスを受けさせようかとも思いましたが,
- 静的なファイルを扱うのが面倒
- SSL対応も面倒
- 通信圧縮とかも面倒
- リダイレクトとかしたくなった時も面倒
Viewの部分には最近流行っていて使うのが簡単そうな Vue.js を使うことにしました.
5年ほど前に一度 AngularJS を使った時にも使いやすさに感動しましたが,Vue.js の方がすんなり使えたような気がします.とはいえ,どちらのフレームワークについても大したことをやらなかったので,明確な差を感じる部分はなかったです.
Vue.jsで作ったフロントエンドを含む静的なコンテンツの提供とリバースプロキシを行うための Webサーバーが必要ですので,ここには nginx を使うことにしました.
いつも Apache ばかりで nginx は初めて使いましたが,やっぱり設定ファイルが簡単なのはいいことですね.
Amazon Web Service へのデプロイ
C++で書かれたアプリケーションサーバーには約10GB (順方向と逆方向のインデックス5GB×2)のメモリが必要です.また,起動時に10GBのデータをHDDなりSSDなりから読み込む必要があるので,普段使いのPCで使うたびに起動するには起動時間が長すぎ,常駐させるにはメモリを食いすぎるという微妙な状況になってしまいました.
というわけで,検索エンジンをサーバーで走らせようと思い立ち,話にはよく聞くもののまともに使ったことがなかった Amazon Web Service (AWS) の EC2 を使ってみることにしました.
スペックを眺めてみたところ r4.large というインスタンスが必要最小限のスペックを持つようだったのでこのインスタンスを作成してデプロイしました.設定も簡単で速く,世間でAWSがもてはやされている理由の一端を体感できたように思います.
サービス公開
というわけで,快適な例文検索環境を手に入れることに成功したのですが,EC2 のr4.large インスタンスには月々1万円程度の維持費がかかり,個人的に使うにはちょっと基礎代謝が大きすぎるサービスになってしまいました.個人所蔵の論文集をコーパスとしていた当時は,検索エンジンが合法かどうか議論されているような時代だったので,サービスの公開はあまり考えませんでしたが,
現時点では検索結果としてarXivにリンクする形でスニペットを表示することは問題ないようです.そこで,サービスを公開して大人数で有効活用することで元を取ることにしましたという次第です.
TeXclipでの経験に基づくと,広告でサーバー代が賄えることはあり得なさそうですが,公開することで得られる経験も色々とあったので,今回も元が取れた気分になるんじゃないかな~と期待しています.
感想
検索アルゴリズムやデータ構造にはプログラミングやコンピュータサイエンスのおもしろい要素が詰まっているので,たまに手をだすとおもしろいです.
冒頭の方でチラッと,今回公開した例文検索サービス以前から個人用のツールを作って使っていたことを書きました.サービス名の Hyper Collocation は10年ほど前から存在するこのツールから引き継いだものです.この名前は
- 開発の初期段階で Hyper Estraier を参考にしたこと
- 当時の先生に 新編 英和活用大辞典―英語を書くための38万例 を教えてもらい,
大変役にたったことが開発のきっかけとなったこと
(この辞書の表紙に THE KENKYUSHA DICTIONARY OF ENGLISH COLLOCATIONS と書いてあり,これを見て初めて collocation の概念を知りました.) - 当時は Web2.0 末期(DoCoMo2.0 がコケたころ)で,ハイパー**というネーミングが相当に使い古された感があったので,Hyper Collocation という名前をつけても名前被りの何かが出現する危険がなさそうに思われたこと
から適当に付けたような気がします.今となっては何もかも過去のことであり,当時のコードは一行も残っていませんが,多少の愛着があったので名前だけは残すことにしました.
参考文献
検索アルゴリズム
- 岡野原 大輔: 高速文字列解析の世界――データ圧縮・全文検索・テキストマイニング
書籍としてまとまっているので勉強に良いです. - アルゴリズム - ハクビシンにもわかる全文検索 - Qiita
- FM-indexによる全文検索
- Succinct Data Structure Library 2.0
概要を掴んだら,あとは実装を読むのが理解の早道なように思いました.Succinct Data Structure Library 2.0 は比較的モダンなC++で書かれていて読みやすいです.
C++
- Crow is C++ microframework for web. (inspired by Python Flask)
- json11 ― A tiny JSON library for C++11.
モダンなC++のライブラリなので,使うと今風の書き方がわかったような気になります.