在pytorch中实现单词丢失

Chu*_*ows 5 pytorch dropout

我想将单词丢失添加到我的网络中,以便我可以有足够的训练示例来训练“unk”标记的嵌入。据我所知,这是标准做法。假设 unk token 的索引为 0,padding 的索引为 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
Run Code Online (Sandbox Code Playgroud)

不要介意填充 - pytorch 没有一种在 CNN 中使用非零填充的简单方法,更不用说可训练的非零填充了,所以我手动进行。Dropout 也不允许我使用非零 dropout,并且我想将 padding 令牌与 unk 令牌分开。我将其保留在我的示例中,因为这是这个问题存在的原因。

这不起作用,因为 dropout 需要浮点张量,以便可以正确缩放它们,而我的输入是不需要缩放的长张量。

在 pytorch 中有一个简单的方法可以做到这一点吗?我本质上想使用 LongTensor 友好的 dropout(奖励:如果它能让我指定一个不为 0 的 dropout 常量,这样我就可以使用零填充,那就更好了)。

blu*_*nox 3

实际上,在将您的输入转换为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)])
Run Code Online (Sandbox Code Playgroud)

编辑:

所以我想到了两个实际上对 GPU 友好的选项。一般来说,这两种解决方案都应该更加有效。

选项一 - 直接在中进行计算 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])
Run Code Online (Sandbox Code Playgroud)

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])
Run Code Online (Sandbox Code Playgroud)

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])
Run Code Online (Sandbox Code Playgroud)

注意:为了查看一些效果,我在这里选择了 0.2 的 dropout 概率。我现实中你可能希望它更小。

您可以为此选择任何您喜欢的令牌/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])
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

注意:我命名了该函数forward_train(),因此您应该使用另一个forward()没有 dropout 的函数来进行评估/预测。但您也可以将一些if conditionstrain().

选项二:使用 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
Run Code Online (Sandbox Code Playgroud)

这有点断章取义,看起来不太优雅,但我想你已经明白了。根据斯坦福大学 Shervine Amidi 的这篇博客文章,在此函数中执行更复杂的预处理步骤应该没有问题:

由于我们的代码[Dataset意味着]被设计为多核友好,请注意,您可以执行更复杂的操作(例如,从源文件进行计算),而不必担心数据生成成为训练过程中的瓶颈。

链接的博客文章 - “如何与 PyTorch 并行生成数据的详细示例”Dataset - 还提供了使用和实现数据生成的良好指南DataLoader

我猜你会更喜欢选项一——只有两行,而且应该非常有效。:)

祝你好运!