豪鬼メモ

一瞬千撃

WordNetを使った辞書検索システムのプロトタイプ

DBMで単語辞書を作る連載の3回目だ。今回はデモを実装した。仕様を単純化したプロトタイプであり、基本的な機能の説明をするのに丁度よいはずだ。それにもかかわらず、普通に実用できるものに仕上がっている。
f:id:fridaynight:20200816134652p:plain


まずはこちらのデモサイトを使ってみていただきたい。検索窓に英語の単語またはフレーズを入れれば、その言葉が検索できる。該当した言葉があれば、その言葉の日本語訳と、英文による語義説明が表示される。同義語やその他の関連語も表示される。日本語の言葉を入力して検索することもできる。

主たるデータソースとしてはWordNetを使っている。WordNetは名前が示す通りに言葉のネットワーク構造を表現することに主眼が置かれており、synsetと呼ばれる同義語(synonym)の集まり毎に語義の説明がつけられている。なので、語義の説明を読むよりは、同義語に目を通した方が理解しやすかったりする。例えば、英文を読んでいて、「hoax」という単語の意味がわからなかったとしよう。それを検索すると、同義語として「fraud」が表示されるので、詐欺とか騙すとかいう意味なんだろうと理解できる。

さらに、今回は日本語WordNetのデータをマージして、日本語訳も表示している。全てのエントリをカバーしているわけではないし、自動生成された翻訳なので間違いもたまにあるが、だいたいの意味を理解するには有用だ。日本語が入力された場合にはこの翻訳語を元に作られた転置索引を使って検索を行う。これで英和辞書としても和英辞書としても英英辞書としても使えることになる。英文読解には英和辞書を、英作文には和英辞書を使うのが通常だが、ついでに英英辞書の語義説明が目に入るとなかなか勉強になって良い。


情報検索システムとして捉えると、単に入力パターンとの完全一致で該当するレコード表示する機能しか実装していないので、えらく単純にも思える。しかし、データを準備するのが面倒くさいのだ。そこに至るまでにそれなりの知見が必要になる。それを説明していきたい。

まず、WordNetのデータを取ってきて、それを辞書検索用のデータベースに変換する必要がある。既に述べたが、WordNetはsynsetという同義語の集合をノードとして、そのノード間のネットワークを記述したデータベースである。語のリストはsynsetの属性として記述される。同じsynsetに属する語は互いに同義語だと言うことができる。また、一つの語が複数のsynsetに属することも多く、それによって語義の多様性が表現される。

WordNetのパッケージをダウンロードして展開すると、dictというディレクトリに、data.noun、data.verb、data.adv、data.adjという4つのファイルを見つけられる。それぞれ、名詞と動詞と形容詞と副詞のsynsetのリストを構造化されたテキストデータとして格納している。他の品詞はない。代名詞も冠詞も前置詞も接続詞もガン無視である。ただ、それらは内容語というよりは機能語として振る舞うので、意味のネットワークの中に位置させられないのは仕方ない。辞書データとして考えても、「the」とか「by」とかわざわざ検索することはあまりないだろうから、それで問題ない。

WordNetのデータファイルは初見では恐ろしげだ。しかし、よく見るとそうでもない。スペース区切りでsplitしてから各要素を見ていけばよい。第1要素がIDで、第2要素が品詞、第3要素が16進数の所属語数だ。各々の所属語は2つの要素を使うので、2個ずつ読めばよい。その後に別のsynsetへのポインタ(=エッジ)の数が10進数で来て、その後に個々のポインタの情報が4つ組で繰り返される。最後に縦棒で区切られて語義の説明文が来る。怖くない。

00001930 03 n 01 physical_entity 0 007 @ 00001740 n 0000 ~ 00002452 n 0000 ~ 00002684 n 0000 ~ 00007347 n 0000 ~ 00020827 n 0000 ~ 00029677 n 0000 ~ 14580597 n 0000 | an entity that has physical existence

WordNetのデータファイルをDBMに変換するプログラムとして、build_wordnet_db.pyというプログラムを書いた。WordNetのデータファイルの仕様はこのページに書いてあるので、それに基づいてデータをパーズして、synsetの連想配列を作る。キーはsynsetのオフセットに品詞情報をつけた「01234567-a」のような文字列で、値は単語のリストと関連するsynsetのIDのリストと語義説明の文字列の3つの要素を持つタプルだ。その連想配列を転置して、語をキーにして、その語が所属するsynsetのリストを値にした別の連想配列を作る。最後に、その構造をDBMファイルとして書き出す。値であるsynsetのリストはJSON形式でシリアライズする。結果として、以下のようなレコードを持つDBMファイルが出来上がる。

key: "japan",
value: """{
  "item": [
    {
      "synset":"08921850-n",
      "surface":"Japan",
      "pos":"noun",
      "gross":"a constitutional monarchy occupying...",
      "synonym":["Nippon","Nihon"],
      "hypernym":["Asian country","Asian nation"]
    }, {
      "synset":"03593222-n",
      "surface":"japan",
      "pos":"noun",
      "gross":"lacquer with a durable glossy black finish...",
      "hypernym":["lacquer"]
    }
  ]
}
"""

英英辞書としての基本的な体裁はこの時点で整っている。小文字に正規化されたキーを使ってDBMを検索するとJSONの構造化データが得られるので、それをUI上でレンダリングすれば良い。端末のコマンドとCGIスクリプトとして動作するsearch_wordnet.pyというプログラムでデモサイトを作った。検索処理のアルゴリズムライブラリにまとめてある。

最初の計画ではTSVで辞書を表現するつもりだったが、思ったより構造が複雑になりそうなので、JSONを格納したDBMを使うことにした。空間効率は最適ではないが、それで性能に問題が出ることはないだろう。


英和辞書として使うには、英語での検索機能はそのままに、日本語で記述した意味を結果の画面に表示する必要がある。日本語WordNetのデータをJSONの構造化データにマージすればよい。それを行うプログラムとしてappend_wordnet_jpn.pyというプログラムを書いた。日本語WordNetのデータはTSVファイルとして提供され、各々のレコードは、「01234567-a」のようなsynsetのIDと、それに対応する日本語の言葉を記述したものである。なので、synsetのIDをキーにした連想配列に日本語の言葉のリストを入れておいてから、DBMの各レコードを読み込んでマージ処理を行えばよい。

ここで工夫が求められる。日本語WordNetのデータには機械翻訳の結果が混じっていて、不自然な訳語も多く収録されている。同じ言葉の表記揺れも多数収録されている。その中から、辞書の語義として有益な訳語を選択して優先的に表示したい。例えば「music」の訳語として「ミュージック」「楽」「笛竹」「音楽」が収録されているが、その中からは「音楽」を選びたい。理想的には、「music」が「an artistic form of auditory communication incorporating instrumental or vocal tones in a structured and continuous manner」という意味で使われた場合に最適だと思われる語を選択したい。その努力は既に日本語WordNetを作るときになされているわけが、残念ながらそこでのスコア情報が提供されていない。ここで同様の翻訳システムを実装するのは大変すぎるので、簡便法を用いる。

文脈を無視して、頻出語を優先することにする。そうすると多くの例で、広義の略語から狭義の略語へという順番で並べられるので、理解しやすい。以前の記事で、英語版と日本語版のWikipediaの記事に出現する単語の頻度を数えて確率を算出した。そのデータベースを再利用すればいけそうだ。「ミュージック」の出現率は0.1%、「楽」の出現率は0.04%、「笛竹」の出現率は0%、「音楽」の出現率は0.5%ということなので、晴れて「音楽」が最初に来る。

結果として、データベース内のJSONデータは以下のようになる。訳語のリストはtranslationという属性で加えられ、訳語の中で最大の出現率をscoreという属性として記録する。また、英語の語自身でも出現率を調べてscoreという属性として記録する。

key: "japan",
value: """{
  "score": ".002866",
  "item": [
    {
      "synset":"08921850-n",
      "surface":"Japan",
      "pos":"noun",
      "gross":"a constitutional monarchy occupying...",
      "synonym":["Nippon","Nihon"],
      "hypernym":["Asian country","Asian nation"]
      "translation":["日本"],
      "score":".029942"
    }, {
      "synset":"03593222-n",
      "surface":"japan",
      "pos":"noun",
      "gross":"lacquer with a durable glossy black finish...",
      "hypernym":["lacquer"],
      "translation":["漆"],
      "score":".000031"
    }
  ]
}
"""

英語と日本語のそれぞれにつけられたスコア情報は、検索結果の項目を並び替えるのに利用される。例えば「japan」が検索された場合、「日本」に相当するsynsetと「漆」に相当するsynsetが得られるが、その中から「日本」のsynsetを優先して表示したい。それは「日本」の出現率が「漆」より高いので可能である。ここでも、広義から狭義へという順番に並べるのは合理的だ。ただ、副作用もある。「peanut」の1位が「少ない」になってしまうように、特殊な文脈で広義の意味を持つ語の語義の並びが直感に反するものになることがある。これを解決するには別の辞書や翻訳システムをを重み付けに使うしかないだろうが、それはまた別の機会に検討する。

英語につけられたスコア情報は、日本語で検索した場合の項目の並び替えに利用される。例えば「走る」で検索した場合、「pelt along」「hasten」「breeze through」「hie」「run」「step on it」「race」「speed」「drive」などなどが該当するが、その中から「run」を選びたい。Wikipedia英語版における出現率は「run」が0.2%、「race」が0.6%、「speed」が0.1%、「drive」が0.09%、その他はやたら低いので、結果として「race」が1位で「run」が2位になる。「race」が1位になるのはWikipediaらしいバイアスの結果っぽいが、「run」が2位に来ているので良しとしよう。なんのスコアリングもしないと、いきなり「pelt along」が表示されて面食らうことになるので、こんな素朴な方法でも役に立つ。

日本語で検索できるようにするには、日本語WordNetで得られた訳語をキーにして、該当する英語のリストを値とする転置索引を作る必要がある。以前の記事で述べた転置索引の作り方を踏襲すればよい。実装はindex_translations.pyだ。

辞書本体のDBMファイルと訳語の転置索引のDBMファイルを置いておくので、それをダウンロードして検索UIだけ作るのも楽しいだろう。あるいは、Kindleとかの電子ブックリーダ用の辞書に変換しても有用だろう。


まとめ。WordNetと日本語WordNetのデータを使って英和、和英、英英辞書として使える辞書検索システムを作った。オープンなデータを使うだけでもここまでできる。言葉の定義や説明の情報量ではOxfordなどの市販の辞書には敵わないし、語彙の量では英辞郎には敵わないが、こんなデモサイトでも普通に実用できるレベルにはなっている。WordNetの有用性は広く知られるところだが、日本語WordNetも我々日本人にとっては素晴らしいと改めて感じた。英辞郎と同じく、精度よりも再現性重視で多数の訳語をぶち込むという方針なのが良い。情報が多い分には、後でフィルタをかけたり並べ替えたりすれば良い。

オープンなデータだけで辞書を強化していくとすれば、Wiktionaryから語義や発音やインフレクションのデータを取ってきてマージするのもよさそうだ。それでも英辞郎の有用性には勝てないだろうが、手法を検討することに価値がある。オープンなデータだけで頑張る方法を検討しつつ、そこで培った技術で自分用の英辞郎ベースの検索システムを構築する算段だ。

検索結果の項目の並び替えや、項目内の語義の並び替えは、非常に興味深いトピックだ。辞書順(ABC順、あいうえお順)に並べるとかいうのではなく、実際に使いそうな言葉を優先的に表示することで、辞書の使いやすさは飛躍的に向上する。機械翻訳やWeb検索など分野で用いられる各種の手法を応用すれば面白いことができそうだ。このあたりの手法は今後も検討していく。次回以降は類語検索などを実装したより実用的なサービスへの進化を模索する。