2016-10-13 3 views
0

Ich versuche, ein wiederkehrendes neuronales Netzwerk mit Tensorflow (r0.10, Python 3.5) auf ein Spielzeug Klassifizierung Problem zu trainieren, aber ich bekomme verwirrende Ergebnisse.Warum Tensorflow RNN nicht Spielzeug Daten lernen

Ich möchte eine Folge von Nullen und Einsen in eine RNN einspeisen und die Zielklasse für ein gegebenes Element der Folge als die Zahl haben, die durch die aktuellen und vorherigen Werte der Sequenz repräsentiert wird, die als binär behandelt werden Nummer. Zum Beispiel:

input sequence: [0,  0,  1,  0,  1,  1] 
binary digits : [-, [0,0], [0,1], [1,0], [0,1], [1,1]] 
target class : [-,  0,  1,  2,  1,  3] 

Es scheint so etwas ist ein RNN sollte recht leicht erlernen kann, sondern mein Modell ist nur in der Lage Klassen zu unterscheiden [0,2] von [1,3]. Mit anderen Worten, es ist in der Lage, die Klassen zu unterscheiden, deren laufende Ziffer 0 ist von denen, deren aktuelle Ziffer 1 ist. Dies führt zu der Annahme, dass das RNN-Modell nicht korrekt lernt, den vorherigen Wert der Sequenz zu betrachten .

Es gibt verschiedene Tutorials und Beispiele ([1], [2], [3]), die zeigen, wie Recurrent Neural Networks (RNNs) in tensorflow zu bauen und zu verwenden, aber nach ihnen das Studium habe ich noch nicht mein Problem sehen (Es hilft nicht, dass alle Beispiele Text als Quelldaten verwenden).

Ich gebe meine Daten zu tf.nn.rnn() als eine Liste der Länge T, deren Elemente sind [batch_size x input_size] Sequenzen. Da meine Sequenz eindimensional ist, ist input_size gleich eins, also glaube ich, dass ich eine Liste von Sequenzen der Länge batch_size eingabe (die documentation ist mir unklar, welche Dimension als Zeitdimension behandelt wird). Ist dieses Verständnis korrekt? Wenn das der Fall ist, dann verstehe ich nicht, warum das RNN-Modell nicht richtig lernt.

Es ist schwer, einen kleinen Satz von Code zu erhalten, die durch meine volle RNN laufen kann, dann ist dies das Beste, was ich tun konnte (es meist aus the PTB model here angepasst ist und the char-rnn model here):

import tensorflow as tf 
import numpy as np 

input_size = 1 
batch_size = 50 
T = 2 
lstm_size = 5 
lstm_layers = 2 
num_classes = 4 
learning_rate = 0.1 

lstm = tf.nn.rnn_cell.BasicLSTMCell(lstm_size, state_is_tuple=True) 
lstm = tf.nn.rnn_cell.MultiRNNCell([lstm] * lstm_layers, state_is_tuple=True) 

x = tf.placeholder(tf.float32, [T, batch_size, input_size]) 
y = tf.placeholder(tf.int32, [T * batch_size * input_size]) 

init_state = lstm.zero_state(batch_size, tf.float32) 

inputs = [tf.squeeze(input_, [0]) for input_ in tf.split(0,T,x)] 
outputs, final_state = tf.nn.rnn(lstm, inputs, initial_state=init_state) 

w = tf.Variable(tf.truncated_normal([lstm_size, num_classes]), name='softmax_w') 
b = tf.Variable(tf.truncated_normal([num_classes]), name='softmax_b') 

output = tf.concat(0, outputs) 

logits = tf.matmul(output, w) + b 

probs = tf.nn.softmax(logits) 

cost = tf.reduce_mean(tf.nn.seq2seq.sequence_loss_by_example(
    [logits], [y], [tf.ones_like(y, dtype=tf.float32)] 
)) 

optimizer = tf.train.GradientDescentOptimizer(learning_rate) 
tvars = tf.trainable_variables() 
grads, _ = tf.clip_by_global_norm(tf.gradients(cost, tvars), 
            10.0) 
train_op = optimizer.apply_gradients(zip(grads, tvars)) 

init = tf.initialize_all_variables() 

with tf.Session() as sess: 
    sess.run(init) 
    curr_state = sess.run(init_state) 
    for i in range(3000): 
     # Create toy data where the true class is the value represented 
     # by the current and previous value treated as binary, i.e. 
     train_x = np.random.randint(0,2,(T * batch_size * input_size)) 
     train_y = train_x + np.concatenate(([0], (train_x[:-1] * 2))) 

     # Reshape into T x batch_size x input_size 
     train_x = np.reshape(train_x, (T, batch_size, input_size)) 

     feed_dict = { 
      x: train_x, y: train_y 
     } 
     for j, (c, h) in enumerate(init_state): 
      feed_dict[c] = curr_state[j].c 
      feed_dict[h] = curr_state[j].h 

     fetch_dict = { 
      'cost': cost, 'final_state': final_state, 'train_op': train_op 
     } 

     # Evaluate the graph 
     fetches = sess.run(fetch_dict, feed_dict=feed_dict) 

     curr_state = fetches['final_state'] 

     if i % 300 == 0: 
      print('step {}, train cost: {}'.format(i, fetches['cost'])) 

    # Test 
    test_x = np.array([[0],[0],[1],[0],[1],[1]]*(T*batch_size*input_size)) 
    test_x = test_x[:(T*batch_size*input_size),:] 
    probs_out = sess.run(probs, feed_dict={ 
      x: np.reshape(test_x, [T, batch_size, input_size]), 
      init_state: curr_state 
     }) 
    # Get the softmax outputs for the points in the sequence 
    # that have [0, 0], [0, 1], [1, 0], [1, 1] as their 
    # last two values. 
    for i in [1, 2, 3, 5]: 
     print('{}: [{:.4f} {:.4f} {:.4f} {:.4f}]'.format(
       [1, 2, 3, 5].index(i), *list(probs_out[i,:])) 
      ) 

Die hier Endausgabe ist

0: [0.4899 0.0007 0.5080 0.0014] 
1: [0.0003 0.5155 0.0009 0.4833] 
2: [0.5078 0.0011 0.4889 0.0021] 
3: [0.0003 0.5052 0.0009 0.4936] 

was zeigt, dass es nur lernt, [0,2] von [1,3] zu unterscheiden. Warum lernt dieses Modell nicht, den vorherigen Wert in der Sequenz zu verwenden?

Antwort

2

Ich habe es herausgefunden, mit Hilfe von this blog post (es hat wunderbare Diagramme der Eingangstensoren). Es stellt sich heraus, dass ich die Form der Eingänge zu tf.nn.rnn() nicht richtig verstanden habe:

Nehmen wir an, Sie haben batch_size Anzahl der Sequenzen. Jede Sequenz hat input_size Dimensionen und hat die Länge T (diese Namen wurden gewählt, um die Dokumentation von tf.nn.rnn()here zu entsprechen). Dann müssen Sie Ihre Eingabe in eine T -Länge-Liste aufteilen, in der jedes Element die Form batch_size x input_size hat. Dies bedeutet, dass Ihre zusammenhängende Sequenz über die Elemente der Liste verteilt wird. Ich dachte, dass zusammenhängende Sequenzen zusammengehalten würden, so dass jedes Element der Liste inputs ein Beispiel für eine Sequenz wäre.

Dies ist im Nachhinein sinnvoll, da wir jeden Schritt durch die Sequenz parallelisieren wollen, also wollen wir den ersten Schritt jeder Sequenz (erstes Element in der Liste), dann den zweiten Schritt jeder Sequenz (zweites Element in Liste) usw.

Arbeits Version des Codes:

import tensorflow as tf 
import numpy as np 

sequence_size = 50 
batch_size = 7 
num_features = 1 
lstm_size = 5 
lstm_layers = 2 
num_classes = 4 
learning_rate = 0.1 

lstm = tf.nn.rnn_cell.BasicLSTMCell(lstm_size, state_is_tuple=True) 
lstm = tf.nn.rnn_cell.MultiRNNCell([lstm] * lstm_layers, state_is_tuple=True) 

x = tf.placeholder(tf.float32, [batch_size, sequence_size, num_features]) 
y = tf.placeholder(tf.int32, [batch_size * sequence_size * num_features]) 

init_state = lstm.zero_state(batch_size, tf.float32) 

inputs = [tf.squeeze(input_, [1]) for input_ in tf.split(1,sequence_size,x)] 
outputs, final_state = tf.nn.rnn(lstm, inputs, initial_state=init_state) 

w = tf.Variable(tf.truncated_normal([lstm_size, num_classes]), name='softmax_w') 
b = tf.Variable(tf.truncated_normal([num_classes]), name='softmax_b') 

output = tf.reshape(tf.concat(1, outputs), [-1, lstm_size]) 

logits = tf.matmul(output, w) + b 

probs = tf.nn.softmax(logits) 

cost = tf.reduce_mean(tf.nn.seq2seq.sequence_loss_by_example(
    [logits], [y], [tf.ones_like(y, dtype=tf.float32)] 
)) 

# Now optimize on that cost 
optimizer = tf.train.GradientDescentOptimizer(learning_rate) 
tvars = tf.trainable_variables() 
grads, _ = tf.clip_by_global_norm(tf.gradients(cost, tvars), 
            10.0) 
train_op = optimizer.apply_gradients(zip(grads, tvars)) 

init = tf.initialize_all_variables() 

with tf.Session() as sess: 
    sess.run(init) 
    curr_state = sess.run(init_state) 
    for i in range(3000): 
     # Create toy data where the true class is the value represented 
     # by the current and previous value treated as binary, i.e. 

     train_x = np.random.randint(0,2,(batch_size * sequence_size * num_features)) 
     train_y = train_x + np.concatenate(([0], (train_x[:-1] * 2))) 

     # Reshape into T x batch_size x sequence_size 
     train_x = np.reshape(train_x, [batch_size, sequence_size, num_features]) 

     feed_dict = { 
      x: train_x, y: train_y 
     } 
     for j, (c, h) in enumerate(init_state): 
      feed_dict[c] = curr_state[j].c 
      feed_dict[h] = curr_state[j].h 

     fetch_dict = { 
      'cost': cost, 'final_state': final_state, 'train_op': train_op 
     } 

     # Evaluate the graph 
     fetches = sess.run(fetch_dict, feed_dict=feed_dict) 

     curr_state = fetches['final_state'] 

     if i % 300 == 0: 
      print('step {}, train cost: {}'.format(i, fetches['cost'])) 

    # Test 
    test_x = np.array([[0],[0],[1],[0],[1],[1]]*(batch_size * sequence_size * num_features)) 
    test_x = test_x[:(batch_size * sequence_size * num_features),:] 
    probs_out = sess.run(probs, feed_dict={ 
      x: np.reshape(test_x, [batch_size, sequence_size, num_features]), 
      init_state: curr_state 
     }) 
    # Get the softmax outputs for the points in the sequence 
    # that have [0, 0], [0, 1], [1, 0], [1, 1] as their 
    # last two values. 
    for i in [1, 2, 3, 5]: 
     print('{}: [{:.4f} {:.4f} {:.4f} {:.4f}]'.format(
       [1, 2, 3, 5].index(i), *list(probs_out[i,:])) 
      )