2015-11-23 23 views
8

Mi piace utilizzare l'architettura proposta nel recente documento "Unsupervised Domain Adaptation by Backpropagation" nel framework Lasagne/Theano.Posso (selettivamente) invertire i gradienti di Theano durante il backpropagation?

La cosa di questo documento che lo rende un po 'insolito è che incorpora un 'strato di inversione di gradiente', che inverte gradiente durante backpropagation:

enter image description here

(Le frecce lungo il fondo dell'immagine sono le backpropagations che hanno il loro gradiente invertito).

Nel documento gli autori sostengono che l'approccio "può essere implementato utilizzando qualsiasi pacchetto di deep learning", e in effetti forniscono uno version made in caffe.

Tuttavia, sto usando il framework Lasagne/Theano, per vari motivi.

È possibile creare uno strato di inversione del gradiente in Lasagne/Theano? Non ho visto alcun esempio di dove si possano applicare trasformazioni scalari personalizzate a gradienti come questo. In tal caso, posso farlo creando un livello personalizzato in Lasagne?

risposta

7

Ecco un'implementazione dello schizzo utilizzando il semplice Theano. Questo può essere integrato in Lasagne abbastanza facilmente.

È necessario creare un'operazione personalizzata che funge da operazione di identità nel passaggio in avanti, ma inverte il gradiente nel passaggio all'indietro.

Ecco un suggerimento su come potrebbe essere implementato. Non è stato testato e non sono sicuro al 100% di aver capito tutto correttamente, ma potresti essere in grado di verificare e correggere come richiesto.

class ReverseGradient(theano.gof.Op): 
    view_map = {0: [0]} 

    __props__ = ('hp_lambda',) 

    def __init__(self, hp_lambda): 
     super(ReverseGradient, self).__init__() 
     self.hp_lambda = hp_lambda 

    def make_node(self, x): 
     return theano.gof.graph.Apply(self, [x], [x.type.make_variable()]) 

    def perform(self, node, inputs, output_storage): 
     xin, = inputs 
     xout, = output_storage 
     xout[0] = xin 

    def grad(self, input, output_gradients): 
     return [-self.hp_lambda * output_gradients[0]] 

Utilizzando la notazione carta e convenzioni di denominazione, ecco una semplice implementazione Theano del modello generale completa che propongono.

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() 

Questo non è testato, ma ciò che segue può permettere che questo op personalizzato da utilizzare come strato di lasagne:

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

Questo è molto cool, sembra almeno a compilare sulla mia macchina. Sto andando a fare una conversione convertendo questo in formato Lasagne e facendo scorrere alcuni dati attraverso di esso. Molte grazie! –

+0

Qualcuno ha mai provato questo? @ Beat Cheatham, ha funzionato per te? – pir