SpatialDropoutは、画像認識の分野でTompsonらによって提案されたドロップアウト方法です。通常のドロップアウトが各要素を独立して落とすのに対して、SpatialDropoutはある領域全体をまるごと落とします。それにより、画像認識の分野で性能向上が報告されています。
本記事では、KerasにおけるSpatialDropoutの動作について理解を深めることを目的としています。KerasにはSpatialDropout1D、SpatialDropout2D、SpatialDropout3Dの3種類がありますが、本記事ではSpatialDropout1Dの動作を確認します。
はじめに、SpatialDropout1Dの入出力について確認しておきましょう。SpatialDropout1Dの入力は3次元のテンソル(samples, timesteps, channels)
です。出力は入力と同じ形のテンソルが出力されます。ただし、出力されたテンソルにはドロップアウトが適用されています。
SpatialDropout1Dの入力についてイメージを具体化するために、テキストについて考えます。テキストを3次元のテンソルで表すと(samples, sequence_length, embedding_dim)
として考えることができます。ここで、sequence_length
は文の長さ、embedding_dim
は分散表現の次元数を表しています。以下のようなイメージで表せます。
このようなテキストに対してSpatialDropout1Dを適用すると、分散表現のある次元全体に対してドロップアウトが適用されます。通常のドロップアウトが各要素を独立に落としていたのに対して、SpatialDropout1Dではある次元をまるごと落としています。これらの違いは以下で表せます。
実際に、コードを書いて動作を確認してみましょう。入力として(1, 7 ,5)のテンソルを用意し、DropoutとSpatialDropout1Dを適用して違いを確認してみましょう。
まずは、入力のテンソルを用意します。
>>> import numpy as np >>> import keras.backend as K >>> ary = np.arange(35).reshape((1, 7, 5)) >>> ary array([[[ 0, 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, 32, 33, 34]]]) >>> inputs = K.variable(ary)
入力を用意したら通常のDropoutを適用してみましょう。ここではKerasのDropout層の中で使われているkeras.backend.dropout
を使ってテンソルにDropoutを適用します。Dropout率は0.5に設定します。Dropout率はK.dropout
のlevel
パラメータで指定できます。
>>> K.eval(K.dropout(inputs, level=0.5)) array([[[ 0., 2., 0., 0., 0.], [10., 12., 14., 0., 18.], [ 0., 22., 0., 0., 28.], [ 0., 32., 0., 36., 0.], [ 0., 0., 44., 46., 48.], [ 0., 0., 54., 56., 58.], [60., 62., 64., 66., 68.]]], dtype=float32)
結果を見ると、各要素を独立に落としているらしいことが確認できます。
次に、SpatialDropout1Dを適用してみます。SpatialDropout1Dを適用するには、Dropoutの形を指定する必要があります。そのために使われるパラメータがnoise_shape
です。SpatialDropout1Dの場合、入力テンソルの形input_shape
に対して以下のようにnoise_shape
を指定します。
>>> input_shape=K.shape(inputs) >>> noise_shape=(input_shape[0], 1, input_shape[2]) >>> K.eval(K.dropout(inputs, 0.5, noise_shape)) array([[[ 0., 2., 4., 6., 0.], [ 0., 12., 14., 16., 0.], [ 0., 22., 24., 26., 0.], [ 0., 32., 34., 36., 0.], [ 0., 42., 44., 46., 0.], [ 0., 52., 54., 56., 0.], [ 0., 62., 64., 66., 0.]]], dtype=float32)
結果を見ると、ある領域をまるごと落としていることが確認できます。
ちなみに、分散表現の縦方向ではなく横方向全体にドロップアウトをかけるには、Dropout層に以下のようなnoise_shape
を渡せばOKです。
>>> noise_shape=(input_shape[0], input_shape[1], 1) >>> K.eval(K.dropout(inputs, 0.5, noise_shape)) array([[[ 0., 0., 0., 0., 0.], [10., 12., 14., 16., 18.], [ 0., 0., 0., 0., 0.], [30., 32., 34., 36., 38.], [40., 42., 44., 46., 48.], [ 0., 0., 0., 0., 0.], [60., 62., 64., 66., 68.]]], dtype=float32)