豪鬼メモ

一瞬千撃

DBMで単語辞書を作ろう

データベースマネージャTkrzwを無事にリリースしたはよいが、ドッグフードは自分で食わないといけない。DBMを作るとまず最初にやりたくなるのが、それを使った単語辞書を作ることである。仕事柄、英和辞書と和英辞書はよく使うのだが、自分で作ったものを毎日便利に使っている。時間効率と空間効率に優れたDBMは辞書データを扱うのにうってつけだ。ということで、PythonでとDBMライブラリを使って単語辞書を実装していこう。


同じ仕組みで英和辞書でも和英辞書でも独和辞書でも中仏辞書でも何でも作れるが、当然ながら辞書データが必要である。私は普段、英辞郎のデータを購入して使っているが、ライセンス上、デモサイトの公開などには使えない。代わりに、この連載ではプリンストン大が公開しているWorldNetというデータベースを利用して英英辞書を作る方法を紹介する。ただし、入力データを先にTSV形式の中間データに落とし込むことで、同じシステムで他の辞書データを使っても簡単に辞書が作れるようにする。そうすれば、英辞郎でも、EDICTとかGene95とか日本語WordNetとかWictionaryとかのフリー辞書でも、はたまた自作の単語集でも、TSVにさえ変換できればデータソースとして使うことができる。

まずはシステムの名前を決めよう。明瞭でユニークなら何でもいいんだが、とりあえず tkrzw-dict ということにする。そして、要求仕様を決めよう。このシステムでやりたいことは以下のものである。

  • Webページ上の入力フォームに単語を入れると、その語義が表示される。
  • 語義だけでなく、類語や対義語や例文などの付加情報も表示される。
  • 大文字小文字や発音区別符号の有無は無視してヒットさせる。
  • 全部のスペルを入力しなくても済むように、入力パターンと前方一致する単語をヒットさせる。
  • 変化形(動詞の活用、形容詞の級変化、名詞の数変化、英米の綴り違いなど)が入力されても原型の語をヒットさせる。
  • スペルミスをしても検索できるように、字面が似ている語をヒットさせる。
  • 入力語に意味が類似した語をヒットさせる類語検索を実装する。
  • 完全一致、前方一致、曖昧一致、変化形一致、類語のそれぞれの検索機能はJSONAPIとして提供される。
  • UIは上記のAPIを非同期に発行して、結果を一画面に表示する。

検索結果の表示画面はこんな感じになるかな。細かいことは実装しながら変えるだろうし、JavaScriptCSSを駆使してファンシーに仕上げるだろうけど。

Query: [apple_______________]
----
Full match

  apple
    a red and tasty fruit.
    ex: I like apples.
    hypernym: fruit, pome
    plural: apples

  Apple
    a computer company.
    ex: Apple released iPhone SE.
----
Forward match

  apple pie : pie with apple filling or topping
  applet : a small application.
----
Similar spelling

  ripple : a small wave on the surface of a liquid
  triple : a set of three similar things considered as a unit
----
Related words

  fruit : the ripened reproductive body of a seed plant
  orange : round yellow or orange fruit of any of several citrus trees

各検索機能は並列非同期に実行されて、結果が出次第表示される。完全一致検索と前方一致検索の処理は刹那の時間で終わり、それが真っ先に表示されることで、ほとんど遅延を体感しない使用感になるだろう。完全一致と前方一致のレコードはDBMを使えば一瞬で求まる。曖昧検索には全ての登録語に対して編集距離を算定してから結果を並び替える必要があるので、かなりの資源と時間を使う。とはいえ、DBM自体にその機能を実装しているので、すなわちC++の実装で処理がなされるので、100万語程度であれば0.3秒もあればいけるだろう。類語検索は専用の索引を検索したり候補の類似度を算定したりしてから結果を並び替える必要があるので、さらに多くの資源と時間を使う。類語検索でも0.5秒くらい時間をかける贅沢な処理を行い、高精度な結果を追求したい。

最も重要なのは類語検索である。シソーラス検索と言い換えてもよいだろう。他の機能も実用的な単語辞書としては不可欠なのだが、辞書データが同じなら、誰が実装しても同じ結果を返すものが出来上がる。一方、類語検索には様々な方法論が考えられる。細かい設計については別の記事で集中して検討することになるだろう。英作文の際に和英辞書の類語検索は本当に役立つ。英辞郎は語義の部分に類語も多数収録することで、それを転置した和英辞書の再現率を稼ぐという戦略をとっているのだが、それでも拾い損ねる言葉も多い。英辞郎以外の辞書ならなおさらだ。そこで、入力語を類語展開してから検索する方法が考えられる。さらに、検索結果の各語彙の特徴量を合成した中心性との類似度という観点でランキングを行うと、かなり直感的な結果が得られる。面倒な実装になるが、やる価値がある。


概要仕様を考える。まず、実装言語にはPythonを採用する。Rubyでも良いのだが、前回Rubyで書いたので、今回は違う言語で行きたい 。C++Javaは書くのもビルドするのも設置するのも面倒なので避けたい。そうするとPythonが残る。このシステムは大きくわけて、辞書データの構築を行うプログラム群と、辞書データを検索するユーザインターフェイスを担うプログラム群からなる。両方とも実行効率が高いに越したことはないのだが、個人で使う分にはあまり神経質になる必要はない。また、このプロジェクトはいわゆるスクリプト言語でのDBMの有用性について検証することも目的の一つなので、Pythonはうってつけだろう。

辞書データはTSV形式で提供されるものとする。そのTSVをどうやって作るかは辞書データの種類によるが、WordNetに関してはこの連載中に実装することになる。TSVの各行は辞書の各エントリを意味し、第1列に見出し後、第2列に語義という形式を想定する。文字コードUTF-8である。入力データにおいて見出語は正規化されていないものとし、大文字小文字や発音区別符号の正規化は内部で適宜行う。語義についても同様である。語義は人間可読な任意のテキストで良いが、それを構造化したHTMLとして整形するプラグインをUI層で実装することも後で考える。

辞書本体を扱うために、正規化された見出語をキーとし、生の見出語と語義のペアをキーとしたDBMファイルが必要である。これをメインデータベースと呼ぶ。前方一致検索を高速化するために、メインデータベースのデータ構造にはB+木のTreeDBMを採用する。メインデータベースのキーのみを取り出して各行に並べたテキストファイルも作る。これをワードリストと呼ぶ。曖昧検索ではそれを用いる。変化形を含む表層表現から原型を知るために、正規化された表層表現をキーとし、正規化された原型のリストを値とするDBMファイルが必要である。これをサーフィスデータベースと呼ぶ。入力語を見出語の類語に展開するために、正規化された入力語の候補をキーとし、その類義語とそのスコアのペアのリストを値とするDBMファイルが必要である。これをソースワードシソーラスデータベースと呼ぶ。入力語の候補から見出語の候補を得るために、正規化された入力後の候補をキーとし、その見出語の候補とそのスコアのペアのリストを値とするDBMファイルが必要である。これをソースターゲットマップデータベースと呼ぶ。各見出語を類語に展開するために、正規化された見出語をキーとし、その類義語とスコアのペアのリストを値とするDBMファイルが必要である。これをターゲットワードシソーラスデータベースと呼ぶ。それぞれをどう作るかは、詳細仕様で検討する。

UI層はCGIスクリプトとして実装する。性能を考えるときちんとしたRPCのサービスを実装した方が良いのだが、Webサーバが稼働していれば手軽に設置できるという点でCGIスクリプトの利点は大きい。検索APICGIスクリプトとして起動してURLからパラメータを受け取ってJSONのデータを返すことで実現できる。ここで、検索時のフローを考えてみよう。

  • ユーザが検索語を入力して検索ボタンを押すと、JavaScriptAJAX機能で各検索APIが叩かれる。
    • 完全一致検索では、メインデータベースの完全一致検索を行い、その結果を返す。
      • オプションで、ターゲットシソーラスデータベースの結果をマージする。
    • 前方一致検索では、メインデータベースの前方一致検索を行い、その結果を返す。
    • 変化形検索では、サーフェスデータベースを調べ、該当する原型を取り出す。
      • その各々でメインデータベースの完全一致検索を行い、その結果を返す。
    • 曖昧検索では、ワードリストから入力語と最も編集距離が小さい語を候補として見つける。
      • その各々でメインデータベースの完全一致検索を行い、その結果を返す。
    • 類語検索では、ソースワードシソーラスデータベースを調べ、入力語の類語の取り出す。
      • 類語の各々でソースターゲットマップデータベースを調べ、見出語候補を登録する。
      • 見出語候補の各々でターゲットシソーラスデータベースを調べ、類語で見出語候補を拡充する。
      • 見出語候補の各々でターゲットシソーラスデータベースを調べ、特徴量を合算する。
      • 合算特徴量からのコサイン距離で見出語候補をランキングする。
  • 各RPCの結果が返り次第、UIの該当の部分をDOMツリーとしてレンダリングする。

とりあえず、これで動くはずだ。どう考えても類語検索の処理が重くなりそうだが、今使っているKyoto CabinetとRubyの実装ではサクサク動いているので、TkrzwとPythonの組み合わせでも問題はないだろう。というか、もし同じアルゴリズムで性能上の問題があるとしたらDBMライブラリおよびそのアプリケーションとしての沽券に関わることなので、立ち向かって解決せねばならない。

ところで、英辞郎の代用になるような、真にオープンでカバレッジの高い英和辞書データは作れないものか。一般の辞書のように語の定義を性格に記すことも大事だが、英辞郎のように翻訳者視点で類語や言い換えを多数収録する方が実用的になる。WikipediaやWictionaryみたいなモデルで辞書を開発できれば、我々日本人の知的レベルを上げるのに一役買うと思うのだが。まあWictionaryに貢献せよってのが現実的ではあるかな。