はじめに
日本語を弱らせるプログラム で類義語を取得する用があり、調べてみた結果WordNetを必要があるとわかったので、WordNetについて調べて見ました。
WordNetとは
日本語WordNetは、「日本語の概念辞書で、個々の概念が"synset"という単位にまとめられており、それらが他のsynsetと意味的に結びついている」ものです(by 提供サイト)。主な用途は類義語検索かと思います。そのほかの用途については要調査です。
WordNetの構造
WordNetはこちらにて提供されており、いくつか種類があるようですが、ここでは、 "Japanese Wordnet and English WordNet in an sqlite3 database" をのぞいてみます。
WordNetに含まれているテーブルは以下の11テーブル。
- ancestor
- link_def
- pos_def
- sense
- synlink
- synset_def
- synset_ex
- synset
- variant
- word
- xlink
このうち、特定の単語の類義語を取得するために必要な最低限のテーブルは、word、synset、senseの3つでした。ただ、それだけですと意味的な情報の関連がなくてさみしいのでsynset_defも入れてあげたうえで、これらのテーブルを抜粋したデータモデルは以下の通りです。
※参考:http://compling.hss.ntu.edu.sg/wnja/pubs/2009-alr-wn.pdf
単語 wordi は概念 synsetj に属していて、それらをsenseという項目でつなぎ合わせているイメージです。
ちなみに、"暖かい"という単語を例に、synset と synset_defを出力した結果は以下のようになりました。
類義語の取得
それでは、WordNetを用いて類義語一覧を取得する手順とプログラムについて説明します。処理の流れは以下の通りです。
- 対象の単語のwordidを取得する
- そのwordidが属するsynsetのsenseを取得する
- synsetに属する単語を類義語として取得する
コードの全体像は以下の通りです。
def search_synonyms(lemma, lang="jpn"):
synonym_list = []
# 1. 単語のwordidを取得する
wobj = get_word(lemma)
if wobj:
word = wobj[0]
# 2. そのwordidが属するsynsetのsenseを取得する
senses = get_senses(word, lang)
for s in senses:
# 3. synsetに属する単語を類義語として取得する
synonyms = get_words_from_synset(s.synset, word, lang)
for syn in synonyms:
if syn.lemma not in synonym_list:
synonym_list.append(syn.lemma)
else:
print(f"'{lemma}'の類義語は見つかりませんでした。")
return synonym_list
* WordNetには英単語も含まれていますが、今回は日本語のみを対象としています。
* 類義語はsynsetごとに存在していますが、synsetの違いは考慮せず全てマージした形で類義語一覧として取得します。
以降、1~3それぞれの処理について述べていきます。
1. 対象の単語のwordidを取得する
対象の単語のwordidを取得する関数get_word(lemma)
の処理は以下です。
なお、ここではwordid単体ではなく、Wordオブジェクトを丸ごと取得しています。(可読性、拡張性の観点からということで。)
Word = namedtuple('Word', 'wordid lang lemma pron pos')
def get_word(lemma):
cur = conn.execute("select * from word where lemma=?", (lemma,))
return [Word(*row) for row in cur]
2. そのwordidが属するsynsetのsenseを取得する
word(id)からsenseを取得する関数get_senses(word[, lang])
の処理は以下です。
Sense = namedtuple('Sense', 'synset wordid lang rank lexid freq src')
def get_senses(word, lang):
cur = conn.execute("select * from sense where wordid=? and lang=?", (word.wordid, lang))
return [Sense(*row) for row in cur]
言語の限定(lang="jpn"
)は次の処理だけでもいいかもしれませんが一応入れておきました。
3. synsetに属する単語を類義語として取得する
synsetからそれに属する単語を取得する関数get_words_from_synset(synset, word[, lang])
の処理は以下です。
def get_words_from_synset(synset, word):
cur = conn.execute("select * from word where wordid in (select wordid from sense where synset=? and lang=?) and wordid<>?;", (synset, lang, word.wordid))
return [Word(*row) for row in cur]
最後のwordid<>{word.wordid}
は、その対象の単語自身を除外するために入れています。SQLの書き方はいくつかパターンがあるかと思います。
※参考1:日本語WordNetのフロントエンドを弄る
※参考2:[自然言語] Wordnet × Pythonで類義語を抽出する
おまけ
1〜3だけで類義語が取得できましたが、それぞれの類義語がどういう概念で類似しているのか見たい場合はsynset_def
も取得すれば実現できます。
SynsetDef = namedtuple('SynsetDef', 'synset lang defi sid')
# defは予約語で使えないのでdefiにしています
def get_synset_def_from_synset(synset, lang):
cur = conn.execute("select * from synset_def where synset=? and lang=?", (synset, lang))
return [SynsetDef(*row) for row in cur]
さいごに
まあたらしい情報がなく申し訳ありませんがほんの少しでも参考になれば。以上です。