【问题标题】:Why are embeddings necessary in Pytorch?为什么在 Pytorch 中需要嵌入?
【发布时间】:2021-02-27 22:44:06
【问题描述】:

我(认为)我了解嵌入背后的基本原理:它们是快速执行操作 (one hot encoded vector) * matrix 的快捷方式,而 实际上 不执行该操作(利用此操作等效的事实索引到矩阵中),同时仍然保持与实际执行该操作相同的梯度。

我知道下面的例子:

e = Embedding(3, 2)
n = e(torch.LongTensor([0, 2]))

n 将是一个形状为 2, 2 的张量。

但是,我们也可以这样做:

p = nn.Parameter(torch.zeros([3, 2]).normal_(0, 0.01))
p[tensor([0, 2])]

并在没有嵌入的情况下获得相同的结果。

这本身不会令人困惑,因为在第一个示例中,n 有一个名为 EmbeddingBackwardgrad_fn,而在第二个示例中,p 有一个名为 IndexBackwardgrad_fn,即我们所期望的,因为我们知道嵌入模拟了不同的导数。

令人困惑的部分是他们使用嵌入来计算电影推荐的 fastbook 的第 8 章。但是,他们在没有嵌入的情况下以基本相同的方式做到这一点,并且模型仍然有效。我希望没有嵌入的版本会失败,因为导数不正确。

嵌入版本:

class DotProductBias(Module):
    def __init__(self, n_users, n_movies, n_factors, y_range=(0,5.5)):
        self.user_factors = Embedding(n_users, n_factors)
        self.user_bias = Embedding(n_users, 1)
        self.movie_factors = Embedding(n_movies, n_factors)
        self.movie_bias = Embedding(n_movies, 1)
        self.y_range = y_range
        
    def forward(self, x):
        users = self.user_factors(x[:,0])
        movies = self.movie_factors(x[:,1])
        res = (users * movies).sum(dim=1, keepdim=True)
        res += self.user_bias(x[:,0]) + self.movie_bias(x[:,1])
        return sigmoid_range(res, *self.y_range)

没有的版本:

def create_params(size):
    return nn.Parameter(torch.zeros(*size).normal_(0, 0.01))

class DotProductBias(Module):
    def __init__(self, n_users, n_movies, n_factors, y_range=(0,5.5)):
        self.user_factors = create_params([n_users, n_factors])
        self.user_bias = create_params([n_users])
        self.movie_factors = create_params([n_movies, n_factors])
        self.movie_bias = create_params([n_movies])
        self.y_range = y_range
        
    def forward(self, x):
        users = self.user_factors[x[:,0]]
        movies = self.movie_factors[x[:,1]]
        res = (users*movies).sum(dim=1)
        res += self.user_bias[x[:,0]] + self.movie_bias[x[:,1]]
        return sigmoid_range(res, *self.y_range)

有人知道怎么回事吗?

【问题讨论】:

  • nn.Parameter 用于为自定义操作创建权重。 nn.Embedding 在后台创建一个查找表(如字典),并为您词汇表中曾经通过嵌入层的每个标记存储一个自定义的随机嵌入。
  • Embedding 包装了一些额外的功能,例如标准化、为使用静态向量填充的索引保留索引、稀疏索引和梯度缩放。原生索引运算符也有点混乱,更容易出错。

标签: python pytorch


【解决方案1】:

除了cmets中提供的要点:

我(认为)我理解嵌入背后的基本原理:它们是 快速执行操作的快捷方式(一个热编码向量)* 没有实际执行该操作的矩阵

这个只是部分正确。索引到矩阵是完全可微的操作,它只是获取部分数据并仅在这条路径上传播梯度。 此处的乘法既浪费又不必要

并在没有嵌入的情况下获得相同的结果。

这个是真的

称为 EmbeddingBackward,而在第二个示例中 p 有一个称为 IndexBackward 的 grad_fn,这是我们所期望的因为我们知道嵌入模拟了不同的导数。(强调我的)

这个不是(或经常不是)。嵌入也选择部分数据,就像你做的那样,grad_fn 由于“额外功能”而不同,但原则上它是相同的(从矩阵中选择一些向量),就像 @987654322 一样@ 类固醇。

令人困惑的部分是他们使用嵌入的 fastbook 的第 8 章 计算电影推荐。但是,他们没有这样做 以基本相同的方式嵌入并且模型仍然有效。 我 会期望没有嵌入的版本会失败,因为 导数是不正确的。(强调我的)

在这种确切的情况下,两种方法是等效的(从随机初始化中给出或获取不同的结果)。

为什么要nn.Embedding

  • 阅读代码时更容易理解您的意图
  • 根据需要添加常用实用程序和功能(如 cmets 中所指出的那样)

【讨论】:

    猜你喜欢
    • 2020-09-03
    • 2021-11-15
    • 2019-04-02
    • 2021-10-02
    • 2020-04-03
    • 2017-12-15
    • 1970-01-01
    • 2018-05-31
    • 1970-01-01
    相关资源
    最近更新 更多