在pytorch中实现word dropout
Implementing word dropout in pytorch
我想在我的网络中添加单词丢失,这样我就可以有足够的训练示例来训练 "unk" 标记的嵌入。据我所知,这是标准做法。假设unk token的index为0,padding的index为1(方便的话可以切换)
这是一个简单的 CNN 网络,它按照我预期的方式实现单词丢失:
class Classifier(nn.Module):
def __init__(self, params):
super(Classifier, self).__init__()
self.params = params
self.word_dropout = nn.Dropout(params["word_dropout"])
self.pad = torch.nn.ConstantPad1d(max(params["window_sizes"])-1, 1)
self.embedding = nn.Embedding(params["vocab_size"], params["word_dim"], padding_idx=1)
self.convs = nn.ModuleList([nn.Conv1d(1, params["feature_num"], params["word_dim"] * window_size, stride=params["word_dim"], bias=False) for window_size in params["window_sizes"]])
self.dropout = nn.Dropout(params["dropout"])
self.fc = nn.Linear(params["feature_num"] * len(params["window_sizes"]), params["num_classes"])
def forward(self, x, l):
x = self.word_dropout(x)
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
不要介意填充 - pytorch 没有在 CNN 中使用非零填充的简单方法,更不用说可训练的非零填充,所以我手动进行。 Dropout 也不允许我使用非零 dropout,我想将 padding 令牌与 unk 令牌分开。我将它保留在我的示例中,因为它是这个问题存在的原因。
这不起作用,因为 dropout 需要浮点张量以便它可以正确缩放它们,而我的输入是不需要缩放的长张量。
在 pytorch 中有没有一种简单的方法可以做到这一点?我基本上想使用 LongTensor 友好的 dropout(奖励:如果它能让我指定一个不为 0 的 dropout 常量更好,这样我就可以使用零填充)。
实际上,在将您的输入转换为 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)])
编辑:
所以我想到了两个实际上对 GPU 友好的选项。一般来说,这两种解决方案都应该更有效。
选项一 - 直接在中进行计算 forward()
:
如果您不使用 torch.utils
并且没有计划以后使用它,这可能是正确的选择。
我们只是在主 PyTorch class 的 forward()
方法中进行计算,而不是在计算之前进行计算。但是我在 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
是一个包含 dropout 统一概率的向量,因此我们稍后可以再次检查我们的 dropout 概率:
>>> 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])
现在我们在输入 x
:
上应用丢失概率 probs
>>> 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 的丢失概率。事实上,您可能希望它更小。
您可以为此选择您喜欢的任何令牌/ID,这是一个示例,其中 42
作为未知令牌 ID:
>>> 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()
应该看起来像这样(当使用小批量处理时,你需要在应用 dropout 之前展平输入):
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()
没有 dropout 进行评估/预测。但是你也可以使用一些 if conditions
和 train()
.
选项二:使用 torch.utils.data.Dataset
:
如果您正在使用 torch.utils
提供的 Dataset
,则很容易高效地进行这种预处理。 Dataset
默认使用强大的多处理加速,所以上面的代码示例只需要在 Dataset
class 的 __getitem__
方法中执行。
这可能是这样的:
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
这有点断章取意,看起来不太优雅,但我想你明白了。按照这个blog post of Shervine Amidi at Stanford在这个函数中做更复杂的预处理步骤应该没问题:
Since our code [Dataset
is meant] is designed to be multicore-friendly, note that you
can do more complex operations instead (e.g. computations from source
files) without worrying that data generation becomes a bottleneck in
the training process.
链接的博客 post - "A detailed example of how to generate your data in parallel with PyTorch" - 还提供了使用 Dataset
和 DataLoader
实现数据生成的良好指南。
我想您会更喜欢选项一 - 只有两行并且它应该非常有效。 :)
祝你好运!
我想在我的网络中添加单词丢失,这样我就可以有足够的训练示例来训练 "unk" 标记的嵌入。据我所知,这是标准做法。假设unk token的index为0,padding的index为1(方便的话可以切换)
这是一个简单的 CNN 网络,它按照我预期的方式实现单词丢失:
class Classifier(nn.Module):
def __init__(self, params):
super(Classifier, self).__init__()
self.params = params
self.word_dropout = nn.Dropout(params["word_dropout"])
self.pad = torch.nn.ConstantPad1d(max(params["window_sizes"])-1, 1)
self.embedding = nn.Embedding(params["vocab_size"], params["word_dim"], padding_idx=1)
self.convs = nn.ModuleList([nn.Conv1d(1, params["feature_num"], params["word_dim"] * window_size, stride=params["word_dim"], bias=False) for window_size in params["window_sizes"]])
self.dropout = nn.Dropout(params["dropout"])
self.fc = nn.Linear(params["feature_num"] * len(params["window_sizes"]), params["num_classes"])
def forward(self, x, l):
x = self.word_dropout(x)
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
不要介意填充 - pytorch 没有在 CNN 中使用非零填充的简单方法,更不用说可训练的非零填充,所以我手动进行。 Dropout 也不允许我使用非零 dropout,我想将 padding 令牌与 unk 令牌分开。我将它保留在我的示例中,因为它是这个问题存在的原因。
这不起作用,因为 dropout 需要浮点张量以便它可以正确缩放它们,而我的输入是不需要缩放的长张量。
在 pytorch 中有没有一种简单的方法可以做到这一点?我基本上想使用 LongTensor 友好的 dropout(奖励:如果它能让我指定一个不为 0 的 dropout 常量更好,这样我就可以使用零填充)。
实际上,在将您的输入转换为 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)])
编辑:
所以我想到了两个实际上对 GPU 友好的选项。一般来说,这两种解决方案都应该更有效。
选项一 - 直接在中进行计算 forward()
:
如果您不使用 torch.utils
并且没有计划以后使用它,这可能是正确的选择。
我们只是在主 PyTorch class 的 forward()
方法中进行计算,而不是在计算之前进行计算。但是我在 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
是一个包含 dropout 统一概率的向量,因此我们稍后可以再次检查我们的 dropout 概率:
>>> 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])
现在我们在输入 x
:
probs
>>> 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 的丢失概率。事实上,您可能希望它更小。
您可以为此选择您喜欢的任何令牌/ID,这是一个示例,其中 42
作为未知令牌 ID:
>>> 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()
应该看起来像这样(当使用小批量处理时,你需要在应用 dropout 之前展平输入):
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()
没有 dropout 进行评估/预测。但是你也可以使用一些 if conditions
和 train()
.
选项二:使用 torch.utils.data.Dataset
:
如果您正在使用 torch.utils
提供的 Dataset
,则很容易高效地进行这种预处理。 Dataset
默认使用强大的多处理加速,所以上面的代码示例只需要在 Dataset
class 的 __getitem__
方法中执行。
这可能是这样的:
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
这有点断章取意,看起来不太优雅,但我想你明白了。按照这个blog post of Shervine Amidi at Stanford在这个函数中做更复杂的预处理步骤应该没问题:
Since our code [
Dataset
is meant] is designed to be multicore-friendly, note that you can do more complex operations instead (e.g. computations from source files) without worrying that data generation becomes a bottleneck in the training process.
链接的博客 post - "A detailed example of how to generate your data in parallel with PyTorch" - 还提供了使用 Dataset
和 DataLoader
实现数据生成的良好指南。
我想您会更喜欢选项一 - 只有两行并且它应该非常有效。 :)
祝你好运!