2016-07-27 17 views
4

Ich baue ein Tensorflow-Modell, um Rückschlüsse auf Textphrasen durchzuführen. Der Einfachheit halber nehme ich an, ich brauche einen Klassifikator mit einer festen Anzahl von Ausgabeklassen, aber einen Text variabler Länge im Eingang. Mit anderen Worten, mein Mini-Batch wäre eine Sequenz von Phrasen, aber nicht alle Phrasen haben die gleiche Länge.Arbeiten mit Text variabler Länge in Tensorflow

data = ['hello', 
     'my name is Mark', 
     'What is your name?'] 

Meine erste Vorverarbeitungsschritt war ein Wörterbuch aller möglichen Wörter im Wörterbuch bauen und jedes Wort auf seine ganze Zahl Wort-Id zuordnen. Der Eingang wird:

data = [[1], 
     [2, 3, 4, 5], 
     [6, 4, 7, 3] 

Was ist der beste Weg, um mit dieser Art von Eingabe umzugehen? Kann tf.placeholder() Eingabe mit variabler Größe innerhalb desselben Datenstapels verarbeiten? Oder sollte ich alle Zeichenfolgen auffüllen, so dass sie alle die gleiche Länge haben, die der Länge der längsten Zeichenfolge entspricht, mit einem Platzhalter für die fehlenden Wörter? Dies scheint sehr ineffizient zu sein, wenn einige Strings viel länger sind als die meisten anderen.

- EDIT -

Hier ist ein konkretes Beispiel.

Wenn ich weiß, die Größe meiner Datenpunkte (und alle Datenpunkt die gleiche Länge haben, zB 3). Ich verwenden normalerweise so etwas wie:

input = tf.placeholder(tf.int32, shape=(None, 3) 

with tf.Session() as sess: 
    print(sess.run([...], feed_dict={input:[[1, 2, 3], [1, 2, 3]]})) 

, wo die erste Dimension des Platzhalter der miniGröße .

Was ist, wenn die Eingabesequenzen Wörter in Sätzen unterschiedlicher Länge sind?

feed_dict={input:[[1, 2, 3], [1]]} 
+0

Text wird oft von einem Sequenzmodell behandelt. IE, Ihr Modell akzeptiert das aktuelle Wort und die Ausgabe des vorherigen Schritts und Sie stapeln Kopien des Modells. Als Grundlinie könnten Sie mit "Beutel mit Wörtern" beginnen - fügen Sie einfach alle Wörter zusammen in einen Wörterbuchvektor ein. –

+0

Danke für Ihre Antwort. Meine Frage bezieht sich mehr auf Tensorflow-Datenstrukturen als auf Modelle. Ich kann ein RNN verwenden, das mit Text gefüllt ist, der mit Sackleinen dargestellt wird. Immer noch, wenn meine Datenpunkte unterschiedliche Länge haben, wo oder wie speichere ich diese Art von Daten? – Marco

+0

Ich redigierte die Frage, die Verweis auf Worteinbettungen löscht und ein konkreteres Beispiel setzte, um meine Frage zu klären. – Marco

Antwort

0

Ich baute eine Sequenz zu Sequenzübersetzer neulich. Was ich getan habe, war, es für eine feste Länge von 32 Wörtern zu machen (was etwas über der durchschnittlichen Satzlänge lag), obwohl Sie es so lange machen können, wie Sie wollen. Ich fügte dann dem Wörterbuch ein NULL-Wort hinzu und füllte alle meine Satzvektoren damit aus. Auf diese Weise konnte ich dem Modell sagen, wo das Ende meiner Sequenz war und das Modell würde am Ende seiner Ausgabe nur NULL ausgeben. Nehmen wir zum Beispiel den Ausdruck "Hallo, wie heißt du?" Dies würde "Hallo, wie heißt du? NULL NULL NULL NULL ... NULL". Es hat ziemlich gut funktioniert, aber dein Verlust und deine Genauigkeit während des Trainings werden etwas höher erscheinen, als es tatsächlich ist, da das Modell normalerweise die NULL-Werte erhält, die zu den Kosten zählen.

Es gibt einen anderen Ansatz namens Maskierung. Auch dies ermöglicht es Ihnen, ein Modell für eine Sequenz fester Länge zu erstellen, aber nur die Kosten bis zum Ende einer kürzeren Sequenz zu bewerten. Sie könnten nach der ersten Instanz von NULL in der Ausgabesequenz (oder der erwarteten Ausgabe, je nachdem, was größer ist) suchen und nur die Kosten bis zu diesem Punkt auswerten. Außerdem denke ich, dass einige Tensorfließfunktionen wie tf.dynamic_rnn die Maskierung unterstützen, was mehr Speichereffizienz bedeutet. Ich bin mir nicht sicher, da ich erst den ersten Ansatz der Polsterung ausprobiert habe.

Schließlich denke ich, dass sie im Tensorflow-Beispiel des Seq2Seq-Modells Buckets für unterschiedlich große Sequenzen verwenden. Dies würde wahrscheinlich Ihr Speicherproblem lösen. Ich denke, Sie könnten die Variablen zwischen den verschiedenen Größen teilen.

0

So, hier ist was ich getan habe (nicht sicher, ob das zu 100% der richtige Weg, um ehrlich zu sein):

In Ihrem vocab dict wo jede Taste eine Zahl ist, zeigt auf ein bestimmtes Wort, fügen Sie einen anderen Schlüssel sagen K was auf "<PAD>" (oder eine andere Darstellung wollen Sie zum Auffüllen verwenden)

nun Ihre Platzhalter für die Eingabe wie folgt aussehen würde:

x_batch = tf.placeholder(tf.int32, shape=(batch_size, None)) 

wo keine in den größten Satz/Satz/Datensatz repräsentiert Ihre Mini-Charge.

Ein weiterer kleiner Trick, den ich verwendete, war die Länge jeder Phrase in meinem Mini-Batch zu speichern. Zum Beispiel:

Wenn meine Eingabe ist: x_batch = [[1], [1,2,3], [4,5]] dann speichere ich: len_batch = [1, 3, 2]

Später verwende ich diese len_batch und die maximale Größe einer Phrase (l_max) in meinem mini eine binäre Maske zu erstellen. Jetzt l_max=3 von oben, so würde meine Maske in etwa so aussehen:

mask = [ 
[1, 0, 0], 
[1, 1, 1], 
[1, 1, 0] 
] 

Nun, wenn Sie diese mit Ihrem Verlust vermehren würden Sie grundsätzlich alle Verluste als Folge der Polsterung eingeführt beseitigen.

Hoffe, das hilft.

1

Die anderen zwei Antworten sind korrekt, aber wenig Details. Ich habe mir nur angeschaut, wie ich das selbst machen soll.

Es gibt Maschinen in TensorFlow zu all dies (für einige Teile kann es Overkill sein).

Ausgehend von einem String-Tensor (Form [3]): (. Beachten Sie den Raum vor dem Fragezeichen)

import tensorflow as tf 
lines = tf.constant([ 
    'Hello', 
    'my name is also Mark', 
    'Are there any other Marks here ?']) 
vocabulary = ['Hello', 'my', 'name', 'is', 'also', 'Mark', 'Are', 'there', 'any', 'other', 'Marks', 'here', '?'] 

Das erste, was zu tun geteilt wird dies in Worte

words = tf.string_split(lines," ") 

Wörter sind jetzt ein spärlicher Tensor (Form [3,7]). Wo die zwei Dimensionen der Indizes sind [Zeilennummer, Position]. Dies wird wie folgt dargestellt:

indices values 
0 0  'hello' 
1 0  'my' 
1 1  'name' 
1 2  'is' 
... 

Jetzt können Sie ein Wort-Lookup:

table = tf.contrib.lookup.index_table_from_tensor(vocabulary) 
word_indices = table.lookup(words) 

Dies gibt eine spärliche Tensor mit den Worten durch ihren Wortschatz Indizes ersetzt.

Jetzt können Sie die Sequenzlängen ausgelesen, indem Sie auf jede Zeile in der maximalen Position suchen:

line_number = word_indices.indices[:,0] 
line_position = word_indices.indices[:,1] 
lengths = tf.segment_max(data = line_position, 
         segment_ids = line_number)+1 

Wenn Sie also mit variabler Länge Sequenzen sind die Verarbeitung ist es wahrscheinlich in einem LSTM setzen ... so lasst uns verwenden, um ein Wort-Einbettung für den Eingang (es erfordert einen dichten Eingang):

EMBEDDING_DIM = 100 

dense_word_indices = tf.sparse_tensor_to_dense(word_indices) 
e_layer = tf.contrib.keras.layers.Embedding(len(vocabulary), EMBEDDING_DIM) 
embedded = e_layer(dense_word_indices) 

nun eine Form von [3,7,100] wird eingebettet, [Zeilen, Worte, embedding_dim].

LSTM_SIZE = 50 
lstm = tf.nn.rnn_cell.BasicLSTMCell(LSTM_SIZE) 

Und die über die Reihenfolge ausführen, die Handhabung des padding:

Dann ein einfaches LSTM gebaut werden kann.

outputs, final_state = tf.nn.dynamic_rnn(
    cell=lstm, 
    inputs=embedded, 
    sequence_length=lengths, 
    dtype=tf.float32) 

Jetzt Ausgänge hat eine Form von [3,7,50] oder [Linie, Wort, lstm_size]. Wenn Sie den Zustand beim letzten Wort jeder Zeile packen möchten, können Sie die Verwendung select_last_activations Funktion (versteckte undokumentierte!):

from tensorflow.contrib.learn.python.learn.estimators.rnn_common import select_last_activations 
final_output = select_last_activations(outputs,tf.cast(lengths,tf.int32)) 

Das macht den ganzen Index schlurfen die Ausgabe aus dem letzten Zeitschritt zu wählen. Daraus ergibt sich eine Größe von [3,50] oder [line, lstm_size]

init_t = tf.tables_initializer() 
init = tf.global_variables_initializer() 
with tf.Session() as sess: 
    init_t.run() 
    init.run() 
    print(final_output.eval().shape()) 

Ich habe nicht die Details ausgearbeitet noch nicht, aber ich denke, das könnte wahrscheinlich alle von einem einzigen tf.contrib.learn.DynamicRnnEstimator ersetzt werden.