kerasとtensorflowを用いたDeep Learning学習（単語分散表現編）

#自然言語処理
単語分散表現は自然言語処理における言語モデルと特徴表現学習を組み合わせた技術の総評として定義づけられます。

>特徴表現学習とは画像や動画、自然言語などから特徴量を抽出しそれを高次元ベクトル上に分布すること。

最も基本的な単語分散表現はMNISTの画像認識でも紹介したOne-hotエンコーディングです。
One-hotエンコーディングの欠点は単語間の類似性を表現できないことです。
ベクトル間の類似度は内積から求めることができますがコーパス内の任意の2単語の内積はほぼ０になってしまいます。

具体的な例で表してみます。数値は適当です。

|  単語 |  　甘さ　  |　  硬さ　  |　大きさ　|
| ---- | ---- | ---- | ---- |
|  バナナ  |  0.61  |  0.23  |  0.37  |
|  リンゴ  |  0.44  |  0.39  |  0.40  |
|  ゴリラ  |  0.02  |  0.51  |  0.77  |


この表のようにそれぞれの単語を項目と数値で表現で表します。この項目がそれぞれの次元ベクトルとなっており、数百個のベクトルが存在しています。この項目の数値が近いもの程（ベクトルの内積）、類似性の高い単語として表される。

http://projector.tensorflow.org/


###1. word2vec
word2vecは分散表現の有名なモデルで一つでGoogleの研究者によって2013年に発明されました。
word2vecには２つのアーキテクチャが存在します。

- CBOW
- Skip-gram

CBOWのほうが高速ですが、Skip-gramのほうが低頻度語の予測に優れています。
###1.1 Skip-gram
Skip-gramではある文章が与えられたとき、中心語からその文脈語を予測するように学習を行います。


###1.2 CBOW
CBOWは逆に文脈語から中心語を学習します。

###2. word2vecによる分散表現
早速、text8コーパス（英語版Wikipediaから文章を抽出したもの）を使用して学習を行ってみたいと思います。
Skip-gramのデフォルトで設定されるCBOWアーキテクチャを使用します。分散処理の次元数を３００、コーパス内で３０以上出現する単語のみを学習に使用します。デフォルトのウィンドサイズは５です。

```console
2020-03-18 14:59:28,569 : INFO : EPOCH 5 - PROGRESS: at 5.94% examples, 703417 words/s, in_qsize 5, out_qsize 0
2020-03-18 14:59:29,571 : INFO : EPOCH 5 - PROGRESS: at 12.00% examples, 707480 words/s, in_qsize 5, out_qsize 0
2020-03-18 14:59:30,576 : INFO : EPOCH 5 - PROGRESS: at 17.88% examples, 704713 words/s, in_qsize 5, out_qsize 0
2020-03-18 14:59:31,589 : INFO : EPOCH 5 - PROGRESS: at 23.76% examples, 702875 words/s, in_qsize 5, out_qsize 0
2020-03-18 14:59:32,593 : INFO : EPOCH 5 - PROGRESS: at 29.64% examples, 703433 words/s, in_qsize 5, out_qsize 0
2020-03-18 14:59:33,601 : INFO : EPOCH 5 - PROGRESS: at 35.46% examples, 702204 words/s, in_qsize 5, out_qsize 0
2020-03-18 14:59:34,609 : INFO : EPOCH 5 - PROGRESS: at 41.28% examples, 700393 words/s, in_qsize 5, out_qsize 0
2020-03-18 14:59:35,621 : INFO : EPOCH 5 - PROGRESS: at 47.16% examples, 700185 words/s, in_qsize 5, out_qsize 0
2020-03-18 14:59:36,622 : INFO : EPOCH 5 - PROGRESS: at 52.98% examples, 699767 words/s, in_qsize 6, out_qsize 0
2020-03-18 14:59:37,638 : INFO : EPOCH 5 - PROGRESS: at 58.81% examples, 698276 words/s, in_qsize 6, out_qsize 0
2020-03-18 14:59:38,643 : INFO : EPOCH 5 - PROGRESS: at 64.69% examples, 698382 words/s, in_qsize 5, out_qsize 0
2020-03-18 14:59:39,650 : INFO : EPOCH 5 - PROGRESS: at 70.45% examples, 697312 words/s, in_qsize 5, out_qsize 0
2020-03-18 14:59:40,660 : INFO : EPOCH 5 - PROGRESS: at 76.51% examples, 698075 words/s, in_qsize 5, out_qsize 1
2020-03-18 14:59:41,662 : INFO : EPOCH 5 - PROGRESS: at 82.33% examples, 697128 words/s, in_qsize 5, out_qsize 0
2020-03-18 14:59:42,675 : INFO : EPOCH 5 - PROGRESS: at 88.44% examples, 698644 words/s, in_qsize 5, out_qsize 0
2020-03-18 14:59:43,683 : INFO : EPOCH 5 - PROGRESS: at 94.21% examples, 697563 words/s, in_qsize 5, out_qsize 0
2020-03-18 14:59:44,628 : INFO : worker thread finished; awaiting finish of 2 more threads
2020-03-18 14:59:44,635 : INFO : worker thread finished; awaiting finish of 1 more threads
2020-03-18 14:59:44,636 : INFO : worker thread finished; awaiting finish of 0 more threads
2020-03-18 14:59:44,636 : INFO : EPOCH - 5 : training on 17005207 raw words (11928635 effective words) took 17.1s, 698887 effective words/s
2020-03-18 14:59:44,636 : INFO : training on a 85026035 raw words (59638379 effective words) took 83.9s, 710542 effective words/s
model.most_similar('woman')
2020-03-18 14:59:44,636 : INFO : precomputing L2-norms of word weight vectors
[('girl', 0.7188235521316528), ('child', 0.7117617130279541), ('man', 0.6950846910476685), ('lady', 0.6474571228027344), ('herself', 0.6310340166091919), ('lover', 0.6236217021942139), ('baby', 0.6171966791152954), ('cub', 0.6051830053329468), ('husband', 0.6006418466567993), ('person', 0.5994666814804077)]
model.most_similar(positive=['woman', 'king'], negative=['man'], topn=10)
[('queen', 0.6286110877990723), ('empress', 0.5562119483947754), ('prince', 0.5555987358093262), ('princess', 0.5550740957260132), ('throne', 0.5475101470947266), ('emperor', 0.5454226136207581), ('daughter', 0.5340555906295776), ('husband', 0.5301674008369446), ('isabella', 0.5280516147613525), ('consort', 0.5257156491279602)]
model.similarity('girl', 'woman')
0.71882355
model.similarity('girl', 'man')
0.58285534
model.similarity('girl', 'car')
0.2724619
model.similarity('bus', 'car')
0.4796353
```

日本語のtext8を使用して分散表現も行ってみます。
以下のコードで学習させます。

```python
import logging
from gensim.models import word2vec

logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
 
sentences = word2vec.Text8Corpus('./data/ja.text8')
model = word2vec.Word2Vec(sentences, size=200)

print(model.most_similar("哲学"))
~
```
学習結果です。

```console
2020-03-18 15:48:58,246 : INFO : EPOCH 5 - PROGRESS: at 6.80% examples, 777471 words/s, in_qsize 5, out_qsize 1
2020-03-18 15:48:59,255 : INFO : EPOCH 5 - PROGRESS: at 13.72% examples, 780567 words/s, in_qsize 5, out_qsize 0
2020-03-18 15:49:00,260 : INFO : EPOCH 5 - PROGRESS: at 20.40% examples, 773863 words/s, in_qsize 5, out_qsize 0
2020-03-18 15:49:01,267 : INFO : EPOCH 5 - PROGRESS: at 27.08% examples, 770078 words/s, in_qsize 6, out_qsize 0
2020-03-18 15:49:02,274 : INFO : EPOCH 5 - PROGRESS: at 33.65% examples, 764904 words/s, in_qsize 5, out_qsize 0
2020-03-18 15:49:03,277 : INFO : EPOCH 5 - PROGRESS: at 40.51% examples, 767796 words/s, in_qsize 5, out_qsize 0
2020-03-18 15:49:04,293 : INFO : EPOCH 5 - PROGRESS: at 47.25% examples, 766361 words/s, in_qsize 5, out_qsize 0
2020-03-18 15:49:05,300 : INFO : EPOCH 5 - PROGRESS: at 54.05% examples, 767284 words/s, in_qsize 5, out_qsize 0
2020-03-18 15:49:06,300 : INFO : EPOCH 5 - PROGRESS: at 60.67% examples, 766242 words/s, in_qsize 5, out_qsize 0
2020-03-18 15:49:07,315 : INFO : EPOCH 5 - PROGRESS: at 67.53% examples, 766888 words/s, in_qsize 5, out_qsize 0
2020-03-18 15:49:08,334 : INFO : EPOCH 5 - PROGRESS: at 74.28% examples, 766144 words/s, in_qsize 4, out_qsize 1
2020-03-18 15:49:09,336 : INFO : EPOCH 5 - PROGRESS: at 81.02% examples, 766302 words/s, in_qsize 5, out_qsize 0
2020-03-18 15:49:10,343 : INFO : EPOCH 5 - PROGRESS: at 87.82% examples, 766795 words/s, in_qsize 5, out_qsize 0
2020-03-18 15:49:11,346 : INFO : EPOCH 5 - PROGRESS: at 94.26% examples, 764524 words/s, in_qsize 5, out_qsize 0
2020-03-18 15:49:12,306 : INFO : worker thread finished; awaiting finish of 2 more threads
2020-03-18 15:49:12,312 : INFO : worker thread finished; awaiting finish of 1 more threads
2020-03-18 15:49:12,321 : INFO : worker thread finished; awaiting finish of 0 more threads
2020-03-18 15:49:12,321 : INFO : EPOCH - 5 : training on 16900026 raw words (11430740 effective words) took 15.1s, 758219 effective words/s
2020-03-18 15:49:12,322 : INFO : training on a 84500130 raw words (57154285 effective words) took 76.9s, 743468 effective words/s
2020-03-18 15:49:12,322 : INFO : precomputing L2-norms of word weight vectors
[('数学', 0.851349949836731), ('神学', 0.8379186391830444), ('史学', 0.7783546447753906), ('マルクス主義', 0.7713480591773987), ('生理学', 0.766730010509491), ('医学', 0.7575567364692688), ('学派', 0.7493468523025513), ('法学', 0.7491728067398071), ('薬学', 0.7422058582305908), ('文学', 0.7349857091903687)]
```

哲学のキーワードに類似度TOP10を出力しています。
大体近しいものが出ましたが、マルクス主義がいるのが若干違和感....関係なくはないけど...

###2.1 学習済みモデルの評価
先ほど学習を行ったが、この学習済みモデルが一般的にみてどの程度の精度を持っているのか評価が難しい。
そのためラベル付けされた分散表現用の日本語データセットを使用してモデルの評価を行います。
今回使用するデータセットはJWSANを使用します。JWSANはcsvファイルで以下の様な形で単語のペアが収録されています。

|  pairID  | 　word1　  |　  word2　  |　POS　|　similarity　|　association　|　n_sim　|　n_asso　|
| ------- | ----- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
|  p0001  |  うら悲しい  |  物憂い  |  3  |  2.85  |  3.49  |  150  |  150  |
|  p0002  |  おっかない  |  酷い  |  3  |  1.56  |  2.78  |  170  |  140  |

それぞれの項目の意味は以下の様になっています。
*airID: ペア番号（JWSAN-2145とJWSAN-1400で共通）
word1, word2: ペアを構成する単語
POS: 品詞（1=名詞，2=動詞，3=形容詞）
similarity: 類似度評定値の平均値(0-6)
association: 関連度評定値の平均値(0-6)
n_sim: 類似度の平均値を算出する際のデータ数（評定者数）
n_asso: 関連度の平均値を算出する際のデータ数（評定者数）*


評価用方法はこちらのサイトを参考に行いました。[学習済み日本語word2vecとその評価について](https://blog.hoxo-m.com/entry/2020/02/20/090000)

- コード：https://github.com/shihono/evaluate_japanese_w2v
- データセット：http://www.utm.inf.uec.ac.jp/JWSAN/

先ほど学習させたモデルを保存してモデルの評価を行います。

```console
(base) root@036be6a215aa:/deep-learning-with-keras-ja/ch05/hyouka/evaluate_japanese_w2v# python eval.py /deep-learning-with-keras-ja/ch05/learn_jp.model /deep-learning-with-keras-ja/ch05/hyouka/evaluate_japanese_w2v/data/jwsan-1400.csv -v --col 1 2 4
[2020-03-31 14:52:22,651] [__main__] [INFO] set logger
[2020-03-31 14:52:23,081] [__main__] [INFO] Word vector 200 dim, Vocab size 75187
[2020-03-31 14:52:23,365] [__main__] [INFO] load filepath : /deep-learning-with-keras-ja/ch05/hyouka/evaluate_japanese_w2v/data/jwsan-1400.csv, 1400 data
[2020-03-31 14:52:23,397] [__main__] [INFO] Evaluate 1252 data
[2020-03-31 14:52:23,415] [__main__] [INFO] spearmanr SpearmanrResult(correlation=0.3195645126906033, pvalue=4.0441840729033805e-31)
Data	1400
OOV	148
Corr	0.320
```

1400の単語ペアを評価データとしてモデルの評価を行いました。
結果は識別率32%となりましが、公開されているfastTextの学習済みモデルが60%以上の精度だったので結構低いですね。。OOVが学習した単語ベクトル内に存在しない未知語の数を表しています。10%以上が単語ベクトル内に存在しない言葉なので結構多いです。