5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

WordNetの構造と類義語検索

Last updated at Posted at 2020-05-10

はじめに

日本語を弱らせるプログラム で類義語を取得する用があり、調べてみた結果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も入れてあげたうえで、これらのテーブルを抜粋したデータモデルは以下の通りです。
ER_WordNet_noKeys.png

※参考:http://compling.hss.ntu.edu.sg/wnja/pubs/2009-alr-wn.pdf

単語 wordi は概念 synsetj に属していて、それらをsenseという項目でつなぎ合わせているイメージです。
ちなみに、"暖かい"という単語を例に、synset と synset_defを出力した結果は以下のようになりました。

wordnet.png

類義語の取得

それでは、WordNetを用いて類義語一覧を取得する手順とプログラムについて説明します。処理の流れは以下の通りです。

  1. 対象の単語のwordidを取得する
  2. そのwordidが属するsynsetのsenseを取得する
  3. 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]

さいごに

まあたらしい情報がなく申し訳ありませんがほんの少しでも参考になれば。以上です。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?