Я пытаюсь реализовать рекуррентную нейронную сеть, используя Numpy в python. Я пытаюсь реализовать RNN "многие-к-одному" для проблемы классификации. Я немного запутался в коде psuedo, особенно в концепции BPTT. Я доволен прямым проходом (не совсем уверен, верна ли моя реализация), но действительно перепутан с обратным проходом, и мне нужен совет от экспертов в этой области.
Я проверил похожие посты:
1) Реализация RNN в numpy
2) Выход для RNN
3) Как построить RNN
Но я чувствую, что моя проблема в том, чтобы сначала понять код / концепцию псевдо, код в этих постах завершен и достиг более высокого уровня, чем мой.
Моя реализация основана на руководстве:
WildML RNN с нуля
Я реализовал нейронную сеть с прямой связью после части урока того же автора, но я действительно запутался с этой реализацией его. RNN-видео Эндрю Нга предлагает 3 различных веса (веса для уровней активации, входного и выходного слоев), но в приведенном выше руководстве есть только два набора весов (поправьте меня, если я ошибаюсь).
Номенклатура в моем коде соответствует номенклатуре псевдокода Эндрю Нга ...
Я изменяю свои входные сэмплы в 3D (batch_size, n_time шаги, n_ измерения) ... Как только я изменяю свои сэмплы, я делаю прямую передачу каждого сэмпла отдельно ...
Вот мой код:
def RNNCell(X, lr, y=None, n_timesteps=None, n_dimensions=None, return_sequence = None, bias = None):
'''Simple function to compute forward and bakward passes for a Many-to-One Recurrent Neural Network Model.
This function Reshapes X,Y in to 3D array of shape (batch_size, n_timesteps, n_ dimensions) and then performs
recurrent operations on each sample of the data for n_timesteps'''
# If user has specified some target variable
if len(y) != 0:
# No. of unique values in the target variables will be the dimesions for the output layer
_,n_unique = np.unique(y, return_counts=True)
else:
# If there's no target variable given, then dimensions of target variable by default is 2
n_unique = 2
# Weights of Vectors to multiply with input samples
Wx = np.random.uniform(low = 0.0,
high = 0.3,
size = (n_dimensions, n_dimensions))
# Weights of Vectors to multiply with resulting activations
Wy = np.random.uniform(low = 0.0,
high = 0.3,
size = (n_dimensions, n_timesteps))
# Weights of Vectors to multiple with activations of previous time steps
Wa = np.random.randn(n_dimensions, n_dimensions)
# List to hold activations of each time step
activations = {'a-0' : np.zeros(shape=(n_timesteps-1, n_dimensions),
dtype=float)}
# List to hold Yhat at each time step
Yhat = []
try:
# Reshape X to align with the shape of RNN architecture
X = np.reshape(X, newshape=(len(X), n_timesteps, n_dimensions))
except:
return "Sorry can't reshape and array in to your shape"
def Forward_Prop(sample):
# Outputs at the last time step
Ot = 0
# In each time step
for time_step in range(n_timesteps+1):
if time_step < n_timesteps:
# activation G ( Wa.a<t> + X<t>.Wx )
activations['a-' + str(time_step+1)] = ReLu( np.dot( activations['a-' + str(time_step)], Wa )
+ np.dot( sample[time_step, :].reshape(1, n_dimensions) , Wx ) )
# IF it's the last time step then use softmax activation function
elif time_step == n_timesteps:
# Wy.a<t> and appending that to Yhat list
Ot = softmax( np.dot( activations['a-' + str(time_step)], Wy ) )
# Return output probabilities
return Ot
def Backward_Prop(Yhat):
# List to hold errors for the last layer
error = []
for ind in range(len(Yhat)):
error.append( y[ind] - Yhat[ind] )
error = np.array(error)
# Calculating Delta for the output layer
delta_out = error * lr
#* relu_derivative(activations['a-' + str(n_timesteps)])
# Calculating gradient for the output layer
grad_out = np.dot(delta_out.reshape(len(X), n_timesteps),
activations['a-' + str(n_timesteps)])
# I'm basically stuck at this point
# Adjusting weights for the output layer
Wy = Wy - (lr * grad_out.reshape((n_dimesions, n_timesteps)))
for sample in X:
Yhat.append( Forward_Prop(sample) )
Backward_Prop(Yhat)
return Yhat
# DUMMY INPUT DATA
X = np.random.random_integers(low=0, high = 5, size = (10, 10 ));
# DUMMY LABELS
y = np.array([[0],
[1],
[1],
[1],
[0],
[0],
[1],
[1],
[0],
[1]])
Я понимаю, что моя реализация BPTT неверна, но я не очень четко представляю себе, и мне нужно мнение некоторых экспертов о том, где именно я упускаю уловку. Я не ожидаю детальной отладки моего кода, мне требуется только общий обзор псевдокода при обратном распространении (при условии, что моя прямая поддержка верна). Я думаю, что моя фундаментальная проблема также может быть связана с тем, как я делаю форвардный проход для каждого образца в отдельности.
Я застрял в этой проблеме уже 3 дня, и это очень расстраивает, что я не могу ясно мыслить. Я был бы очень благодарен, если бы кто-то мог указать мне правильное направление и устранить мою путаницу. Спасибо за ваше время заранее! Я действительно ценю это еще раз!