# Training outputs and losses. (Выводы и потери при обучении)
if forward_only:
self.outputs, self.losses = seq2seq.model_with_buckets(
self.encoder_inputs, self.decoder_inputs, targets,
self.target_weights, buckets, lambda x, y:
seq2seq_f(x, y, True),
softmax_loss_function=softmax_loss_function)
# If we use output projection, we need to project outputs
# for decoding. (Если используется проекция вывода, для декодирования необходимо проецировать выводы)
if output_projection is not None:
for b in xrange(len(buckets)):
self.outputs[b] = [
tf.matmul(output, output_projection[0]) +
output_projection[1]
for output in self.outputs[b]
]
else:
self.outputs, self.losses = seq2seq.model_with_buckets(
self.encoder_inputs, self.decoder_inputs, targets,
self.target_weights, buckets,
lambda x, y: seq2seq_f(x, y, False),
softmax_loss_function=softmax_loss_function)
Наконец, нужно обновить параметры модели (ведь это обучающиеся переменные) при помощи градиентного спуска. Мы берем обычный стохастический спуск с обрезанием градиента, но с тем же успехом можно использовать любой оптимизатор: результаты улучшатся, и обучение пойдет гораздо быстрее. Затем сохраняем все переменные.
# Gradients and SGD update operation for training the model. (Градиенты и стохастический градиентный спуск позволяют обновить операцию для обучения модели)
params = tf.trainable_variables()
if not forward_only:
self.gradient_norms = []
self.updates = []
opt = tf.train.GradientDescentOptimizer(
self.learning_rate)
for b in xrange(len(buckets)):
gradients = tf.gradients(self.losses[b], params)
clipped_gradients, norm = tf.clip_by_global_norm(
gradients,
max_gradient_norm)
self.gradient_norms.append(norm)
self.updates.append(opt.apply_gradients(
zip(clipped_gradients, params), global_step=
self.global_step))
self.saver = tf.train.Saver(tf.all_variables())
Описав высокоуровневые детали графа вычислений, переходим к последнему и самому нижнему уровню модели — внутренним элементам seq2seq.embedding_attention_seq2seq(). При инициализации модели некоторые флаги и аргументы передаются как параметры функции. Подробнее изучим аргумент feed_previous. При его значении true декодер будет использовать выведенный логит на временном шаге T как вход на временном шаге T+1. Он станет последовательно декодировать очередной токен на основе всех предыдущих. Такой тип декодирования, при котором каждое выходное значение зависит от всех предыдущих, можно назвать авторегрессионным :
def embedding_attention_seq2seq(encoder_inputs,
decoder_inputs,
cell,
num_encoder_symbols,
num_decoder_symbols,
embedding_size,
output_projection=None,
feed_previous=False,
dtype=None,
scope=None,
initial_state_attention=False):
Сначала создаем обертку для декодера.
with variable_scope.variable_scope(
scope or "embedding_attention_seq2seq", dtype=dtype)
as scope:
dtype = scope.dtype
encoder_cell = rnn_cell.EmbeddingWrapper(
cell,
embedding_classes=num_encoder_symbols,
embedding_size=embedding_size)
encoder_outputs, encoder_state = rnn.rnn(
encoder_cell, encoder_inputs, dtype=dtype)
В следующем фрагменте кода вычисляем конкатенацию выходных данных кодера, на которые нужно обратить внимание. Это важно, поскольку позволяет декодеру проходить по этим состояниям как по распределению:
# First calculate a concatenation of encoder outputs
# to put attention on. (Сначала вычисляем конкатенацию выводов кодера, на которые нужно обратить внимание)
top_states = [
array_ops.reshape(e, [-1, 1, cell.output_size]) for e
in encoder_outputs
]
attention_states = array_ops.concat(1, top_states)
Создаем декодер. Если флаг output_projection не выбран, нейрон оборачивается, чтобы использовать проекцию выходного значения:
output_size = None
if output_projection is None:
cell = rnn_cell.OutputProjectionWrapper(cell,
num_decoder_symbols)
output_size = num_decoder_symbols
Отсюда вычисляем выходные значения и состояния с помощью embedding_attention_decoder:
if isinstance(feed_previous, bool):
return embedding_attention_decoder(
decoder_inputs,
encoder_state,
attention_states,
cell,
num_decoder_symbols,
embedding_size,
output_size=output_size,
output_projection=output_projection,
feed_previous=feed_previous,
initial_state_attention=initial_state_attention)
Стоит отметить, что embedding_attention_decoder — небольшое улучшение по сравнению с attention_decoder, описанным в предыдущем разделе. Здесь выходные значения проецируются на обученное пространство векторного представления, что обычно повышает производительность. Функция loop, которая просто описывает динамику рекуррентного нейрона с векторным представлением, вызывается на этом шаге:
def embedding_attention_decoder(decoder_inputs,
initial_state,
attention_states,
cell,
num_symbols,
embedding_size,
output_size=None,
output_projection=None,
feed_previous=False,
update_embedding_for_previous=
True,
dtype=None,
scope=None,
initial_state_attention=False):
if output_size is None:
output_size = cell.output_size
if output_projection is not None:
proj_biases = ops.convert_to_tensor(output_projection[1], dtype=dtype)
proj_biases.get_shape(). assert_is_compatible_with(
[num_symbols])
with variable_scope.variable_scope(
scope or "embedding_attention_decoder", dtype=dtype)
as scope:
embedding = variable_scope.get_variable("embedding",
[num_symbols, embedding_size])
loop_function = _extract_argmax_and_embed(
embedding, output_projection,
update_embedding_for_previous) if feed_previous
else None
emb_inp = [
embedding_ops.embedding_lookup(embedding, i) for i in
decoder_inputs
]
return attention_decoder(
emb_inp,
initial_state,
attention_states,
cell,
output_size=output_size,
Читать дальше
Конец ознакомительного отрывка
Купить книгу