言語処理100本ノック(2020)-81: RNNによる予測(keras)

[言語処理100本ノック 2020 (Rev2)](https://nlp100.github.io/ja/)の[「第9章: RNN, CNN」](https://nlp100.github.io/ja/ch09.html)の[81本目「RNNによる予測」](https://nlp100.github.io/ja/ch09.html#81-rnn%E3%81%AB%E3%82%88%E3%82%8B%E4%BA%88%E6%B8%AC)記録です。今回は過去に私が経験したことのある内容の延長なので簡単です。RNNにはLSTMより軽いGRUを使っています(今回程度の内容であれば、十分なはず)。
[記事「まとめ: 言語処理100本ノックで学べることと成果」](https://qiita.com/FukuharaYohei/items/6f0af3c4e270d8af2d99)に[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)についてはまとめていますが、追加で差分の[言語処理100本ノック 2020 (Rev2)](https://nlp100.github.io/ja/)についても更新します。

# 参考リンク

|リンク|備考|
|:--|:--|
|[81_RNNによる予測.ipynb](https://github.com/YoheiFukuhara/nlp100-2020/blob/main/09.RNN%2CCNN/81_RNN%E3%81%AB%E3%82%88%E3%82%8B%E4%BA%88%E6%B8%AC.ipynb)|回答プログラムのGitHubリンク|
|[言語処理100本ノック 2020 第9章: RNN, CNN](https://qiita.com/nymwa/items/4542b45837a10766890b)|(PyTorchだけど)解き方の参考|
|[【言語処理100本ノック 2020】第9章: RNN, CNN](https://qiita.com/yamaru/items/6dbd1a44f5910d8848f6)|(PyTorchだけど)解き方の参考|
|[まとめ: 言語処理100本ノックで学べることと成果](https://qiita.com/FukuharaYohei/items/6f0af3c4e270d8af2d99)|言語処理100本ノックまとめ記事|
|[【Keras入門(5)】単純なRNNモデル定義](https://qiita.com/FukuharaYohei/items/25de4a0faf634ad34efc)|前にKerasとRNN勉強したときの記事|
|[【Keras入門(6)】単純なRNNモデル定義(最終出力のみ使用)](https://qiita.com/FukuharaYohei/items/39f865bb53cdd5052179)|前にKerasとRNN勉強したときの記事|
|[【TensorFlow公式ガイド】単語の埋め込み](https://www.tensorflow.org/text/guide/word_embeddings)|Embeddingのガイド|
|[【TensorFlow公式サンプル】トークンインデックスのシーケンスとしてテキストをエンコードする](https://www.tensorflow.org/guide/keras/preprocessing_layers#%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%82%A4%E3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%82%B9%E3%81%AE%E3%82%B7%E3%83%BC%E3%82%B1%E3%83%B3%E3%82%B9%E3%81%A8%E3%81%97%E3%81%A6%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%92%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89%E3%81%99%E3%82%8B)|Embeddingの使い方サンプル|

# 環境
後々GPUを使わないと厳しいので、Google Colaboratory使いました。Pythonやそのパッケージでより新しいバージョンありますが、新機能使っていないので、プリインストールされているものをそのまま使っています。

|種類|バージョン|内容|
|:--|:--|:--|
|Python|3.7.12|Google Colaboratoryのバージョン|
|google|2.0.3|Google Driveのマウントに使用|
|tensorflow|2.7.0|ディープラーニングの主要処理|
|nltk|3.2.5|Tokenの辞書作成に使用|
|pandas|1.1.5|行列に関する処理に使用|

# [第8章: ニューラルネット](https://nlp100.github.io/ja/ch08.html)
## 学習内容
> 深層学習フレームワークを用い，再帰型ニューラルネットワーク（RNN）や畳み込みニューラルネットワーク（CNN）を実装します．

### [81. RNNによる予測](https://nlp100.github.io/ja/ch09.html#81-rnn%E3%81%AB%E3%82%88%E3%82%8B%E4%BA%88%E6%B8%AC)
> ID番号で表現された単語列$\boldsymbol{x} = (x_1, x_2, \dots, x_T)$がある．ただし，$T$は単語列の長さ，$x_t \in \mathbb{R}^{V}$は単語のID番号のone-hot表記である（$V$は単語の総数である）．再帰型ニューラルネットワーク（RNN: Recurrent Neural Network）を用い，単語列$\boldsymbol{x}$からカテゴリ$y$を予測するモデルとして，次式を実装せよ．
$
\overrightarrow h_0 = 0, \\
\overrightarrow h_t = {\rm \overrightarrow{RNN}}(\mathrm{emb}(x_t), \overrightarrow h_{t-1}), \\
y = {\rm softmax}(W^{(yh)} \overrightarrow h_T + b^{(y)})
$
ただし，$\mathrm{emb}(x) \in \mathbb{R}^{d_w}$は単語埋め込み（単語のone-hot表記から単語ベクトルに変換する関数），$\overrightarrow h_t \in \mathbb{R}^{d_h}$は時刻$t$の隠れ状態ベクトル，${\rm \overrightarrow{RNN}}(x,h)$は入力$x$と前時刻の隠れ状態$h$から次状態を計算するRNNユニット，$W^{(yh)} \in \mathbb{R}^{L \times d_h}$は隠れ状態ベクトルからカテゴリを予測するための行列，$b^{(y)} \in \mathbb{R}^{L}$はバイアス項である（$d_w, d_h, L$はそれぞれ，単語埋め込みの次元数，隠れ状態ベクトルの次元数，ラベル数である）．RNNユニット${\rm \overrightarrow{RNN}}(x,h)$には様々な構成が考えられるが，典型例として次式が挙げられる．
$
{\rm \overrightarrow{RNN}}(x,h) = g(W^{(hx)} x + W^{(hh)}h + b^{(h)})
$
ただし，$W^{(hx)} \in \mathbb{R}^{d_h \times d_w}，W^{(hh)} \in \mathbb{R}^{d_h \times d_h}, b^{(h)} \in \mathbb{R}^{d_h}$はRNNユニットのパラメータ，$g$は活性化関数（例えば$\tanh$やReLUなど）である．
なお，この問題ではパラメータの学習を行わず，ランダムに初期化されたパラメータで$y$を計算するだけでよい．次元数などのハイパーパラメータは，$d_w = 300, d_h=50$など，適当な値に設定せよ（以降の問題でも同様である）．

# 回答
## 回答結果
未学習状態なので、4値分類でほぼ同じ確率です。

```:結果
array([[0.2583952 , 0.25148967, 0.22984934, 0.2602658 ]], dtype=float32)
```

## 回答プログラム [81_RNNによる予測.ipynb](https://github.com/YoheiFukuhara/nlp100-2020/blob/main/09.RNN%2CCNN/81_RNN%E3%81%AB%E3%82%88%E3%82%8B%E4%BA%88%E6%B8%AC.ipynb)
GitHubには確認用コードも含めていますが、ここには必要なものだけ載せています。

```python
import nltk
import pandas as pd
import tensorflow as tf
from google.colab import drive

drive.mount('/content/drive')

BASE_PATH = '/content/drive/MyDrive/ColabNotebooks/ML/NLP100_2020/06.MachineLearning/'
max_len = 0


def get_vocabulary(type_):
    global max_len
    df = pd.read_table(BASE_PATH+type_+'.feature.txt')
    df.info()
    sr_title = df['title'].str.split().explode()
    max_len_ = df['title'].map(lambda x: len(x.split())).max()
    if max_len < max_len_:
        max_len = max_len_
    fdist = nltk.FreqDist(sr_title)
    print(f'Top 3 tokens: {fdist.most_common(3)}')
    return [k for k, v in fdist.items() if v > 1]


vocabulary = get_vocabulary('train')
vocabulary.extend(get_vocabulary('valid'))
vocabulary.extend(get_vocabulary('test')) # あまりこだわらずにテストデータセットも追加

# setで重複削除し、タプル形式に設定
tup_voc = tuple(set(vocabulary))

print(f'vocabulary size before removing duplicates: {len(vocabulary)}')
print(f'vocabulary size after removing duplicates: {len(tup_voc)}')
print(f'sample vocabulary: {tup_voc[:10]}')
print(f'max length is {max_len}')

vectorize_layer = tf.keras.layers.TextVectorization(
 output_mode='int',
 vocabulary=tup_voc,
 output_sequence_length=max_len)

print(f'sample vocabulary: {vectorize_layer.get_vocabulary()[:10]}')

model = tf.keras.models.Sequential()
model.add(tf.keras.Input(shape=(1,), dtype=tf.string))
model.add(vectorize_layer)
model.add(tf.keras.layers.Embedding(vectorize_layer.vocabulary_size(), 300, mask_zero=True))
model.add(tf.keras.layers.GRU(50))
model.add(tf.keras.layers.Dense(4, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='sgd')
model.summary()
model.predict([['this is a pen']])
```

## 回答解説
以前KerasとRNNは以下の記事で書いたので解説をかなり割愛します。記事は少し古いですが、基本的に変わっていません。

- [【Keras入門(5)】単純なRNNモデル定義](https://qiita.com/FukuharaYohei/items/25de4a0faf634ad34efc)
- [【Keras入門(6)】単純なRNNモデル定義(最終出力のみ使用)](https://qiita.com/FukuharaYohei/items/39f865bb53cdd5052179)

### Embedding
Ebmedding(単語埋め込み)についてです。
単語数を`TextVectorization`の`vocabulary_size`で取得しています。maskと未知語(UNK)も含まれる数をくれるので、`mask_zero`を`True`にしてもその値でOKです。
後にGPUを使うのでRNN(GRU)のパラメータはデフォルト値にしておきます。GPU使用注意点は[公式tf.keras.layers.GRU](https://www.tensorflow.org/api_docs/python/tf/keras/layers/GRU/)に記載があります。

```python:
model = tf.keras.models.Sequential()
model.add(tf.keras.Input(shape=(1,), dtype=tf.string))
model.add(vectorize_layer)
model.add(tf.keras.layers.Embedding(vectorize_layer.vocabulary_size(), 300, mask_zero=True))
model.add(tf.keras.layers.GRU(50))
model.add(tf.keras.layers.Dense(4, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='sgd')
model.summary()
```

[TensorFlow公式ガイド「単語の埋め込み」](https://www.tensorflow.org/text/guide/word_embeddings)に記載がありますが、ローカルのTensorBoardまたは[インターネット上のプロジェクター](http://projector.tensorflow.org/)でEmbeddingの視覚化ができます。そのためのファイル出力方法もガイド内に記載があります。