tensorflowのEmbedding レイヤーは何をするか？

# Embedding レイヤーは何をするか？

## [Text classification with movie reviews](https://www.tensorflow.org/tutorials/keras/basic_text_classification)において、Embedding は何をしているのか？

* Embedding レイヤーは複数の文章を入力とし、文中の単語をベクトル表現にした文章を返す。
   * ※ word2vector など、単語をベクトル表現する方法がある。それを用いて、文章を変換する。
   * 以下で例とともに説明する。

* 例とする文章。
   * Hope to see you soon.
   * Nice to see you again.
      * この2つの文において、Hope と Nice は似た使われ方をしている。単語のベクトル表現を行うと、この  ような単語は近い値になる。soon と again についても同様である。
      * また、使われ方の異なる Hope と soon は遠い値になる。Nice と again についても同様である。

* 入力
   * 文をそのまま解析することはできないので、数値で表現する。
      * ここでは単純に、出現した単語に順に番号を振っていく。
         * Hope: 0
         * to: 1
         * see: 2
         * you: 3
         * soon: 4
         * Nice: 5
         * again: 6
   * これによって、上記の二つの文章を数値化すると以下になる。
      * Hope to see you soon.
         * 数値: 0 1 2 3 4
      * Nice to see you again.
         * 数値: 5 1 2 3 6

   * 上記の2つの文章をnumpy配列にしたものを入力とする。

      ```python
      input_array = np.array([[0, 1, 2, 3, 4],[5, 1, 2, 3, 6]])
      ```

* 出力
   * 入力に対して、一つ一つの単語をベクトル表現にしたものを Embedding レイヤーは返す。
   * 例えば、下記のような出力が得られる。
      * Hope to see you soon.(0 1 2 3 4)

         ```python
         [[ 0.03687655,  0.03447837],
         [ 0.02281625,  0.0151977 ],
         [ 0.03535204, -0.04515104],
         [ 0.03956452, -0.00173484],
         [-0.02474284, -0.00723051]]
         ```

      * Nice to see you again.(5 1 2 3 6)

         ```python
         [[ 0.00383741, -0.02212029],
         [ 0.02281625,  0.0151977 ],
         [ 0.03535204, -0.04515104],
         [ 0.03956452, -0.00173484],
         [-0.02669907,  0.03838631]]
         ```

   * この例においては、各々の単語には以下のようなベクトルに変換されている。
      * Hope: [ 0.03687655,  0.03447837]
      * to: [ 0.02281625,  0.0151977 ]
      * see: [ 0.03535204, -0.04515104]
      * you: [ 0.03956452, -0.00173484]
      * soon: [-0.02474284, -0.00723051]
      * again:  [-0.02669907,  0.03838631]

* サンプルコード

   ```python
   import numpy as np
   from numpy.random import seed
   from tensorflow import keras
   from tensorflow import set_random_seed

   # 毎回の計算結果を同一にするための設定
   seed(1)
   set_random_seed(2)

   input_array = np.array([[0, 1, 2, 3, 4],[5, 1, 2, 3, 6]])
   vocab_size = 7

   model = keras.Sequential()
   model.add(keras.layers.Embedding(vocab_size, 2))
   model.compile(optimizer='rmsprop', loss='mse')

   output_array = model.predict(input_array)
   print(output_array)

   # 出力は以下
   # [[[-0.02981087  0.0290232 ]
   #   [ 0.02917961  0.00819954]
   #   [-0.00517254  0.02441156]
   #   [-0.02445861  0.03841419]
   #   [-0.03786787 -0.006068  ]]
   #
   #  [[ 0.03650666  0.01115639]
   #   [ 0.02917961  0.00819954]
   #   [-0.00517254  0.02441156]
   #   [-0.02445861  0.03841419]
   #   [-0.02990758 -0.00500231]]]
   ```

* 参考サイト
   * [How does Keras 'Embedding' layer work?](https://stats.stackexchange.com/questions/270546/how-does-keras-embedding-layer-work)

## GlobalAveragePooling1D レイヤーは何をするか。

* Embedding レイヤーで得られた値を GlobalAveragePooling1D() レイヤーの入力とするが、これは何をしているのか？

* Embedding レイヤーで得られる情報を圧縮する。
   * Embedding レイヤーは文章を入力として、各々の単語をベクトル表現したものを返す。GlobalAveragePooling1D レイヤーはこれを入力とし、単語のベクトルの次元ごとに平均値を取る。
   * この値によって、各文章の特徴を小さなデータで表現できる。
   * 以下で例とともに説明する。

* 入力
   * 「Hope to see you soon」「Nice to see you again」を Embedding に入れた場合の出力を、GlobalAveragePooling1D レイヤーの入力とする。

      ```python
      [[[ 0.03687655,  0.03447837],
      [ 0.02281625,  0.0151977 ],
      [ 0.03535204, -0.04515104],
      [ 0.03956452, -0.00173484],
      [-0.02474284, -0.00723051]],

      [[ 0.00383741, -0.02212029],
      [ 0.02281625,  0.0151977 ],
      [ 0.03535204, -0.04515104],
      [ 0.03956452, -0.00173484],
      [-0.02669907,  0.03838631]]]
      ```

* 出力
   * 単語の次元ごとに平均をとったものが、出力となる。

      ```python
      [[ 0.0219733 , -0.00088807],
      [ 0.01497423, -0.00308443]]
      ```

      * 平均の計算補足: 例えば、上記の 0.0219733 は以下のように求める。

         ```python
         0.0219733 = (0.03687655 + 0.02281625 + 0.03535204 + 0.03956452 + -0.02474284) / 5
         ```

   * 結果、この処理によって、各文章は以下のように表現される。
      * Hope to see you soon : [ 0.0219733 , -0.00088807]
      * Nice to see you again: [ 0.01497423, -0.00308443]

* 補足
   * GlobalAveragePooling1D レイヤーの出力は、Embeeding レイヤーの出力と比べてコンパクトになる。しかしながら、この情報圧縮により単語の前後関係が失われることは気に留めるべきである。

* サンプルコード

   ```python
   import numpy as np
   from numpy.random import seed
   from tensorflow import keras
   from tensorflow import set_random_seed

   # 毎回の計算結果を同一にするための設定
   seed(1)
   set_random_seed(2)

   input_array = np.array([[0, 1, 2, 3, 4],[5, 1, 2, 3, 6]])
   vocab_size = 7

   model = keras.Sequential()
   model.add(keras.layers.Embedding(vocab_size, 2))
   model.add(keras.layers.GlobalAveragePooling1D())
   model.compile(optimizer='rmsprop', loss='mse')

   output_array = model.predict(input_array)
   print(output_array)

   # 出力は以下
   # [[-0.01362606  0.0187961 ]
   #  [ 0.00122951  0.01543587]]
   ```