crispy.data

日本語 WordNet を良い感じに Neo4j にインポートする

2024-06-23

前準備

  1. DB Browser for SQLite をインストールする。Mac の場合は brew でインストールできる。
brew install --cask db-browser-for-sqlite
  1. 公式のページから sqlite3 のデータファイルをダウンロードする。2024 年 5 月時点では「Japanese Wordnet and English WordNet in an sqlite3 database」のリンクからダウンロードできる。ダウンロードしたファイルを解凍した後、$HOME/sqlite3/wnjpn.db などに保存する。

DB Browser for SQLite を使って csv を出力

  1. 上部の「データベースを開く」から前準備で保存した wnjpn.db を選択。
  2. 「SQL実行」のタブを選択し、以下のクエリを実行する。
SELECT w.wordid, w.lemma, synlink.synset1, synlink.synset2, -- リレーション型の名称として使用するため大文字に変換 upper(synlink.link) as link FROM word w LEFT JOIN sense ON w.wordid = sense.wordid LEFT JOIN synlink ON sense.synset = synlink.synset1 WHERE w.lang = 'jpn' AND w.pos = 'n'
  1. 「結果のビューを保存」から「CSVにエクスポートを選択」を押下し、クエリ結果を ja-wn.csv として保存。なお、保存時のオプションは以下の通りデフォルトで OK。

Neo4j に csv をインポート

  1. インポート時に APOC を使用するため、まだインストールしていない場合は初めにインストールする。 (Neo4j Desktop を利用している場合)データベース名のエリアをクリックして出てくる窓から 1 クリックでインストールできる。
    Neo4j Desktop での APOC のインストール方法
    (Neo4j Desktop を使用していない場合)APOC のリリースページから .jar ファイルをダウンロードし、neo4j/plugins ディレクトリに配置する。Neo4j を再起動すればインストール完了。
  2. ja-wn.csv を配置する。配置先は以下の通り。 (Neo4j Desktop を利用している場合)「Open folder」から「Import」を選択し、開かれたディレクトリ内に配置する。
    Neo4j Desktop 経由での Import ディレクトリの探し方
    (Neo4j Desktop を使用していない場合)neo4j/import ディレクトリに配置する。
  3. Neo4j Browser 等から以下の Cypher クエリを実行する。
:auto LOAD CSV WITH HEADERS FROM 'file:///ja-wn.csv' AS row CALL { WITH row MERGE (w:Word {word_id: row.wordid}) SET w.lemma = row.lemma MERGE (s1:Synset {synset_id: row.synset1}) MERGE (s2:Synset {synset_id: row.synset2}) MERGE (w)-[:BE_CLASSIFIED_AS]->(s1) WITH s1, row, s2 CALL apoc.merge.relationship(s1, row.link, null, null, s2) YIELD rel RETURN rel } IN TRANSACTIONS OF 200 ROWS RETURN row

WordNet を Neo4j にインポートした結果を確認

まずはスキーマの構造がどのようになったか確認してみる。
CALL db.schema.visualization()
:Word:Synset に分類され、:Synset 間には様々な関係性が成り立っていることが分かる。 :Synset 間に作成されたリンク情報の詳細は、日本語 WordNet のページの「詳細」のセクションを参照すると良い。 試しに「天気」と同一の synset に属する単語を出力してみる。
MATCH (anchorWord:Word {lemma: '天気'})-[:BE_CLASSIFIED_AS]->(:Synset)<-[:BE_CLASSIFIED_AS]-(targetWord:Word) WHERE anchorWord.word_id <> targetWord.word_id RETURN targetWord.lemma
targetWord.lemma "天気模様" "ウエザー" "お天気" "気象" "天候" "空合い" "日和" "天象" "ウェザー" "天色"
「購入」に類似している単語が取得できる。 今度は「天気」が包含している単語を出力してみる。
MATCH (anchorWord:Word {lemma: '天気'})-[:BE_CLASSIFIED_AS]->(:Synset)-[:HYPO]->(:Synset)<-[:BE_CLASSIFIED_AS]-(targetWord:Word) WHERE anchorWord.word_id <> targetWord.word_id RETURN targetWord.lemma LIMIT 10
targetWord.lemma "ウィンド" "ウインド" "気流" "風" "雪解け" "暑気" "日本ばれ" "日本晴れ" "晴天" "好天"
日本語 WordNet は英語で作成された元の WordNet をベースにしているということもあり、抽出されている単語の表現自体にやや違和感はあるものの、具体的な天気を示す単語を抽出できていることが分かる。

使い道など

WordNet のように単語間のセマンティクスを事前に整理しておけば、ユーザーの意図に応じた検索クエリの拡張などに使える。 例えば、Recall 重視の検索システムであれば、与えられた検索クエリから :HYPE の関係にある :Synset を辿って抽象的な語彙での検索も行うようにする、といった具合。 その他、LLM などを使って半自動的に知識グラフを構築する場合、作成されるノードが持つプロパティの値に表記揺れが生じることが多くある。このように構築した知識グラフに対してクエリする際に、表記揺れが生じているノードから WordNet を経由し、別の表記揺れが生じているノードへと走査できるようにすることで、表記揺れの影響を軽減できる。