<jupyter_start><jupyter_text>Text generation using a RNN * Reference code: [TensorFlow official tutorials](https://www.tensorflow.org/tutorials/sequences/text_generation)  Import modules<jupyter_code>from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import os
import time

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from IPython.display import clear_output

import tensorflow as tf
from tensorflow.keras import layers
tf.enable_eager_execution()

os.environ["CUDA_VISIBLE_DEVICES"]="0"
print('TensorFlow version: {}'.format(tf.__version__))<jupyter_output><empty_output><jupyter_text>Download the Shakespeare datasetChange the following line to run this code on your own data.<jupyter_code>path_to_file = tf.keras.utils.get_file('shakespeare.txt',
                                       'https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt')<jupyter_output><empty_output><jupyter_text>Read the dataFirst, look in the text.<jupyter_code># Read, then decode for py2 compat.
text = open(path_to_file, 'rb').read().decode(encoding='utf-8')
# length of text is the number of characters in it
print ('Length of text: {} characters'.format(len(text)))
# Take a look at the first 250 characters in text
print(text[:250])
# The unique characters in the file
vocab = sorted(set(text))
print ('{} unique characters'.format(len(vocab)))
print(vocab)<jupyter_output><empty_output><jupyter_text>Process the text Vectorize the textBefore training, we need to map strings to a numerical representation. Create two lookup tables: one mapping characters to numbers, and another for numbers to characters.<jupyter_code># Creating a mapping from unique characters to indices
char2idx = {u:i for i, u in enumerate(vocab)}
idx2char = np.array(vocab)

text_as_int = np.array([char2idx[c] for c in text])
print('{')
for char, _ in zip(char2idx, range(20)):
    print('  {:4s}: {:3d},'.format(repr(char), char2idx[char]))
print('  ...\n}')
# Show how the first 13 characters from the text are mapped to integers
print ('{} ---- characters mapped to int ---- > {}'.format(repr(text[:13]), text_as_int[:13]))<jupyter_output><empty_output><jupyter_text>The prediction taskGiven a character, or a sequence of characters, what is the most probable next character? This is the task we're training the model to perform. The input to the model will be a sequence of characters, and we train the model to predict the output—the following character at each time step.Since RNNs maintain an internal state that depends on the previously seen elements, given all the characters computed until this moment, what is the next character?  Create training examples and targetsNext divide the text into example sequences. Each input sequence will contain `seq_length` characters from the text.For each input sequence, the corresponding targets contain the same length of text, except shifted one character to the right.So break the text into chunks of `seq_length+1`. For example, say `seq_length` is 4 and our text is "Hello". The input sequence would be "Hell", and the target sequence "ello".To do this first use the `tf.data.Dataset.from_tensor_slices` function to convert the text vector into a stream of character indices.<jupyter_code># The maximum length sentence we want for a single input in characters
seq_length = 100
examples_per_epoch = len(text)//seq_length

# Create training examples / targets
char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int)

for i in char_dataset.take(5):
  print(idx2char[i.numpy()])
sequences = char_dataset.batch(seq_length+1, drop_remainder=True)

for item in sequences.take(2):
  print(item.numpy())
  print(repr(''.join(idx2char[item.numpy()])))
  print('\n')
def split_input_target(chunk):
    input_text = chunk[:-1]
    target_text = chunk[1:]
    return input_text, target_text

dataset = sequences.map(split_input_target)
for input_example, target_example in dataset.take(1):
  print ('Input data: ', repr(''.join(idx2char[input_example.numpy()])))
  print ('Target data:', repr(''.join(idx2char[target_example.numpy()])))
for i, (input_idx, target_idx) in enumerate(zip(input_example[:5], target_example[:5])):
  print("Step {:4d}".format(i))
  print("  input: {} ({:s})".format(input_idx, repr(idx2char[input_idx])))
  print("  expected output: {} ({:s})".format(target_idx, repr(idx2char[target_idx])))<jupyter_output><empty_output><jupyter_text>Create training batchesWe used `tf.data` to split the text into manageable sequences. But before feeding this data into the model, we need to shuffle the data and pack it into batches.<jupyter_code># Batch size 
BATCH_SIZE = 64
steps_per_epoch = examples_per_epoch//BATCH_SIZE

# Buffer size to shuffle the dataset
# (TF data is designed to work with possibly infinite sequences, 
# so it doesn't attempt to shuffle the entire sequence in memory. Instead, 
# it maintains a buffer in which it shuffles elements).
BUFFER_SIZE = 10000

dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)

dataset<jupyter_output><empty_output><jupyter_text>Build the modelUse `tf.keras.Sequential` to define the model. For this simple example three layers are used to define our model:* `tf.keras.layers.Embedding`: The input layer. A trainable lookup table that will map the numbers of each character to a vector with `embedding_dim` dimensions;* `tf.keras.layers.GRU`: A type of RNN with size units=rnn_units (You can also use a LSTM layer here.)* `tf.keras.layers.Dense`: The output layer, with vocab_size outputs.<jupyter_code># Length of the vocabulary in chars
vocab_size = len(vocab)

# The embedding dimension 
embedding_dim = 256

# Number of RNN units
rnn_units = 1024
if tf.test.is_gpu_available():
  rnn = tf.keras.layers.CuDNNGRU
else:
  import functools
  rnn = functools.partial(
    tf.keras.layers.GRU, recurrent_activation='sigmoid')
def build_model(vocab_size, embedding_dim, rnn_units, batch_size):
  model = tf.keras.Sequential()
  model.add(layers.Embedding(vocab_size, embedding_dim,
                             batch_input_shape=[batch_size, None]))
  model.add(rnn(rnn_units, return_sequences=True, 
                recurrent_initializer='glorot_uniform', stateful=True))
  model.add(layers.Dense(vocab_size))
  
  return model
model = build_model(
  vocab_size = len(vocab), 
  embedding_dim=embedding_dim, 
  rnn_units=rnn_units, 
  batch_size=BATCH_SIZE)<jupyter_output><empty_output><jupyter_text>![text_generation_training](https://user-images.githubusercontent.com/11681225/54700662-ca135700-4b76-11e9-936b-73518ea8f16d.png)  Try the modelNow run the model to see that it behaves as expected.First check the shape of the output:  Train the model<jupyter_code>for input_example_batch, target_example_batch in dataset.take(1): 
  example_batch_predictions = model(input_example_batch)
  print(example_batch_predictions.shape, "# (batch_size, sequence_length, vocab_size)")
model.summary()<jupyter_output><empty_output><jupyter_text>Try it for the first example in the batch:<jupyter_code>sampled_indices = tf.random.categorical(example_batch_predictions[0], num_samples=1)
sampled_indices = tf.squeeze(sampled_indices,axis=-1).numpy()
print(sampled_indices)<jupyter_output><empty_output><jupyter_text>Decode these to see the text predicted by this untrained model:<jupyter_code>print("Input: \n", repr("".join(idx2char[input_example_batch[0]])))
print()
print("Next Char Predictions: \n", repr("".join(idx2char[sampled_indices ])))<jupyter_output><empty_output><jupyter_text>Train the modelAt this point the problem can be treated as a standard classification problem. Given the previous RNN state, and the input this time step, predict the class of the next character. Attach an optimizer, and a loss functionThe standard `tf.keras.losses.sparse_softmax_crossentropy` loss function works in this case because it is applied across the last dimension of the predictions.Because our model returns logits, we need to set the `from_logits` flag.<jupyter_code>def loss(labels, logits):
  return tf.keras.losses.sparse_categorical_crossentropy(labels, logits, from_logits=True)

example_batch_loss  = loss(target_example_batch, example_batch_predictions)
print("Prediction shape: ", example_batch_predictions.shape, " # (batch_size, sequence_length, vocab_size)") 
print("scalar_loss:      ", example_batch_loss.numpy().mean())
model.compile(optimizer = tf.train.AdamOptimizer(),
              loss = loss)<jupyter_output><empty_output><jupyter_text>Configure checkpointsUse a `tf.keras.callbacks.ModelCheckpoint` to ensure that checkpoints are saved during training:<jupyter_code># Directory where the checkpoints will be saved
checkpoint_dir = './training_checkpoints'
# Name of the checkpoint files
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt_{epoch}")

checkpoint_callback=tf.keras.callbacks.ModelCheckpoint(
    filepath=checkpoint_prefix,
    save_weights_only=True)<jupyter_output><empty_output><jupyter_text>Execute the trainingTo keep training time reasonable, use 3 epochs to train the model. In Colab, set the runtime to GPU for faster training.<jupyter_code>EPOCHS = 3
history = model.fit(dataset.repeat(), epochs=EPOCHS,
                    steps_per_epoch=steps_per_epoch, callbacks=[checkpoint_callback])<jupyter_output><empty_output><jupyter_text>Generate textRestore the latest checkpointTo keep this prediction step simple, use a batch size of 1.Because of the way the RNN state is passed from timestep to timestep, the model only accepts a fixed batch size once built.To run the model with a different `batch_size`, we need to rebuild the model and restore the weights from the checkpoint.<jupyter_code>tf.train.latest_checkpoint(checkpoint_dir)
model = build_model(vocab_size, embedding_dim, rnn_units, batch_size=1)

model.load_weights(tf.train.latest_checkpoint(checkpoint_dir))

model.build(tf.TensorShape([1, None]))
model.summary()<jupyter_output><empty_output><jupyter_text>The prediction loopThe following code block generates the text:* It Starts by choosing a start string, initializing the RNN state and setting the number of characters to generate.* Get the prediction distribution of the next character using the start string and the RNN state.* Then, use a multinomial distribution to calculate the index of the predicted character. Use this predicted character as our next input to the model.* The RNN state returned by the model is fed back into the model so that it now has more context, instead than only one word. After predicting the next word, the modified RNN states are again fed back into the model, which is how it learns as it gets more context from the previously predicted words. ![text_generation_sampling](https://user-images.githubusercontent.com/11681225/54701181-d3e98a00-4b77-11e9-8805-da62266807af.png)<jupyter_code>def generate_text(model, start_string):
  # Evaluation step (generating text using the learned model)

  # Number of characters to generate
  num_generate = 1000

  # Converting our start string to numbers (vectorizing)
  input_eval = [char2idx[s] for s in start_string]
  input_eval = tf.expand_dims(input_eval, 0)

  # Empty string to store our results
  text_generated = []

  # Low temperatures results in more predictable text.
  # Higher temperatures results in more surprising text.
  # Experiment to find the best setting.
  temperature = 1.0

  # Here batch size == 1
  model.reset_states()
  for i in range(num_generate):
      predictions = model(input_eval)
      # remove the batch dimension
      predictions = tf.squeeze(predictions, 0)

      # using a categorical distribution to predict the word returned by the model
      predictions = predictions / temperature
      predicted_id = tf.random.categorical(predictions, num_samples=1)[-1,0].numpy()

      # We pass the predicted word as the next input to the model
      # along with the previous hidden state
      input_eval = tf.expand_dims([predicted_id], 0)

      text_generated.append(idx2char[predicted_id])

  return (start_string + ''.join(text_generated))
print(generate_text(model, start_string=u"ROMEO: "))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09087197909862835, 'fraction_numerical': 0.013308295231874592, 'mean_word_length': 5.635427952329361, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 62, 'https://': 4, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '15', 'dir': 'jupyter-structured-clean-dedup', 'id': '265731', 'license': 'Apache-2.0', 'n_tokens_mistral': 3815, 'n_tokens_neox': 3662, 'n_words': 1184, 'path': 'tf.version.2/04.rnn/04.text.generation.ipynb', 'repo_name': 'ilguyi/tensorflow.tutorials', 'size': '12248'}