Uwaga jest metodą agregacji zbioru wektorów vi w jeden wektor, często za pośrednictwem wektora odnośnika u . Zwykle vi jest albo danymi wejściowymi do modelu, albo stanami ukrytymi poprzednich kroków czasowych, albo stanami ukrytymi jeden poziom niżej (w przypadku skumulowanych LSTM).
Wynik jest często nazywany wektorem kontekstu c , ponieważ zawiera kontekst odpowiedni dla bieżącego kroku czasowego.
Ten dodatkowy wektor kontekstu c jest następnie podawany również do RNN / LSTM (można go po prostu połączyć z oryginalnym wejściem). Dlatego kontekst może służyć do przewidywania.
Najprostszym sposobem osiągnięcia tego jest, aby obliczyć prawdopodobieństwo wektora p=softmax(VTu) i c=∑ipivi , gdzie V jest połączeniem wszystkich poprzednichvi . Typowym wyszukiwanie wektorau jest obecny stan ukrytyht .
Jest na to wiele odmian i możesz sprawić, że wszystko będzie tak skomplikowane, jak tylko chcesz. Na przykład, zamiast używania vTiu jako logów, można zamiast tego wybrać f(vi,u) , gdzie f jest dowolną siecią neuronową.
Mechanizm wspólnej uwagi dla modeli sekwencyjno-sekwencyjnych wykorzystuje p=softmax(qTtanh(W1vi+W2ht)) , gdzie v są stanami ukrytymi enkodera, a ht jest bieżącym stanem ukrytym dekodera. q i oba W s są parametrami.
Niektóre artykuły prezentujące różne warianty pomysłu uwagi:
Pointer Networks zwraca uwagę na dane wejściowe w celu rozwiązania problemów optymalizacji kombinatorycznej.
Sieci jednostek cyklicznych utrzymują oddzielne stany pamięci dla różnych bytów (ludzi / obiektów) podczas czytania tekstu i aktualizują poprawny stan pamięci, zwracając uwagę.
Modele transformatorowe również szeroko wykorzystują uwagę. Ich formuła uwagi jest nieco bardziej ogólnie, a także wiąże się kluczowymi wektory ki : the ciężary uwaga p faktycznie obliczane między klawiszami oraz odnośnika, a kontekst jest wówczas zbudowany z vi .
Oto szybkie wdrożenie jednej formy uwagi, chociaż nie mogę zagwarantować poprawności poza tym, że przeszedł kilka prostych testów.
Podstawowy RNN:
def rnn(inputs_split):
bias = tf.get_variable('bias', shape = [hidden_dim, 1])
weight_hidden = tf.tile(tf.get_variable('hidden', shape = [1, hidden_dim, hidden_dim]), [batch, 1, 1])
weight_input = tf.tile(tf.get_variable('input', shape = [1, hidden_dim, in_dim]), [batch, 1, 1])
hidden_states = [tf.zeros((batch, hidden_dim, 1), tf.float32)]
for i, input in enumerate(inputs_split):
input = tf.reshape(input, (batch, in_dim, 1))
last_state = hidden_states[-1]
hidden = tf.nn.tanh( tf.matmul(weight_input, input) + tf.matmul(weight_hidden, last_state) + bias )
hidden_states.append(hidden)
return hidden_states[-1]
Z uwagą dodajemy tylko kilka wierszy przed obliczeniem nowego stanu ukrytego:
if len(hidden_states) > 1:
logits = tf.transpose(tf.reduce_mean(last_state * hidden_states[:-1], axis = [2, 3]))
probs = tf.nn.softmax(logits)
probs = tf.reshape(probs, (batch, -1, 1, 1))
context = tf.add_n([v * prob for (v, prob) in zip(hidden_states[:-1], tf.unstack(probs, axis = 1))])
else:
context = tf.zeros_like(last_state)
last_state = tf.concat([last_state, context], axis = 1)
hidden = tf.nn.tanh( tf.matmul(weight_input, input) + tf.matmul(weight_hidden, last_state) + bias )
pełny kod