豪鬼メモ

一瞬千撃

WictionaryとWordNetから英和辞書を作ろう

英和辞書・和英辞書として使える辞書検索システムを作る連載の4回目である。今回はWictionaryから辞書データを抽出して、WordNetとも併合して、本物の英和辞書として使えるシステムを構築した。今回もデモサイトを作ったので、お試しいただきたい。凝った機能はまだ入れていないが、普通に使えるレベルになっていると思う。
f:id:fridaynight:20200914170906p:plain


英語の文字列を入力して検索すると、英和辞書としてその語を検索する。単語だけではなく複数語からなる熟語も数多く収録されている。日本語を入力すると和英辞書として逆引きしてそれに対応する英語を検索する。 英語も日本語も現状では完全一致のみで検索を行う。

デフォルトでは文字種を見て正引き(英和モード)か逆引き(和英モード)かが自動的に選択される。正引きと逆引きを明示的に切り替えたい場合は、「Auto Mode」を「En-to-Ja」か「Ja-to-En」に変えればよい。表示方法もデフォルトでは自動的に設定される。すなわち、ヒット数が1件の場合には詳細な語義を表示し、ヒット数が5件までの場合には主要語義だけの簡略化した表示を行い、ヒット数がそれ以上の場合にはさらに簡略化して見出し語と和文翻訳のリスト表示を行う。「Auto View」を「Full」や「Simple」や「List」に明示的に切り替えることもできる。見出し語をクリックすると詳細表示に遷移できる。

表示上の工夫としては、見出語の直後に翻訳語のリストを置いたことがある。そこだけ見れば大意が掴めるようにしてあり、より詳しく知りたい場合にはその下にある語義を吟味するという流れができる。「ひと目で分かる」ことが重要である。この使用感は、Wiktionary本家を検索するだけでは味わえない。


前回の記事でWordNetを使った辞書検索システムを構築したが、その辞書データは英語による説明文とその日本語訳を付随させるという形式だった。WordNetのコンテンツは、lexicographerと呼ばれるその筋の人達が編纂したものなので信頼性は高いが、説明文が英文なのが我々日本人にとっては難点である。それを補うべく日本語訳をつけたのが日本語WordNetであるが、それは機械翻訳の結果が多くを締めるので信頼性は相対的に低くなる。

一方で、Wictionaryには日本語版もあって、そこでは英語の語についての記事も多くある。元々日本語の記事なので自然文としての質は高いのだが、基本的には素人が書いた記事であり、著者がネイティブ話者レベルのバイリンガルではないことが多いだろうから、英和辞書としての信頼性はそれほど高くない。また、収録語彙数もWordNetにはかなり劣る。「robust」「tremendous」「void」「lick」とかいったくらいの、大学入試やTOEFLを受けるなら当然知っているよねという単語が漏れていたりするので、「これひとつでブログや新聞や小説や論文が読める」というカバー率には全く達していない。ただ、Wictionaryには英語版もある。当然ながら英語の語義説明しかついていないが、それでよければカバー率は英辞郎をも上回る辞書が作れる。英語版であっても素人が書いているのだが、おそらく著者はネイティブ話者かそれ同等に流暢な人がほとんどで、またユーザ数に応じて内容が磨かれていると考えられるので、辞書としての質は日本語版よりは高いと考えられる。とはいえ、「rub」の語義に「to rub something」って書いてあったりして、辞書とは何なのかが分かっていない記述も見られる。

それぞれ一長一短があるので、もうこうなったら全部合体させてしまおうという考えに至った。ある語を検索すると、最初にWictionary日本語版の結果があれば表示して、次にWordNetとその日本語版の結果があれば表示する、最後にWictionary英語版の結果があれば表示する。日常で出会う95%以上の単語はWiktionary日本語版がカバーしてくれ、その範囲の単語に関しては日本語の語義説明を読むことができる。残り5%の難しい語に関してはWiktionary英語版に由来する英語版の説明を見ることができ、私が読めるレベルの英文でのカバー率は、固有名詞を除けばほぼ100%である。当然「robust」も「tremendous」もあって、それぞれ「Evincing strength and health」「awe-inspiring; teffific」という語義説明を得ることができる。日本語の語義説明を見るのに比べると英語の語義説明を理解するのには1秒多くの時間を費やし、「evince」や「awe」といった語を知らないと二重引きを強いられる可能性があるが、その分だけ勉強にはなる。また、専門家による語義説明を確認したい場合には、WordNetの説明も読めばよい。Wictionary英語版とWordNetの両方が取りこぼすような語はネイティブ英語話者でも知らないのが普通だろうから、英文中に出てくるなら文脈で判断できるか言い換え表現があるはずだし、英作文で敢えて選択する必要も全くない。

見出語の直後に現れる翻訳語のリストは、Wiktionary日本語版の説明文と、WordNet日本語版による翻訳語リストと、Wiktionary英語版の翻訳語リストを合成して作成している。複数のコーパスに共通して現れるものは前に、また日本語コーパスでの出現率が高いものほど前に持ってくることにより、典型的な訳語が優先的に表示されるようになっている。


WordNetのデータ処理については前回述べたので、今回はWictionaryのデータ処理について述べる。まず、他のWikimedia系データと同様にこちらのダウンロードサイトからBZ2圧縮のXMLファイルをダウンロードする。それを、共起語カウントの記事で述べたのと同様に、SAXパーザで読み込んで処理する。各記事は /mediawiki/page 要素であり、語の文字列は /mediawiki/page/title 要素であり、記事の内容は /mediawiki/page/revision/text 要素だ。記事の内容はMediaWiki形式なので、それをうまいこと解釈して自分の目的に合った情報を抽出することになる。

さて、ここでWikiならではの問題にぶち当たる。「半」構造化データは所詮、構造化データではないのだ。HTMLとJavaScriptを介して最終的にページがレンダリングされた際の「見栄え」が整っていて、読者にとって分かりやすければ、記事自体の生データがどのように書かれているかはあまり配慮されない。見栄えだけを気にすればよいので記事編集が気楽に行える反面、見栄えさえ合っていればデータ構造の一貫性など気にされずに編集が重ねられる。敢えて制限を緩めてコンテンツの成長を早めた結果として、解析するプログラムを書く際にはそのシワ寄せを思いっきり食う羽目になる。率直に言い換えると、データが汚い。スタイルマニュアルというのがあるが、守られていないことも多い。

例えば、英語の単語やフレーズに関する記事を書く場合にはレベル2のヘッダに英語のラベルを示すテンプレートを合わせて「=={en}==」というのが先行することになっている。それは「英語」という見出しでレンダリングされるので、「==英語==」と書いてあっても誰も気づかないし、実際にそういう例が多い。多くのローカライズ用のテンプレートを使った慣習は、テンプレートを使わない表現と混在するのが許容されることで、かえって複雑性を増すのに貢献してしまっている。「{alter}」を使うべきところで「別表記」「異表記」「異綴」が使われる。英語であることを示すヘッダがレベル2でなく3や4だったりもするし、カテゴリ用のテンプレートだけで英語であることを表現しようとしている場合もある。動詞の過去形とかのインフレクションは「{{en-verb|a|b|c|d}}」などといったテンプレートで書くことが推奨されているが、それが展開された「三人称単数現在系: runs、過去形: run、...」といった文字列がそのまま記事データとして書いてあったりもする。それら例外をいちいち確認しながら、対処するコードを書き足していかねばならない。結果として、それを解析するためのプログラムは、難読正規表現を並べたゴミコードにならざるを得ない。今現在99%の記事を解析できていたとしても、明日には新たな例外が現れて98%になり、半年後には90%になってしまうかもしれない。底のない沼である。

愚痴はその辺にしておこう。解析対象となる膨大なデータがあるだけで有難いことであり、それに比べれば解析が面倒臭いというのは大した問題ではないのだ。とりあえず、ゴミと割り切ってコードを書いてみた。英語版Wictionaryと日本語版Wictionaryから英単語もしくはフレーズの語義や発音やインフレクションの情報を抽出してTSVとして書き出すプログラムを、それぞれparse_wiktionary_en.pyおよびparse_wiktionary_ja.pyとして実装した。誰かの役に立つかもしれないので、抽出結果も置いておこう。

日本語版Wiktionaryからの抽出語数は2万語、英語版だと48万語、WordNetだと15万語くらいである。Wiktionaryの記事の解析はヒューリスティックの塊であり、コーナーケースに対応していくとキリがないのだが、今のところ、9割9分の記事はまともに解析できていると思う。それぞれのデータソースからは英和辞書として必要そうなデータだけを抽出している。上述のTSVデータは、各語のIPA発音記号やインフレクション情報だけを使うのにも有用かもしれない。また、以前作ったWordNetのDBMファイルからも同じ形式のTSVファイルを抽出しておいた。各行が各語の情報を示し、TSVの各列は「name=value」の形式で名前付きのレコードを示し、そして名前には以下のものがある。

  • word : 語の基本形(レンマ=名詞の単数形、動詞の原形、形容詞・副詞の原級)
  • pronunciation_ipa : IPA形式の発音記号
  • pronunciation_sampa : SAMPA形式の発音記号
  • inflection_noun_plural : 名詞の複数形
  • inflection_verb_singular : 動詞の三人称単数現在形
  • inflection_verb_present_participle : 動詞の現在分詞
  • inflection_verb_past : 動詞の過去形
  • inflection_verb_past_participle : 動詞の過去分詞
  • inflection_adjective_comparative : 形容詞の比較級
  • inflection_adjective_superative : 形容詞の最上級
  • inflection_adjective_comparative : 副詞の比較級
  • inflection_adjective_superative : 副詞の最上級
  • noun, verb, adjectie, adverb, pronoun, auxverb, preposition, article, interjection
    • 名詞、動詞、形容詞、副詞、代名詞、助動詞、前置詞、冠詞、完投詞の語義
  • prefix, suffix, abbreviation
    • 接頭辞、接尾辞、略語の語義
  • derivative, alternative、synonym、related
    • 派生語、代替語、類義語、関連語のリスト
  • translation
    • 和訳表現のリスト

この形式でTSVファイルを書き出しておけば、辞書構築用のプログラムに読み込ませることができる。英辞郎やその他のデータソースにも対応できる。


英語版Wiktionary、日本語版Wictionary、そしてWordNetのTSVが出揃ったら、それらをマージしてDBM形式に変換すれば辞書が完成する。キーは基本形をさらに正規化して発音記号なしの小文字にしたものだ。値はJSON形式の構造化データである。基本形の文字列が全く同じ複数の語があったとしてもそれは区別されないが、基本形の文字列が異なる複数の語が同一キーに紐づけられるすることはありうる。具体的に言うと、「Japan」と「japan」、「résumé」「resume」は別の語だが同じキーに紐づけられる。Wikipediaの記事名では大文字小文字の区別がされないが、Wiktionaryでは区別されるので、この状況は頻出する。WordNetでも大文字小文字は区別される。したがって、値のJSONデータの根はリストになる。

辞書データにおいてリストを表現する場合、要素間の順序はどうやって決めるのかが問題になる。辞書における表示順は重要度に応じていることが期待されるからだ。現状では、WordNet日本語版の訳語の日本語コーパスにおける出現率が高いものから表示している。つまり、「Japan」の訳語である「日本」と「japan」の訳語である「漆」では、「日本」の方がよく出現するので、「Japan」の方が優先して表示される。

各語は複数の辞書に由来するデータを持つ。それらをどうマージするか。発音とインフレクションの情報に関しては、英語版Wikitionary由来のものがあればそれを採用し、無ければ日本語版Wiktionary由来のものを採用する。発音記号に関しては日本語版だとIPAという属性名でありつつもIPAでなくJones式で書かれていることがあるので、英語版を優先する。インフレクションも英語版の方が精度が良さそうなので、英語版を優先する。IPAとSAMPAではIPAを優先し、IPAがなくてSAMPAのみがある場合にはSAMPAをIPAに変換して使う。語義に関しては、マージせずに、日本語版WiktionaryWordNet、英語版Wiktionaryの順に区別して表示する。その区別は大枠の構造で行うのではなく、各語義に "label" という属性を持たせて行う。

各語義はkey-value連想配列とし、辞書ラベルの他にも、品詞(pos)や語義説明(text)などの属性を持たせる。語義説明は基本的に人間可読なテキストだが、改行及びインデントを意味するマークをUI側で解釈させる。Wiktionary英語版に関しては、語義説明の2行目以降は無視する。2行目以降は引用による例文提示であることが多いのだが、聖書とかシェークスピアとか古い文書の引用がやたら多くて実用上の利点があまりないからだ。WordNetの語義には類義語(synonym)や対義語(antonym)等の情報も載せ、また日本語WordNet由来の翻訳も載せる。

以上のことをサンプルに落とし込むと、以下のようになる。

[
  {
    "word": "run",
    "inflection_noun_plural": "runs",
    "inflection_verb_singular": "runs",
    "inflection_verb_present_paticiple": "running",
    "inflection_verb_past": "ran",
    "inflection_verb_past_paticiple": "run",
    "item": [
      {
        "label": "wj"
        "pos": "verb",
        "text": "自動詞: 走る。 [-] He runs in the park. [--] 彼は公園を走る。"
      },
      {
        "label": "wj"
        "pos": "verb",
        "text": "他動詞: 実行する。 [-] He ran the program. [--] 彼はプログラムを実行した。"
      },
      {
        "label": "wj"
        "pos": "noun",
        "text": "走ること。"
      },
      {
        "label": "wj"
        "pos": "verb",
        "text": "intransitive: To Run."
      },
      {
        "label": "we"
        "pos": "verb",
        "text": "intransitive: To run."
      },
      {
        "label": "we"
        "pos": "verb",
        "text": "transitive: To execute."
      },
      {
        "label": "wn"
        "pos": "verb",
        "text": "To execute. [-] [translation]: 実行, 動作, 履行 [-] [synonym]: execute",
      },
    ],
  },
  {
    "word": "Run"
    "item": [
      {
        "label": "we"
        "pos": "noun",
        "text": "A TV drama starring Tsuyoshi Nagabuchi."
      },
    ]
  }
]

Wictionaryにおいては、日本語版でも英語版でも、各語の語義は箇条書きのリストとして書かれている。その各々はJSONデータの中でitem要素の子要素として格納される。またここでリストが出てきたので、順序について考えねばならない。しかし、この場合は単純で、記事内で出てきた順序をそのまま維持すれば良い。Wictionary自身が辞書であり、英語版のスタイルガイドにはこうある。

「長いエントリでは、ほとんどのユーザは、最初の何個かの語義をチラ見することしかしない。したがって、最も共通(common)な語義を最初に置くことが重要である。たとえそれが論理的もしくは歴史的な並べ方に反するとしても。」

素晴らしい方針である。全てのユーザがこれをきちんと理解して遵守しているわけではないのがプロが作る辞書とは異なるところだが、多数のユーザによる合議により、たいていの記事ではスタイルガイドが順守されていると期待していい。エンドユーザの目に触れない生データの書式は汚いまま放置されても、エンドユーザの目に触れる内容は時間と共に洗練されていくものだ。なお、WordNetにおいても複数の語義の順序があるが、それは日本語WordNet翻訳語の頻度情報を使って解決するという話を以前の記事で述べた。今回使うTSVデータはその順序を維持している。よって、全ての入力データに対して語義の順序を維持するという実装で良いという結論になる。


検索機能に関しては、英語による検索と日本語による検索に分けて考える。英語で検索する場合、入力されたクエリを正規化してDBMを検索すれば良い。得られたJSONデータを適当にレンダリングすれば完了である。とりあえずは完全一致だけを実装して、前方一致や曖昧一致などは後で実装することにしよう。いずれにせよ、入力クエリに該当するレコードのリストを取り出して表示するというだけだ。

日本語で検索する機能は、辞書データだけでは実現不可能だ。語義に含まれる重要語を抽出して転置索引を作る必要がある。その際、語義に含まれる全ての単語を使った「全文検索」にしてもよいのだが、そうすると余計な語を拾ってしまって、再現率は上がるが精度が下がることになる。経験上、キーワード検索の方がはるかに便利だ。ところで、英辞郎の場合、語義の先頭にキーワードとなるべき逐語訳を箇条書きするというスタイルガイドに則ってデータが作られているので、それを転置すれば英和辞書が適切な和英辞書になる。実際のところ、和英辞郎はそうして作られたデータだ。つまり、英辞書では英和辞書のデータを作る時点で和英辞書への変換を念頭に置いているのだ。WordNet日本語版に関しても似たような方針が感じられる。語義は説明文ではなく逐語訳の日本語なので、それをそのままキーワードとして扱えばよい。一方で、Wiktionaryのデータは必ずしもそうではない。スタイルガイドによると、語義はgrossとfull definitionに分けて考えられている。前者は外国語の説明に適するとされ、その外国語から、英語版なら英語に、日本語版なら日本語に翻訳した訳語を並べたものである。後者は、英語版なら英語の、日本語版なら日本語の言葉に適するとされ、その言葉の意味を説明した文章である。grossの方はキーワードとして適切だが、full definitionの方はそうではない。日本語版Wikipediaにおける英単語の記事はgrossであることが多いが、必ずしもそうではない。そして、その語義がgrossなのかfull definitionなのかを区別する手段は提供されていない。

Wiktionary日本語版の語義からキーワードを抜き出すためのヒューリスティクスを考える必要がある。まず、短めの語義はgrossである可能性が高いと仮定する。例えば、「run」の動詞の第一義は「走る」であり、「run」の名詞の第一義は「走る動作」である。第一義のgrossの後ろに句点で区切ってfull definitionが来る場合もあるが、第一義の言い換え表現が読点や句点で区切って列挙される場合も多い。grossは拾いたいがfull definitionは拾いたくないので、それらの区別ができないのは痛い。仕方ないので、区別は諦めて、読点や句点で区切った文字列の各々をgrossと仮定することにした。各々のgrossをトークナイズしてから先頭6トークンだけを採用することよって、長いfull definitionを拾った場合のインデックスの汚れを緩和する。

なお、「走行する」「走行すること」のように「する」「すること」「される」「されること」で終わる場合、それら全体とともに語幹の部分もキーワードとして扱う。「即席の」「即興で」「無制限に」のように、漢字の語に格助詞が接尾した場合も同様に扱い、語幹もキーワードとして扱う。さらに、WordNet日本語版由来の翻訳もキーワードとして合わせて使う。再現率と精度のトレードオフがあるなか、再現率を重視して索引を作っている。

キーワードのリストが取れるようになれば、それを転置索引にすればよい。キーはキーワードを正規化した文字列で、値はそれに対応する辞書エントリのキーのタブ区切りリストにする。以前に述べた通り、TkrzwのBabyDBMを使うと転置索引は簡単に作れる。

入力クエリの日本語に該当する複数の英語の語のリストが得られたとして、その順序をどうするかがまた問題になる。「走る」の第一候補は「run」であるべきだし、「歩く」の第一候補は「walk」であるべきだ。重要度順に表示するのが理想的なのだが、内部に翻訳モデルを持っているわけじゃないので難しい。とりあえずはWikipediaのワードカウントデータベースを用いて頻度の高い語を優先して出すようにする。


まとめ。WiktionaryWordNetのそれぞれ英語版と日本語版を併合して、実用的な英和辞書を作ることができた。訳語の転置索引を構築することにより和英辞書としても利用することができる。4つのコーパスを合わせることで、それらの良さを足してさらに相乗効果が出せている。日本語版Wiktionaryによって理解しやすさが向上し、英語版Wiktionaryによって英和検索の再現率が向上し、英語版WordNetによって正確さが向上し、日本語版WordNetによって和英検索の再現率が向上する。訳語の選定では3つのコーパスの多数決を取り入れることで利便性を向上させている。組み合わせの妙で、真にオープンなデータだけを使って英辞郎に匹敵する利便性を実現できたと思う。

例によってGitHubコードを公開しているので、ご興味があればダウンロードして使ってみて欲しい。デモサイトのDBMをダウンロード(辞書本体転置索引)すれば、辞書の構築手順はすっ飛ばして、あなた独自の辞書検索システムを作ることができる。

辞書の構築に関しては完成を見たので、あとは検索システムを作り込むことになる。次回以降は、曖昧検索や類語検索をサポートするとともに、AJAX等を駆使して、より使いやすいユーザインターフェイスを実現していく。