1番コモディティ化しているのは、Word2vecでしょう。
この場合は、単語ベクトルのコサイン類似度を使います。
で、今日はWord2vecではなくWordnetを使います。
〇〇NetというとNN脳の人はすぐ有名ネットワークだと思ってしまいますが、こちらはニューラルネットワークとは無関係です。
他人の力1 Wordnet
Wordnetは、ニューラルネットブームより前から人手で構築されているとても歴史のある概念辞書です。概念辞書とは、単語同士の上位語・下位語などの関係をまとめたものです。
ここでいう関係というのは、例えば、犬/ダックスフンドは上位語/下位語などです。
もとは英語のプロジェクトですが、日本語版もあり、BSDライセンスで無償公開されていて、利用しやすくなっています。
http://compling.hss.ntu.edu.sg/wnja/index.ja.html
他人の力2 Wordnet2networkx
Wordnetでは単語IDと概念IDが紐付いています。
厳密にいうと概念ID同士が先ほど説明した上位・下位などの関係で結びついています。
厳密にいうと概念ID同士が先ほど説明した上位・下位などの関係で結びついています。
なので、実態は概念IDをノード、概念同士のつながりをリンクとしたネットワークです。
networkxに流し込めると概念間の最短経路など一発で求められて楽そうです。
で、ありがたいことにやってくれている方がいました。
こちらのコードで概念ネットワークのルートノードまでの最短経路が取得可能です。
entity
(生命がある、あるいは生命がないに関わらず)それ自身の明確な存在を持つと感知される、知られている、あるいは推定される何か
こちらのentityというノードがWordnetにおける最上位ノードのようです。
そのためWordnetはentityというノードをルートノードとした、木構造ネットワークになります。
さて、リンク先のコードでは、単語を入力すると、まずその単語の概念を求めて、そこから最上位概念entityまでのパスを計算してくれます。
2つの単語同士の、ルートノードまでのパスがどれくらい似ているかで、その単語同士の概念の近さを図ることができそうです。
他人の力3 概念距離の定義式
で、ルートノードまでのパスがわかったところで、概念距離を定式化します。
ありがたいことにこちらも既知の公式があるようです。
\( 1-\frac{2C}{L_1 + L_2} \)
L1,L2は2つの単語のルートノードまでのパスの長さです。
これは「他人の力2」のコードで取得したルートノードまでのリストの長さですね。
Cは2つの単語のパスの共通部分の長さに相当します。
2単語が同じ概念に属していたとき、L1=L2=Cになるので、上式の第二項は1ですね。
なので上式全体は0になります。
同じ概念に属しているということは、概念距離0に他ならないので正しい結果です。
逆に共通部分Cが少ないときは、第二項は小さくなるので、上式全体は1に近くなります。
また、2CはL1+L2より必ず小さいため、上式は範囲[0,1]のお行儀のよい関数になります(厳密にいうとC=0にはならないので、1にはなりませんが)。
扱いやすくて最高ですね。
さて、Wordnetには、93,834 words入っていると書かれていますが、NLPである以上OOV(Out of Vocaburary)は避けられません。
どちらかの単語がOOVで概念距離を取得できない場合は、例外処理で概念距離=1にしてしまうようです。
ここだけ自分で実装します。
※上記リンク先で定義されている変数・関数を使っています。
def calc_similarity(word1, word2):
try:
path1 = calc_synset_path(conn, G, word1)
path2 = calc_synset_path(conn, G, word2)
C = len(set(path1) & set(path2))
L1 = len(path1)
L2 = len(path2)
return 1-2*C/(L1+L2)
except:
return 1
他人の力4 話題分類単語辞書
色々準備したので実験したくなります。
ところが手頃な類語辞典がないんですね。
クローリングするほどじゃないし。
そこでこちらを使わせていただきます。
http://www.jnlp.org/SNOW/D11
単語が話題(トピック)に紐付いているので、同じトピックの単語を類語、とは言わないまでも関連性の強い=概念距離の近い単語と考えます。
エクセルファイルで配布されているところがすてきです。
さくっと試せます。
同一トピックから3000ペア、別トピックから3000ペア、適当にサンプリングします。
これでデータ数6000件になりますが、OOVが75%もあり、使用可能なのは約1500件でした。
標準偏差・平均は以下のとおりです。
同一トピック
平均 0.71
標準偏差 0.20
別トピック
平均 0.77
標準偏差 0.12
標準偏差 0.20
別トピック
平均 0.77
標準偏差 0.12
有意差なしですね。
同じトピックの単語というだけでは大味すぎたか。
度数分布もグラフ的にそんなにきれいに分かれなかったので、パーセンタイル点をチェックして一応納得しておきます。
同じトピックの場合
0.0 0.000000
0.1 0.403620
0.2 0.636364
0.3 0.714286
0.4 0.733333
0.5 0.764706
0.6 0.846154
0.7 0.857143
0.8 0.866667
0.9 0.882353
別トピックの場合
0.0 0.176471
0.1 0.625000
0.2 0.714286
0.3 0.733333
0.4 0.764706
0.5 0.833333
0.6 0.857143
0.7 0.866667
0.8 0.866667
0.9 0.882353
70%点あたりから、別トピックのケースのほうが一段遅れになっていることがわかります。
流石に差が出ていてよかったです。
今日のベストプラクティス
今回は触れていませんが、Wordnetの概念リンクにはいくつか種類があるので、それをもっと活かすアプリケーションのほうが向いているかも。
0 件のコメント:
コメントを投稿