Давайте рассмотрим решение шаг за шагом
Обрамление проблемы
Учитывая вашу формулировку проблемы, вам придется использовать LSTM для классификации, а не обычное использование тегов. LSTM развернут для определенного временного шага, и это является причиной того, что входные и выходные значения периодических моделей равны
- Вход:
batch size X time steps X input size
- Выход:
batch size X time steps X hidden size
Теперь, так как вы хотите использовать его для классификации, у вас есть два варианта:
- Поместите плотный слой поверх результатов всех временных шагов / развертываний [Мой пример ниже использует это]
- Игнорировать все выходные данные временного шага, кроме последнего, и поместить последний слой поверх последнего временного шага
Таким образом, входными данными для нашей модели LSTM являются имена, поданные как один символ на временной шаг LSTM и вывод будет классом, соответствующим его языку.
Как обрабатывать ввод / имена переменной длины
Здесь у нас снова два варианта.
- Пакетные имена одинаковой длины вместе. Это называется группированием
- Фиксировать максимальную длину на основе среднего размера имен, которые у вас есть. Дополните меньшие имена и отрежьте более длинные имена [В моем примере ниже используется максимальная длина 10]
Нужен ли нам слой Embedding?
Нет. Встраиваемые слои обычно используются для изучения хороших векторных представлений слов. Но в случае символьной модели ввод является символом, а не словом, поэтому добавление встраиваемых слоев не помогает. Символ может быть непосредственно закодирован в число, и встраивание слоев делает очень мало в захвате отношений между различными символами. Вы все еще можете использовать слой для встраивания, но я твердо верю, что это не поможет.
Код модели LSTM игрушечного персонажа
import numpy as np
import torch
import torch.nn as nn
# Model architecture
class Recurrent_Model(nn.Module):
def __init__(self, output_size, time_steps=10):
super(Recurrent_Model, self).__init__()
self.time_steps = time_steps
self.lstm = nn.LSTM(1,32, bidirectional=True, num_layers=2)
self.linear = nn.Linear(32*2*time_steps, output_size)
def forward(self, x):
lstm_out, _ = self.lstm(x)
return self.linear(lstm_out.view(-1,32*2*self.time_steps))
# Sample input and output
names = ['apple', 'dog', 'donkey', "elephant", "hippopotamus"]
lang = [0,1,2,1,0]
def pad_sequence(name, max_len=10):
x = np.zeros((len(name), max_len))
for i, name in enumerate(names):
for j, c in enumerate(name):
if j >= max_len:
break
x[i,j] = ord(c)
return torch.FloatTensor(x)
x = pad_sequence(names)
x = torch.unsqueeze(x, dim=2)
y = torch.LongTensor(lang)
model = Recurrent_Model(3)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), 0.01)
for epoch in range(500):
model.train()
output = model(x)
loss = criterion(output, y)
print (f"Train Loss: {loss.item()}")
optimizer.zero_grad()
loss.backward()
optimizer.step()
Примечание
- Все тензоры загружаются в память, поэтому, если у вас огромный набор данных, вам придется использовать набор данных и загрузчик данных, чтобы избежать ошибки OOM.
- Вам нужно будет разбить данные на тесты по поездам и проверить их на наборе тестовых данных (стандартный материал построения модели)
- Вам придется нормализовать входные тензоры перед передачей его модели (опять же стандартная модель построения)
Наконец
так как убедиться, что в вашей архитектуре модели нет ошибок или она обучается. Как говорит Андрей Карпатий, наденьте модель на небольшой набор данных, и если она переоснащается, то у нас все хорошо.