LSTM с последующим средним пулом (TensorFlow)

Я знаю, что есть похожая тема в LSTM, за которой следует средний пул, но это речь идет о Keras, и я работаю в чистом TensorFlow.

У меня есть сеть LSTM, в которой повторение обрабатывается:

outputs, final_state = tf.nn.dynamic_rnn(cell,
                                         embed,
                                         sequence_length=seq_lengths,
                                         initial_state=initial_state)

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

Прямо сейчас я извлекаю последний соответствующий вывод с помощью следующего метода:

def extract_axis_1(data, ind):
    """
    Get specified elements along the first axis of tensor.
    :param data: Tensorflow tensor that will be subsetted.
    :param ind: Indices to take (one for each element along axis 0 of data).
    :return: Subsetted tensor.
    """

    batch_range = tf.range(tf.shape(data)[0])
    indices = tf.stack([batch_range, ind], axis=1)
    res = tf.reduce_mean(tf.gather_nd(data, indices), axis=0)

где я передаю sequence_length - 1 в качестве индексов. Что касается последней темы, я хотел бы выбрать все релевантные результаты, за которыми следует среднее объединение, а не только последний.

Теперь я попытался передать вложенные списки как индексы в extract_axis_1, но tf.stack не принимает это.

Любые направления решения для этого?


person riccardo_92    schedule 04.09.2017    source источник
comment
Что вы имеете в виду под релевантным выводом? Обычно вы обучаете сеть предсказывать также символ STOP: ваш реальный результат — это то, что находится между символом GO и символом STOP. Что вы собираетесь делать после соответствующей фильтрации вывода?   -  person Giuseppe Marra    schedule 04.09.2017
comment
Я имею в виду, что может быть 100 выходов (количество развернутых ячеек), но входная последовательность была только размера 10. Я хочу, чтобы выходы соответствовали этим 10 входам/ячейкам. После их получения я хочу усреднить их, а затем предсказать двоичный класс (с простым полносвязным слоем). Прямо сейчас я пытаюсь это сделать только с последним релевантным выводом, но это оказывается сложно.   -  person riccardo_92    schedule 04.09.2017


Ответы (1)


Вы можете использовать параметр weight функции tf.contrib.seq2seq.sequence_loss.

Из документации:

weights: тензор формы [batch_size, sequence_length] и dtype float. веса составляют взвешивание каждого предсказания в последовательности. При использовании weights в качестве маскирования установите для всех допустимых временных шагов значение 1, а для всех дополненных временных шагов значение 0, например. маска, возвращенная tf.sequence_mask.

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

Если вы не можете/не должны использовать потерю последовательности, вы можете сделать то же самое вручную. Вы вычисляете двоичную маску, а затем умножаете свои выходные данные на эту маску и предоставляете их в качестве входных данных для своего полносвязного слоя.

person Giuseppe Marra    schedule 04.09.2017
comment
Сейчас я использую только последний соответствующий вывод. Поскольку потери вычисляются с использованием только соответствующих выходных данных, мне не нужно маскировать свои выходные данные. Я ошибаюсь? - person riccardo_92; 04.09.2017
comment
Но вы сказали, что хотели бы попробовать все соответствующие выходы, а не только последний. - person Giuseppe Marra; 04.09.2017
comment
Именно так, но поскольку я хочу усреднить их (по оси времени), мне все равно придется выполнить маскировку перед вычислением потерь. - person riccardo_92; 04.09.2017
comment
Взвешивание в любом случае необходимо. Вы должны сделать что-то вроде: avg(output*mask) / sum(mask). Однако подумайте об использовании ВСЕХ ваших выходов, а не только их среднего значения. Это совсем другое. Усреднение приводит к потере информации о времени прогнозов. - person Giuseppe Marra; 04.09.2017
comment
Я думаю, вы делаете хорошее замечание. Таким образом, mask будет в основном тензором, содержащим 1 и 0 (и тогда * будет означать tf.matmul())? - person riccardo_92; 04.09.2017
comment
Да, маска — это маска 0/1 (0 для недопустимых входных данных, 1 для допустимых входных данных). * является поэлементным (вам нужно сохранить точно такие же размеры, вы просто обнуляете выходные данные, соответствующие недопустимым (т.е. заполненным) вводам). - person Giuseppe Marra; 04.09.2017
comment
Это действительно осуществимое решение в случае одной партии. Позвольте мне продемонстрировать: a = tf.constant([[[1,2,3], [4,5,6]], [[7,8,9], [10, 11, 12]]]) mask = np.array( [[[1, 0, 0], [0, 1, 1]], [[1, 0, 0], [0, 1, 1]]] ) with tf.Session() as sess: masked = tf.multiply(a, mask) print(masked.eval()), который вернет [[[ 1 0 0] [ 0 5 6]] [[ 7 0 0] [ 0 11 12]]], как и ожидалось. Однако первое измерение представляет мини-пакеты, и мне все равно придется усреднять по времени (второе измерение), но для мини-пакетов. Это оставляет меня с той же проблемой. - person riccardo_92; 04.09.2017
comment
Это осуществимо в любом случае. tf.reduce_sum (tf.multiply (выход, маска), ось = 1) / tf.reduce_sum (маска, ось = 1). tf.multiply поддерживает широковещательную передачу: он позаботится о тайлинге входного измерения. - person Giuseppe Marra; 04.09.2017