Поскольку этот вопрос довольно открытый, я начну с последних частей, переходя к более общему ответу на главный вопрос, поставленный в заголовке.
Быстрое примечание: , как указано в комментариях @ Qusai Alothman , вы должны найти лучший ресурс по теме, этот довольно редкий, когда речь идет о необходимой информации.
Дополнительное примечание: полный код для процесса, описанного в последнем разделе, займет слишком много места , чтобы предоставить в качестве точного ответа, это будет больше пост в блоге. Я выделю возможные шаги, которые нужно предпринять, чтобы создать такую сеть с полезными ссылками по мере продвижения.
Последнее замечание: Если внизу есть что-то тупое (или вы хотите расширить ответ каким-либо образом или в любой форме, пожалуйста, исправьте меня / добавьте информацию, разместив комментарий ниже).
Вопрос по вводу
Входные данные здесь генерируются из случайного нормального распределения и не имеют никакой связи с фактическими словами. Он должен представлять вложения слов , например представление слов в виде чисел, несущих семантическое (это важно!) значение (иногда также зависящее от контекста (см. один из современных подходов уровня техники, например, BERT )).
Форма ввода
В вашем примере он представлен как:
seq_len, batch_size, embedding_size
,
, где
seq_len
- означает длину одного предложения (варьируется в зависимости от вашего
набор данных), мы вернемся к этому позже.
batch_size
- сколько предложений
должны быть обработаны за один шаг forward
проход (в случае
PyTorch
это прямой метод наследования класса от
torch.nn.Module )
embedding_size
- вектор, в котором представлено одно слово (it
может варьироваться от обычного 100/300
с использованием word2vec
до 4096
или
поэтому, используя более современные подходы, такие как BERT , упомянутый
выше)
В данном случае все это жестко запрограммировано на размер один, что не очень полезно для новичка, оно лишь обрисовывает идею таким образом.
Зачем в этом случае нужны теги начала и конца предложения?
Поправьте меня, если я ошибаюсь, но вам это не нужно , если ваш ввод разделен на предложения . Он используется, если вы предоставляете несколько предложений для модели и хотите однозначно указать начало и конец каждого (по-видимому, для моделей, которые зависят от предыдущих / следующих предложений, чтобы не было дела здесь). Они закодированы специальными токенами (которых нет во всем корпусе), поэтому нейронная сеть «может узнать», что они представляют конец и начало предложения (одного специального токена для этого подхода будет достаточно).
Если бы вы использовали серьезный набор данных, я бы посоветовал разделить ваш текст с помощью таких библиотек, как spaCy или nltk (первое - удовольствие использовать IMO), они делают действительно хорошая работа для этой задачи.
Ваш набор данных может быть уже разбит на предложения, в таких случаях вы как бы готовы к работе.
Почему я не вижу ввод, на котором обучается модель, как другие классические проблемы НЛП?
Я не помню, чтобы модели обучались на корпусах , как есть , например. используя строки. Обычно они представлены числами с плавающей запятой, используя:
- Простые подходы, например сумка
Слова или
TF-IDF
- Более сложные, которые предоставляют некоторую информацию о слове
отношения (например,
king
более семантически связаны с queen
чем, скажем, banana
). Те были уже связаны выше, некоторые
другие заметные могут быть
перчатка или
ELMo и множество других креативов
подходы.
Вопрос по выводу
Нужно вывести индексов в вложений , которые, в свою очередь, соответствуют словам, представленным вектором (более сложный подход, упомянутый выше).
Каждая строка в такомВнедрение представляет собой уникальное слово, и его соответствующие столбцы являются их уникальными представлениями (в PyTorch первый индекс может быть зарезервирован для слов, для которых представление неизвестно [при использовании предварительно обученных вложений], вы также можете удалить эти слова или представить их как ajсреднее предложение / документ, есть и другие жизнеспособные подходы).
Потеря, представленная в примере
# for language models, use cross-entropy :)
loss = nn.MSELoss()
Для этой задачи нет смысла, так как Mean SquaredОшибка является метрикой регрессии, а не классификационной.
Мы хотим использовать ее для классификации, поэтому softmax следует использовать для случая мультикласса (мы должны выводить числа, охватывающие [0, N]
, где N
- количество уникальных слов в нашем корпусе).
PyTorch's CrossEntropyLoss уже принимает логиты (вывод последнего слоя без активации , как softmax) и возвращает значение потерь для каждого примера.Я бы посоветовал этот подход, так как он численно стабилен (и он мне нравится как самый минимальный).
Я пытаюсь заполнить пробел, используя двунаправленный RNN и pytorch
ЭтоДлинный, я только выделю шаги, которые я предприму, чтобы создать модель, идея которой соответствует описанной в посте.
Базовая подготовка набора данных
Вы можете использовать ту, которую вы упомянуливыше или начните с чего-то более простого, например, 20 групп новостей из scikit-learn .
Первые шаги должны быть примерно такими:
- Удалитеметаданные (если таковые имеются) из вашего набора данных (это могут быть теги HTML, некоторые заголовки и т. д.)
- разбить ваш текст на предложения с использованием предварительно созданной библиотеки (упомянутой выше)
Далее, вы хотели бы создать свою цель (например, слова для заполнения) в каждом предложении.Каждое слово должно быть заменено специальным токеном (скажем, <target-token>
) и перемещено к цели.
Пример:
- Предложение:
Neural networks can do some stuff.
даст нам следующие предложения и соответствующие им цели:
- предложение:
<target-token> networks can do some stuff.
цель: Neural
- предложение:
Neural <target-token> can do some stuff.
цель: networks
- предложение:
Neural networks <target-token> do some stuff.
цель: can
- предложение:
Neural networks can <target-token> some stuff.
цель: do
- предложение:
Neural networks can do <target-token> stuff.
цель: some
- предложение:
Neural networks can do some <target-token>.
цель: some
- предложение:
Neural networks can do some stuff <target-token>
цель: .
Вы должны адаптировать этот подход к проблеме, исправляя опечатки, еслиесть какие-либо, токенизация, лемматизация и другие, эксперимент!
Вложения
Каждое слово в каждом предложении должно быть заменено целым числом, которое, в свою очередь, указывает на его вложение.
Я бы посоветовал вам использовать предварительно обученный.spaCy предоставляет векторы слов, но другой интересный подход, который я очень рекомендую, - это библиотека с открытым исходным кодом flair .
Вы можете тренироваться самостоятельно, но это займет много времени + многоданных для обучения без контроля, и я думаю, что это выходит за рамки этого вопроса.
Пакетирование данных
Нужно использовать PyTorch torch.utils.data.Dataset и torch.utils.data.DataLoader .
В моем случае хорошей идеей было бы предоставить пользовательский collate_fn
- DataLoader
, который отвечает за создание дополненных пакетов данных.(или представлен как torch.nn.utils.rnn.PackedSequence уже).
Важное замечание: в настоящее время необходимо отсортировать партию по длине (по словам) и держите индексы в состоянии «рассортировать» пакет в его первоначальном виде, вы должны помнить это во время реализации.Вы можете использовать torch.sort
для этой задачи.В будущих версиях PyTorch есть шанс, который может и не понадобиться, см. эту проблему .
Да, и не забудьте перетасовать ваш набор данных, используя DataLoader
, пока мы на нем.
Модель
Вы должны создать правильную модель, наследуя от torch.nn.Module
.Я бы посоветовал вам создать более общую модель, в которой вы можете предоставить ячейки PyTorch (например, GRU, LSTM или RNN), многослойные и двунаправленные (как описано в посте).
Что-то в этом роде, когда дело доходит до построения модели:
import torch
class Filler(torch.nn.Module):
def __init__(self, cell, embedding_words_count: int):
self.cell = cell
# We want to output vector of N
self.linear = torch.nn.Linear(self.cell.hidden_size, embedding_words_count)
def forward(self, batch):
# Assuming batch was properly prepared before passing into the network
output, _ = self.cell(batch)
# Batch shape[0] is the length of longest already padded sequence
# Batch shape[1] is the length of batch, e.g. 32
# Here we create a view, which allows us to concatenate bidirectional layers in general manner
output = output.view(
batch.shape[0],
batch.shape[1],
2 if self.cell.bidirectional else 1,
self.cell.hidden_size,
)
# Here outputs of bidirectional RNNs are summed, you may concatenate it
# It makes up for an easier implementation, and is another often used approach
summed_bidirectional_output = output.sum(dim=2)
# Linear layer needs batch first, we have to permute it.
# You may also try with batch_first=True in self.cell and prepare your batch that way
# In such case no need to permute dimensions
linear_input = summed_bidirectional_output.permute(1, 0, 2)
return self.linear(embedding_words_count)
Как вы можете видеть, информацию о формах можно получить в общем виде.Такой подход позволит вам создать модель с тем количеством слоев, которое вы хотите, двунаправленным или нет (аргумент batch_first
проблематичен, но вы можете обойти его и в общем, оставив его для большей ясности), см. Ниже:
model = Filler(
torch.nn.GRU(
# Size of your embeddings, for BERT it could be 4096, for spaCy's word2vec 300
input_size=300,
hidden_size=100,
num_layers=3,
batch_first=False,
dropout=0.4,
bidirectional=True,
),
# How many unique words are there in your dataset
embedding_words_count=10000,
)
Вы можете передать torch.nn.Embedding
в свою модель (если она прошла предварительную подготовку и уже заполнена), создать ее из простой матрицы или множества других подходов, очень сильно зависит от того, как именно вы структурируете свой код .Тем не менее, пожалуйста, сделайте ваш код более общим, не кодируйте жестко фигуры , если это не является абсолютно необходимым (обычно это не так).
Помните, что это всего лишь демонстрация, вам придется настроитьи исправить это по своему усмотрению .Эта реализация возвращает logits, и слой softmax
не используется.Если вы хотите вычислить растерянность, вам, возможно, придется добавить ее, чтобы получить правильное распределение вероятностей по всем возможным векторам.
Кстати: Здесь - это некоторая информация о сцеплении двунаправленного выводаRNN.
Обучение модели
Я бы настоятельно рекомендовал PyTorch ignite , так как он вполне настраиваемый, вы можете регистрировать много информации, используя его, выполнятьвалидация и абстрактные загромождающие части, такие как циклы в обучении.
Да, и разбейте вашу модель, обучение и другие на отдельные модули, не помещайте все в один нечитаемый файл.
Заключительные примечания
Это схема того, как я мог бы подойти к этой проблеме, вам может быть веселее использовать сети внимания, а не просто использовать последний выходной слой, как в этом примере, хотя вы не должныНе начинайте с этого.
И, пожалуйста, проверьте документацию PyTorch 1.0 и не следуйте слепо учебным пособиям или сообщениям в блогах, которые вы видите в Интернете, поскольку они могут быть действительно устаревшими, очень быстрыми и качество кодасильно варьируется.Например, torch.autograd.Variable равно устарело , как видно по ссылке.