На самом деле, я бы сделал это за пределами вашей модели, прежде чем конвертировать ваши входные данные в LongTensor
.
Это будет выглядеть так:
import random
def add_unk(input_token_id, p):
#random.random() gives you a value between 0 and 1
#to avoid switching your padding to 0 we add 'input_token_id > 1'
if random.random() < p and input_token_id > 1:
return 0
else:
return input_token_id
#than you have your input token_id
#for this example I take just a random number, lets say 127
input_token_id = 127
#let p be your probability for UNK
p = 0.01
your_input_tensor = torch.LongTensor([add_unk(input_token_id, p)])
Edit:
Так что мне на ум приходят два варианта, которые на самом деле дружественны к GPU. В целом оба решения должны быть намного эффективнее.
Вариант 1 - Выполнение вычислений непосредственно в forward()
:
Если вы не используете torch.utils
и не планируете использовать его позже, возможно, это и есть путь.
Вместо того, чтобы делать вычисления, прежде чем мы просто сделаем это в методе forward()
основного класса PyTorch. Однако я не вижу (простого) способа сделать это в torch 0.3.1.
, поэтому вам нужно будет обновить его до версии 0.4.0
:
Итак, представьте, x
- ваш входной вектор:
>>> x = torch.tensor(range(10))
>>> x
tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
probs
- это вектор, содержащий равномерные вероятности отсева, поэтому мы можем позже проверить нашу вероятность отсева:
>>> probs = torch.empty(10).uniform_(0, 1)
>>> probs
tensor([ 0.9793, 0.1742, 0.0904, 0.8735, 0.4774, 0.2329, 0.0074,
0.5398, 0.4681, 0.5314])
Теперь мы применяем вероятности отсева probs
на нашем входе x
:
>>> torch.where(probs > 0.2, x, torch.zeros(10, dtype=torch.int64))
tensor([ 0, 0, 0, 3, 4, 5, 0, 7, 8, 9])
Примечание: чтобы увидеть какой-то эффект, я выбрал здесь вероятность выпадения 0,2. На самом деле вы, вероятно, хотите, чтобы он был меньше.
Вы можете выбрать для этого любой токен / идентификатор, который вам нравится, вот пример с 42
в качестве неизвестного идентификатора токена:
>>> unk_token = 42
>>> torch.where(probs > 0.2, x, torch.empty(10, dtype=torch.int64).fill_(unk_token))
tensor([ 0, 42, 42, 3, 4, 5, 42, 7, 8, 9])
torch.where
поставляется с PyTorch 0.4.0
:
https://pytorch.org/docs/master/torch.html#torch.where
Я не знаю о формах вашей сети, но ваш forward()
должен выглядеть примерно так (при использовании мини-пакетирования вам нужно сгладить ввод перед применением исключения):
def forward_train(self, x, l):
# probabilities
probs = torch.empty(x.size(0)).uniform_(0, 1)
# applying word dropout
x = torch.where(probs > 0.02, x, torch.zeros(x.size(0), dtype=torch.int64))
# continue like before ...
x = self.pad(x)
embedded_x = self.embedding(x)
embedded_x = embedded_x.view(-1, 1, x.size()[1] * self.params["word_dim"]) # [batch_size, 1, seq_len * word_dim]
features = [F.relu(conv(embedded_x)) for conv in self.convs]
pooled = [F.max_pool1d(feat, feat.size()[2]).view(-1, params["feature_num"]) for feat in features]
pooled = torch.cat(pooled, 1)
pooled = self.dropout(pooled)
logit = self.fc(pooled)
return logit
Примечание: я назвал функцию forward_train()
, поэтому вы должны использовать другой forward()
без выпадения для оценки / прогнозирования. Но вы также можете использовать if conditions
с train()
.
Вариант два: использование torch.utils.data.Dataset
:
Если вы используете Dataset
, предоставленный torch.utils
, очень легко эффективно выполнить такую предварительную обработку. Dataset
по умолчанию использует сильное ускорение мультиобработки, поэтому приведенный выше пример кода должен быть выполнен в методе __getitem__
вашего класса Dataset
.
Это может выглядеть так:
def __getitem__(self, index):
'Generates one sample of data'
# Select sample
ID = self.input_tokens[index]
# Load data and get label
# using add ink_unk function from code above
X = torch.LongTensor(add_unk(ID, p=0.01))
y = self.targets[index]
return X, y
Это немного вне контекста и выглядит не очень элегантно, но я думаю, вы поняли идею. Согласно этому сообщению в блоге Шервайн Амиди в Стэнфорде не должно быть проблем с выполнением более сложных этапов предварительной обработки в этой функции:
Поскольку наш код [Dataset
предназначен] предназначен для многоядерности, обратите внимание, что вы
вместо этого можно выполнять более сложные операции (например, вычисления из источника
файлы), не беспокоясь о том, что генерация данных становится узким местом в
учебный процесс.
Связанное сообщение в блоге - "Подробный пример того, как генерировать ваши данные параллельно с PyTorch" - также предоставляет хорошее руководство по реализации генерации данных с Dataset
и DataLoader
.
Полагаю, вы предпочтете первый вариант - только две строки, и он должен быть очень эффективным. :)
Удачи!