RNN от теории к PyTorch

МЕНЮ


Искусственный интеллект
Поиск
Регистрация на сайте
Помощь проекту

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


Позвольте мне показать вам, что такое RNN(Recurrent neural network) , где они используются, как они работают и назад и как использовать их в PyTorch.

RNN от теории к PyTorch

Большинство типов нейронных сетей построены так, чтобы делать прогнозы на выборках, на которых они обучались. Ярким примером является набор данных MNIST. Обычная нейронная сеть, такая как MLP, знает, что есть 10 цифр, и только на их основе она делает прогнозы, даже если изображения сильно отличаются от тех, на которых сеть была обучена.

Теперь представьте, что мы могли бы использовать такую сеть с последовательным анализом, предоставив последовательность из 9 упорядоченных цифр и позволив сети угадать 10-ю. Сеть будет не только знать, как различать 10 цифр, но также будет знать, что в последовательности от 0 до 8 следующая цифра, скорее всего, будет 9.

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

Исследователи Кембриджского университета определяют последовательность либо как «порядок, в котором вещи или события следуют друг за другом», либо, что наиболее важно, «серию связанных вещей или событий». Чтобы приспособить это определение к области глубокого обучения, последовательность - это набор данных, который содержит обучаемый контекст, и удаление некоторых из элементов последовательности может сделать его бесполезным.

Но что содержит последовательность? Какие фрагменты сгруппированных данных могут иметь контекст? И как мы можем извлечь контекст, чтобы использовать возможности нейронных сетей? Прежде чем перейти к самой нейронной сети, позвольте мне показать вам два типа проблем, которые часто решаются с помощью рекуррентных нейронных сетей (RNN). Прогнозирование временных рядов

Прогнозирование временных рядов

RNN от теории к PyTorch

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

Если мы составим последовательность из ежемесячных расходов домохозяйства на энергию на протяжении многих лет, мы увидим, что существует восходящая синусоидальная тенденция с внезапным падением. Контекст синусоидальной части может заключаться в различных энергетических потребностях в течение года от лета до зимы и снова до лета. Рост энергозатрат может быть связан с использованием большего количества электроприборов и устройств или переходом на более мощные, которые могут потребовать больше энергии. Контекст внезапного падения может означать, что один человек стал достаточно взрослым, чтобы покинуть дом, и энергия, необходимая этому человеку, больше не существует.

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

Обработка естественного языка

Mary rides the bycicle, the bycicle is ____.

Мария едете на велосепеде, ведосепед __.

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

Допустим, наша сеть обучена завершать предложения притяжательными местоимениями. Хорошо обученная сеть поймет, что предложение построено от третьего лица единственного числа и что Мария(Mary), скорее всего, женское имя. Таким образом, предсказанное местоимение должно быть «ее» вместо мужского «его» или множественного числа «их».

Теперь, когда мы рассмотрели два примера упорядоченных данных, давайте рассмотрим, как сеть распространяется в прямом и обратном направлении.

Конфигурации RNN

Как мы видели, RNN извлекают информацию из последовательностей для улучшения своей предсказательной способности.

Простая повторяющаяся сетевая диаграмма.
Простая повторяющаяся сетевая диаграмма.

Выше представлена простая диаграмма RNN. Зеленый узел пропускает некоторый вход x ^ t и выводит некоторое значение h ^ t, которое также передается в узел, снова содержащий информацию, собранную из входа. Какой бы шаблон ни был в том, что подается на узел, он изучает его и сохраняет эту информацию для следующего ввода.Верхний индекс t означает шаг по времени.

RNN от теории к PyTorch
Повторяющиеся конфигурации сети.
Повторяющиеся конфигурации сети.

Есть несколько вариантов конфигурации нейронной сети в зависимости от формы ввода или вывода, мы поймем, что происходит внутри узлов позже.

Отношение «многие к одному» - это когда мы подаем несколько входов в разные временные интервалы для получения одного выходного сигнала, который может быть анализом настроений, зафиксированным в различных кадрах сцены фильма.

Один ко многим использует один вход для получения нескольких выходов. Например, мы могли бы закодировать стихотворение, передающее определенную эмоцию, используя конфигурацию «многие-к-одному», и использовать конфигурацию «один-ко-многим», чтобы создать новые строки стихотворения с той же эмоцией.

Функция «многие ко многим» использует несколько входов для получения нескольких выходов, например, используя последовательность значений, например, в использовании энергии, и прогнозирует двенадцать месяцев в будущем вместо одного.

Сложенная конфигурация - это просто сеть с более чем одним скрытым слоем узлов.

RNN шаг вперед

Чтобы понять, что происходит внутри узлов нейронной сети, мы воспользуемся простым набором данных в качестве примера прогнозирования временных рядов. Ниже представлена полная последовательность значений и ее реструктуризация как набор данных для обучения и тестирования.

RNN от теории к PyTorch

Теперь давайте разделим наборы данных на партии.

RNN от теории к PyTorch

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

Идея состоит в том, чтобы предсказать одно значение в будущем. Итак, скажем, мы выбираем первую строку данных: [10 20 30], после обучения нашей сети мы должны получить значение 40. Чтобы протестировать нейронную сеть, можно передать вектор [70 80 90] и ожидать получения значение близко 100, если сеть хорошо обучена.

Мы будем использовать отношение «многие к одному», подавая три временных шага каждой последовательности отдельно. При использовании рекуррентных сетей вход - это не единственное значение, которое попадает в сеть, есть также скрытый массив, который является структурой, которая будет передавать контекст последовательности от узла к узлу. Мы инициализируем его как массив нулей, и он присоединяется ко входу. Его размерность (1 x 2) - это личный выбор, просто чтобы использовать размер, отличный от пошагового ввода 1 x 1.

Повторяющийся прямой проход.
Повторяющийся прямой проход.

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

RNN от теории к PyTorch
RNN от теории к PyTorch

Глобальный входной вектор X ^ t, весовая матрица W и матрица смещения B, а также вычисление скрытых массивов представлены выше. Не хватает только одного шага, чтобы завершить пас вперед. Мы пытаемся спрогнозировать одно значение в будущем, и у нас есть три скрытых массива с информацией о каждом входе в качестве выходов, поэтому нам нужно преобразовать их в одно значение, которое, будем надеяться, будет правильным после многих эпох обучения.

RNN от теории к PyTorch

Объединив и изменив форму массива, мы можем добавить линейный слой для вычисления окончательного результата. Полная сеть имеет следующий вид:

Полная повторяющаяся диаграмма.
Полная повторяющаяся диаграмма.

Вы видите конфигурацию "многие к одному"? Мы вводим три входа из последовательности, их контекст фиксируется матрицами веса и смещения и сохраняется в скрытом массиве, который обновляется новой информацией на каждом временном шаге. Наконец, этот контекст, хранящийся в скрытом массиве, проходит через другой набор весов и смещений, и одно значение вычисляется после того, как все временные шаги последовательности были введены в сеть.

RNN от теории к PyTorch

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

Это было прямое распространение RNN, но мы еще не видели обратного прохода.

RNN проход назад

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

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

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

RNN от теории к PyTorch

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

Небольшое примечание: нулем также могут быть максимальные значения, которые нестабильны, и оптимизация не должна идти туда, или седловая точка, которая сама по себе также не очень стабильное положение. И минимум может быть глобальным (самый низкий минимум функции) или локальным. Это не имеет значения для моего объяснения, но если вы хотите узнать об этом больше, вы можете найти его!

Пример градиентного спуска. Два шара катятся с холма.
Пример градиентного спуска. Два шара катятся с холма.

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

Внимательно прочтите следующий абзац и при необходимости вернитесь к рисунку.

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

Математически мы имеем следующее:

RNN от теории к PyTorch
регулирует пропорцию производной, которую мы используем для обновления весов и смещений.

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

RNN от теории к PyTorch

Следует иметь в виду, что формы четырех первых производных массивов, которые мы ищем, должны быть такими же, как параметры, которые мы будем обновлять. Например, форма массива dL / dW_h должна быть такой же, как и массив весов W_h. Верхний индекс T означает, что матрица транспонирована.

Мы вернулись к параметрам линейного слоя. Поскольку мы преобразовали массивы скрытых состояний в линейный вектор, мы должны преобразовать dL / dH ^ t в исходную форму объединенного массива скрытых состояний. На данный момент это массив 6 x 1, но форма скрытых массивов составляет 3 x 2, когда они вычисляются из повторяющихся слоев.Давайте также объединим все глобальные входные данные вместе (t = 1, 2 и 3), и теперь мы можем продолжить обратный проход.

RNN от теории к PyTorch

Теперь все, что осталось сделать, это применить уравнение градиентного спуска, которое мы видели ранее, чтобы обновить параметры, и модель готова к следующей итерации. Давайте посмотрим, как построить простую RNN с помощью PyTorch.

RNN с PyTorch

Использование PyTorch делает это очень простым, поскольку нам не нужно беспокоиться об обратном проходе. Однако я по-прежнему считаю важным знать, как это работает, даже если мы не используем его напрямую.

Двигаясь дальше, если мы обратимся к документации PyTorch, мы увидим, что у них уже есть объект RNN, готовый к использованию. При его определении есть два основных параметра:

input_size - Количество ожидаемых функций на входе x

hidden_size - Количество функций в скрытом состоянии h

Input_size равен 1, поскольку мы используем один временной шаг каждой последовательности (например, 10 из последовательности 10, 20, 30) за раз, а hidden_size равен 2, поскольку мы получаем скрытое состояние, содержащее два значения.

Определение параметра n_layers равным 2 означало бы, что у нас есть составная RNN с двумя скрытыми слоями.

Также мы собираемся определить для параметра batch_first значение True. Это означает, что размер пакета при вводе и выводе идет первым (не ошибайтесь с вводом и выводом).

Входы: input, h_0

input of shape (seq_len, batch, input_size): тензор, содержащий признаки входной последовательности.

h_0 формы (num_layers * num_directions, batch, hidden_size): тензор, содержащий начальное скрытое состояние для каждого элемента в пакете.

Входными данными RNN должен быть входной массив формы 1 x 3 x 1. Последовательность содержит три временных шага, которые составляют 10, 20 и 30 для первого пакета набора данных. Из каждой партии вход размера один будет подан в сеть три раза, что составляет три временных шага последовательности.

Скрытое состояние h_0 - это наш первый скрытый массив, который мы скармливаем сети вместе с вводом первого временного шага формы 1 x 1 x 2.

Выходы: output, h_n

output of shape (seq_len, batch, num_directions * hidden_size): тензор, содержащий выходные характеристики (h_t) из последнего слоя RNN, для каждого t.

h_n формы (num_layers * num_directions, batch, hidden_size): тензор, содержащий скрытое состояние для t = seq_len.

Выходные данные содержат все скрытые состояния, вычисленные нейронной сетью на каждом временном шаге формы 1 x 3 x 2, а h_n - это скрытое состояние с последнего временного шага. Это полезно сохранить, потому что, если мы решим использовать составную рекуррентную сеть, это будет скрытое состояние, которое будет загружено на первом временном шаге и будет иметь форму 1 x 1 x 2.

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

Конфигурация такого типа здесь рассматриваться не будет. Я оставлю фрагмент кода, в котором я реализую RNN и как его обучать. Я также предоставлю вам возможность использовать его по мере необходимости с нужным вам набором данных. Не забудьте нормализовать данные и создать набор данных и загрузчик данных перед использованием сети.

import torch.nn as nn

class RNN_LSTM_Base(nn.Module):
def training_step(self, batch):
samples, targets = batch
outputs = self(samples.double())
loss = nn.functional.mse_loss(outputs, targets)
return loss

class VanillaRNN(RNN_LSTM_Base):
def __init__(self, in_size, hid_size_rnn, hid_size_lin, out_size, n_layers=1):
super(VanillaRNN, self).__init__()
# Define dimensions for the layers
self.input_size = in_size
self.hidden_size_rnn = hid_size_rnn
self.hidden_size_lin = hid_size_lin
self.output_size = out_size
self.n_layers = n_layers
# Defining the RNN layer
self.rnn = nn.RNN(in_size, hid_size_rnn, n_layers, batch_first=True)
# Defining the linear layer
self.linear = nn.Linear(hid_size_lin, out_size)

def forward(self, x):
# x must be of shape (batch_size, seq_len, input_size)
xb = x.view(x.size(0), x.size(1), self.input_size).double()
# Initialize the hidden layer's array of shape (n_layers*n_dirs, batch_size, hidden_size_rnn)
h0 = torch.zeros(self.n_layers, x.size(0), self.hidden_size_rnn, requires_grad=True).double()
# out is of shape (batch_size, seq_len, num_dirs*hidden_size_rnn)
out, hn = self.rnn(xb, h0)
# out needs to be reshaped into dimensions (batch_size, hidden_size_lin)
out = out.reshape(x.size(0), self.hidden_size_lin)
out = nn.functional.relu(out)
# Finally we get out in the shape (batch_size, output_size)
out = self.linear(out)
return out

def fit(epochs, lr, model, train_loader, test_loader, opt_func=torch.optim.SGD):
optimizer = opt_func(model.parameters(), lr)
for epoch in range(epochs):
# Training phase
model.train()
for batch in train_loader:
loss = model.training_step(batch)
# Calculate gradients from chain rule
loss.backward()
# Apply gradient descent step
optimizer.step()
# Remove gradients for next iteration
optimizer.zero_grad()
return 'Trained for {} epochs'.format(epochs)

Заключительные мысли

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

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

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

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

Наконец, мы просмотрели часть документации PyTorch по RNN и обсудили наиболее важные аспекты построения базовой повторяющейся сети.

 

Источник: zen.yandex.ru

Комментарии: