dynamic_rnn.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. """ Dynamic Recurrent Neural Network.
  2. TensorFlow implementation of a Recurrent Neural Network (LSTM) that performs
  3. dynamic computation over sequences with variable length. This example is using
  4. a toy dataset to classify linear sequences. The generated sequences have
  5. variable length.
  6. Links:
  7. [Long Short Term Memory](http://deeplearning.cs.cmu.edu/pdfs/Hochreiter97_lstm.pdf)
  8. Author: Aymeric Damien
  9. Project: https://github.com/aymericdamien/TensorFlow-Examples/
  10. """
  11. from __future__ import print_function
  12. import tensorflow as tf
  13. import random
  14. # ====================
  15. # TOY DATA GENERATOR
  16. # ====================
  17. class ToySequenceData(object):
  18. """ Generate sequence of data with dynamic length.
  19. This class generate samples for training:
  20. - Class 0: linear sequences (i.e. [0, 1, 2, 3,...])
  21. - Class 1: random sequences (i.e. [1, 3, 10, 7,...])
  22. NOTICE:
  23. We have to pad each sequence to reach 'max_seq_len' for TensorFlow
  24. consistency (we cannot feed a numpy array with inconsistent
  25. dimensions). The dynamic calculation will then be perform thanks to
  26. 'seqlen' attribute that records every actual sequence length.
  27. """
  28. def __init__(self, n_samples=1000, max_seq_len=20, min_seq_len=3,
  29. max_value=1000):
  30. self.data = []
  31. self.labels = []
  32. self.seqlen = []
  33. for i in range(n_samples):
  34. # Random sequence length
  35. len = random.randint(min_seq_len, max_seq_len)
  36. # Monitor sequence length for TensorFlow dynamic calculation
  37. self.seqlen.append(len)
  38. # Add a random or linear int sequence (50% prob)
  39. if random.random() < .5:
  40. # Generate a linear sequence
  41. rand_start = random.randint(0, max_value - len)
  42. s = [[float(i)/max_value] for i in
  43. range(rand_start, rand_start + len)]
  44. # Pad sequence for dimension consistency
  45. s += [[0.] for i in range(max_seq_len - len)]
  46. self.data.append(s)
  47. self.labels.append([1., 0.])
  48. else:
  49. # Generate a random sequence
  50. s = [[float(random.randint(0, max_value))/max_value]
  51. for i in range(len)]
  52. # Pad sequence for dimension consistency
  53. s += [[0.] for i in range(max_seq_len - len)]
  54. self.data.append(s)
  55. self.labels.append([0., 1.])
  56. self.batch_id = 0
  57. def next(self, batch_size):
  58. """ Return a batch of data. When dataset end is reached, start over.
  59. """
  60. if self.batch_id == len(self.data):
  61. self.batch_id = 0
  62. batch_data = (self.data[self.batch_id:min(self.batch_id +
  63. batch_size, len(self.data))])
  64. batch_labels = (self.labels[self.batch_id:min(self.batch_id +
  65. batch_size, len(self.data))])
  66. batch_seqlen = (self.seqlen[self.batch_id:min(self.batch_id +
  67. batch_size, len(self.data))])
  68. self.batch_id = min(self.batch_id + batch_size, len(self.data))
  69. return batch_data, batch_labels, batch_seqlen
  70. # ==========
  71. # MODEL
  72. # ==========
  73. # Parameters
  74. learning_rate = 0.01
  75. training_steps = 10000
  76. batch_size = 128
  77. display_step = 200
  78. # Network Parameters
  79. seq_max_len = 20 # Sequence max length
  80. n_hidden = 64 # hidden layer num of features
  81. n_classes = 2 # linear sequence or not
  82. trainset = ToySequenceData(n_samples=1000, max_seq_len=seq_max_len)
  83. testset = ToySequenceData(n_samples=500, max_seq_len=seq_max_len)
  84. # tf Graph input
  85. x = tf.placeholder("float", [None, seq_max_len, 1])
  86. y = tf.placeholder("float", [None, n_classes])
  87. # A placeholder for indicating each sequence length
  88. seqlen = tf.placeholder(tf.int32, [None])
  89. # Define weights
  90. weights = {
  91. 'out': tf.Variable(tf.random_normal([n_hidden, n_classes]))
  92. }
  93. biases = {
  94. 'out': tf.Variable(tf.random_normal([n_classes]))
  95. }
  96. def dynamicRNN(x, seqlen, weights, biases):
  97. # Prepare data shape to match `rnn` function requirements
  98. # Current data input shape: (batch_size, n_steps, n_input)
  99. # Required shape: 'n_steps' tensors list of shape (batch_size, n_input)
  100. # Unstack to get a list of 'n_steps' tensors of shape (batch_size, n_input)
  101. x = tf.unstack(x, seq_max_len, 1)
  102. # Define a lstm cell with tensorflow
  103. lstm_cell = tf.contrib.rnn.BasicLSTMCell(n_hidden)
  104. # Get lstm cell output, providing 'sequence_length' will perform dynamic
  105. # calculation.
  106. outputs, states = tf.contrib.rnn.static_rnn(lstm_cell, x, dtype=tf.float32,
  107. sequence_length=seqlen)
  108. # When performing dynamic calculation, we must retrieve the last
  109. # dynamically computed output, i.e., if a sequence length is 10, we need
  110. # to retrieve the 10th output.
  111. # However TensorFlow doesn't support advanced indexing yet, so we build
  112. # a custom op that for each sample in batch size, get its length and
  113. # get the corresponding relevant output.
  114. # 'outputs' is a list of output at every timestep, we pack them in a Tensor
  115. # and change back dimension to [batch_size, n_step, n_input]
  116. outputs = tf.stack(outputs)
  117. outputs = tf.transpose(outputs, [1, 0, 2])
  118. # Hack to build the indexing and retrieve the right output.
  119. batch_size = tf.shape(outputs)[0]
  120. # Start indices for each sample
  121. index = tf.range(0, batch_size) * seq_max_len + (seqlen - 1)
  122. # Indexing
  123. outputs = tf.gather(tf.reshape(outputs, [-1, n_hidden]), index)
  124. # Linear activation, using outputs computed above
  125. return tf.matmul(outputs, weights['out']) + biases['out']
  126. pred = dynamicRNN(x, seqlen, weights, biases)
  127. # Define loss and optimizer
  128. cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y))
  129. optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(cost)
  130. # Evaluate model
  131. correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
  132. accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
  133. # Initialize the variables (i.e. assign their default value)
  134. init = tf.global_variables_initializer()
  135. # Start training
  136. with tf.Session() as sess:
  137. # Run the initializer
  138. sess.run(init)
  139. for step in range(1, training_steps + 1):
  140. batch_x, batch_y, batch_seqlen = trainset.next(batch_size)
  141. # Run optimization op (backprop)
  142. sess.run(optimizer, feed_dict={x: batch_x, y: batch_y,
  143. seqlen: batch_seqlen})
  144. if step % display_step == 0 or step == 1:
  145. # Calculate batch accuracy & loss
  146. acc, loss = sess.run([accuracy, cost], feed_dict={x: batch_x, y: batch_y,
  147. seqlen: batch_seqlen})
  148. print("Step " + str(step*batch_size) + ", Minibatch Loss= " + \
  149. "{:.6f}".format(loss) + ", Training Accuracy= " + \
  150. "{:.5f}".format(acc))
  151. print("Optimization Finished!")
  152. # Calculate accuracy
  153. test_data = testset.data
  154. test_label = testset.labels
  155. test_seqlen = testset.seqlen
  156. print("Testing Accuracy:", \
  157. sess.run(accuracy, feed_dict={x: test_data, y: test_label,
  158. seqlen: test_seqlen}))