言語処理100本ノック(2020)-79: 多層ニューラルネットワーク(TensorFlow)

[言語処理100本ノック 2020 (Rev2)](https://nlp100.github.io/ja/)の[「第8章: ニューラルネット」](https://nlp100.github.io/ja/ch08.html)の[79本目「多層ニューラルネットワーク」](https://nlp100.github.io/ja/ch08.html#79-%E5%A4%9A%E5%B1%A4%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF)記録です。ようやくニューラルネットワークとしては一般的なものとなってきました。今までやったバイアスを使わないなど、むしろそんなオプションを知らなかったです。[74本目](https://qiita.com/FukuharaYohei/items/f5d199da5b6001f51a8b)に85%程度だった正解率から5%ほどあがり、90%の大台に達しています。
[記事「まとめ: 言語処理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/)についても更新します。

# 参考リンク

|リンク|備考|
|:--|:--|
|[79_多層ニューラルネットワーク.ipynb](https://github.com/YoheiFukuhara/nlp100-2020/blob/main/08.%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88/79_%E5%A4%9A%E5%B1%A4%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF.ipynb)|回答プログラムのGitHubリンク|
|[言語処理100本ノック 2020 第8章: ニューラルネット](https://qiita.com/nymwa/items/9c8484ff511123e03ba8)|(PyTorchだけど)解き方の参考|
|[【言語処理100本ノック 2020】第8章: ニューラルネット](https://qiita.com/yamaru/items/b54989d772c4323bde69)|(PyTorchだけど)解き方の参考|
|[まとめ: 言語処理100本ノックで学べることと成果](https://qiita.com/FukuharaYohei/items/6f0af3c4e270d8af2d99)|言語処理100本ノックまとめ記事|

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

|種類|バージョン|内容|
|:--|:--|:--|
|Python|3.7.12|Google Colaboratoryのバージョン|
|google|2.0.3|Google Driveのマウントに使用|
|tensorflow|2.6.0|ディープラーニングの主要処理|

# [第8章: ニューラルネット](https://nlp100.github.io/ja/ch08.html)
## 学習内容
> 深層学習フレームワークの使い方を学び，ニューラルネットワークに基づくカテゴリ分類を実装します．

## ノック内容
> 第6章で取り組んだニュース記事のカテゴリ分類を題材として，ニューラルネットワークでカテゴリ分類モデルを実装する．なお，この章ではPyTorch, TensorFlow, Chainerなどの機械学習プラットフォームを活用せよ．

### [79. 多層ニューラルネットワーク](https://nlp100.github.io/ja/ch08.html#79-%E5%A4%9A%E5%B1%A4%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF)
> 問題78のコードを改変し，バイアス項の導入や多層化など，ニューラルネットワークの形状を変更しながら，高性能なカテゴリ分類器を構築せよ．

# 回答
## 回答結果
`evaluate`関数の結果、90.2%(+5.4%)の正答率、損失も0.27(-0.19)と大幅に精度向上しています。もっと上昇する余地もあるのでしょうが、時間が惜しいので先に進みます。

```:結果
42/42 [==============================] - 1s 4ms/step - loss: 0.2675 - acc: 0.9019
[0.26749172806739807, 0.9019461274147034]
```

## 回答プログラム [79_多層ニューラルネットワーク.ipynb](https://github.com/YoheiFukuhara/nlp100-2020/blob/main/08.%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88/79_%E5%A4%9A%E5%B1%A4%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF.ipynb)
GitHubには確認用コードも含めていますが、ここには必要なものだけ載せています。

```python
import tensorflow as tf
from google.colab import drive

drive.mount('/content/drive')
LOG_DIR = './logs'

# Check if GPU is available
tf.config.list_physical_devices('GPU')

def _parse_function(example_proto):
    # 特徴の記述
    feature_description = {
        'title': tf.io.FixedLenFeature([], tf.string),
        'category': tf.io.FixedLenFeature([], tf.string)}
  
  # 上記の記述を使って入力の tf.Example を処理
    features = tf.io.parse_single_example(example_proto, feature_description)
    X = tf.io.decode_raw(features['title'], tf.float32)
    y = tf.io.decode_raw(features['category'], tf.int32)
    return X, y

BASE_PATH = '/content/drive/MyDrive/ColabNotebooks/ML/NLP100_2020/08.NeuralNetworks/'

def get_dataset(file_name):
    ds_raw = tf.data.TFRecordDataset(BASE_PATH+file_name+'.tfrecord')

    #shuffleはここを見て理解。データ件数取る方法がわからず、1000件に設定
    #https://qiita.com/exy81/items/d1388f6f02a11c8f1d7e
    return ds_raw.map(_parse_function).shuffle(1000).batch(32)

train_ds = get_dataset('train')
valid_ds = get_dataset('valid')
test_ds = get_dataset('test')

model = tf.keras.Sequential(
    [tf.keras.layers.Dense(64, activation='relu', input_dim=300), # , use_bias=False, kernel_initializer='random_uniform'
     tf.keras.layers.Dropout(0.2),
     tf.keras.layers.Dense(4, activation='softmax'),
     ])
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['acc'])

callbacks = []
callbacks.append(tf.keras.callbacks.TensorBoard(log_dir=LOG_DIR))

model.fit(train_ds, 
          epochs=100, 
          validation_data=valid_ds, 
          callbacks=callbacks)

model.evaluate(test_ds)
```

## 回答解説
### モデル
変更したモデル部分です。以下の点を変更。

1. 密結合のレイヤを追加
1. ドロップアウトのレイヤを追加(率は根拠なく設定)
1. 出力層にバイアス項を追加(`use_bias=False`をなくす)
1. 出力層の重みの初期化をなくす(`kernel_initializer='random_uniform'`をなくす)：精度に関係するか不明

```python:
model = tf.keras.Sequential(
    [tf.keras.layers.Dense(64, activation='relu', input_dim=300), # , use_bias=False, kernel_initializer='random_uniform'
     tf.keras.layers.Dropout(0.2),
     tf.keras.layers.Dense(4, activation='softmax'),
     ])
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['acc'])
model.summary()
```

`summary`関数でモデルを出力した結果です。

```:結果
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 64)                19264     
_________________________________________________________________
dropout (Dropout)            (None, 64)                0         
_________________________________________________________________
dense_1 (Dense)              (None, 4)                 260       
=================================================================
Total params: 19,524
Trainable params: 19,524
Non-trainable params: 0
_________________________________________________________________
```

比較のために変更前のモデルです。学習する変数が一気に増えているのがわかります。

```:結果
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 4)                 1200      
=================================================================
Total params: 1,200
Trainable params: 1,200
Non-trainable params: 0
```