2015-11-23 9 views
8

Ich bin daran interessiert, die Architektur zu nutzen, die in der jüngsten Arbeit "Unsupervised Domain Adaptation by Backpropagation" im Rahmen von Lasagne/Theano vorgeschlagen wurde.Kann ich Theano-Gradienten während der Rückpropagation (selektiv) invertieren?

Der an diesem Papier, das es ein wenig ungewöhnlich macht, ist, dass es eine ‚Gradientenumkehr Schicht‘ enthält, die inverts das Gradient während des Backpropagation:

enter image description here

(Die Pfeile entlang der unteren des Bildes sind die Rückpropagationen, deren Steigung umgekehrt ist).

In dem Papier behaupten die Autoren, dass der Ansatz "mit jedem Deep Learning-Paket implementiert werden kann", und tatsächlich bieten sie eine version made in caffe.

Allerdings verwende ich das Lasagne/Theano-Framework, aus verschiedenen Gründen.

Kann in Lasagne/Theano eine solche Gradientenumkehrschicht erzeugt werden? Ich habe keine Beispiele gesehen, wo man benutzerdefinierte Skalartransformationen auf solche Gradienten anwenden kann. Wenn ja, kann ich dies tun, indem ich eine benutzerdefinierte Ebene in Lasagne erstelle?

Antwort

7

Hier ist eine Skizze Implementierung mit einfachen Theano. Dies kann leicht in Lasagne integriert werden.

Sie müssen eine benutzerdefinierte Operation erstellen, die im Vorwärtsdurchlauf als Identitätsoperation fungiert, während der Rückwärtsdurchlauf umgekehrt wird.

Hier ist ein Vorschlag, wie das implementiert werden könnte. Es ist nicht getestet und ich bin nicht 100% sicher, dass ich alles richtig verstanden habe, aber Sie können es eventuell überprüfen und beheben.

Unter Verwendung der Papiernotation und der Namenskonventionen folgt hier eine einfache Theano-Implementierung des von ihnen vorgeschlagenen allgemeinen Modells.

import numpy 
import theano 
import theano.tensor as tt 


def g_f(z, theta_f): 
    for w_f, b_f in theta_f: 
     z = tt.tanh(theano.dot(z, w_f) + b_f) 
    return z 


def g_y(z, theta_y): 
    for w_y, b_y in theta_y[:-1]: 
     z = tt.tanh(theano.dot(z, w_y) + b_y) 
    w_y, b_y = theta_y[-1] 
    z = tt.nnet.softmax(theano.dot(z, w_y) + b_y) 
    return z 


def g_d(z, theta_d): 
    for w_d, b_d in theta_d[:-1]: 
     z = tt.tanh(theano.dot(z, w_d) + b_d) 
    w_d, b_d = theta_d[-1] 
    z = tt.nnet.sigmoid(theano.dot(z, w_d) + b_d) 
    return z 


def l_y(z, y): 
    return tt.nnet.categorical_crossentropy(z, y).mean() 


def l_d(z, d): 
    return tt.nnet.binary_crossentropy(z, d).mean() 


def mlp_parameters(input_size, layer_sizes): 
    parameters = [] 
    previous_size = input_size 
    for layer_size in layer_sizes: 
     parameters.append((theano.shared(numpy.random.randn(previous_size, layer_size).astype(theano.config.floatX)), 
          theano.shared(numpy.zeros(layer_size, dtype=theano.config.floatX)))) 
     previous_size = layer_size 
    return parameters, previous_size 


def compile(input_size, f_layer_sizes, y_layer_sizes, d_layer_sizes, hp_lambda, hp_mu): 
    r = ReverseGradient(hp_lambda) 

    theta_f, f_size = mlp_parameters(input_size, f_layer_sizes) 
    theta_y, _ = mlp_parameters(f_size, y_layer_sizes) 
    theta_d, _ = mlp_parameters(f_size, d_layer_sizes) 

    xs = tt.matrix('xs') 
    xs.tag.test_value = numpy.random.randn(9, input_size).astype(theano.config.floatX) 
    xt = tt.matrix('xt') 
    xt.tag.test_value = numpy.random.randn(10, input_size).astype(theano.config.floatX) 
    ys = tt.ivector('ys') 
    ys.tag.test_value = numpy.random.randint(y_layer_sizes[-1], size=9).astype(numpy.int32) 

    fs = g_f(xs, theta_f) 
    e = l_y(g_y(fs, theta_y), ys) + l_d(g_d(r(fs), theta_d), 0) + l_d(g_d(r(g_f(xt, theta_f)), theta_d), 1) 

    updates = [(p, p - hp_mu * theano.grad(e, p)) for theta in theta_f + theta_y + theta_d for p in theta] 
    train = theano.function([xs, xt, ys], outputs=e, updates=updates) 

    return train 


def main(): 
    theano.config.compute_test_value = 'raise' 
    numpy.random.seed(1) 
    compile(input_size=2, f_layer_sizes=[3, 4], y_layer_sizes=[7, 8], d_layer_sizes=[5, 6], hp_lambda=.5, hp_mu=.01) 


main() 

Dies ist nicht getestet, aber das Folgende dieses Brauch op ermöglichen, als eine Lasagne Schicht verwendet werden:

class ReverseGradientLayer(lasagne.layers.Layer): 
    def __init__(self, incoming, hp_lambda, **kwargs): 
     super(ReverseGradientLayer, self).__init__(incoming, **kwargs) 
     self.op = ReverseGradient(hp_lambda) 

    def get_output_for(self, input, **kwargs): 
     return self.op(input) 
+0

Das ist sehr cool, so scheint es zumindest auf meinem Rechner zu kompilieren. Ich werde es versuchen, es in Lasagne-Format anzupassen und einige Daten durch es laufen zu lassen. Danke vielmals! –

+0

Hat jemand das jemals getestet? @BillCheatham, hat es für dich funktioniert? – pir