多層パーセプトロン

ディープラーニングチュートリアル
http://deeplearning.net/tutorial/intro.html

今回はMultilayer perceptron を読んでいきます。
http://deeplearning.net/tutorial/mlp.html

多層パーセプトロン

今回扱うのは、隠れ変数の層が一つある多層パーセプトロン
隠れ変数間や観測変数間の接続はないものとする。

これは、入力に非線形変換が施されたロジスティック回帰とみなせる。

なんで非線形変換なんかするかって言うと、線形分離可能にするため。

モデル

ただしは入力ベクトルのサイズ、
は出力ベクトルのサイズ


行列形式で書けば、

はバイアスベクトル
は重み行列
は活性化関数

で隠れ変数の状態を計算可能

ただし、 は入力変数と隠れ変数をつなぐ重み行列で, は隠れ変数の数

sとしてはtanhシグモイド関数がよくつかわれる
ここではtanhを使う

するとさっきの式は

となる。

Gとしてsoftmax関数を採用すれば、これは前回やったロジスティック回帰とまったく同じ(入力が変換されている点をのぞいて)

多層パーセプトロンを学習させるには、全パラメタを更新していかなければならない。

の計算はTheanoがしてくれるからここではくわしくは触れない。

ロジスティック回帰から多層パーセプトロン

ここからは実装の話

入力をで変換して、
その結果をロジスティック回帰の入力にしてやるのだからまず

ここで、重みの初期値はの範囲からランダムにとるとうまくいくってことが知られているのでそれに従う。

class HiddenLayer(object):
    def __init__(self, rng, input, n_in, n_out, activation=T.tanh):
        self.input = input

        # 初期値の設定
        W_values = numpy.asarray(rng.uniform(
                low=-numpy.sqrt(6. / (n_in + n_out)),
                high=numpy.sqrt(6. / (n_in + n_out)),
                size=(n_in, n_out)), dtype=theano.config.floatX)
        if activation == theano.tensor.nnet.sigmoid:
            W_values *= 4
            
        self.W = theano.shared(value=W_values, name='W')

        b_values = numpy.zeros((n_out), dtype=theano.config.floatX)
        self.b = theano.shared(value=b_values, name='b')
        
        # W, b と inputと outputの関係を定義
        self.output = activation(T.dot(input, self.W) + self.b)
        self.params = [self.W, self.b]

class MLP(object):
  def __init__(self, rng, input, n_in, n_hidden, n_out):
      # 入力を一旦h(x)で変換
      self.hiddenLayer = HiddenLayer(rng = rng, input = input,
                               n_in = n_in, n_out = n_hidden,
                               activation = T.tanh)

      # h(x)で変換されたものを入力としてロジスティック回帰を行う
      self.logRegressionLayer = LogisticRegression(
                                  input=self.hiddenLayer.output,
                                  n_in=n_hidden,
                                  n_out=n_out)

ここで行っているのはあくまでもTheanoのシンボル同士の関係性を定義しているだけ。

そして、ここではL1とL2制約を用いる。

これは過学習を抑えるためだとおもう

      self.L1 = abs(self.hiddenLayer.W).sum() \
          + abs(self.logRegressionLayer.W).sum()

      self.L2_sqr = (self.hiddenLayer.W ** 2).sum() \
          + (self.logRegressionLayer.W ** 2).sum()

      self.negative_log_likelihood = self.logRegressionLayer.negative_log_likelihood
      self.errors = self.logRegressionLayer.errors

      self.params = self.hiddenLayer.params + self.logRegressionLayer.params

前回のロジスティック回帰では、負の対数尤度を最小化したが、今回は負の対数尤度 + L1制約 + L2制約を最小化する

"ロジスティック回帰に一段隠れ変数の層を加えたのはその変換によって線形分離可能にするため"

であったはずで、その結果の学習方法が

"ロジスティック回帰の負の対数尤度 + L1制約 + L2制約の最小化"

というのがちょっとピンとこない

単純に、"隠れ変数の層への変換で線形分離可能"になれば、負の対数尤度関数は小さくなるだろうし、モデルの自由度が増えた分、過学習しやすくなっているから制約条件を入れるって言うのもわかるけどあとでちゃんと確認した方が良さそう

ということで学習部分

      gparams = []
      for param in classifier.params:
          gparam  = T.grad(cost, param)
          gparams.append(gparam)

      updates = []

      for param, gparam in zip(classifier.params, gparams):
          updates.append((param, param - learning_rate * gparam))

          train_model = theano.function(inputs=[index], outputs=cost,
                                        updates=updates,
                                        givens={
                  x: train_set_x[index * batch_size:(index + 1) * batch_size],
                  y: train_set_y[index * batch_size:(index + 1) * batch_size]})

おわりに

前回は問題自体も解法自体も初めて扱ったのでやることが多かったけれど、今回はアルゴリズムがちょこっと変わっただけだったので前回の内容が理解できていれば比較的理解しやすかったのではないかと思いました。