LoginSignup
2
6

More than 3 years have passed since last update.

【学習メモ】ゼロから作るDeepLearning 〜Dropoutの実装〜

Last updated at Posted at 2020-05-09

背景

最近Deep Learningを
ゼロから作るDeep Learning
で勉強し始めています。
dropoutの実装も載っているのですが、中身がどう動いているのかもう少し掘ってみました。

droputとは?

機械学習では、学習データに特化してしまう過学習という問題があります。
過学習に関してですが、文章と意味を暗記して他の文章の意味を理解しようとした場合を例に挙げてみます。暗記した文章で「はし」という言葉が「橋」という意味でよく使われたとして、他の文章で「はし」が「端」として使われてても「橋」として理解してしまうような状況を指します。(あくまでも僕の現状の理解なので、間違ってたらコメントください。)
それを防ぐのがDropoutという手法です。もう少し詳しい説明はここを参照してください。

dropoutの実装

ここでは、
・python3
・numpy
を使っています。
では、Dropoutレイヤーの実装を見てみましょう。

import numpy as np

class Dropout:
    """
    http://arxiv.org/abs/1207.0580
    """
    def __init__(self, dropout_ratio=0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None

    def forward(self, x, train_flg=True):
        if train_flg:
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio
            return x * self.mask
        else:
            return x * (1.0 - self.dropout_ratio)

    def backward(self, dout):
        return dout * self.mask

動かしながら中身の処理を確認

まず、初期化の部分です。

    def __init__(self, dropout_ratio=0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None

ここは単純に引数のdropout_ratioを内部変数につめるのと、dropoutのマスクを初期化します。

次に、順伝搬(forward)部分です。

    def forward(self, x, train_flg=True):
        if train_flg:
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio
            return x * self.mask
        else:
            return x * (1.0 - self.dropout_ratio)

train_flgは学習時にTrue、推論時にFalseにします。この辺の詳しい話は「ゼロから作るDeep Learning」を読んでみてください。
それぞれの処理を分解して、Shellで処理内容を見ていきましょう(ここでの値は一例です)。

#まずは適当なinputとなるxを生成
>>> import numpy as np
>>> x = np.array([0,1,2,3,4])
>>> x
array([0, 1, 2, 3, 4])

#np.random(rand(*x.shape)の出力
>>> rand = np.random.rand(*x.shape)
>>> rand
array([0.15816005, 0.03610269, 0.86484777, 0.2235985 , 0.64981875])

#np.random.rand(*x.shape) > self.dropout_ratio(ここでは0.5にしています)の出力
>>> dropout_ratio = 0.5
>>> mask = rand > dropout_ratio
>>> mask
array([False, False,  True, False,  True])

#return x * self.maskの出力
#maskでFalseになっている部分はxに掛け合わせると0になる
>>> x * mask
array([0, 0, 2, 0, 4])

#return x * (1.0 - self.dropout_ratio)の出力
>>> x * (1.0 - dropout_ratio)
array([0. , 0.5, 1. , 1.5, 2. ])

次に、逆伝搬(backward)部分です。

    def backward(self, dout):
        return dout * self.mask

forwardと同じようにShellで実行してみます。
maskはforwardで生成したをもの利用し、doutは適当なものを生成します。
実際にもforwardで生成したmaskはbackwardに引き継がれます。

#doutを生成
>>> dout = [0.0, 0.1, 0.2, 0.3, 0.4]

#return dout * self.maskの出力
>>> dout * mask
array([0. , 0. , 0.2, 0. , 0.4])

最後に、実装したDropoutの動きをみます。今回は、順伝(forward)のみ。
一度、sample.pyというファイルに今回のDropoutを記載しておいたのを使っています。

>>> import numpy as np
>>> from sample import Dropout
>>> 
>>> dropout = Dropout()
>>> 
>>> x = np.array([0,1,2,3,4])

#乱数生成しているので、forwardの出力は実行のたびに変わる
>>> dropout.forward(x)
array([0, 0, 2, 3, 0])
>>> dropout.forward(x)
array([0, 0, 0, 0, 4])
>>> dropout.forward(x)
array([0, 0, 2, 0, 4])

forwardの出力が確認できました。
train_flg=Falseやbackwardの動きも動かして確認してみてください。

2
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
6