RNN テキストマイニング

# Recurrent Neural Network（RNN）
時系列データを扱うためにニューラルネットワークを拡張したもの。文章構造を学習させる時によく使用される。

# RNN LSTMを用いた文章分類
## データの準備
```shell
wget https://archive.ics.uci.edu/ml/machine-learning-databases/00228/smsspamcollection.zip
unzip smsspamcollection.zip
```
## 分かち書き
日本語を扱ったテキストマイニングの場合は事前にMeCab等を使用して、分かち書きする必要があります。

## 文章のベクトル化
用意したテキストは文字だけでなく、ベクトル化しておきます。

### トークン化

- https://keras.io/ja/preprocessing/text/
- http://faroit.com/keras-docs/1.2.2/preprocessing/text/

kerasのメソッドを使用することで、文章を数字に変換することができる。例えば、単語に重複なしで通し番号を振ることで、日本語を数字群（=ベクトル）に変換できる（ex. 青空文庫「吾輩は猫である」）。

- `fit_on_texts`：引数として学習に使用するテキストのリストを取る
    - word_index：単語ごとに割り当てられたインデックス番号
    - word_counts：文章内の単語の出現回数
    - document_count：入力した文章の数

```python
from keras.preprocessing.text import Tokenizer
sentences = ['吾輩 は 猫 で ある 。 名前 は まだ 無い 。 ', 'どこ で 生れ た か とんと 見当 が つか ぬ 。 ', '何 でも 薄暗い じめじめ し た 所 で ニャーニャー 泣い て いた事 だけ は 記憶 し て いる 。 ']
tokenizer = Tokenizer()
tokenizer.fit_on_texts(sentences)
print(tokenizer.word_index)
#{'。': 1, 'は': 2, 'で': 3, 'た': 4, 'し': 5, 'て': 6, '吾輩': 7, '猫': 8, 'ある': 9, '名前': 10, 'まだ': 11, '無い': 12, 'どこ': 13, '生れ': 14, 'か': 15, 'とんと': 16, '見当': 17, 'が': 18, 'つか': 19, 'ぬ': 20, '何': 21, 'でも': 22, '薄暗い': 23, 'じめじめ': 24, '所': 25, 'ニャーニャー': 26, '泣い': 27, 'いた事': 28, 'だけ': 29, '記憶': 30, 'いる': 31}
```

- `texts_to_sequences`：引数としてベクトル化したいテキストを取る
    - 学習用のテキストに含まれない文字は削除される
    - Only words known by the tokenizer will be taken into account.

```python
tokenized_sentences = tokenizer.texts_to_sequences(sentences)
print(tokenized_sentences)
#[[7, 2, 8, 3, 9, 1, 10, 2, 11, 12, 1], 
# [13, 3, 14, 4, 15, 16, 17, 18, 19, 20, 1], 
# [21, 22, 23, 24, 5, 4, 25, 3, 26, 27, 6, 28, 29, 2, 30, 5, 6, 31, 1]]
```

- `texts_to_matrix`：
    - NumPy.ndarrayを返す
        - この例の`len(tokenized_sentences[0])`が32を示すことからも分かるように、`fit_on_texts`で振られたインデックスを元に処理を進めていく
        - インデックス0は予約番号なので、要素1つ目からが単語として意味のある値
    - binary, count, tfgdf, freqのいずれかの方法でベクトル化する
        - `binary`は単語が出てきたら1,出てきていなかったら0を詰める。
        - `count`はその単語が文章中に何回登場するかを表現する

ex. `binary`モード

```python
tokenized_sentences = tokenizer.texts_to_matrix(sentences, "binary")
print(len(tokenized_sentences[0]))
#32 (学習に使用したテキストに登場する単語数に対応している)
print(tokenized_sentences[0])
#[0. 1. 1. 1. 0. 0. 0. 1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
```

1が詰まっているインデックスを取り出すと下の様になる。

```python
print(np.where(tokenized_sentences[0]==1))
#(array([ 1,  2,  3,  7,  8,  9, 10, 11, 12]),)
```
これは、`fit_on_text`で計算したインデックス番号と対応していて、「。」「は」「で」「吾輩」「猫」「ある」「名前」「まだ」「無い」の単語がこの文章中には使用されていますよ、ということを表現していることになる。この考え方は`texts_to_matrix`の他のモードでも同じ。


### Padding
先にベクトル化した文章は、可変長であるためさらに扱いやすくするためにパディングと呼ばれる処理を行う。このパディングではベクトルの次元数を揃えるために、ゼロ埋めをする。例として100個の長さになるようにする。

```python
from keras.preprocessing.sequence import pad_sequences
padding_sentences = pad_sequences(tokenized_sentences, maxlen=100)
print(padding_sentences)
#[[ 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
#   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
#   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
#   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  7  2  8  3  9  1 10
#   2 11 12  1]
#   ...
```

## Train/Testデータ分割
```python
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=0,)
# x : トレーニングデータの中身
# y : テストデータの中身、正解ラベル
# test_size : 3割をテスト用のデータとして置く
# random_state : データを分割する際の乱数のシード値
```

## 分類結果を評価する
### confusion matrix

```python
```


# Reference
- https://qiita.com/gacky01/items/26cd642731e3eddde60d#%EF%BC%91%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB
- https://qiita.com/kenchin110100/items/b34f5106d5a211f4c004

- https://qiita.com/t_Signull/items/21b82be280b46f467d1b
- https://ohke.hateblo.jp/entry/2019/04/27/154500