「日本語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で類義語を抽出する