LoginSignup
127
120

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-09-08

日本語WordNetを使って、何か面白いことができないか調べていきたいと思います。
今回は、とりあえず日本語WordNetについて簡単に情報をまとめ、類義語を検索できるツールをpython3で作ってみました。

WordNetとは?

同義語、上位・下位語などがまとめられている、概念の辞書です。
元々英語で整備が進んでいました。
日本語版は、2006~2010に、国立研究開発法人情報通信研究機構(NICT)が整備を進めました。

詳しくはWikipediaで
https://ja.wikipedia.org/wiki/WordNet

WordNetはどうやったら使えるの?

以下の日本語WordNetのHPから、ダウンロードできます。商用フリーです。申し込み不要です。

日本語 WordNet:
http://compling.hss.ntu.edu.sg/wnja/

私はpython経由で使いやすそうな「Japanese Wordnet and English WordNet in an sqlite3 database」をダウンロードしました。
以降では、sqliteDB形式のWordNetをpythonから呼ぶ方式で説明します。

WordNetをpythonから呼ぶ

WordNetをダウンロードしたら、pythonからWordNetを呼びます。
sqlite3という標準ライブラリを使えば、通常のSQLの感覚で操作できます。

まず、WordNetのDBに接続します。

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

どのようなテーブルが含まれているか確認してみます。

# 含まれるテーブルの確認
cur = conn.execute("select name from sqlite_master where type='table'")
for row in cur:
    print(row)

出力:
('pos_def',)
('link_def',)
('synset_def',)
('synset_ex',)
('synset',)
('synlink',)
('ancestor',)
('sense',)
('word',)
('variant',)
('xlink',)


出力を確認すると、
'pos_def','link_def','synset_def','synset_ex','synset','synlink','ancestor','sense','word','variant','xlink'
の計11のテーブルが含まれることが分かりました。(思ったより、多い。。)
調べた感じだと、以下の5つを、とりあえず押さえておけば良いようです。

テーブル名 内容
word 単語と品詞に関する情報。単語と単語IDを紐づけている
sense 単語とsynset(概念)に関する情報。単語IDとsynsetのIDを紐づけている
synset 概念に関する情報。synsetのIDと概念名称を紐づけている
synset_def 概念の定義に関する情報。synsetのIDと概念の定義を紐づけている
synlink 2つの概念の関連性の情報。上位・下位、包含・被包含などが定義されている

以降で簡単に中身を見てみます。

WordNetからデータを抽出する

以下のようにSQL感覚で、データ抽出できます。

# Wordnetデータの内容の確認
cur = conn.execute("select * from word limit 10")
for row in cur:
    print(row)

出力:
(1, 'eng', 'expletive', None, 'n')
(2, 'eng', 'measles', None, 'n')
(3, 'eng', 'contras', None, 'n')
(4, 'eng', 'beef_man', None, 'n')
(5, 'eng', 'dwelling', None, 'n')
(6, 'eng', 'acetum', None, 'n')
(7, 'eng', 'mount_carmel', None, 'n')
(8, 'eng', 'class_taxopsida', None, 'n')
(9, 'eng', 'vascular_ray', None, 'n')
(10, 'eng', 'genus_salamandra', None, 'n')


実行結果は、sqlite3内で定義されているオブジェクトで返ってきますが、for文で回せばtupleで要素を取り出すことができます。
ちなみに列名が取得したくなった場合は、以下のようにします。
(このあたりの書き方は、SQLiteとMySQLとかで違う感じです)

# Wordnetデータに含まれるカラムの確認
cur = conn.execute("PRAGMA TABLE_INFO(word)")
for row in cur:
    print(row)

出力:
(0, 'wordid', 'integer', 0, None, 1)
(1, 'lang', 'text', 0, None, 0)
(2, 'lemma', 'text', 0, None, 0)
(3, 'pron', 'text', 0, None, 0)
(4, 'pos', 'text', 0, None, 0)


一応データ数のオーダーも確認しておきます。

# Wordnetに登録されているデータ数の確認(wordDB)
cur = conn.execute("select count(*) from word")
for row in cur:
    print("Wordnetに登録されているWordDBの単語数:%s" % row[0])

出力:
Wordnetに登録されている単語数:247528


日本語版WordNetについて分かったこと

前述のような感じで、少し触ってみて分かったことをまとめます。

  • 基本的に、英語と日本語の単語が混在している(一部英語のみ)
  • 標準形を前提として整備されている
    • 形態素解析で標準形に変換しておく必要がありそう
  • 名詞(n)、動詞(v)、形容詞(a)、副詞(r)の4種類の品詞が対象
  • 一部の情報は英語にしか付与されていない(senseテーブルのfreqカラムなど)

どうも本家の英語版のWordNetに日本語の情報を足しているようで、一部の情報は日本語版では不足しているようです。今後活用を検討する上で注意が必要そうです。

とりあえず類義語を検索できるツールを作ってみた

本当は、上位・下位などの関係性を上手く使ってみたかったのですが、まずはシンプルな類義語の検索処理部分をツールにしてみたいと思います。

以下のように類義語検索関数を書きました。

# 特定の単語を入力とした時に、類義語を検索する関数
def SearchSimilarWords(word):

    # 問い合わせしたい単語が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
        cur3 = conn.execute("select wordid from sense where (synset='%s' and wordid!=%s)" % (synset,word_id))
        sub_no = 1
        for row3 in cur3:
            target_word_id = row3[0]
            cur3_1 = conn.execute("select lemma from word where wordid=%s" % target_word_id)
            for row3_1 in cur3_1:
                print("類義語%s : %s" % (sub_no, row3_1[0]))
                sub_no += 1
        print("\n")
        no += 1

この関数に単語を入力することで、類義語を検索できます。
初めに「ネコ」の類義語を検索してみます。

# 「ネコ」の類義語を検索する
SearchSimilarWords("ネコ")

出力:
【「ネコ」の類似語を出力します】

1つめの概念 : true_cat
意味1 : 通常、厚く柔らかい毛皮を持ち、吠えることのできないネコ科の哺乳類:家ネコ
意味2 : ヤマネコ
類義語1 : true_cat
類義語2 : cat
類義語3 : ねんねこ
類義語4 : にゃんにゃん
類義語5 : 猫
類義語6 : キャット


何となく上手くいってそうです。
「ねんねこ」とか「にゃんにゃん」もWordNetに含まれているのですね。。恐るべし。

別の単語でも確認してみます。

# 「月」の類義語を検索する
SearchSimilarWords("月")

出力:
【「月」の類似語を出力します】

1つめの概念 : moonlight
意味1 : 月の光
類義語1 : moonlight
類義語2 : moon
類義語3 : moonshine
類義語4 : 月灯かり
類義語5 : 月色
類義語6 : 月明り
類義語7 : ムーンライト
類義語8 : 月あかり
類義語9 : 月影
類義語10 : 月桂
類義語11 : 月明
類義語12 : 月光
類義語13 : 月明かり

2つめの概念 : month
意味1 : 暦年を12に分けた中の1つ
類義語1 : month
類義語2 : calendar_month
類義語3 : 暦月

3つめの概念 : month
意味1 : 約30日間という時間単位
類義語1 : month

4つめの概念 : moon
意味1 : 地球の天然衛星
類義語1 : moon
類義語2 : 月代
類義語3 : 御月様
類義語4 : お月さま
類義語5 : 玉兎
類義語6 : ムーン
類義語7 : 玉桂
類義語8 : 月球
類義語9 : 桂月
類義語10 : お月様
類義語11 : 月夜見
類義語12 : 月の輪
類義語13 : 太陰
類義語14 : 月桂
類義語15 : 月読
類義語16 : 月輪
類義語17 : 月読み


一見上手くいってそうですが、「月曜日=monday」の概念がないので、実用上は問題が生じるかもしれません。

まとめと今後

日本語版WordNetは利用は簡単ではあるものの、英語版ではない固有の問題(日本語の構造自体に起因するものも含め)がありそうなことが分かりました。
今後は、上位・下位の概念を用いるなど、WordNetならではの解析に挑戦してみたいです。

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

参考:

[1] 日本語 WordNet
[2] [自然言語] Wordnet × Pythonで類義語を抽出する
[3] 【API】日本語WordNetの全内容にアクセスできるWebAPIを公開した

127
120
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
127
120