0 шт.
Выбрать страницу

PyTorch — рекуррентные нейронные сети


Рекуррентные нейронные сети

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

В этом туториале мы будем работать с аудио данными, используя LSTM.

Этот ноутбук и датасеты доступны в моем Github репозитории:

git clone https://github.com/andreiliphd/reinforcement-content.git

Если нет Git, то его нужно установить.

Linux:

sudo apt-get update
sudo apt-get install git

Windows: скачайте Git с сайта git-scm.com.

Если вы нашли ошибку на сайте, ее можно исправить самостоятельно сделав Pull Request в Git.

Объяснение принципа работы слоя torch.nn.LSTM

lstm_formula.png

$i_t$ - входной затвор контролирует сколько данных выходит из $g_t$ в формуле $c_t$.

$f_t$ - затвор забывания контролирует сколько данных от скрытого состояния идет в следующую по времени клетку $t+1$ в формуле $c_t$.

$g_t$ - затвор клетки выводит обработанные данные.

$o_t$ - выходной затвор - наш output то, что мы дальнешем используем в нейронной сети.

$c_t$ - состояние клетки определяет сколько данных надо забыть с использованием затвора забывания и затвора клетки.

$h_t$ - скрытое состояние

$W$ и $b$ - это torch.nn.Parameter другими словами параметры, которые оптимизируются, что позволяет всем затворам работать. На самом деле их четыре два $W$ и два $b$.

Вход:

$x_t$ - входные данные в LSTM

Выходы:

$o_t$ - выходной затвор, output

$c_t$ - состояние клетки, которое податся обратно в следующую клетку во времени $t+1$, h_0

$h_t$ - скрытое состояние, которое податся обратно в следующую клетку во времени $t+1$, c_0

Визуализация

principles_of_an_lstm_formula.png

Видео

Видео, которое объясняет суть формулы и принцип рекуррентных сетей.

In [1]:
from IPython.display import IFrame

IFrame('https://www.youtube.com/embed/RSs9auEznRw', width="560", height="315")
Out[1]:

Подготовительный этап

Нам нужно установить зависимости прежде чем начать работу. Выполните команды:

conda install -c conda-forge sox

и

conda install -c pytorch torchaudio

Этими командами мы установим torchaudio, которая является библиотекой от Facebook для работы со звуком.

Этапы решения задачи

  1. Загрузка и аугментация данных.
  2. Декларирование модели.
  3. Инстанциирование модели.
  4. Инстанциирование функции потерь(лосс).
  5. Инстанциирование оптимизатора.
  6. Создание тренировочной петли(лупа).

Загрузка и аугментация данных

Какие проблемы перед нами стоят:

  1. В torchaudio нет готового класса для загрузки аудио.
  2. Аудио нужно конвертировать в MFCC для того, чтобы извлечь черты.
  3. Нужно изменить форму данных в нашем загрузчике, чтобы отвечать формату [batch, seq_len, input_size].
  4. Чтобы использовать такой формат при создании LSTM нужно указать batch_first=True.
In [153]:
import glob
import torch
import torchaudio

class VoiceDataset(torch.utils.data.Dataset):
    
    def __init__(self, root):
        super(VoiceDataset, self).__init__()
        self.root = root
        self.files = glob.glob(root + '*/*') 
        classes, class_to_idx = self._find_classes(self.root)
        self.classes = classes
        self.class_to_idx = class_to_idx
        
    def __getitem__(self, index):
        waveform, sample_rate = torchaudio.load(self.files[index])
        sample = torchaudio.transforms.MFCC(sample_rate=sample_rate)(waveform)
        sample = sample.reshape(80,442)
        target = self.class_to_idx[self.files[index].split('/')[2]]
        
        return sample, target
    
    def __len__(self):
        return len(self.files)
    
    # the code below is taken from PyTorch source code DatasetFolder class by @andreiliphd
    def _find_classes(self, dir):
        if sys.version_info >= (3, 5):
            classes = [d.name for d in os.scandir(dir) if d.is_dir()]
        else:
            classes = [d for d in os.listdir(dir) if os.path.isdir(os.path.join(dir, d))]
        classes.sort()
        class_to_idx = {classes[i]: i for i in range(len(classes))}
        return classes, class_to_idx

Для того, чтобы создать свой датасет, которым потом можно будет пользоваться в загрузчике нужно сделать класс VoiceDataset наследовать от torch.utils.data.Dataset, а также необходимо переопределить методы __len__ и __getitem__. Метод __len__ выдает количество данных(количество наших аудио файлов) и метод __getitem__ по индексу загружает данных с диска. Есть встроенные датасеты для изображений в PyTorch, но в нашем случае пришлось идти длинной дорогой, так как датасет для аудио я не нашел. Такая же логика для любых данных, которые нативно не поддерживает PyTorch.

In [154]:
sound = torch.utils.data.DataLoader(VoiceDataset('datasets/sound/'),batch_size=2, shuffle=True)

Передаем класс VoiceDataset в даталоадер.

Декларирование модели

In [162]:
import torch.nn as nn
import torch.nn.functional as F
class RNN(nn.Module):
    def __init__(self):
        super(RNN, self).__init__()
        self.lstm1 = nn.LSTM(input_size=442, hidden_size=256,num_layers=3, batch_first=True)
        self.fc1 = nn.Linear(in_features=20480, out_features=128)
        self.fc2 = nn.Linear(in_features=128, out_features=64)
        self.fc3 = nn.Linear(in_features=64, out_features=32)
        self.fc4 = nn.Linear(in_features=32, out_features=3)

    def forward(self, x):
        x = torch.tanh(self.lstm1(x)[0])
        x = x.view(x.shape[0],-1)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = F.relu(self.fc3(x))
        x = self.fc4(x)
        return x

Обратите внимание на self.lstm1(x)[0], мы не берем скрытое состояние LSTM, так как нам наши данные независимы от друг друга. Каждый батч по отдельности. И каждый батч это один, два или три.

Инстанциирование модели

In [163]:
model = RNN()
In [164]:
if torch.cuda.is_available():
    model = model.cuda()

И перемещение ее на видеокарту для увеличения скорости обучения.

Инстанциирование функции потерь(лосс)

In [165]:
criterion = torch.nn.CrossEntropyLoss()

Стандартный лосс для задач классификации.

Инстанциирование оптимизатора

In [160]:
optimizer = torch.optim.Adam(model.parameters(), lr=0.0003)

Создание тренировочной петли(лупа)

In [161]:
for epoch in range(10):
    model.train()
    for data, label in sound:
        if torch.cuda.is_available():
            data, label = data.cuda(), label.cuda()
        output = model(data)
        loss = criterion(output, label)        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    model.eval()
    print('Epoch: ', epoch, 'Loss: ', loss.item())
    total_correct = 0
    total = 0
    for data, target in sound:
        if torch.cuda.is_available():
            data, target = data.cuda(), target.cuda()
        output = model(data)
        loss = criterion(output, target)
        max_arg_output = torch.argmax(output, dim=1)
        total_correct += int(torch.sum(max_arg_output == target))
        total += data.shape[0]
    print('Training accuracy: {:.0%}'.format(total_correct/total))
Epoch:  0 Loss:  1.065126657485962
Training accuracy: 33%
Epoch:  1 Loss:  1.1673775911331177
Training accuracy: 40%
Epoch:  2 Loss:  1.0520007610321045
Training accuracy: 67%
Epoch:  3 Loss:  0.9513782262802124
Training accuracy: 67%
Epoch:  4 Loss:  0.2251937985420227
Training accuracy: 93%
Epoch:  5 Loss:  0.4559391736984253
Training accuracy: 100%
Epoch:  6 Loss:  0.16064739227294922
Training accuracy: 97%
Epoch:  7 Loss:  0.031351327896118164
Training accuracy: 100%
Epoch:  8 Loss:  0.0012655258178710938
Training accuracy: 100%
Epoch:  9 Loss:  0.00048160552978515625
Training accuracy: 100%

Обучение

Рекуррентные сети обладают достаточно большой формулой, но принцип ее работы простой. Также просто делается рекуррентная модель.

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

In [ ]: