Вопрос или проблема
Я начинающий в машинном обучении и пытаюсь создать модель для коррекции орфографии, которая проверяет правописание для небольшого количества словарных фраз (примерно 1000 фраз). В настоящее время я обращаюсь к урокам tensorflow 2.0 по 1. NMT с вниманием и 2. Генерации текста. Я завершил изучение до уровня кодирования, но сейчас у меня возникают проблемы с приведением формы следующих слоев (декодер и внимание) в соответствие с предыдущими (кодировщиком). Кодировщик в уроке не является двунаправленным, тогда как я пытаюсь реализовать двунаправленный кодировщик. Ниже приведен мой код для кодировщика и слоя внимания.
class Encoder(tf.keras.Model):
def __init__(self, vocab_size, embedding_dim, enc_units, batch_sz):
super(Encoder, self).__init__()
self.batch_sz = batch_sz
self.enc_units = enc_units
self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
self.bigru = tf.keras.layers.Bidirectional(
tf.keras.layers.GRU(self.enc_units,
return_sequences=True,
return_state=True,
recurrent_initializer="glorot_uniform",
dropout=0.2,
recurrent_dropout=0.2))
def call(self, x, hidden):
x = self.embedding(x)
output, forward_state, backward_state = self.bigru(x, initial_state = hidden)
hidden_state = tf.convert_to_tensor([forward_state, backward_state])
return output, hidden_state
def initialize_hidden_state(self):
init_state = [tf.zeros((self.batch_sz, self.enc_units)) for i in range(2)]
return init_state
embedding_dim = 10
enc_units = 100
batch_size = 64
encoder = Encoder(vocab_size, embedding_dim, enc_units, batch_size)
# пример ввода
sample_hidden = encoder.initialize_hidden_state()
sample_output, sample_hidden = encoder(example_input_batch, sample_hidden)
print ('Форма выхода кодировщика: (размер партии, длина последовательности, единицы) {}'.format(sample_output.shape))
print ('Форма скрытого состояния кодировщика: (размер партии, единицы) {}'.format(sample_hidden.shape))
WARNING:tensorflow:Слой gru_20 не будет использовать ядро cuDNN, так как оно не соответствует критериям ядра cuDNN. Он будет использовать общее ядро GPU в качестве резервного варианта при выполнении на GPU
WARNING:tensorflow:Слой gru_20 не будет использовать ядро cuDNN, так как оно не соответствует критериям ядра cuDNN. Он будет использовать общее ядро GPU в качестве резервного варианта при выполнении на GPU
WARNING:tensorflow:Слой gru_20 не будет использовать ядро cuDNN, так как оно не соответствует критериям ядра cuDNN. Он будет использовать общее ядро GPU в качестве резервного варианта при выполнении на GPU
Форма выхода кодировщика: (размер партии, длина последовательности, единицы) (64, 27, 200)
Форма скрытого состояния кодировщика: (размер партии, единицы) (2, 64, 100)
Форма скрытого состояния кодировщика (обратного направления): (размер партии, единицы) (64, 100)
class BahdanauAttention(tf.keras.layers.Layer):
def __init__(self, units):
super(BahdanauAttention, self).__init__()
self.W1 = tf.keras.layers.Dense(units)
self.W2 = tf.keras.layers.Dense(units)
self.V = tf.keras.layers.Dense(1)
def call(self, query, values):
# форма скрытого состояния запроса == (batch_size, hidden size)
# query_with_time_axis форма == (batch_size, 1, hidden size)
# значения форма == (batch_size, max_len, hidden size)
# мы делаем это для того, чтобы выполнить операцию сложения вдоль временной оси для вычисления оценки
query_with_time_axis = tf.expand_dims(query, 1)
# оценка форма == (batch_size, max_length, 1)
# мы получаем 1 на последней оси, потому что мы применяем оценку к self.V
# форма тензора перед применением self.V — (batch_size, max_length, units)
score = self.V(tf.nn.tanh(
self.W1(query_with_time_axis) + self.W2(values)))
# attention_weights форма == (batch_size, max_length, 1)
attention_weights = tf.nn.softmax(score, axis=1)
# форма вектор контекста после суммы == (batch_size, hidden_size)
context_vector = attention_weights * values
context_vector = tf.reduce_sum(context_vector, axis=1)
return context_vector, attention_weights
attention_layer = BahdanauAttention(10)
attention_result, attention_weights = attention_layer(sample_hidden, sample_output)
print("Форма результата внимания: (размер партии, единицы) {}".format(attention_result.shape))
print("Форма весов внимания: (размер партии, длина последовательности, 1) {}".format(attention_weights.shape))
InvalidArgumentError Traceback (most recent call last)
in ()
1 attention_layer = BahdanauAttention(10)
—-> 2 attention_result, attention_weights = attention_layer(sample_hidden, sample_output)
3
4 print(“Форма результата внимания: (размер партии, единицы) {}”.format(attention_result.shape))
5 print(“Форма весов внимания: (размер партии, длина последовательности, 1) {}”.format(attention_weights.shape))
6 фреймов
/usr/local/lib/python3.6/dist-packages/six.py in raise_from(value, from_value)
InvalidArgumentError: Несовпадающие размеры: [2,1,64,10] vs. [64,27,10] [Op:AddV2]
В этом учебнике они создали новый класс BahdanauAttention()
, который должен быть встроен в объект Decoder()
. Внимание – это то, что использует декодер, а не кодировщик. Модель, как она есть, не полна. Добавьте декодер и измените его входную форму, чтобы он заработал.
Тем не менее, позвольте мне подвести итоги некоторыми мыслями об этой реализации:
Я не думаю, что вам нужна двунаправленность, когда вы используете механизмы внимания. Цель внимания – позволить полезному сигналу, который находится далеко во времени, быстрее добраться до выхода (не проходя через все ячейки RNN и не теряясь на полпути). По этой причине, до появления механизмов внимания, двунаправленные RNN слои были лучшим инструментом для достижения этой повышенной “полной связности” Seq2seq сетей. Но теперь, когда у вас есть внимание, вам это уже не нужно, поскольку у вас есть инструмент, который делает это более гибко и эффективно.
Добавляя двунаправленность, вы заставляете модель распределять внимание по дублирующей тенденции, декодер будет получать две копии одного и того же сигнала (слева направо и справа налево), но с одним вниманием, которое нужно распределить по всем. Я считаю, что это противоречит интуиции и нежелательно, сам концепт внимания искажается. Я предлагаю отказаться от двунаправленности и использовать просто обычное внимание. Было бы интересно сравнить его производительность с моделью с двунаправленностью и без внимания (я уверен, что модели с вниманием выиграют с легкостью).
-.-.-.-.-.-
PS: Начиная с tensorflow 2.1, класс BahdanauAttention()
теперь упакован в слой keras под названием AdditiveAttention()
, который вы можете вызвать как любой другой слой и вставить его в класс Decoder()
. Также есть еще один слой keras, просто называющийся Attention()
, который реализует внимание Лионга; было бы интересно сравнить их производительность.
Ответ или решение
Для создания модели коррекции орфографии с использованием слоя расшифровки и внимания в TensorFlow 2.0, необходимо учитывать ряд важных аспектов, особенно когда речь идет о направлении кодировщика и механизме внимания. Давайте рассмотрим, как это можно реализовать шаг за шагом.
Шаг 1: Кодировщик
Вы уже реализовали кодировщик с использованием двунаправленного RNN (в данном случае – GRU). Основная идея здесь заключается в том, чтобы захватить контекст из обоих направлений последовательности:
class Encoder(tf.keras.Model):
def __init__(self, vocab_size, embedding_dim, enc_units, batch_sz):
super(Encoder, self).__init__()
self.batch_sz = batch_sz
self.enc_units = enc_units
self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
self.bigru = tf.keras.layers.Bidirectional(
tf.keras.layers.GRU(self.enc_units,
return_sequences=True,
return_state=True,
recurrent_initializer="glorot_uniform",
dropout=0.2,
recurrent_dropout=0.2))
def call(self, x, hidden):
x = self.embedding(x)
output, forward_state, backward_state = self.bigru(x, initial_state=hidden)
hidden_state = tf.concat([forward_state, backward_state], axis=-1) # Объединение состояний
return output, hidden_state
def initialize_hidden_state(self):
return [tf.zeros((self.batch_sz, self.enc_units)) for _ in range(2)]
# Пример использования
embedding_dim = 10
enc_units = 100
batch_size = 64
encoder = Encoder(vocab_size, embedding_dim, enc_units, batch_size)
Шаг 2: Слой внимания
Слой внимания будет принимать выходы кодировщика и текущее состояние декодера для генерации контекстного вектора:
class BahdanauAttention(tf.keras.layers.Layer):
def __init__(self, units):
super(BahdanauAttention, self).__init__()
self.Wa = tf.keras.layers.Dense(units)
self.Ua = tf.keras.layers.Dense(units)
self.Va = tf.keras.layers.Dense(1)
def call(self, query, values):
query_with_time_axis = tf.expand_dims(query, 1) # Изменяем форму для сложения
score = self.Va(tf.nn.tanh(self.Wa(query_with_time_axis) + self.Ua(values)))
attention_weights = tf.nn.softmax(score, axis=1)
context_vector = attention_weights * values
context_vector = tf.reduce_sum(context_vector, axis=1)
return context_vector, attention_weights
Шаг 3: Декодер
Декодер будет использовать слой внимания для генерации выходных последовательностей на основе контекстного вектора, извлеченного из кодировщика:
class Decoder(tf.keras.Model):
def __init__(self, vocab_size, embedding_dim, dec_units, batch_sz):
super(Decoder, self).__init__()
self.batch_sz = batch_sz
self.dec_units = dec_units
self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
self.gru = tf.keras.layers.GRU(self.dec_units,
return_sequences=True,
return_state=True,
recurrent_initializer="glorot_uniform")
self.fc = tf.keras.layers.Dense(vocab_size)
self.attention = BahdanauAttention(dec_units)
def call(self, x, hidden, encoder_output):
context_vector, attention_weights = self.attention(hidden, encoder_output)
x = self.embedding(x)
x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1) # Объединяем контекст и вход
output, hidden = self.gru(x)
output = self.fc(output)
return output, hidden, attention_weights
Шаг 4: Обучение модели
Теперь, когда вы добавили все необходимые компоненты, можно обучать модель. Следите за тем, чтобы корректно настраивать размеры входных данных и обновлять веса.
Заключение
Создание модели с механизмом внимания и декодером требует внимательности к деталям, особенно для соответствия форматов входных данных. Разумное использование двунаправленного кодировщика в сочетании с вниманием может значительно улучшить качество вашей модели коррекции орфографии, но важно отметить, что гиперпараметры и архитектура должны быть оптимизированы в соответствии с вашими данными.
Не забывайте тестировать различные конфигурации: как с двунаправленным кодировщиком, так и без него, чтобы найти наилучшее решение для вашей задачи. Удачи в реализации вашего проекта!