2

我想以矢量化的方式实现场感知分解模型(FFM)。在 FFM 中,通过以下等式进行预测

$ \sum_{j_1=1}^n \sum_{j_2=j_1+1}^n (\textbf{w}_{j_1, f_2}, \textbf{w}_{j_2, f_1}) x_{j_1} x_{j_2} $

其中 w 是依赖于特征和另一个特征的字段的嵌入。有关详细信息,请参阅FFM(4)中的方程式。

为此,我定义了以下参数:

import torch

W = torch.nn.Parameter(torch.Tensor(n_features, n_fields, n_factors), requires_grad=True)

现在,给定xsize的输入(batch_size, n_features),我希望能够计算前面的等式。这是我当前的(非矢量化)实现:

total_inter = torch.zeros(x.shape[0])
for i in range(n_features):
    for j in range(i + 1, n_features):
        temp1 = torch.mm(
            x[:, i].unsqueeze(1),
            W[i, feature2field[j], :].unsqueeze(0))
        temp2 = torch.mm(
            x[:, j].unsqueeze(1),
            W[j, feature2field[i], :].unsqueeze(0))
        total_inter += torch.sum(temp1 * temp2, dim=1)

不出所料,这个实现非常慢,因为n_features很容易达到 1000!但是请注意,大多数条目x都是 0。感谢所有输入!

编辑:

如果它可以在任何方面有所帮助,以下是 PyTorch 中此模型的一些实现:

不幸的是,我无法弄清楚他们是如何做到的。

附加更新:

我现在可以通过以下方式以更有效的方式获得x产品W

temp = torch.einsum('ij, jkl -> ijkl', x, W)

因此,我的循环现在是:

total_inter = torch.zeros(x.shape[0])
for i in range(n_features):
    for j in range(i + 1, n_features):
        temp1 = temp[:, i, feature2field[j], :]
        temp2 = temp[:, j, feature2field[i], :]
        total_inter += 0.5 * torch.sum(temp1 * temp2, dim=1)

然而,由于该循环进行了大约 500 000 次迭代,因此仍然太长。

4

1 回答 1

2
  1. 使用 pytorch sparse tensors 可能会帮助您加快乘法速度。

  2. 还有一些可能有效的方法如下:创建n 个数组,每个特征 i 一个,每个特征i将在每一行中保存其相应的字段因子。例如对于特征 i = 0

[ W[0, feature2field[0], :],
  W[0, feature2field[1], :],
  W[0, feature2field[n], :]]

然后计算这些数组的乘法,我们称它们为 F,用 X

R[i] = F[i] * X

因此,R 中的每个元素都将保存 F[i] 与 X 相乘的结果,即一个数组。

接下来,您将每个 R[i] 与其转置相乘

R[i] = R[i] * R[i].T

现在您可以像以前一样在循环中进行求和

for i in range(n_features):
    total_inter += torch.sum(R[i], dim=1)

请把这个和一粒盐一起吃,因为我还没有测试过。无论如何,我认为它会为您指明正确的方向。

可能发生的一个问题是转置乘法,其中每个元素也将与其自身相乘,然后添加到总和中。我认为它不会影响分类器,但无论如何您都可以使转置对角线中的元素高于 0(包括对角线)。

此外,尽管很小,但请将第一个 unsqueeze 操作移到嵌套 for 循环之外。

我希望它有所帮助。

于 2019-07-10T14:14:55.640 回答