流れ
自然言語処理やword2vecの概要を述べた上で、
2017年1月~2017年11月の判例をダウンロードし、Word2Vecで遊んでみます。
1. 自然言語処理の概要
自然言語処理は3つの処理からなり、意味解析が他の処理よりも難しいそうです。
今回、単語分解と構文解析では形態素解析を行うソフトウェアとしてMeCab、意味解析ではベクトル空間解析を行うWord2Vecを用います。
自然言語処理 | 方法 | 概要 |
---|---|---|
単語分解 | 形態素解析 | 大量の辞書と文法の知識を使って品詞に分解 |
N-gram 解析 |
機械的に文をN個に分解 | |
構文解析 | 形態素解析 | 品詞情報から係り受け判別を行い文構造を解析 |
意味解析 | シソーラス解析 | 大量の類義語/反義語辞書を使って、出現語をグループ化 |
ベクトル空間解析 | 統計解析的手法を用いて、語の類似度を数学的に計算 |
用語
-
シソーラス
単語の上位/下位関係などによって単語を分類し体系づけた類語辞典 -
コーパス
言語学において自然言語処理の研究に用いるため、自然言語の文章を構造化し大規模に集積したもの
2. 意味の定義とは?
自然言語処理では意味解析が難しいが、そもそも意味の定義って何だろうか?
よく「意味不明」とか言われてきたけど、その定義がよく分からずよく悩みました(笑)。
ここで、自然言語処理研究から単語の持つ意味をおさらい。
分布仮説(Distributional hypothesis)
単語の意味はその単語が出現した際の周囲の単語によって決まる
結局、単語の意味は周囲の文脈によって決まるということかー
論理的な文章を書くように努めます。。。
さておき、この分布仮説という考え方は単語間の類似度を与えることができ、例えば、主成分分析、潜在ディレクレ配分、Word2Vecを用いる単語の類似度の推定はこの仮説に基づく方法論だと解釈できます。
3. MeCabを用いた形態素解析
MeCabを用いて、形態素解析(分かち書き)をやってみます。
MeCab内部ではラティスを構築し、そのラティスから最適なパスを選択しています。
そのときに、単語の生起コストと品詞の連接コストを算出し、それらの累積コストを最小にするパスを選択しています(以下のリンクに図を示します)。
形態素解析(引用:クックパッド開発者ブログ)
MeCabでは形態素解析を行う際に用いる辞書を指定でき、以下のコマンドで確認することができます。
mecab -D
filename: /usr/local/lib/mecab/dic/mecab-ipadic-neologd/sys.dic
version: 102
charset: UTF8
type: 0
size: 4527596
left size: 1316
right size: 1316
私はデフォルトのipadic(IPA辞書)ではなく、多数のWeb上の言語資源から得た新語を追加することでカスタマイズしたmecab-ipadic-NEologdを使っています。
さて、そろそろMeCabでわかち書きをPythonでやってみましょう!
import MeCab
import codecs
#判例のファイルリストを入力
input_list = "hanrei.list"
#分かち書きした結果を書き出すファイル
fo = open("saiban.txt","w")
#大量の判例ファイルをそれぞれ分かち書きする
for file in open(input_list,"r"):
file = file.strip('\n') #判例ファイルに無駄な改行があるので削除
fi =codecs.open(file, 'rb', 'utf-8')
lines = fi.read()
lines=lines.replace('\n','')
m = MeCab.Tagger ("-O wakati")
words = m.parse(lines)
fo.write(words)
fo.close()
saiban.txtの一部を抜粋してみます。
平成28年(行ヒ)第169号 相続税更正及び加算税賦課決定取消請求事件の平成29年2月28日 第三小法廷判決を例に示してみます。
- 原文
1 本件は,共同相続人である上告人らが,相続財産である土地の一部につき,財産評価基本通達(昭和39年4月25日付け直資56,直審(資)17国税庁長官通達。以下「評価通達」という。)の24に定める私道の用に供されている宅地(以下「私道供用宅地」という。)として相続税の申告をしたところ,相模原税務署長から,これを貸家建付地として評価すべきであるとしてそれぞれ更正処分及び過少申告加算税賦課決定処分(以下,これらを併せて「本件各処分」という。)を受けたため,被上告人を相手に,本件各処分(更正処分については申告額を超える部分)の取消しを求める事案である。
2(1) 相続税法22条は,同法第3章で特別の定めのあるものを除くほか,相続,遺贈又は贈与により取得した財産の価額は,当該財産の取得の時における時価により,当該財産の価額から控除すべき債務の金額は,その時の現況による旨を定めている。
- わかち書き
'1', '本件', 'は', ',', '共同', '相続', '人', 'で', 'ある', '上告', '人', 'ら', 'が', ',', '相続財産', 'で', 'ある', '土地', 'の', '一部', 'に', 'つき', ',', '財産', '評価', '基本', '通達', '(', '昭和', '3', '9', '年', '4月', '2', '5', '日', '付け', '直資', '5', '6', ',', '直', '審', '(', '資', ')', '1', '7', '国税庁長官', '通達', '。', '以下', '「', '評価', '通達', '」', 'と', 'いう', '。', ')', 'の', '2', '4', 'に', '定める', '私道', 'の', '用', 'に', '供', 'さ', 'れ', 'て', 'いる', '宅地', '(', '以下', '「', '私道', '供用', '宅地', '」', 'と', 'いう', '。', ')', 'として', '相続税', 'の', '申告', 'を', 'し', 'た', 'ところ', ',', '相模原', '税務署長', 'から', ',', 'これ', 'を', '貸家建付地', 'として', '評価', 'す', 'べき', 'で', 'ある', 'として', 'それぞれ', '更正', '処分', '及び', '過少', '申告', '加算', '税', '賦課', '決定', '処分', '(', '以下', ',', 'これら', 'を', '併せ', 'て', '「', '本件', '各', '処分', '」', 'と', 'いう', '。', ')', 'を', '受け', 'た', 'ため', ',', '被', '上告', '人', 'を', '相手', 'に', ',', '本件', '各', '処分', '(', '更正', '処分', 'について', 'は', '申告', '額', 'を', '超える', '部分', ')', 'の', '取消し', 'を', '求める', '事案', 'で', 'ある', '。', '2', '(', '1', ')', '相続税法', '2', '2', '条', 'は', ',', '同法', '第', '3', '章', 'で', '特別', 'の', '定め', 'の', 'ある', 'もの', 'を', '除く', 'ほか', ',', '相続', ',', '遺贈', '又は', '贈与', 'により', '取得', 'し', 'た', '財産', 'の', '価額', 'は', ',', '当該', '財産', 'の', '取得', 'の', '時', 'における', '時価', 'により', ',', '当該', '財産', 'の', '価額', 'から', '控除', 'す', 'べき', '債務', 'の', '金額', 'は', ',', 'その', '時', 'の', '現況', 'による', '旨', 'を', '定め', 'て', 'いる', '。'
なんとなく物々しい文章が分かち書きされていることがわかります。
4. Word2Vecを用いた意味解析
Word2Vecを使う前に、少し勉強してみます。
実はWord2Vecというモデルはなく、CBOWとskip-gramという2種類のモデルを総称して用いられる用語です。
単語などの自然言語処理で扱う記号は人間が恣意的に定義したものであり物理量などを反映した数値ではないため、記号そのものの情報から記号間の類似度や関連性を直接計算するのが難しいです。そこで、以下では単語を数値ベクトル化したもの(分散表現という)を用いて各モデルについてお話します。
分散表現の定義
CBOWやskip-gramは、これまでの文脈が与えられたときに次の単語の生起確率を推定するモデルです。
入力単語(これまでの文脈で出現した単語)および出力単語(次に出現すると予測する単語)の語彙を$\nu$とします。語彙数は$|\nu|$です。以下に入力単語や出力単語などの定義を示します。
- $i$番目の単語番号の入力単語(one-hotベクトル) $\boldsymbol{x_i}$
\boldsymbol{x}_i =
\begin{pmatrix}
0 \ _{1番目} \\
0 \ _{2番目} \\
\vdots \\
1 \ _{i番目}\\
\vdots \\
0 \ _{|\nu|番目}
\end{pmatrix}
- $\boldsymbol{x_i}$に割り当てる単語埋め込みベクトル $\boldsymbol{e_i}$
\boldsymbol{e}_i =
\begin{pmatrix}
a_{1i} \\
\vdots \\
a_{ki}\\
\vdots \\
a_{Di}
\end{pmatrix}
- $\boldsymbol{e_i}$を1から$|\nu|$まで並べた単語埋め込み行列 E
E =
\left(
\begin{array}{ccccc}
a_{11} & \cdots & a_{1i} & \cdots & a_{1|\nu|}\\
\vdots & \ddots &\vdots & & \vdots \\
a_{k1} & & a_{ki} & & a_{k|\nu|} \\
\vdots & &\vdots & \ddots & \vdots \\
a_{D1} & \cdots & a_{Di} & \cdots & a_{D|\nu|}
\end{array}
\right)
- $j$番目の単語番号の出力単語(one-hotベクトル) $\boldsymbol{y_j}$
\boldsymbol{y}_j =
\begin{pmatrix}
0 \ _{1番目} \\
0 \ _{2番目} \\
\vdots \\
1 \ _{j番目}\\
\vdots \\
0 \ _{|\nu|番目}
\end{pmatrix}
- $\boldsymbol{y_j}$に割り当てる単語埋め込みベクトル $\boldsymbol{o_j}$
\boldsymbol{o}_j =
\begin{pmatrix}
a_{1j} \\
\vdots \\
a_{sj}\\
\vdots \\
a_{Dj}
\end{pmatrix}
- $\boldsymbol{o_j}$を1から$|\nu|$まで並べた単語埋め込み行列 O
O =
\left(
\begin{array}{ccccc}
a_{11} & \cdots & a_{1j} & \cdots & a_{1|\nu|}\\
\vdots & \ddots &\vdots & & \vdots \\
a_{s1} & & a_{sj} & & a_{s|\nu|} \\
\vdots & &\vdots & \ddots & \vdots \\
a_{D1} & \cdots & a_{Dj} & \cdots & a_{D|\nu|}
\end{array}
\right)
ちなみに、one-hotベクトルというのは語彙$\nu$中の各単語に1から|$\nu$|までの単語番号を割り振ったとき、与えられた文の$i$番目に出現した単語の単語番号が$n$であった場合に、$i$番目の単語を表すベクトル$\boldsymbol{x_i}$は$n$番目の要素を1とし、それ以外を0とするベクトルのことを言います。
CBOWモデル
CBOWでは入力単語リストHが与えられたときに、$j$番目の出力単語$\boldsymbol{y_j}$が出力される確率を以下の式で定義します。
P(\boldsymbol{y}_j|H) = \frac{\exp(\phi(H,\boldsymbol{y}_j))}{\sum_{\boldsymbol{y}_j^{'}\in\nu}\exp(\phi(H,\boldsymbol{y}_j^{'}))}
\phi(H,\boldsymbol{y}_j) = \sum_{i:\boldsymbol{x}_i\in H}(E\boldsymbol{x}_i) \cdot (O\boldsymbol{y}_j)= \sum_{i:\boldsymbol{x}_i\in H}\boldsymbol{e}_i \cdot \boldsymbol{o}_j
skip-gramモデル
入力語彙|$\nu$|中の$i$番目単語 $\boldsymbol{x_i}$が与えられたとき、出力語彙|$\nu$|中の$j$番目単語$\boldsymbol{y_j}$が出力される確率を以下の式で定義します。
P(\boldsymbol{y}_j|\boldsymbol{x}_i) = \frac{\exp(\phi(\boldsymbol{x}_i,\boldsymbol{y}_j))}{\sum_{\boldsymbol{y}_j^{'}\in\nu}\exp(\phi(\boldsymbol{x}_i,\boldsymbol{y}_j^{'}))}
\phi(H,\boldsymbol{y}_j) =(E\boldsymbol{x}_i) \cdot (O\boldsymbol{y}_j)= \boldsymbol{e}_i \cdot \boldsymbol{o}_j
違いは?
単語のリストか単一の単語かの違いだけで、skip-gramは$H={\boldsymbol{x_i}}$の場合、入力単語が1つだけ与えられる際のCBOWの特殊形だと言えます。つまり、CBOWがskip-gramを包含しています。
獲得方法
分散表現の具体的な値(行列やベクトルの中身)を獲得する方法について述べます。
Word2Vecでは語彙数に対する計算の高速化の方法として負例サンプリングを採用しています。
負例サンプリングでは与えられた入出力の組が「実データの確率分布から取得されたデータ」か「別の確率分布から生成されたデータ」かの2クラスを判別する識別モデルを学習していることに相当します。この負例サンプリングによる獲得方法について説明します。まず、以下のように「実データ」を訓練データ、「別の確率」をノイズデータに置き換えて確率を定義します。
- 訓練データ$D$を生成する確率分布 $P^{D}$
- 学習用の1つのデータ$(H,\boldsymbol{y_j})$
- $(H,\boldsymbol{y_j})$が$P^{D}$から生成された場合を表す確率モデル
$P((H,\boldsymbol{y_j})\sim P^{D})$ - ノイズデータ$D^{'}$を生成する確率分布 $P^{D^{'}}$
- $(H,\boldsymbol{y_j})$が$P^{D^{'}}$から生成された場合を表す確率モデル $P((H,\boldsymbol{y_j})\sim P^{D^{'}})$
また、以下のジグモイド関数を用いて$P((H,\boldsymbol{y_j})\sim P^{D})$をモデル化します。
P((H,\boldsymbol{y_j})\sim P^{D}) = \frac{1}{1+\exp(-\phi(H,\boldsymbol{y_j}))}
以上をもとに、実データとノイズの識別問題の対数尤度関数は次のようになります。
\begin{align}
L(E,O|D,D^{'}) &= -\sum_{(H,\boldsymbol{y_j})\in D}\log(P((H,\boldsymbol{y_j})\sim P^{D})) +\sum_{(H,\boldsymbol{y_j})\in D^{'}}\log(P((H,\boldsymbol{y_j})\sim P^{D^{'}})) \\
&=\sum_{(H,\boldsymbol{y_j})\in D}\log(1+\exp(-\phi(H,\boldsymbol{y_j})))-\sum_{(H,\boldsymbol{y_j})\in D^{'}}\log(1+\exp(-\phi(H,\boldsymbol{y_j})))
\end{align}
これを最小にして、分散表現を獲得します。
実践
さて、少し数学的にみたところで、コードを書いて実践してみます。
先ほど、分かち書きした結果を出力したsaiban.txtを用いて学習させて、モデルをセーブしておきます。
word2vecのオプションについては以下の引用をご覧ください。
from gensim.models import word2vec
sentences = word2vec.LineSentence("saiban.txt")
model = word2vec.Word2Vec(sentences,
sg=1,
size=100,
min_count=1,
window=10,
hs=1,
negative=0)
model.save("saiban.model")
このセーブしたモデルを使って、遊んでみます。
以下のコードの"positive=["不正"]"を変えることで、いろんな単語の類似度を見ることができます。
from gensim.models import word2vec
model = word2vec.Word2Vec.load("./saiban.model")
results = model.most_similar(positive=["不正"], topn=10)
for result in results:
print(result[0], '\t', result[1])
以下にいろんな単語で遊んでみた結果を示します。
- 不正
競争 0.85169517993927
不正競争防止法 0.6987705230712891
告知 0.6730687022209167
不正取得 0.6292716264724731
営業秘密 0.6146512031555176
行為 0.5962961316108704
偽り 0.570838451385498
誤認表示 0.5617186427116394
侵奪 0.5580458641052246
著作権侵害 0.5580265522003174
- 死刑
無期懲役 0.7566020488739014
処す 0.7112569808959961
減軽 0.7012585401535034
無差別 0.6922720074653625
刑事責任 0.6917919516563416
実刑 0.6786264181137085
法定刑 0.6703411340713501
通り魔 0.6703017354011536
殺人 0.6584153771400452
科さ 0.6555818319320679
- 犯罪
殺人罪 0.6740726232528687
正犯 0.6695655584335327
犯行 0.6672478914260864
手口 0.6633844375610352
悪質 0.6633068323135376
盗犯 0.6598390340805054
犯罪組織 0.656005859375
嫌疑 0.6555171608924866
前科 0.6499699950218201
詐欺 0.6486559510231018
不正に対しては競争、営業秘密が上位になっており、なんとなく不正感が出ています。
死刑も犯罪もそれなりに似ているものが並んでいるような気がします。
ただ、"科さ"など?なものもあるので、分かち書きのときに工夫が必要かもしれません。
(ここには出していないですが、いろいろと変なものがありました)
今後
分かち書きを内容語や品詞を絞って行い、もっと精度のよいものを作りたいと思っています。さらに、AI裁判官を作成できればいいなと思っております。