2014-12-18 14 views
5

Mi sono battuto la testa contro questo per un po 'e non riesco a capire cosa ho fatto di sbagliato (se non altro) nell'attuazione di questi RNR. Per risparmiarvi la fase avanzata, posso dirvi che le due implementazioni calcolano le stesse uscite, quindi la fase di avanzamento è corretta. Il problema è nella fase di arretramento.pure-python RNN e theano RNN calcolo di gradienti diversi - codice e risultati forniti

Ecco il mio codice a ritroso di Python. Si segue lo stile di neuraltalk di karpathy molto da vicino ma non esattamente:.

def backward(self, cache, target,c=leastsquares_cost, dc=leastsquares_dcost): 
     ''' 
     cache is from forward pass 

     c is a cost function 
     dc is a function used as dc(output, target) which gives the gradient dc/doutput 
     ''' 
     XdotW = cache['XdotW'] #num_time_steps x hidden_size 
     Hin = cache['Hin'] # num_time_steps x hidden_size 
     T = Hin.shape[0] 
     Hout = cache['Hout'] 
     Xin = cache['Xin'] 
     Xout = cache['Xout'] 

     Oin = cache['Oin'] # num_time_steps x output_size 
     Oout=cache['Oout'] 

     dcdOin = dc(Oout, target) # this will be num_time_steps x num_outputs. these are dc/dO_j 


     dcdWho = np.dot(Hout.transpose(), dcdOin) # this is the sum of outer products for all time 

     # bias term is added at the end with coefficient 1 hence the dot product is just the sum 
     dcdbho = np.sum(dcdOin, axis=0, keepdims=True) #this sums all the time steps 

     dcdHout = np.dot(dcdOin, self.Who.transpose()) #reflects dcdHout_ij should be the dot product of dcdoin and the i'th row of Who; this is only for the outputs 
     # now go back in time 
     dcdHin = np.zeros(dcdHout.shape) 
     # for t=T we can ignore the other term (error from the next timestep). self.df is derivative of activation function (here, tanh): 
     dcdHin[T-1] = self.df(Hin[T-1]) * dcdHout[T-1] # because we don't need to worry about the next timestep, dcdHout is already corrent for t=T 

     for t in reversed(xrange(T-1)): 
      # we need to add to dcdHout[t] the error from the next timestep 
      dcdHout[t] += np.dot(dcdHin[t], self.Whh.transpose()) 
      # now we have the correct form for dcdHout[t] 
      dcdHin[t] = self.df(Hin[t]) * dcdHout[t] 
     # now we've gone through all t, and we can continue 
     dcdWhh = np.zeros(self.Whh.shape) 
     for t in range(T-1): #skip T bc dHdin[T+1] doesn't exist 
      dcdWhh += np.outer(Hout[t], dcdHin[t+1]) 
     # and we can do bias as well 
     dcdbhh = np.sum(dcdHin,axis=0, keepdims=True) 


     # now we need to go back to the embeddings 
     dcdWxh = np.dot(Xout.transpose(), dcdHin) 

     return {'dcdOout': dcdOout, 'dcdWxh': dcdWxh, 'dcdWhh': dcdWhh, 'dcdWho': dcdWho, 'dcdbhh': dcdbhh, 'dcdbho': dcdbho, 'cost':c(Oout, target)} 

Ed ecco il codice Theano (copiato principalmente da un'altra applicazione che ho trovato on-line inizializzo i pesi per pesi randomizzati mia pura-python di RNN in modo che tutto è lo stesso.):

# input (where first dimension is time) 
u = TT.matrix() 
# target (where first dimension is time) 
t = TT.matrix() 
# initial hidden state of the RNN 
h0 = TT.vector() 
# learning rate 
lr = TT.scalar() 
# recurrent weights as a shared variable 
W = theano.shared(rnn.Whh) 
# input to hidden layer weights 
W_in = theano.shared(rnn.Wxh) 
# hidden to output layer weights 
W_out = theano.shared(rnn.Who) 

# bias 1 
b_h = theano.shared(rnn.bhh[0]) 
# bias 2 
b_o = theano.shared(rnn.bho[0]) 


# recurrent function (using tanh activation function) and linear output 
# activation function 
def step(u_t, h_tm1, W, W_in, W_out): 
    h_t = TT.tanh(TT.dot(u_t, W_in) + TT.dot(h_tm1, W) + b_h) 
    y_t = TT.dot(h_t, W_out) + b_o 
    return h_t, y_t 

# the hidden state `h` for the entire sequence, and the output for the 
# entrie sequence `y` (first dimension is always time) 
[h, y], _ = theano.scan(step, 
         sequences=u, 
         outputs_info=[h0, None], 
         non_sequences=[W, W_in, W_out]) 
# error between output and target 
error = (.5*(y - t) ** 2).sum() 
# gradients on the weights using BPTT 
gW, gW_in, gW_out, gb_h, gb_o = TT.grad(error, [W, W_in, W_out, b_h, b_o]) 
# training function, that computes the error and updates the weights using 
# SGD. 

Ora ecco la cosa pazza. Se faccio funzionare il seguente:

fn = theano.function([h0, u, t, lr], 
        [error, y, h, gW, gW_in, gW_out, gb_h, gb_o], 
        updates={W: W - lr * gW, 
          W_in: W_in - lr * gW_in, 
          W_out: W_out - lr * gW_out}) 

er, yout, hout, gWhh, gWhx, gWho, gbh, gbo =fn(numpy.zeros((n,)), numpy.eye(5), numpy.eye(5),.01) 
cache = rnn.forward(np.eye(5)) 
bc = rnn.backward(cache, np.eye(5)) 

print "sum difference between gWho (theano) and bc['dcdWho'] (pure python):" 
print np.sum(gWho - bc['dcdWho']) 
print "sum differnce between gWhh(theano) and bc['dcdWho'] (pure python):" 
print np.sum(gWhh - bc['dcdWhh']) 
print "sum difference between gWhx (theano) and bc['dcdWxh'] (pure pyython):" 
print np.sum(gWhx - bc['dcdWxh']) 

print "sum different between the last row of gWhx (theano) and the last row of bc['dcdWxh'] (pure python):" 
print np.sum(gWhx[-1] - bc['dcdWxh'][-1]) 

ottengo il seguente output:

sum difference between gWho (theano) and bc['dcdWho'] (pure python): 
-4.59268040265e-16 
sum differnce between gWhh(theano) and bc['dcdWhh'] (pure python): 
0.120527063611 
sum difference between gWhx (theano) and bc['dcdWxh'] (pure pyython): 
-0.332613468652 
sum different between the last row of gWhx (theano) and the last row of bc['dcdWxh'] (pure python): 
4.33680868994e-18 

Quindi, sto ottenendo i derivati ​​della matrice peso tra lo strato nascosto e destra in uscita, ma non i derivati della matrice di peso nascosta -> nascosta o inserita -> nascosta. Ma questa cosa assurda è che ottengo SEMPRE l'ULTIMA FILA dell'ingresso della matrice di peso -> nascosto corretto. Questa è follia per me. Non ho idea di cosa stia succedendo qui. Si noti che l'ultima riga dell'ingresso della matrice di peso -> nascosta NON corrisponde all'ultimo timestep o nulla (questo sarebbe spiegato, ad esempio, da me calcolando correttamente le derivate per l'ultimo timestep ma non propagandando indietro nel tempo correttamente). dcdWxh è la somma su tutti i passi temporali di dcdWxh - quindi come posso ottenere una riga di questa corretta ma nessuna delle altre ???

Qualcuno può aiutare? Sono completamente fuori di idee qui.

risposta

0

È necessario calcolare la somma del valore assoluto pointwise della differenza delle due matrici. La somma normale potrebbe essere vicina allo zero a causa di specifici compiti di apprendimento (emuli la funzione zero? :), a seconda di quale sia.

L'ultima riga implementa presumibilmente i pesi da un costante su neurone, cioè il bias, quindi sembra che tu abbia sempre il pregiudizio corretto (tuttavia, controlla la somma dei valori assoluti).

sembra anche row-major e notazione colonna principale di matrici sono confusi, come in

gWhx - bc['dcdWxh'] 

che legge come peso da "nascosta a x" in opposto a "x a nascosto".

preferirei pubblicare questo come un commento, ma mi manca la reputazione in questo modo. scusa!

Problemi correlati