TheanoでSentiment analysis (CNN)②
前回でマックスプーリングまでの層の定義をまとめた。
今回は、MLP層についてまとめる。
MLPDropput
ここに書いたこととほとんど同じ。
呼び出し側
classifier = MLPDropout(rng,
input=layer1_input,
layer_sizes=hidden_units,
activations=activations,
dropout_rates=dropout_rate)
引数について
input
:入力のshape(マックスプーリング後の特徴量を結合したもの(ここでは、[batch_num,300]))layer_sizes
:中間層の入力・出力のshape(ここでは、[300,2])activations
:活性化関数(ここでは、「恒等関数」 )dropout_rates
:ドロップアウトの割合(ここでは、「0.5」)
_dropout_from_layer
まずは、_dropout_from_layerの処理を追いかける。
def _dropout_from_layer(rng, layer, p): """p is the probablity of dropping a unit""" srng = theano.tensor.shared_randomstreams.RandomStreams(rng.randint(999999)) # p=1-p because 1's indicate keep and p is prob of dropping mask = srng.binomial(n=1, p=1-p, size=layer.shape) # The cast is important because # int * float32 = float64 which pulls things off the gpu output = layer * T.cast(mask, theano.config.floatX) return output
theanoでの二項分布に基づく乱数の生成プログラムは下記の通り。
import numpy import theano import theano.tensor as T from theano.tensor.shared_randomstreams import RandomStreams numpy_rng = numpy.random.RandomState(99999) theanoRng = RandomStreams(numpy_rng.randint(99999)) fbino = theanoRng.binomial(n=1, p=0.5, size=(4, 5)) f1 = theano.function([], fbino) print f1() >> [[0 1 1 1 1] >> [1 1 1 1 0] >> [1 1 1 0 0] >> [1 1 0 1 0]]
n=1, p=0.5でnの生成確率を決めることが出来る。 (p=0.1の場合、1の出現確率は0.1になるということ。)
T.cast()
は型変換。
GPUは倍精度(64bit)浮動小数点数の計算が苦手で,単精度(32bit)の方がよいらしいので、
theano.config.floatX
で指定した型に変換しておく。
*呼び出し時のTHEANO_FLAGS.floatX
の型となるので、float32
となる
THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python conv_net_sentence.py
outputは、二項分布(p=0.5)に従い、入力を0にした(半分は×1なのでそのまま)もの。 それをMLPDropoutに返す。
ドロップアウトのイメージはこんなん。
DropoutHiddenLayer
次は、隠れ層(DropoutHiddenLayer)の呼び出し。
結論から言うと、layer_sizes=(300,2)の場合、隠れ層は作られない。
*MLPの入力に対し、ドロップアウトを行うだけ
next_dropout_layer_input = _dropout_from_layer(rng, input, p=dropout_rates[0]) layer_counter = 0 for n_in, n_out in self.weight_matrix_sizes[:-1]: next_dropout_layer = DropoutHiddenLayer(rng=rng, input=next_dropout_layer_input, activation=activations[layer_counter], n_in=n_in, n_out=n_out, use_bias=use_bias, dropout_rate=dropout_rates[layer_counter]) self.dropout_layers.append(next_dropout_layer) next_dropout_layer_input = next_dropout_layer.output # Reuse the parameters from the dropout layer here, in a different # path through the graph. next_layer = HiddenLayer(rng=rng, input=next_layer_input, activation=activations[layer_counter], # scale the weight matrix W with (1-p) W=next_dropout_layer.W * (1 - dropout_rates[layer_counter]), b=next_dropout_layer.b, n_in=n_in, n_out=n_out, use_bias=use_bias) self.layers.append(next_layer) next_layer_input = next_layer.output #first_layer = False layer_counter += 1
ここでは、weight_matrix_sizes
に基づき、繰り返し処理を行っているが、
MLP層の隠れ層が1層の場合hidden_units
=(300,2)の場合、weight_matrix_sizes
[:-1]=[]となり、
繰り返し処理は行われない。
hidden_units
=(300,100,2)とした場合、weight_matrix_sizes
[:-1]=[300,100]となるので、DropoutHiddenLayerクラスが1回だけ呼ばれる。
このクラスでは、隠れ層が複数あった場合、それぞれにドロップアウトを適用している。
hidden_units=(300,2)
の場合、ループ処理が行われず、隠れ層が作られないので、「あれ」と思ったが、
issueにもある通り、標準では隠れ層なしで直接、出力層に繋げるようになっているらしい。
隠れ層の処理は通常のMLPと同じなので、今回は標準の隠れ層なしパターンで進める。
LogisticRegression
次は、出力層(LogisticRegression)の呼び出し。(インスタンスの生成)
# Set up the output layer n_in, n_out = self.weight_matrix_sizes[-1] dropout_output_layer = LogisticRegression( input=next_dropout_layer_input, n_in=n_in, n_out=n_out) self.dropout_layers.append(dropout_output_layer) # Again, reuse paramters in the dropout output. output_layer = LogisticRegression( input=next_layer_input, # scale the weight matrix W with (1-p) W=dropout_output_layer.W * (1 - dropout_rates[-1]), b=dropout_output_layer.b, n_in=n_in, n_out=n_out) self.layers.append(output_layer)
一応、dropout_output_layerとoutput_layerの2つのLogisticRegressionインスタンスが生成されていることは押さえておく。
# LogisticRegression class # initialize with 0 the weights W as a matrix of shape (n_in, n_out) if W is None: self.W = theano.shared( value=numpy.zeros((n_in, n_out), dtype=theano.config.floatX), name='W') else: self.W = W # initialize the baises b as a vector of n_out 0s if b is None: self.b = theano.shared( value=numpy.zeros((n_out,), dtype=theano.config.floatX), name='b') else: self.b = b
dropout_output_layerインスタンスの生成時は、パラメータW,bは未指定。
なので、0で初期化される。
inputには、next_dropout_layer_inputを指定している。
これは、_dropout_from_layer関数のoutputであり、乱数でドロップアウトした結果である。
output_layernext_layer_inputインスタンスのinputには、next_layer_inputが指定されている。
これは、DropoutHiddenLayerが存在しない場合、MLPDropoutのインスタンスの生成時にセットされたinputである。
このinputは、下記の通りCNN層の結合結果であることが分かる。
layer1_input = T.concatenate(layer1_inputs,1)
パラメータはdropout_output_layerのW,bを利用している。
多分、CNNの処理結果を受け取るoutput_layerインスタンスがメインとなるLogisticRegressionで、dropout_output_layerはその事前のドロップアウトの役割に過ぎないと思われる。
LogisticRegressionクラス自体は、通常のMLPと同じ処理なので、深く突っ込まず、ロジスティック分類により入力が二値分類されるという程度にしておく。
最後に、MLPDropputに戻って
# Use the negative log likelihood of the logistic regression layer as # the objective. self.dropout_negative_log_likelihood = self.dropout_layers[-1].negative_log_likelihood self.dropout_errors = self.dropout_layers[-1].errors self.negative_log_likelihood = self.layers[-1].negative_log_likelihood self.errors = self.layers[-1].errors # Grab all the parameters together. self.params = [ param for layer in self.dropout_layers for param in layer.params ]
各モデル関数の損失関数としては、output_layerのnegative_log_likelihood,errorsが使われている。
dropout_output_layerのdropout_negative_log_likelihoodは、勾配計算のsgd_updates_adadeltaの引数として用いられる。
dropout_output_layerのdropout_errorsに関しては、参照されることがない。。
MLPDropoutのinit関数はここまで。
次回、MLPDropoutがメインの関数train_conv_netに返された後を見ていきたい。