2014-12-18 1 views
5

Я некоторое время стучал головой об этом и не могу понять, что я сделал неправильно (если что-либо) при реализации этих RNNs. Чтобы избавить вас от передовой фазы, я могу сказать вам, что две реализации вычисляют одни и те же выходы, поэтому прямая фаза правильная. Проблема находится в обратной фазе.pure-python RNN и anano RNN вычисляют разные градиенты - код и полученные результаты

Вот мой код на обратной стороне python. Отсюда следует стилю neuraltalk karpathy довольно близко, но не точно:.

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

А вот код Theano (в основном скопирован с другой реализации я нашел онлайн инициализации весов в рандомизированных весы моих чистого Python РНН, так что все то же самое.):

# 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. 

Теперь вот сумасшедшая вещь. Если я запускаю следующее:

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

я получаю следующий результат:

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 

Таким образом, я получаю производные весовой матрицы между скрытым слоем и выходным справа, но не производные весовой матрицы скрыта -> скрыта или введена -> скрыта. Но эта сумасшедшая вещь заключается в том, что я ВСЕГДА получаю ПОСЛЕДНЕЕ РУКОВОДСТВО ввода весовой матрицы -> скрыто правильно. Это безумие для меня. Я понятия не имею, что здесь происходит. Обратите внимание, что последняя строка ввода весовой матрицы -> скрытая НЕ соответствует последнему временному значению или чему-либо (это будет объяснено, например, тем, что я правильно вычисляю производные для последнего временного времени, но не правильно распространяя время назад). dcdWxh - сумма по всем временным шагам dcdWxh - так как я могу получить одну строку этого правильного, но ни одного из других?

Может ли кто-нибудь помочь? У меня здесь все идеи.

ответ

0

Вычисление суммы постинсультной абсолютной величины разницы двух матриц. Простая сумма может быть близка к нулю из-за конкретных задач обучения (вы эмулируете нулевую функцию? :), что бы это ни было.

Последний ряд, по-видимому, реализует весовые коэффициенты от константы на нейроне, т. Е. Смещение, поэтому вы, кажется, всегда получаете правое смещение (однако, проверяйте сумму абсолютных значений).

Это также выглядит как строка-основные и столбцы из основных обозначений матриц перепутаны, как в

gWhx - bc['dcdWxh'] 

, который читает как вес от «скрытых х» в противоположном «х, чтобы скрытый».

Я бы предпочел опубликовать это как комментарий, но мне не хватает репутации при этом. Прости!