5

5 つの隠れ層を持つ人工ニューラル ネットワークがあるとします。ここでは、バイアス、使用される活性化関数、データの種類など、ニューラル ネットワーク モデルの詳細については忘れてください。もちろん、活性化関数は微分可能です。

以下は、シンボリック微分を使用して、レイヤーの重みに対する目的関数の勾配を計算します。

w1_grad = T.grad(lost, [w1])
w2_grad = T.grad(lost, [w2])
w3_grad = T.grad(lost, [w3])
w4_grad = T.grad(lost, [w4])
w5_grad = T.grad(lost, [w5])
w_output_grad = T.grad(lost, [w_output])

このように、w1に関する勾配を計算するには、w2、w3、w4、および w5 に関する勾配を最初に計算する必要があります。同様に、w2に関する勾配を計算するには、 w3、w4、および w5 に関する勾配を最初に計算する必要があります。

ただし、次のコードでも、各重み行列に対する勾配を計算できます。

w1_grad, w2_grad, w3_grad, w4_grad, w5_grad, w_output_grad = T.grad(lost, [w1, w2, w3, w4, w5, w_output])

パフォーマンスの点で、これら 2 つの方法に違いはありますか? Theano は、2 番目の方法を使用して勾配を再計算することを回避するのに十分なほどインテリジェントですか? インテリジェントとは、w3_grad を計算することを意味します。Theano は、[できれば] w_output_grad、w5_grad、および w4_grad を再計算する代わりに、事前に計算された勾配を使用する必要があります。

4

1 に答える 1

4

Theano は、以前に計算された勾配を使用して、計算グラフの下層の勾配を計算しないことがわかりました。これは、3 つの隠れ層と出力層を持つニューラル ネットワークのダミーの例です。ただし、反復ごとに勾配を計算する必要がない限り、勾配の計算は一生に一度の操作であるため、大したことではありませんTheano は導関数のシンボリック式を計算グラフとして返し、それ以降は単純に関数として使用できます。その時点から、単純に Theano によって導出された関数を使用して数値を計算し、それらを使用して重みを更新します。

import theano.tensor as T
import time
import numpy as np
class neuralNet(object):
    def __init__(self, examples, num_features, num_classes):
        self.w = shared(np.random.random((16384, 5000)).astype(T.config.floatX), borrow = True, name = 'w')
        self.w2 = shared(np.random.random((5000, 3000)).astype(T.config.floatX), borrow = True, name = 'w2')
        self.w3 = shared(np.random.random((3000, 512)).astype(T.config.floatX), borrow = True, name = 'w3')
        self.w4 = shared(np.random.random((512, 40)).astype(T.config.floatX), borrow = True, name = 'w4')
        self.b = shared(np.ones(5000, dtype=T.config.floatX), borrow = True, name = 'b')
        self.b2 = shared(np.ones(3000, dtype=T.config.floatX), borrow = True, name = 'b2')
        self.b3 = shared(np.ones(512, dtype=T.config.floatX), borrow = True, name = 'b3')
        self.b4 = shared(np.ones(40, dtype=T.config.floatX), borrow = True, name = 'b4')
        self.x = examples

        L1 = T.nnet.sigmoid(T.dot(self.x, self.w) + self.b)
        L2 = T.nnet.sigmoid(T.dot(L1, self.w2) + self.b2)
        L3 = T.nnet.sigmoid(T.dot(L2, self.w3) + self.b3)
        L4 = T.dot(L3, self.w4) + self.b4
        self.forwardProp = T.nnet.softmax(L4)
        self.predict = T.argmax(self.forwardProp, axis = 1)

    def loss(self, y):
        return -T.mean(T.log(self.forwardProp)[T.arange(y.shape[0]), y])

x = T.matrix('x')
y = T.ivector('y')

nnet = neuralNet(x)
loss = nnet.loss(y)

diffrentiationTime = []
for i in range(100):
    t1 = time.time()
    gw, gw2, gw3, gw4, gb, gb2, gb3, gb4 = T.grad(loss, [nnet.w, nnet.w2, logReg.w3, nnet.w4, nnet.b, nnet.b2, nnet.b3, nnet.b4])
    diffrentiationTime.append(time.time() - t1)
print 'Efficient Method: Took %f seconds with std %f' % (np.mean(diffrentiationTime), np.std(diffrentiationTime))

diffrentiationTime = []
for i in range(100):
    t1 = time.time()
    gw = T.grad(loss, [nnet.w])
    gw2 = T.grad(loss, [nnet.w2])
    gw3 = T.grad(loss, [nnet.w3])
    gw4 = T.grad(loss, [nnet.w4])
    gb = T.grad(loss, [nnet.b])
    gb2 = T.grad(loss, [nnet.b2])
    gb3 = T.grad(loss, [nnet.b3])
    gb4 = T.grad(loss, [nnet.b4])
    diffrentiationTime.append(time.time() - t1)
print 'Inefficient Method: Took %f seconds with std %f' % (np.mean(diffrentiationTime), np.std(diffrentiationTime))

これにより、以下が出力されます。

Efficient Method: Took 0.061056 seconds with std 0.013217
Inefficient Method: Took 0.305081 seconds with std 0.026024

これは、Theano が動的計画法のアプローチを使用して、効率的な方法の勾配を計算することを示しています。

于 2015-12-27T21:51:47.537 に答える