MRi*_*cci 1 python numpy scipy stride pytorch
我正在编写临时的 PyTorch 版本scipy.linalg.toeplitz,目前具有以下形式:
def toeplitz_torch(c, r=None):
c = torch.tensor(c).ravel()
if r is None:
r = torch.conj(c)
else:
r = torch.tensor(r).ravel()
# Flip c left to right.
idx = [i for i in range(c.size(0)-1, -1, -1)]
idx = torch.LongTensor(idx)
c = c.index_select(0, idx)
vals = torch.cat((c, r[1:]))
out_shp = len(c), len(r)
n = vals.stride(0)
return torch.as_strided(vals[len(c)-1:], size=out_shp, stride=(-n, n)).copy()
Run Code Online (Sandbox Code Playgroud)
但torch.as_strided目前不支持负步幅。因此,我的函数抛出错误:
RuntimeError :
as_strided: 目前不支持负步幅,得到步幅:[-1, 1]。
我(可能不正确)的理解as_strided是,它将第一个参数的值插入到一个新数组中,该数组的大小由第二个参数指定,并且它是通过在原始数组中线性索引这些值并将它们放置在下标索引的步幅中来实现的由最后一个参数给出。
NumPy 和 PyTorch 文档都有关于as_strided“极度小心”使用该函数的可怕警告,我不完全理解这个函数,所以我想问:
as_strided正确吗?c(或r)吗toeplitz_torch?as_strided?步幅是张量访问底层连续数据缓冲区的接口。它不会插入值,也不会复制值torch.as_strided,步幅定义了我们所说的多维数组(在 NumPy 中)或张量(在 PyTorch 中)的人工布局。
正如Andreas K.在另一个答案中所说:
\n\n\n步幅是为了沿着数组的每个方向/维度从一个项目到达下一个项目而在内存中跳过的字节数。换句话说,它是每个维度的连续项之间的字节分隔。
\n
如果您在跨步时遇到问题,请随时阅读那里的答案。在这里,我们将以您的示例为例,看看它是如何使用as_strided.
Scipy for 给出的例子如下:linalg.toeplitz
>>> toeplitz([1,2,3], [1,4,5,6])\narray([[1, 4, 5, 6],\n [2, 1, 4, 5],\n [3, 2, 1, 4]])\nRun Code Online (Sandbox Code Playgroud)\n为此,他们首先构建值列表(我们可以将其称为基础值,而不是实际的基础数据):vals其构建为[3 2 1 4 5 6],即展平的 Toeplitz 列和行。
现在注意传递给的参数np.lib.stride_tricks.as_strided:
values:vals[len(c)-1:]注意切片:张量显示较小,但底层值仍然存在,并且它们对应于 的值vals。继续将两者进行比较storage_offset:它只是 的偏移量2,值仍然存在!它的工作原理是,它本质上改变了索引,以便index=0引用 value 1、index=1to4等......
shape:由列/行输入给出,此处(3, 4)。这是生成的对象的形状。
strides:这是最重要的部分:(-n, n),在本例中(-1, 1)
使用步幅最直观的事情是描述多维空间:(i, j) \xe2\x88\x88 [0,3[ x [0,4[和扁平化一维空间:之间的映射k \xe2\x88\x88 [0, 3*4[。由于步幅等于(-n, n) = (-1, 1),因此映射为-n*i + n*j = -1*i + 1*j = j-i。从数学上讲,您可以将矩阵描述为M[i, j] = F[j-i]其中F是 展平值向量[3 2 1 4 5 6]。
例如,让我们尝试使用i=1和j=2。如果你看看上面的托普利茨矩阵M[1, 2] = 4。的确F[k] = F[j-i] = F[1] = 4
如果你仔细观察,你会发现负步幅背后的技巧:它们允许你“引用”负索引:例如,如果你采用j=0和i=2,那么你会看到k=-2。记住如何通过切片vals给出偏移量。如果你看看它自己的底层数据存储,它仍然是,但有一个偏移量。(在本例中)的映射是由于偏移造成的。这意味着。2vals[len(c)-1:][3 2 1 4 5 6]valsi: 1D -> k: 1DM\'[i] = F\'[k] = F\'[i+2]M\'[-2] = F\'[0] = 3
在上面我定义M\'为vals[len(c)-1:]基本上相当于以下张量:
>>> torch.as_strided(vals, size=(len(vals)-2,), stride=(1,), storage_offset=2)\ntensor([1, 4, 5, 6])\nRun Code Online (Sandbox Code Playgroud)\n同样,我将其定义F\'为基础值的展平向量:[3 2 1 4 5 6]。
使用步幅确实是定义托普利茨矩阵的一种非常聪明的方法!
\n问题是,PyTorch 中没有实现负步长......我不相信有办法解决它torch.as_strided,否则扩展当前的实现并为该功能提供支持将相当容易。
然而,还有其他方法可以解决该问题。在 PyTorch 中构建 Toeplitz 矩阵是完全可能的,但在torch.as_strided.
我们将自己进行映射:对于M索引为 的每个元素(i, j),我们将找到相应的索引k,即j-i。这可以轻松完成,首先(i, j)从以下位置收集所有对M:
>>> i, j = torch.ones(3, 4).nonzero().T\n(tensor([0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2]),\n tensor([0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]))\nRun Code Online (Sandbox Code Playgroud)\n现在我们基本上有k:
>>> j-i\ntensor([ 0, 1, 2, 3, -1, 0, 1, 2, -2, -1, 0, 1])\nRun Code Online (Sandbox Code Playgroud)\n我们只需要从行r和列c输入中构造一个包含所有可能值的展平张量。负索引值( 的内容c)放在最后并翻转:
>>> values = torch.cat((r, c[1:].flip(0)))\ntensor([1, 4, 5, 6, 3, 2])\nRun Code Online (Sandbox Code Playgroud)\n最后索引values并k重塑:
>>> values[j-i].reshape(3, 4)\ntensor([[1, 4, 5, 6],\n [2, 1, 4, 5],\n [3, 2, 1, 4]])\nRun Code Online (Sandbox Code Playgroud)\n总而言之,我建议的实现是:
\ndef toeplitz(c, r):\n vals = torch.cat((r, c[1:].flip(0)))\n shape = len(c), len(r)\n i, j = torch.ones(*shape).nonzero().T\n return vals[j-i].reshape(*shape)\nRun Code Online (Sandbox Code Playgroud)\nc(或r)吗toeplitz_torch?这是一个有趣的问题,因为torch.as_strided没有实现后向函数。这意味着您将无法反向传播到c和r!然而,上述方法使用“向后兼容”内置函数,向后传递是免费的。
请注意grad_fn输出:
>>> toeplitz(torch.tensor([1.,2.,3.], requires_grad=True), \n torch.tensor([1.,4.,5.,6.], requires_grad=True))\ntensor([[1., 4., 5., 6.],\n [2., 1., 4., 5.],\n [3., 2., 1., 4.]], grad_fn=<ViewBackward>)\nRun Code Online (Sandbox Code Playgroud)\n这是一个快速草稿(确实花了一点时间写下来),我将进行一些编辑。
如果您有任何疑问或意见,请随时发表评论!
我有兴趣看到其他答案,因为我不是进步的专家,这只是我对问题的看法。
| 归档时间: |
|
| 查看次数: |
1263 次 |
| 最近记录: |