web-dev-qa-db-fra.com

TensorFlow utilisant des LSTM pour générer du texte

Je voudrais utiliser tensorflow pour générer du texte et modifier le didacticiel LSTM ( https://www.tensorflow.org/versions/master/tutorials/recurrent/index.html#recurrent-neural-networks ) code Pour ce faire, cependant, ma solution initiale semble générer des bêtises, même après une longue formation, elle ne s'améliore pas. Je ne vois pas pourquoi. L'idée est de commencer avec une matrice zéro puis de générer un mot à la fois.

C'est le code auquel j'ai ajouté les deux fonctions ci-dessous https://tensorflow.googlesource.com/tensorflow/+/master/tensorflow/models/rnn/ptb/ptb_Word_lm.py

Le générateur ressemble à ce qui suit

def generate_text(session,m,eval_op):

    state = m.initial_state.eval()

    x = np.zeros((m.batch_size,m.num_steps), dtype=np.int32)

    output = str()
    for i in xrange(m.batch_size):
        for step in xrange(m.num_steps):
            try:
                # Run the batch 
                # targets have to bee set but m is the validation model, thus it should not train the neural network
                cost, state, _, probabilities = session.run([m.cost, m.final_state, eval_op, m.probabilities],
                                                            {m.input_data: x, m.targets: x, m.initial_state: state})

                # Sample a Word-id and add it to the matrix and output
                Word_id = sample(probabilities[0,:])
                output = output + " " + reader.Word_from_id(Word_id)
                x[i][step] = Word_id

            except ValueError as e:
                print("ValueError")

    print(output)

J'ai ajouté la variable "probabilités" au ptb_model et c'est simplement un softmax sur les logits.

self._probabilities = tf.nn.softmax(logits)

Et l'échantillonnage:

def sample(a, temperature=1.0):
    # helper function to sample an index from a probability array
    a = np.log(a) / temperature
    a = np.exp(a) / np.sum(np.exp(a))
    return np.argmax(np.random.multinomial(1, a, 1))
13
seberik

Je travaille pour atteindre exactement le même objectif et je viens de le faire fonctionner. Vous avez beaucoup de modifications à apporter ici, mais je pense que vous avez raté quelques étapes.

Tout d'abord, pour générer du texte, vous devez créer une version différente du modèle, qui ne représente qu'un seul pas de temps. La raison en est que nous devons échantillonner chaque sortie y avant de pouvoir l'insérer dans la prochaine étape du modèle. J'ai fait cela en faisant une nouvelle configuration qui définit num_steps et batch_size égaux à 1.

class SmallGenConfig(object):
  """Small config. for generation"""
  init_scale = 0.1
  learning_rate = 1.0
  max_grad_norm = 5
  num_layers = 2
  num_steps = 1 # this is the main difference
  hidden_size = 200
  max_Epoch = 4
  max_max_Epoch = 13
  keep_prob = 1.0
  lr_decay = 0.5
  batch_size = 1
  vocab_size = 10000

J'ai également ajouté des probabilités au modèle avec ces lignes:

self._output_probs = tf.nn.softmax(logits)

et

@property
def output_probs(self):
  return self._output_probs

Ensuite, il y a quelques différences dans ma fonction generate_text(). La première est que je charge les paramètres de modèle enregistrés à partir du disque à l'aide de l'objet tf.train.Saver(). Notez que nous faisons cela après avoir instancié le PTBModel avec la nouvelle configuration présentée ci-dessus.

def generate_text(train_path, model_path, num_sentences):
  gen_config = SmallGenConfig()

  with tf.Graph().as_default(), tf.Session() as session:
    initializer = tf.random_uniform_initializer(-gen_config.init_scale,
                                                gen_config.init_scale)    
    with tf.variable_scope("model", reuse=None, initializer=initializer):
      m = PTBModel(is_training=False, config=gen_config)

    # Restore variables from disk.
    saver = tf.train.Saver() 
    saver.restore(session, model_path)
    print("Model restored from file " + model_path)

La deuxième différence est que je récupère la table de recherche d'id en chaînes de mots (j'ai dû écrire cette fonction, voir le code ci-dessous).

    words = reader.get_vocab(train_path)

Je configure l'état initial de la même manière que vous le faites, mais ensuite, je configure le jeton initial d'une manière différente. Je veux utiliser le jeton "fin de phrase" pour que je commence ma phrase avec les bons types de mots. J'ai parcouru l'index de Word et découvert que <eos> avait l'index 2 (déterministe), je l'ai donc codé en dur. Enfin, je l'enveloppe dans une matrice Numpy 1x1 afin que ce soit le bon type pour les entrées du modèle.

    state = m.initial_state.eval()
    x = 2 # the id for '<eos>' from the training set
    input = np.matrix([[x]])  # a 2D numpy matrix 

Enfin, voici la partie où nous générons des phrases. Notez que nous demandons à session.run() de calculer le output_probs et le final_state. Et nous lui donnons l'entrée et l'état. Dans la première itération, l'entrée est <eos> et l'état est le initial_state, mais lors des itérations suivantes, nous donnons en entrée notre dernière sortie échantillonnée et nous transmettons l'état de la dernière itération. Notez également que nous utilisons la liste words pour rechercher la chaîne Word à partir de l'index de sortie.

    text = ""
    count = 0
    while count < num_sentences:
      output_probs, state = session.run([m.output_probs, m.final_state],
                                   {m.input_data: input,
                                    m.initial_state: state})
      x = sample(output_probs[0], 0.9)
      if words[x]=="<eos>":
        text += ".\n\n"
        count += 1
      else:
        text += " " + words[x]
      # now feed this new Word as input into the next iteration
      input = np.matrix([[x]]) 

Ensuite, tout ce que nous avons à faire est d’imprimer le texte que nous avons accumulé.

    print(text)
  return

Voilà pour la fonction generate_text()

Enfin, laissez-moi vous montrer la définition de la fonction pour get_vocab(), que j’ai mise dans reader.py.

def get_vocab(filename):
  data = _read_words(filename)

  counter = collections.Counter(data)
  count_pairs = sorted(counter.items(), key=lambda x: (-x[1], x[0]))

  words, _ = list(Zip(*count_pairs))

  return words

La dernière chose à faire est de pouvoir sauvegarder le modèle après l’avoir formé, ce qui ressemble à

save_path = saver.save(session, "/tmp/model.ckpt")

Et c'est le modèle que vous chargerez plus tard à partir du disque lors de la génération de texte.

Il y avait un autre problème: j'ai trouvé que parfois la distribution de probabilité produite par la fonction softmax de Tensorflow ne totalisait pas exactement 1,0. Lorsque la somme était supérieure à 1,0, np.random.multinomial() génère une erreur. J'ai donc dû écrire ma propre fonction d'échantillonnage, qui ressemble à ceci

def sample(a, temperature=1.0):
  a = np.log(a) / temperature
  a = np.exp(a) / np.sum(np.exp(a))
  r = random.random() # range: [0,1)
  total = 0.0
  for i in range(len(a)):
    total += a[i]
    if total>r:
      return i
  return len(a)-1 

Lorsque vous mettez tout cela ensemble, le petit modèle a été capable de générer des phrases sympas. Bonne chance.

17
Teg Grenager

J'utilise votre code, cela ne semble pas correct. Donc, je le modifie un peu, il semble que le travail fonctionne ... Voici mon code, et je ne suis pas sûr

def generate_text(session,m,eval_op, Word_list):
output = []
for i in xrange(20):
    state = m.initial_state.eval()
    x = np.zeros((1,1), dtype=np.int32)
    y = np.zeros((1,1), dtype=np.int32)
    output_str = ""
    for step in xrange(100):
        if True:
            # Run the batch 
            # targets have to bee set but m is the validation model, thus it should not train the neural network
            cost, state, _, probabilities = session.run([m.cost, m.final_state, eval_op, m.probabilities],
                                                        {m.input_data: x, m.targets: y, m.initial_state: state})
            # Sample a Word-id and add it to the matrix and output
            Word_id = sample(probabilities[0,:])
            if (Word_id<0) or (Word_id > len(Word_list)):
                continue
            #print(Word_id)
            output_str = output_str + " " + Word_list[Word_id]
            x[0][0] = Word_id
    print(output_str)
    output.append(output_str)
return output
0
macg