Использовать код учебника LSTM для предсказания следующего слова в предложении?

Я пытался понять пример кода с https://www.tensorflow.org/tutorials/recurrent который вы можете найти в https://github.com/tensorflow/models/blob/master/tutorials/rnn/ptb/ptb_word_lm.py

(Использование тензорного потока 1.3.0.)

Я обобщил (что я думаю) ключевые части, для моего вопроса ниже:

 size = 200
 vocab_size = 10000
 layers = 2
 # input_.input_data is a 2D tensor [batch_size, num_steps] of
 #    word ids, from 1 to 10000

 cell = tf.contrib.rnn.MultiRNNCell(
    [tf.contrib.rnn.BasicLSTMCell(size) for _ in range(2)]
    )

 embedding = tf.get_variable(
      "embedding", [vocab_size, size], dtype=tf.float32)
 inputs = tf.nn.embedding_lookup(embedding, input_.input_data)

inputs = tf.unstack(inputs, num=num_steps, axis=1)
outputs, state = tf.contrib.rnn.static_rnn(
    cell, inputs, initial_state=self._initial_state)

output = tf.reshape(tf.stack(axis=1, values=outputs), [-1, size])
softmax_w = tf.get_variable(
    "softmax_w", [size, vocab_size], dtype=data_type())
softmax_b = tf.get_variable("softmax_b", [vocab_size], dtype=data_type())
logits = tf.matmul(output, softmax_w) + softmax_b

# Then calculate loss, do gradient descent, etc.

Мой самый большой вопрос: как я могу использовать созданную модель для создания следующего предложения слов, учитывая первые несколько слов предложения? Конкретно, я полагаю, что поток подобен этому, но я не могу понять, что будет для кода комментариев:

prefix = ["What", "is", "your"]
state = #Zeroes
# Call static_rnn(cell) once for each word in prefix to initialize state
# Use final output to set a string, next_word
print(next_word)

Мои вопросы:

  • Зачем использовать случайное (неинициализированное, неподготовленное) слово-вложение?
  • Зачем использовать softmax?
  • Должен ли скрытый слой соответствовать размерности ввода (т.е. размерность вложений word2vec)
  • Как/Могу ли я ввести предварительно подготовленную модель word2vec вместо этого неинициализированного?

(Я задаю им все как один вопрос, так как я подозреваю, что они все связаны, и связаны с некоторым пробелом в моем понимании.)

То, что я ожидал увидеть здесь, это загрузить существующий набор word2vec вложений слов (например, с помощью gensim KeyedVectors.load_word2vec_format()), преобразовать каждое слово во входящем корпусе в это представление при загрузке в каждом предложении, а затем LSTM выплюнуть вектор той же размерности, и мы попытаемся найти наиболее похожее слово (например, используя gensim similar_by_vector(y, topn=1)).

Использует softmax, спасая нас от относительно медленного вызова similar_by_vector(y, topn=1)?


Кстати, для ранее существовавшей части word2vec моего вопроса Использование предварительно подготовленного word2vec с LSTM для генерации слов аналогично. Однако ответы там, в настоящее время, не то, что я ищу. То, что я надеюсь, - это простое объяснение на английском языке, которое переключает свет на меня, и подключает все, что в моем понимании. Использовать предварительно подготовленный word2vec в языковой модели lstm? - еще один подобный вопрос.

ОБНОВЛЕНИЕ: Предсказание следующего слова с использованием примера тензорной модели языковой модели и Предсказание следующего слова с использованием примера LSTM ptb model tendorflow - подобные вопросы. Тем не менее, ни один из них не показывает код, который фактически принимает первые несколько слов предложения, и распечатывает его предсказание следующего слова. Я пробовал вставлять код из второго вопроса и qaru.site/info/359909/... (который поставляется с веткой github), но не может запускаться без ошибок. Я думаю, что они могут быть для более ранней версии TensorFlow?

ДРУГОЕ ОБНОВЛЕНИЕ: Еще один вопрос, задающий в основном одно и то же: Прогнозирование следующего слова модели LSTM из примера Tensorflow Он ссылается на Предсказание следующего слова с использованием примера тензорной модели языковой модели (и, опять же, ответы там не совсем то, что я ищу).

В случае, если все еще неясно, что я пытаюсь написать высокоуровневую функцию под названием getNextWord(model, sentencePrefix), где model - это ранее построенный LSTM, который я загрузил с диска, а sentencePrefix - строка, например "Открыть", и она может вернуть "pod". Затем я мог бы назвать это "Open pod", и он вернет "залив" и т.д.

Пример (с символом RNN и использованием mxnet) - это функция sample(), показанная в конце https://github.com/zackchase/mxnet-the-straight-dope/blob/master/chapter05_recurrent-neural-networks/simple-rnn.ipynb Вы можете называть sample() во время тренировки, но вы также можете называть его после тренировки и любым предложением, которое вы хотите.

Ответы

Ответ 1

Основной вопрос

Загрузка слов

Загрузка пользовательских данных вместо использования тестового набора:

[email protected]_raw_data

test_path = os.path.join(data_path, "ptb.test.txt")
test_data = _file_to_word_ids(test_path, word_to_id)  # change this line

test_data должен содержать идентификаторы слов (распечатать word_to_id для отображения). В качестве примера это должно выглядеть так: [1, 52, 562, 246]...

Отображение прогнозов

Нам нужно вернуть вывод уровня FC (logits) в вызове sess.run

[email protected]__init__

    logits = tf.reshape(logits, [self.batch_size, self.num_steps, vocab_size])
    self.top_word_id = tf.argmax(logits, axis=2)  # add this line

[email protected]_epoch

  fetches = {
      "cost": model.cost,
      "final_state": model.final_state,
      "top_word_id": model.top_word_id # add this line
  }

Позже в функции vals['top_word_id'] будет иметь массив целых чисел с идентификатором верхнего слова. Посмотрите это в word_to_id, чтобы определить предсказанное слово. Я сделал это некоторое время назад с небольшой моделью, и точность первого уровня была довольно низкой (20-30% iirc), хотя недоумение было тем, что было предсказано в заголовке.

подвопросы

Зачем использовать случайное (неинициализированное, неподготовленное) слово-вложение?

Вам нужно будет спросить авторов, но, на мой взгляд, обучение вложений делает это более самостоятельным учебником: вместо того, чтобы рассматривать вложение как черный ящик, он показывает, как он работает.

Зачем использовать softmax?

Конечное предсказание не, определяемое схожести косинуса с выходом скрытого слоя. После LSTM есть уровень FC, который преобразует внедренное состояние в горячее кодирование конечного слова.

Здесь представлен эскиз операций и измерений в нейронной сети:

word -> one hot code (1 x vocab_size) -> embedding (1 x hidden_size) -> LSTM -> FC layer (1 x vocab_size) -> softmax (1 x vocab_size)

Нужно ли скрытому слою соответствовать размерности ввода (т.е. размерность вложений word2vec)

Технически, нет. Если вы посмотрите на уравнения LSTM, вы заметите, что x (вход) может быть любого размера, если весовая матрица отрегулирована соответствующим образом.

LSTM equation

Как/Могу ли я ввести предварительно подготовленную модель word2vec вместо этого неинициализированного?

Я не знаю, извините.

Ответ 2

Есть много вопросов, я попытаюсь прояснить некоторые из них.

как я могу использовать созданную модель для генерации предложения следующего слова, учитывая первые несколько слов предложения?

Ключевым моментом здесь является следующее поколение слов, на самом деле это классификация слов в словаре. Поэтому вам нужен классификатор, поэтому на выходе есть softmax.

Принцип заключается в том, что на каждом временном шаге модель выводит следующее слово, основанное на вложении последнего слова и внутренней памяти предыдущих слов. tf.contrib.rnn.static_rnn автоматически объединяет ввод в память, но нам нужно предоставить последнее слово вложения и классифицировать следующее слово.

Мы можем использовать предварительно подготовленную модель word2vec, просто запустите матрицу embedding с предварительно подготовленной. Я думаю, что в учебнике для простоты используется случайная матрица. Размер памяти не связан с размером вложения, вы можете использовать больший объем памяти, чтобы сохранить дополнительную информацию.

Эти учебные пособия являются высокоуровневыми. Если вы хотите глубоко понять детали, я бы предложил посмотреть исходный код на простой python/numpy.

Ответ 3

Мой самый большой вопрос: как я могу использовать созданную модель для создания следующего предложения слов, учитывая первые несколько слов предложения?

т.е. Я пытаюсь написать функцию с сигнатурой: getNextWord (model, sentencePrefix)

Прежде чем я объясню свой ответ, сначала сделайте замечание о своем предложении # Call static_rnn(cell) once for each word in prefix to initialize state: имейте в виду, что static_rnn не возвращает значение, подобное массиву numpy, а тензор. Вы можете оценить тензор на значение при его запуске (1) в сеансе (сеанс хранит состояние вашего графа вычислений, включая значения параметров вашей модели) и (2) с помощью ввода, который необходим для вычисления значение тензора. Вход может поставляться с помощью считывателей ввода (подход в учебнике) или с использованием заполнителей (что я буду использовать ниже).

Теперь следует фактический ответ: Модель в учебнике была разработана для чтения входных данных из файла. Ответ @user3080953 уже показал, как работать с вашим собственным текстовым файлом, но, как я понимаю, вам нужно больше контролировать, как данные передаются в модель. Для этого вам нужно будет определить свои собственные заполнители и передать данные этим заполнителям при вызове session.run().

В приведенном ниже коде я подклассифицировал PTBModel и взял на себя ответственность за явную подачу данных в модель. Я ввел специальный PTBInteractiveInput, который имеет интерфейс, похожий на PTBInput, чтобы вы могли повторно использовать функциональность в PTBModel. Для обучения вашей модели вам все равно нужно PTBModel.

class PTBInteractiveInput(object):
  def __init__(self, config):
    self.batch_size = 1
    self.num_steps = config.num_steps
    self.input_data = tf.placeholder(dtype=tf.int32, shape=[self.batch_size, self.num_steps])
    self.sequence_len = tf.placeholder(dtype=tf.int32, shape=[])
    self.targets = tf.placeholder(dtype=tf.int32, shape=[self.batch_size, self.num_steps])

class InteractivePTBModel(PTBModel):

  def __init__(self, config):
    input = PTBInteractiveInput(config)
    PTBModel.__init__(self, is_training=False, config=config, input_=input)
    output = self.logits[:, self._input.sequence_len - 1, :]
    self.top_word_id = tf.argmax(output, axis=2)

  def get_next(self, session, prefix):
    prefix_array, sequence_len = self._preprocess(prefix)
    feeds = {
      self._input.sequence_len: sequence_len,
      self._input.input_data: prefix_array,
    }
    fetches = [self.top_word_id]
    result = session.run(fetches, feeds)
    self._postprocess(result)

  def _preprocess(self, prefix):
    num_steps = self._input.num_steps
    seq_len = len(prefix)
    if seq_len > num_steps:
      raise ValueError("Prefix to large for model.")
    prefix_ids = self._prefix_to_ids(prefix)
    num_items_to_pad = num_steps - seq_len
    prefix_ids.extend([0] * num_items_to_pad)
    prefix_array = np.array([prefix_ids], dtype=np.float32)
    return prefix_array, seq_len

  def _prefix_to_ids(self, prefix):
    # should convert your prefix to a list of ids
    pass

  def _postprocess(self, result):
    # convert ids back to strings
    pass

В __init__ функции PTBModel вам нужно добавить эту строку:

self.logits = logits

Зачем использовать случайное (неинициализированное, неподготовленное) слово-вложение?

Прежде всего, обратите внимание, что, хотя вложения вначале случайны, они будут обучаться с остальной частью сети. Встраивания, которые вы получаете после обучения, будут иметь аналогичные свойства, чем вложения, которые вы получаете с помощью моделей word2vec, например, возможность отвечать на вопросы по аналогии с векторными операциями (king-man + woman = queen и т.д.). В задачах у вас значительная сумма данных обучения, таких как языковое моделирование (которые не нуждаются в аннотированных данных обучения) или нейронных машинных переводах, чаще всего обучают вложения с нуля.

Зачем использовать softmax?

Softmax - это функция, которая нормализует вектор оценок подобия (логиты), к распределению вероятности. Вам нужно распределение вероятности, чтобы обучить вас модели с потерей кросс-энтропии и иметь возможность пробовать модель. Обратите внимание: если вас интересуют только наиболее вероятные слова обученной модели, вам не нужен softmax, и вы можете напрямую использовать логиты.

Нужно ли скрытому слою соответствовать размерности ввода (т.е. размерность вложений word2vec)

Нет, в принципе это может быть любая ценность. Однако использование скрытого состояния с более низким размером, чем размер вашего внедрения, не имеет большого смысла.

Как/Могу ли я ввести предварительно подготовленную модель word2vec вместо этого неинициализированного?

Вот самостоятельный пример инициализации вложения с заданным массивом numpy. Если вы хотите, чтобы вложение оставалось фиксированным/постоянным во время обучения, установите trainable в False.

import tensorflow as tf
import numpy as np
vocab_size = 10000
size = 200
trainable=True
embedding_matrix = np.zeros([vocab_size, size]) # replace this with code to load your pretrained embedding
embedding = tf.get_variable("embedding",
                            initializer=tf.constant_initializer(embedding_matrix),
                            shape=[vocab_size, size],
                            dtype=tf.float32,
                            trainable=trainable)

Ответ 4

Вы можете найти весь код в конце ответа.


Большинство ваших вопросов (почему Softmax, как использовать предварительно обработанный слой внедрения и т.д.) были отвечены, я считаю. Однако, поскольку вы все еще ждали краткого кода для создания сгенерированного текста из семени, я попытаюсь сообщить, как я это сделал сам.

Я боролся, начиная с официального учебника Tensorflow, чтобы понять, могу ли я легко генерировать слова из созданной модели. К счастью, после принятия некоторых ответов на практически все ответы, которые вы упомянули в своем вопросе, я получил более полное представление о проблеме (и решениях). Это может содержать ошибки, но, по крайней мере, он запускает и генерирует некоторый текст...

как я могу использовать созданную модель для генерации предложения следующего слова, учитывая первые несколько слов предложения?

Я завершу следующее предложение слова в цикле, чтобы сгенерировать целое предложение, но вы легко уменьшите его до одного слова.

Скажем, вы следовали текущему руководству, данным тензорным потоком (v1.4 на момент написания) здесь, который сохранит модель после обучая его.

Тогда нам остается только загрузить его с диска и написать функцию, которая принимает эту модель, а также вводит семя и возвращает сгенерированный текст.


Сгенерировать текст из сохраненной модели

Я предполагаю, что мы пишем весь этот код в новом python script. Всего script внизу в качестве реферата, здесь я объясню основные этапы.

Первые необходимые шаги

FLAGS = tf.flags.FLAGS
FLAGS.model = "medium" # or whatever size you used

Теперь, что немаловажно, мы создаем dictionnaries для сопоставления идентификаторов со словами и наоборот (поэтому нам не нужно читать список целых чисел...).

word_to_id = reader._build_vocab('../data/ptb.train.txt') # here we load the word -> id dictionnary ()
id_to_word = dict(zip(word_to_id.values(), word_to_id.keys())) # and transform it into id -> word dictionnary
_, _, test_data, _ = reader.ptb_raw_data('../data')

Затем мы загружаем класс конфигурации, также устанавливаем num_steps и batch_size в 1, так как мы хотим пример 1 слова за раз, в то время как LSTM будет обрабатывать также одно слово за раз, Также создание экземпляра ввода "на лету":

eval_config = get_config()
eval_config.num_steps = 1
eval_config.batch_size = 1
model_input = PTBInput(eval_config, test_data)

График построения

Чтобы загрузить сохраненную модель (как было сохранено модулем Supervisor.saver в учебнике), нам нужно сначала перестроить график (легко с классом PTBModel), который должен использовать тот же как при обучении:

sess = tf.Session()
initializer = tf.random_uniform_initializer(-eval_config.init_scale, eval_config.init_scale)
# not sure but seems to need the same name for variable scope as when saved ....!!
with tf.variable_scope("Model", reuse=None, initializer=initializer):
    tf.global_variables_initializer()
    mtest = PTBModel(is_training=False, config=eval_config, input=model_input)

Восстановление сохраненных весов:

sess.run(tf.global_variables_initializer())
saver = tf.train.Saver()
saver.restore(sess, tf.train.latest_checkpoint('../Whatever_folder_you_saved_in')) # the path must point to the hierarchy where your 'checkpoint' file is

... Сэмплирование слов из заданного семени:

Сначала нам нужна модель, которая будет содержать доступ к выводам логики или, точнее, распределение вероятности по всему лексике. Поэтому в файле ptb_lstm.py добавьте строку:

# the line goes somewhere below the reshaping "logits = tf.reshape(logits, [self.batch_size, ..."
self.probas = tf.nn.softmax(logits, name="probas")

Затем мы можем спроектировать некоторую функцию выборки (вы можете использовать все, что вам нравится здесь, лучший подход - это выборка с температурой, которая имеет тенденцию сглаживать или заострять распределение), вот базовый метод случайной выборки:

def sample_from_pmf(probas):
    t = np.cumsum(probas)
    s = np.sum(probas)
    return int(np.searchsorted(t, np.random.rand(1) * s))

И, наконец, функция, которая берет семя, вашу модель, словарь, который сопоставляет слово с идентификаторами, и наоборот, как вводит и выводит сгенерированную строку текстов:

def generate_text(session, model, word_to_index, index_to_word, 
                  seed='</s>', n_sentences=10):
    sentence_cnt = 0
    input_seeds_id = [word_to_index[w] for w in seed.split()]
    state = session.run(model.initial_state)

    # Initiate network with seeds up to the before last word:
    for x in input_seeds_id[:-1]:
        feed_dict = {model.initial_state: state,
                     model.input.input_data: [[x]]}
        state = session.run([model.final_state], feed_dict)

    text = seed
    # Generate a new sample from previous, starting at last word in seed
    input_id = [[input_seeds_id[-1]]]
    while sentence_cnt < n_sentences:
        feed_dict = {model.input.input_data: input_id,
                     model.initial_state: state}
        probas, state = session.run([model.probas, model.final_state],
                                 feed_dict=feed_dict)
        sampled_word = sample_from_pmf(probas[0])
        if sampled_word == word_to_index['</s>']:
            text += '.\n'
            sentence_cnt += 1
        else:
            text += ' ' + index_to_word[sampled_word]
        input_wordid = [[sampled_word]]

    return text

TL; DR

Не забудьте добавить строку:

self.probas = tf.nn.softmax(logits, name='probas')

В файле ptb_lstm.py в определении __init__ класса PTBModel, где-нибудь после строки logits = tf.reshape(logits, [self.batch_size, self.num_steps, vocab_size]).

Весь script, просто запустите его из того же каталога, где у вас есть reader.py, ptb_lstm.py:

import reader
import numpy as np
import tensorflow as tf
from ptb_lstm import PTBModel, get_config, PTBInput

FLAGS = tf.flags.FLAGS
FLAGS.model = "medium"

def sample_from_pmf(probas):
    t = np.cumsum(probas)
    s = np.sum(probas)
    return int(np.searchsorted(t, np.random.rand(1) * s))

def generate_text(session, model, word_to_index, index_to_word, 
                  seed='</s>', n_sentences=10):
    sentence_cnt = 0
    input_seeds_id = [word_to_index[w] for w in seed.split()]
    state = session.run(model.initial_state)

    # Initiate network with seeds up to the before last word:
    for x in input_seeds_id[:-1]:
        feed_dict = {model.initial_state: state,
                     model.input.input_data: [[x]]}
        state = session.run([model.final_state], feed_dict)

    text = seed
    # Generate a new sample from previous, starting at last word in seed
    input_id = [[input_seeds_id[-1]]]
    while sentence_cnt < n_sentences:
        feed_dict = {model.input.input_data: input_id,
                     model.initial_state: state}
        probas, state = sess.run([model.probas, model.final_state],
                                 feed_dict=feed_dict)
        sampled_word = sample_from_pmf(probas[0])
        if sampled_word == word_to_index['</s>']:
            text += '.\n'
            sentence_cnt += 1
        else:
            text += ' ' + index_to_word[sampled_word]
        input_wordid = [[sampled_word]]

    print(text)

if __name__ == '__main__':

    word_to_id = reader._build_vocab('../data/ptb.train.txt') # here we load the word -> id dictionnary ()
    id_to_word = dict(zip(word_to_id.values(), word_to_id.keys())) # and transform it into id -> word dictionnary
    _, _, test_data, _ = reader.ptb_raw_data('../data')

    eval_config = get_config()
    eval_config.batch_size = 1
    eval_config.num_steps = 1
    model_input = PTBInput(eval_config, test_data, name=None)

    sess = tf.Session()
    initializer = tf.random_uniform_initializer(-eval_config.init_scale,
                                            eval_config.init_scale)
    with tf.variable_scope("Model", reuse=None, initializer=initializer):
        tf.global_variables_initializer()
        mtest = PTBModel(is_training=False, config=eval_config, 
                         input_=model_input)

    sess.run(tf.global_variables_initializer())

    saver = tf.train.Saver()
    saver.restore(sess, tf.train.latest_checkpoint('../models'))

    while True:
        print(generate_text(sess, mtest, word_to_id, id_to_word, seed="this sentence is"))
        try:
            raw_input('press Enter to continue ...\n')
        except KeyboardInterrupt:
            print('\b\bQuiting now...')
            break