Aufmerksamkeit ist ein Verfahren zum Ansammeln einer Menge von Vektoren vi in nur einem Vektor, oft über eine Lookup - Vektor u . In der Regel sind vi entweder die Eingaben in das Modell oder die verborgenen Zustände früherer Zeitschritte oder die verborgenen Zustände eine Ebene tiefer (im Fall von gestapelten LSTMs).
Das Ergebnis wird oft als Kontextvektor c , da es den für den aktuellen Zeitschritt relevanten Kontext enthält .
Dieser zusätzliche Kontextvektor c wird dann ebenfalls in das RNN / LSTM eingespeist (er kann einfach mit der ursprünglichen Eingabe verkettet werden). Daher kann der Kontext zur Vorhersage verwendet werden.
Der einfachste Weg, dies zu tun, besteht darin, den Wahrscheinlichkeitsvektor p=softmax(VTu) und c=∑ipivi zu berechnen, wobei V die Verkettung aller vorherigen vi . Ein üblicher Nachschlagevektor u ist der aktuelle verborgene Zustand ht .
Es gibt viele Variationen, und Sie können die Dinge so kompliziert gestalten, wie Sie möchten. Beispielsweise kann man stattdessen f ( v i , u ) wählen , wobei f ein beliebiges neuronales Netzwerk ist , anstatt vTiu als Protokoll zu verwenden .f(vi,u)f
Ein häufige Aufmerksamkeit Mechanismus für sequenz to-Sequenzmodelle Verwendungen p=softmax(qTtanh(W1vi+W2ht)) , wobei v die versteckten Zustände des Codierers und ht ist der aktuelle verborgene Zustand des Decoders. q und beide W s sind Parameter.
Einige Artikel, die verschiedene Variationen der Aufmerksamkeitsidee zeigen:
Zeigernetzwerke verwenden die Aufmerksamkeit auf Referenzeingaben, um kombinatorische Optimierungsprobleme zu lösen.
Wiederkehrende Entitätsnetzwerke verwalten separate Speicherzustände für verschiedene Entitäten (Personen / Objekte), während sie Text lesen, und aktualisieren den korrekten Speicherzustand mit Aufmerksamkeit.
Transformatormodelle machen auch ausgiebig Gebrauch von Aufmerksamkeit. Ihre Aufmerksamkeitsformulierung ist etwas allgemeiner und beinhaltet auch Schlüsselvektoren ki : Die Aufmerksamkeitsgewichte p werden tatsächlich zwischen den Schlüsseln und dem Nachschlagen berechnet, und der Kontext wird dann mit dem vi konstruiert .
Hier ist eine schnelle Implementierung einer Form von Aufmerksamkeit, obwohl ich die Richtigkeit nicht garantieren kann, außer dass sie einige einfache Tests bestanden hat.
Grundlegende 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]
Mit Aufmerksamkeit fügen wir nur ein paar Zeilen hinzu, bevor der neue verborgene Zustand berechnet wird:
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 )
den vollständigen Code