Долгая краткосрочная память — различия между версиями
Vanyaland (обсуждение | вклад) (Новая страница: «'''Долгая краткосрочная память''' (англ. ''Long short-term memory'', ''LSTM'') {{---}} особая разновидность арх…») |
Vanyaland (обсуждение | вклад) (→Принцип работы) |
||
| Строка 25: | Строка 25: | ||
== Принцип работы == | == Принцип работы == | ||
| − | Вначале LSTM | + | Вначале LSTM определяет, какую информацию можно выбросить из состояния ячейки. Это решение принимает сигмоидальный слой, называемый “слоем фильтра забывания” (англ. ''forget gate layer''). Он смотрит на <math>h_{t-1}</math> и <math>x_t</math> и возвращает число от 0 до 1 для каждого числа из состояния ячейки <math>C_t{t-1}</math> . 1 означает “полностью сохранить”, а 0 – “полностью выбросить”. |
[[File:Lstm-1.png|none|650px]] | [[File:Lstm-1.png|none|650px]] | ||
Версия 17:15, 2 апреля 2019
Долгая краткосрочная память (англ. Long short-term memory, LSTM) — особая разновидность архитектуры рекуррентных нейронных сетей, способная к обучению долговременным зависимостям, предложенная в 1997 году Сеппом Хохрайтером и Юргеном Шмидхубером[1].
Содержание
Описание
LSTM разработаны специально, чтобы избежать проблемы долговременной зависимости. Запоминание информации на долгие периоды времени — это их обычное поведение. Ключом к данной возможности является то, что LSTM-модуль не использует функцию активации внутри своих рекуррентных компонентов.
Ключевой компонент LSTM — это состояние ячейки — горизонтальная линия, проходящая по верхней части схемы. Состояние ячейки напоминает конвейерную ленту. Она проходит напрямую через всю цепочку, участвуя лишь в нескольких линейных преобразованиях. Информация может легко течь по ней, не подвергаясь изменениям.
Тем не менее, LSTM может удалять информацию из состояния ячейки; этот процесс регулируется структурами, называемыми фильтрами. Фильтры позволяют пропускать информацию на основании некоторых условий. Они состоят из слоя сигмоидальной[2] нейронной сети и операции поточечного умножения.
Сигмоидальный слой возвращает числа от нуля до единицы, которые обозначают, какую долю каждого блока информации следует пропустить дальше по сети. Ноль в данном случае означает “не пропускать ничего”, единица – “пропустить все”.
Основные компоненты
- Состояние ячейки
- Фильтры, контролирующие состояние ячейки
- Забывания
- Входной
- Выходной
Принцип работы
Вначале LSTM определяет, какую информацию можно выбросить из состояния ячейки. Это решение принимает сигмоидальный слой, называемый “слоем фильтра забывания” (англ. forget gate layer). Он смотрит на и и возвращает число от 0 до 1 для каждого числа из состояния ячейки . 1 означает “полностью сохранить”, а 0 – “полностью выбросить”.
Далее — решается, какая новая информация будет храниться в состоянии ячейки. Этот этап состоит из двух частей. Сначала сигмоидальный слой под названием “слой входного фильтра” (англ. input layer gate) определяет, какие значения следует обновить. Затем tanh-слой[3] строит вектор новых значений-кандидатов , которые можно добавить в состояние ячейки.
Для замены старого состояния ячейки на новое состояние . Необходимо умножить старое состояние на , забывая то, что мы решили забыть ранее. Затем прибавляем . Это новые значения-кандидаты, умноженные на – на сколько мы хотим обновить каждое из значений состояния.
На последнем этапе определяется, то, какую информацию мы хотим получать на выходе. Выходные данные будут основаны на нашем состоянии ячейки, к ним будут применены некоторые фильтры. Сначала мы применяем сигмоидальный слой, который решает, какую информацию из состояния ячейки мы будем выводить. Затем значения состояния ячейки проходят через tanh-слой, чтобы получить на выходе значения из диапазона от -1 до 1, и перемножаются с выходными значениями сигмоидального слоя, что позволяет выводить только требуемую информацию.
Полученные таким образом и передаются далее по цепочке.
Вариации
Одна из популярных вариаций LSTM, предложенная Герсом и Шмидхубером[4], характеризуется добавлением так называемых “смотровых глазков” (“peephole connections”). С их помощью слои фильтров могут видеть состояние ячейки.
На схеме выше “глазки” есть у каждого слоя, но во многих работах они добавляются лишь к некоторым слоям.
Другие модификации включают объединенные фильтры “забывания” и входные фильтры. В этом случае решения, какую информацию следует забыть, а какую запомнить, принимаются не отдельно, а совместно. Мы забываем какую-либо информацию только тогда, когда необходимо записать что-то на ее место. Мы добавляем новую информацию с состояние ячейки только тогда, когда забываем старую.
Немного больше отличаются от стандартных LSTM управляемые рекуррентные нейроны (англ. Gated recurrent units, GRU), впервые описанные в работе Cho[5]. В ней фильтры «забывания» и входа объединяют в один фильтр «обновления» (update gate). Кроме того, состояние ячейки объединяется со скрытым состоянием, есть и другие небольшие изменения. Построенная в результате модель проще, чем стандартная LSTM, и популярность ее неуклонно возрастает.
Существует множество других модификаций, как, например, глубокие управляемые рекуррентные нейронные сети (англ. Depth Gated RNNs), представленные в работе Yao[6]. Есть и другие способы решения проблемы долговременных зависимостей, например, Clockwork RNN Яна Кутника[7].
Примеры кода
TensorFlow
Пример кода с библиотекой TensorFlow[8]
# Импорты
from __future__ import print_function
import tensorflow as tf
from tensorflow.contrib import rnn
# Импорт MNIST датасета
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)
# Определение параметров обучения
learning_rate = 0.001
training_steps = 10000
batch_size = 128
display_step = 200
# Определение параметров сети
num_input = 28
timesteps = 28
num_hidden = 128
num_classes = 10
# Входные данные для графа
X = tf.placeholder("float", [None, timesteps, num_input])
Y = tf.placeholder("float", [None, num_classes])
# Определение весов
weights = {
'out': tf.Variable(tf.random_normal([num_hidden, num_classes]))
}
biases = {
'out': tf.Variable(tf.random_normal([num_classes]))
}
def RNN(x, weights, biases):
x = tf.unstack(x, timesteps, 1)
# Определение LSTM ячейки
lstm_cell = rnn.BasicLSTMCell(num_hidden, forget_bias=1.0)
# Получение выхода LSTM ячейки
outputs, states = rnn.static_rnn(lstm_cell, x, dtype=tf.float32)
return tf.matmul(outputs[-1], weights['out']) + biases['out']
logits = RNN(X, weights, biases)
prediction = tf.nn.softmax(logits)
# Определение функции потерь и оптимизатора
loss_op = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(
logits=logits, labels=Y))
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
train_op = optimizer.minimize(loss_op)
# Оценка модели
correct_pred = tf.equal(tf.argmax(prediction, 1), tf.argmax(Y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
# Инициализация
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for step in range(1, training_steps+1):
batch_x, batch_y = mnist.train.next_batch(batch_size)
batch_x = batch_x.reshape((batch_size, timesteps, num_input))
# Запуск оптимизатора (обратное распространение ошибки)
sess.run(train_op, feed_dict={X: batch_x, Y: batch_y})
if step % display_step == 0 or step == 1:
loss, acc = sess.run([loss_op, accuracy], feed_dict={X: batch_x, Y: batch_y})
print("Step " + str(step) + ", Minibatch Loss= " + \
"{:.4f}".format(loss) + ", Training Accuracy= " + \
"{:.3f}".format(acc))
print("Optimization Finished!")
test_len = 128
test_data = mnist.test.images[:test_len].reshape((-1, timesteps, num_input))
test_label = mnist.test.labels[:test_len]
print("Testing Accuracy:", \
sess.run(accuracy, feed_dict={X: test_data, Y: test_label}))
Keras
Пример кода с использованием библиотеки Keras.[9]
# Импорты
import numpy as np
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Dense, Activation, Embedding
from keras.layers import LSTM
from keras.datasets import imdb
# Устанавливаем seed для обеспечения повторяемости результатов
np.random.seed(42)
# Указываем количество слов из частотного словаря, которое будет использоваться (отсортированы по частоте использования)
max_features = 5000
# Загружаем данные (датасет IMDB содержит 25000 рецензий на фильмы с правильным ответом для обучения и 25000 рецензий на фильмы с правильным ответом для тестирования)
(X_train, y_train), (X_test, y_test) = imdb.load_data(nb_words = max_features)
# Устанавливаем максимальную длину рецензий в словах, чтобы они все были одной длины
maxlen = 80
# Заполняем короткие рецензии пробелами, а длинные обрезаем
X_train = sequence.pad_sequences(X_train, maxlen = maxlen)
X_test = sequence.pad_sequences(X_test, maxlen = maxlen)
# Создаем модель последовательной сети
model = Sequential()
# Добавляем слой для векторного представления слов (5000 слов, каждое представлено вектором из 32 чисел, отключаем входной сигнал с вероятностью 20% для предотвращения переобучения)
model.add(Embedding(max_features, 32, dropout = 0.2))
# Добавляем слой долго-краткосрочной памяти (100 элементов для долговременного хранения информации, отключаем входной сигнал с вероятностью 20%, отключаем рекуррентный сигнал с вероятностью 20%)
model.add(LSTM(100, dropout_W = 0.2, dropout_U = 0.2))
# Добавляем полносвязный слой из 1 элемента для классификации, в качестве функции активации будем использовать сигмоидальную функцию
model.add(Dense(1, activation = 'sigmoid'))
# Компилируем модель нейронной сети
model.compile(loss = 'binary_crossentropy',
optimizer = 'adam',
metrics = ['accuracy'])
# Обучаем нейронную сеть (данные для обучения, ответы к данным для обучения, количество рецензий после анализа которого будут изменены веса, число эпох обучения, тестовые данные, показывать progress bar или нет)
model.fit(X_train, y_train,
batch_size = 64,
nb_epoch = 7,
validation_data = (X_test, y_test),
verbose = 1)
# Проверяем качество обучения на тестовых данных (если есть данные, которые не участвовали в обучении, лучше использовать их, но в нашем случае таковых нет)
scores = model.evaluate(X_test, y_test, batch_size = 64)
print('Точность на тестовых данных: %.2f%%' % (scores[1] * 100))
См. также
- Рекуррентные нейронные сети
- Нейронные сети, перцептрон
- Сверточные нейронные сети
- Рекурсивные нейронные сети
Примечания
- ↑ Sepp Hochreiter, Jurgen Schmidhuber. Long short-term memory (1997). Neural Computation.
- ↑ Сигмоида.
- ↑ Гиперболические функции.
- ↑ Gers, Schmidhuber. Recurrent Nets that Time and Count (2000).
- ↑ Cho. Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation (2014).
- ↑ SeppKaisheng Yao. Depth-Gated Recurrent Neural Networks (2015).
- ↑ Jan Koutnik. A Clockwork RNN (2014).
- ↑ TensorFlow
- ↑ Keras RNN with LSTM layer