1 回答
TA贡献1744条经验 获得超4个赞
在交换轴时使用permute
而不是使用view
,请参阅答案的结尾以获得有关差异的直觉。
关于 RegressorNet(神经网络模型)
如果您使用
from_pretrained
. 正如文档所述,它不使用梯度更新。这部分:
self.w2v_rnode = nn.GRU(embeddings.size(1), hidden_dim, bidirectional=True, dropout=drop_prob)
尤其是
dropout
没有可提供性num_layers
是完全没有意义的(因为不能用浅层网络指定 dropout)。BUG 和主要问题:在您的
forward
函数中,您使用的view
不是permute
,这里是:w2v_out, _ = self.w2v_rnode(embeds.view(-1, batch_size, embeds.size(2)))
请参阅此答案和每个函数的相应文档,并尝试改用此行:
w2v_out, _ = self.w2v_rnode(embeds.permute(1, 0, 2))
您可以考虑
batch_first=True
在w2v_rnode
创建期间使用参数,您不必以这种方式排列索引。检查torch.nn.GRU 的文档,您是在序列的最后一步之后,而不是在您拥有的所有序列之后,因此您应该在:
_, last_hidden = self.w2v_rnode(embeds.permute(1, 0, 2))
但我认为这部分很好,否则。
数据准备
没有进攻,但是prepare_lines
是非常不可读,似乎非常难以维持为好,不说察觉最终的bug(我想这是就出在这里)。
首先,您似乎正在手动填充。请不要那样做,使用torch.nn.pad_sequence处理批处理!
本质上,首先您将每个句子中的每个单词编码为指向嵌入的索引(就像您在 中所做的那样prepare_w2v
),然后您使用torch.nn.pad_sequence
andtorch.nn.pack_padded_sequence
或者 torch.nn.pack_sequence
如果行已经按长度排序。
适当的配料
这部分非常重要,似乎您根本没有这样做(这可能是您实现中的第二个错误)。
PyTorch 的 RNN 单元不是将输入作为填充张量,而是作为torch.nn.PackedSequence对象。这是一个有效的对象存储索引,它指定每个序列的未填充长度。
在此处、此处以及网络上的许多其他博客文章中查看有关该主题的更多信息。
批处理中的第一个序列必须是最长的,所有其他序列必须以降序长度提供。接下来是:
您必须每次按序列长度对批次进行排序,并以类似的方式对目标进行排序或
对您的批次进行排序,将其推送到网络,然后将其取消排序以匹配您的目标。
两者都可以,这是您的电话,对您来说似乎更直观。我喜欢做的或多或少如下,希望它有所帮助:
为每个单词创建唯一的索引并适当地映射每个句子(您已经完成了)。
torch.utils.data.Dataset
为每个geitem创建返回单个句子的常规对象,它作为由特征 (torch.Tensor
) 和标签(单个值)组成的元组返回,似乎您也在这样做。创建自定义
collate_fn
以与torch.utils.data.DataLoader一起使用,它负责在此场景中对每个批次进行排序和填充(+ 它返回要传递到神经网络的每个句子的长度)。使用分类和填充特性和它们的长度我用
torch.nn.pack_sequence
里面的神经网络的forward
方法(做嵌入了!)通过RNN层来推动它。根据用例,我使用torch.nn.pad_packed_sequence解压缩它们。在您的情况下,您只关心最后一个隐藏状态,因此您不必那样做。如果您使用了所有隐藏输出(例如注意力网络的情况),您将添加这部分。
说到第三点,这里是 的示例实现collate_fn
,您应该明白了:
import torch
def length_sort(features):
# Get length of each sentence in batch
sentences_lengths = torch.tensor(list(map(len, features)))
# Get indices which sort the sentences based on descending length
_, sorter = sentences_lengths.sort(descending=True)
# Pad batch as you have the lengths and sorter saved already
padded_features = torch.nn.utils.rnn.pad_sequence(features, batch_first=True)
return padded_features, sentences_lengths, sorter
def pad_collate_fn(batch):
# DataLoader return batch like that unluckily, check it on your own
features, labels = (
[element[0] for element in batch],
[element[1] for element in batch],
)
padded_features, sentences_lengths, sorter = length_sort(features)
# Sort by length features and labels accordingly
sorted_padded_features, sorted_labels = (
padded_features[sorter],
torch.tensor(labels)[sorter],
)
return sorted_padded_features, sorted_labels, sentences_lengths
像collate_fn
in一样使用它们DataLoaders
,您应该差不多就好了(可能需要稍作调整,因此您必须了解其背后的想法)。
其他可能的问题和提示
训练循环:很多小错误的好地方,您可能希望通过使用PyTorch Ignite来最小化这些错误。我在经历你的 Tensorflow-like-Estimator-like-API-like 训练循环(例如
self.model = self.w2v_vocab = self.criterion = self.optimizer = self.scheduler = None
这个)时遇到了令人难以置信的困难。请不要这样做,将每个任务(数据创建、数据加载、数据准备、模型设置、训练循环、日志记录)分离到各自的模块中。总而言之,PyTorch/Keras 比 Tensorflow 更具可读性和完整性是有原因的。使嵌入的第一行等于向量包含零:默认情况下,torch.nn.functional.embedding期望第一行用于填充。因此,您应该从 1 开始为每个单词建立唯一索引,或者
padding_idx
为不同的值指定一个参数(尽管我强烈反对这种方法,充其量会造成混淆)。
我希望这个答案至少对您有所帮助,如果有什么不清楚的,请在下面发表评论,我会尝试从不同的角度/更详细地解释它。
一些最后的评论
此代码不可重现,也不是问题的具体内容。我们没有您使用的数据,我们也没有您的词向量,随机种子不固定等。
附注。最后一件事:检查您在数据的非常小的子集(比如 96 个示例)上的性能,如果它不收敛,则很可能您的代码中确实存在错误。
关于时间:它们可能已关闭(我想是因为没有排序和没有填充),通常 Keras 和 PyTorch 的时间非常相似(如果我按预期理解您的问题的这一部分),可以正确有效地实现。
置换 vs 视图 vs 重塑解释
这个简单的例子显示了permute()
和之间的区别view()
。第一个交换轴,而第二个不改变内存布局,只是将数组分块成所需的形状(如果可能)。
import torch
a = torch.tensor([[1, 2], [3, 4], [5, 6]])
print(a)
print(a.permute(1, 0))
print(a.view(2, 3))
输出将是:
tensor([[1, 2],
[3, 4],
[5, 6]])
tensor([[1, 3, 5],
[2, 4, 6]])
tensor([[1, 2, 3],
[4, 5, 6]])
reshape
几乎就像view
, 是为那些来自 的人添加的numpy
,所以对他们来说更容易、更自然,但它有一个重要的区别:
view
从不复制数据并且仅在连续内存上工作(因此在像上面那样排列之后,您的数据可能不连续,因此访问它可能会更慢)reshape
如果需要,可以复制数据,因此它也适用于非连续数组。
添加回答
举报