Ошибка проверки градиента RNN - PullRequest
0 голосов
/ 09 января 2019

Так что я строю RNN с нуля, используя numpy, просто чтобы понять, как они работают внутри. Мое обратное распространение через время здесь:

    def backprop_through_time(self, X, Y):
        assert(len(X.shape) == 3)
        seq_length = Y.shape[1] if self.return_sequences else 1
        _, (Z_states, States, Z_outs, Outs) = self.feed_forward(X, cache=True)
        if not self.return_sequences:
            Outs = Outs[:,-1,:]

        # setup gradients
        dLdU = np.zeros(self.U.shape)
        dLdV = np.zeros(self.V.shape)
        dLdW = np.zeros(self.W.shape)

        dLdB_state = np.zeros(self.B_state.shape)
        dLdB_out = np.zeros(self.B_out.shape)

        dLdOuts = self.loss_function_prime(Outs, Y)
        if not self.return_sequences:
            # we need dLdOuts to have a seq_length dim at axis 1
            dLdOuts = np.expand_dims(dLdOuts, axis=1)
        for t in range(seq_length):
            adjusted_t = seq_length-1 if not self.return_sequences else t
            # print("adjusted_t {}".format(adjusted_t))

            dOuts_tdZ_out = self.output_activation_function_prime(Z_outs[:,adjusted_t,:])
            dLdZ_out = np.multiply(dLdOuts[:, adjusted_t, :], dOuts_tdZ_out)

            # Z_state = dot(X_t, self.U) + dot(State_{t-1}, self.W) + self.B_state
            # State_t = f(Z_state)
            # Z_out = dot(State_t, self.V) + self.B_out
            # Out_t = g(Z_out)

            dLdV += np.dot(States[:,adjusted_t,:].T, dLdZ_out)
            dLdB_out += np.sum(dLdZ_out, axis=0, keepdims=True)
            dLdZ_state = np.multiply(np.dot(dLdZ_out, self.V.T),
                                     self.hidden_activation_function_prime(Z_states[:,adjusted_t,:]))
            for t_prev in range(max(0, adjusted_t-self.backprop_through_time_limit), adjusted_t+1)[::-1]:
                dLdB_state += np.sum(dLdZ_state, axis=0, keepdims=True)
                dLdW += np.dot(States[:,t_prev-1,:].T, dLdZ_state)
                dLdU += np.dot(X[:,t_prev,:].T, dLdZ_state)
                dLdZ_state = np.multiply(np.dot(dLdZ_state, self.W.T),
                                         self.hidden_activation_function_prime(States[:,t_prev-1,:]))
        return (dLdU, dLdV, dLdW), (dLdB_state, dLdB_out)
Однако мне все еще не удается проверить градиент для параметров `dLdU, dLdW, dLdB_state`. Я прошел математику около десятка раз, и я не могу найти, что не так с моей реализацией.

Я предполагаю, что X и Y являются трехмерными матрицами с X, имеющим форму: X.shape := (batch_size, seq_length, input_dim) в то время как Y имеет форму: Y.shape := (batch_size, seq_length, output_dim)

Кэшируя операцию feed_forward, я возвращаю Z_states с формой Z_states.shape := (batch_size, seq_length, hidden_dim), Z_outs и Outs с формой Z_outs.shape, Outs.shape := (batch_size, seq_length, output_dim) и состояния как States.shape := (batch_size, seq_length+1, hidden_dim). Состояния [:, - 1 ,:] - это исходные нули формы States[:,-1,:].shape := (batch_size, hidden_dim), с которыми инициализируется состояние RNN. Кто-нибудь может мне помочь?

EDIT Я нашел свой ответ. Моя математика верна, но я вызывал не ту переменную. Когда я обновляю dLdZ_state во 2-м внутреннем цикле (обратная часть времени), я умножаю на self.hidden_activation_function_prime(States[:,t_prev-1,:]) Этот шут вместо этого будет self.hidden_activation_function_prime(Z_states[:,t_prev-1,:])

...