LoginSignup
48
55

More than 5 years have passed since last update.

日本語WordNetを使って、上位語を検索できるツールをpythonで作ってみた

Posted at

「日本語WordNetを使って、何か面白いことができないか」の第二弾です。

前回:
日本語WordNetを使って、類義語を検索できるツールをpythonで作ってみた

今回は、前回の知識を使って、上位語を検索できるツールをpython3で作ってみました。

WordNetを利用するまで

WordNetを利用するまでに必要な準備を振り返ります。
WordNetは、同義語、上位・下位語などがまとめられている、概念の辞書です。
sqliteDB形式のWordNetを、日本語WordNetのページからダウンロードして利用します。

WordNetのDBに接続する際は、sqlite3という標準ライブラリを使います。

import sqlite3
conn = sqlite3.connect("wnjpn.db")

WordNetに含まれる単語を階層化する

日本語WordNetには、上位語、下位語などの概念の階層関係に関する情報が含まれています。
まずは、この情報をWordNetから抽出してみます。

Pythonでの木構造を定義し、その中にWordNetの概念情報を格納します。
木構造の実装は、藤井章博 研究室に倣います。

class node:
    def __init__(self, name, children=None):
        self.name = name  # String
        self.children = children  # List of Class node

    # 結果表示用
    def display(self, indent = 0):
        if self.children != None:
            print(' '*indent + self.name)
            for c in self.children:
                c.display(indent+1)
        else:
            print(' '*indent + self.name)

WordNetの中のsynlinkテーブルにアクセスし、link = 'hypo'に該当するものに絞ることで、
上位-下位の関係にある概念だけを抽出します。

# 上位-下位の関係にある概念の抽出
hierarchy_dict = {}  # key:上位語(String), value:下位語(List of String)
n_term_set = set()  # 下位語に含まれる単語集合

cur = conn.execute("select synset1,synset2 from synlink where link='hypo'")  # 上位語-下位語の関係にあるものを抽出
for row in cur:
    b_term = row[0]
    n_term = row[1]

    if b_term not in hierarchy_dict:
        hierarchy_dict[b_term] = []

    hierarchy_dict[b_term].append(n_term) 
    n_term_set.add(n_term)

print("上位語に含まれる単語の数 : %s" % len(hierarchy_dict))

top_concepts = list(set(hierarchy_dict.keys()) - n_term_set)
print("上位語に含まれる単語の中で下位語に含まれない単語の数 : %s" % len(top_concepts))

出力:
上位語に含まれる単語の数 : 20008
上位語に含まれる単語の中で下位語に含まれない単語の数 : 346


synsetのIDからsynsetの名称への変換を楽にするために、辞書を定義します。
synsetテーブルから情報を抽出します。
synsetの名称は全て英語です。

# synset(概念)のIDから、概念の名称に変換する辞書の作成
synset_name_dict = {}  # key:synsetのID, value:synsetの名称
cur = conn.execute("select synset,name from synset")
for row in cur:
    synset_name_dict[row[0]] = row[1]
for k,v in synset_name_dict.items():
    print("%s : %s" % (k,v))
    break

出力:
07125096-n : expletive


ここまでの定義を用いて、上位-下位の関係を木構造に入れていきます。
詳細の説明は省きますが、下位の語が未登録の場合、再帰的に呼ばれる関数を用いて、データを投入します。

# 下位の語が未登録の場合、再帰的に呼ばれる関数
def recrusive_register(term, synset_name_dict, hierarchy_dict, node_tree_dict):
    for term in hierarchy_dict[term]:
        if term not in node_tree_dict:
            if term in hierarchy_dict:
                recrusive_register(term, synset_name_dict, hierarchy_dict, node_tree_dict)
                node_tree_dict[term] = node(synset_name_dict[term], [node_tree_dict[t] for t in hierarchy_dict[term]])
            else:
                node_tree_dict[term] = node(synset_name_dict[term])

# データ投入
node_tree_dict = {}
for k in top_concepts:  # 最上位の語を起点として木構造を作成
    recrusive_register(k, synset_name_dict, hierarchy_dict, node_tree_dict)
    node_tree_dict[k] = node(synset_name_dict[k], [node_tree_dict[term] for term in hierarchy_dict[k]])

node_tree_dictの中に木構造の情報が保存されています。試しに、ある1つの上位語を起点とした木構造を出力してみます。

node_tree_dict[top_concepts[10]].display()

出力:
experience
-incline
-recapture
-pride
-smolder
-harbor
-cool_off
-see_red
--bridle
--steam
:
(中略)
:
-rejoice
--cheer_up
---triumph
----glory
---buoy_up
--gladden
--walk_on_air
---triumph
-sympathize
-pride_oneself
-burn
-die
-fly_high
-shine
-glow


experienceという上位概念にあたる単語の下位に属する概念を、木構造で出力することができました。

上位語を検索できるツールを作ってみた

ここまでの知識を用いて、指定語の上位概念を検索するツールを作ってみたいと思います。
まずは、下位-上位の関係にある概念を抽出します。
上位語を検索できるようしたいため、下位語がKey、上位語がValueとなるようにデータを持ちます。

# 下位-上位の関係にある概念の抽出
cur = conn.execute("select synset1,synset2 from synlink where link='hypo'")

hierarchy_dict = {}  # key:下位語(String), value:上位語(String)

for row in cur:
    b_term = row[0]
    n_term = row[1]

    if n_term not in hierarchy_dict:
        hierarchy_dict[n_term] = b_term

上位語を検索できる関数を以下のように書きました。前回作成した関数を追加・修正しています。

# 特定の単語を入力とした時に、上位語を検索する関数
def SearchTopConceptWords(word, hierarchy_dict):

    # 問い合わせしたい単語がWordnetに存在するか確認する
    cur = conn.execute("select wordid from word where lemma='%s'" % word)
    word_id = 99999999  #temp 
    for row in cur:
        word_id = row[0]

    # Wordnetに存在する語であるかの判定
    if word_id==99999999:
        print("「%s」は、Wordnetに存在しない単語です。" % word)
        return
    else:
        print("【「%s」の最上位概念を出力します】\n" % word)

    # 入力された単語を含む概念を検索する
    cur = conn.execute("select synset from sense where wordid='%s'" % word_id)
    synsets = []
    for row in cur:
        synsets.append(row[0])

    # 概念に含まれる単語を検索して画面出力する
    no = 1
    for synset in synsets:
        cur1 = conn.execute("select name from synset where synset='%s'" % synset)
        for row1 in cur1:
            print("%sつめの概念 : %s" %(no, row1[0]))
        cur2 = conn.execute("select def from synset_def where (synset='%s' and lang='jpn')" % synset)
        sub_no = 1
        for row2 in cur2:
            print("意味%s : %s" %(sub_no, row2[0]))
            sub_no += 1

        # 上位語の検索部分
        b_term = ""
        while(synset in hierarchy_dict.keys()):
            synset = hierarchy_dict[synset]

        cur1 = conn.execute("select name from synset where synset='%s'" % synset)
        for row1 in cur1:
            print("最上位概念 : %s" % row1[0])

        cur2 = conn.execute("select def from synset_def where (synset='%s' and lang='jpn')" % synset)
        sub_no = 1
        for row2 in cur2:
            print("意味%s : %s" %(sub_no, row2[0]))
            sub_no += 1

        # 更新          
        print("\n")
        no += 1

この関数に単語と、下位-上位の関係に関する情報を入力することで、指定した単語の最上位の概念を検索できます。
初めに、前回と同様に「ネコ」の上位語を検索してみます。

SearchTopConceptWords("ネコ", hierarchy_dict)

出力:
【「ネコ」の最上位概念を出力します】

1つめの概念 : true_cat
意味1 : 通常、厚く柔らかい毛皮を持ち、吠えることのできないネコ科の哺乳類:家ネコ
意味2 : ヤマネコ
最上位概念 : entity
意味1 : それ自身の確かな存在(生きているか、死んでいるの)を持つために認められる、知られている、あるいは推測される


ネコを最上位の概念にまで抽象化させると、entity(存在)まで行きついてしまいました。

次いで、前回と同様に「月」の上位語を検索してみます。

SearchTopConceptWords("月", hierarchy_dict)

出力:
【「月」の最上位概念を出力します】

1つめの概念 : moonlight
意味1 : 月の光
最上位概念 : entity
意味1 : それ自身の確かな存在(生きているか、死んでいるの)を持つために認められる、知られている、あるいは推測される

2つめの概念 : month
意味1 : 暦年を12に分けた中の1つ
最上位概念 : entity
意味1 : それ自身の確かな存在(生きているか、死んでいるの)を持つために認められる、知られている、あるいは推測される

3つめの概念 : month
意味1 : 約30日間という時間単位
最上位概念 : entity
意味1 : それ自身の確かな存在(生きているか、死んでいるの)を持つために認められる、知られている、あるいは推測される

4つめの概念 : moon
意味1 : 地球の天然衛星
最上位概念 : moon
意味1 : 地球の天然衛星


「月」で検索した場合にも、大体entity(存在)に行きつきました。
moonに対しては、WordNetで上位語が定義されていなかったため、moon自体が最上位の概念となりました。

最後に、毛色の違う単語として、「忖度」で検索したいと思います。

SearchTopConceptWords("忖度", hierarchy_dict)

出力:
【「忖度」の最上位概念を出力します】

1つめの概念 : hypothesise
意味1 : 特に不確かであるか仮の根拠で信じる
最上位概念 : cerebrate
意味1 : 推論、決定をするためまたは、結論や判断に達するために心、あるいは理性を使うまたはを発揮する


一応それっぽい結果になりました。

まとめと今後

今回は、WordNetの上位-下位の概念を用いて動作するツールを作って遊んでみました。
次回は、実際のデータを用いた解析に何らかチャレンジしてみたいと思います。

参考

[1] 日本語WordNetのページ
[2] 藤井章博研究室 : Python による 木構造の再帰定義による実装
[3] [自然言語] Wordnet × Pythonで類義語を抽出する

48
55
1

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
48
55