相关文章推荐
胡子拉碴的便当  ·  寻味客家 | ...·  3 月前    · 
逼格高的铅笔  ·  老兵永远跟党走 | ...·  7 月前    · 

torch.sparse 是一个专门处理稀疏张量的模块。通常,张量会按一定的顺序连续地进行存取。但是,对于一个存在很多空值的稀疏张量来说,顺序存储的效率显得较为低下。因此,pytorch推出了稀疏张量的处理模块。在这里,有意义的值被称为 specified elements ,而无意义的值(空值,通常为0,但是也可以是其他值)则被称为 fill value 。只有足够稀疏的张量使用这种方式进行存储才能获得更高的效率。稀疏张量的表示方式有多种,比如COO、CSR/CSC、LIL等,下面介绍COO和CSR两种格式。

1 Sparse COO Tensor

1.1 Coordinate format

这种格式的稀疏矩阵是通过 indices values 两个张量共同存储的,其中indices张量存储的是 specified elements 的坐标值,维度为 (ndim, nse) ,类型为 torch.int64 values 张量存储的是 specified elements 的值,维度为 (nse,) 。其中, ndim 是张量的维度, nse specified elements 的个数。
下面举个例子演示如何使用 torch.sparse_coo_tensor() 函数来构造一个COO稀疏矩阵,以及通过 to_dense() 方法将其变为一般的张量。其中3位于(0, 2)位置,4位于(1, 0)位置,5位于(1, 2)位置。

>>> i = [[0, 1, 1],
         [2, 0, 2]]
>>> v =  [3, 4, 5]
>>> s = torch.sparse_coo_tensor(i, v, (2, 3))
tensor(indices=tensor([[0, 1, 1],
                       [2, 0, 2]]),
       values=tensor([3, 4, 5]),
       size=(2, 3), nnz=3, layout=torch.sparse_coo)    # nnz指specified elements个数
>>> s.to_dense()
tensor([[0, 0, 3],
        [4, 0, 5]])

  上述例子中,i的第一行表示第一维坐标值,第二行表示第二维坐标值。i也可以使用坐标对来表示,但是在构造稀疏矩阵时,需要将i进行转置,如下:

>>> i = [[0, 2], [1, 0], [1, 2]]
>>> v =  [3,      4,      5    ]
>>> s = torch.sparse_coo_tensor(list(zip(*i)), v, (2, 3))
>>> # Or another equivalent formulation to get s
>>> s = torch.sparse_coo_tensor(torch.tensor(i).t(), v, (2, 3))
>>> torch.sparse_coo_tensor(i.t(), v, torch.Size([2,3])).to_dense()
tensor([[0, 0, 3],
        [4, 0, 5]])

  一个空的稀疏COO张量可以通过如下方式进行构造:

>>> torch.sparse_coo_tensor(size=(2, 3))
tensor(indices=tensor([], size=(2, 0)),
       values=tensor([], size=(0,)),
       size=(2, 3), nnz=0, layout=torch.sparse_coo)

1.2 Hybrid sparse COO tensors

  上面的例子中,每个坐标下的值都是一个标量(零维数据)。但其实也可以是一个多维的数据,这一拓展的数据被称为Hybrid sparse COO tensors。在这里,indices张量的维度为(sparse_dims, nse)values张量的维度为(nse, dense_dims)。记sparse_dims的大小为M,dense_dims的大小为K,则这个hybrid张量的维度为N=M+K。
  举个例子,下面构造一个sparse_dims为2、dense_dims为1的三维hybrid张量。其中[3, 4]位于(0, 2),[5, 6]位于(1, 0),[7, 8]位于(1, 2)。

>>> i = [[0, 1, 1],
         [2, 0, 2]]
>>> v =  [[3, 4], [5, 6], [7, 8]]
>>> s = torch.sparse_coo_tensor(i, v, (2, 3, 2))
tensor(indices=tensor([[0, 1, 1],
                       [2, 0, 2]]),
       values=tensor([[3, 4],
                      [5, 6],
                      [7, 8]]),
       size=(2, 3, 2), nnz=3, layout=torch.sparse_coo)
>>> s.to_dense()
tensor([[[0, 0],
         [0, 0],
         [3, 4]],
        [[5, 6],
         [0, 0],
         [7, 8]]])

  记M = s.sparse_dim()K = s.dense_dim(),有如下关系成立:

M + K == len(s.shape) == s.ndim
s.indices().shape == (M, nse)
s.values().shape == (nse,) + s.shape[M : M + K]
s.values().layout == torch.strided

1.3 Uncoalesced sparse COO tensors

  COO稀疏张量又可以分为两种:uncoalesced和coalesced。其中uncoalesced允许同一个索引下存在多个不同的值,而coalesced则不行。它们之间可以相互转换,例子如下:

>>> i = [[1, 1]]
>>> v =  [3, 4]
>>> s=torch.sparse_coo_tensor(i, v, (3,))
tensor(indices=tensor([[1, 1]]),
       values=tensor(  [3, 4]),
       size=(3,), nnz=2, layout=torch.sparse_coo)
>>> s.is_coalesced()
False
>>> s.coalesce()
tensor(indices=tensor([[1]]),
       values=tensor([7]),
       size=(3,), nnz=1, layout=torch.sparse_coo)
>>> s.is_coalesced()

  上述例子说的是,在索引1处存在3和4两个值,而将其coalesce后,则相同索引的值会被相加合并,它们可以通过is_coalesced()方法进行判断。这两种形式并不存在优劣之分,通常也不必太在意它们是哪种方式。在有些情况下uncoalesced效率高,而在有些情况下coalesced效率高。但是,如果你在重复进行一些会产生重复值的操作(比如torch.Tensor.add())时,需要时常将稀疏张量进行coalesce,不然结果会变得非常大。
  

1.4 Working with sparse COO tensors

  下面是COO稀疏张量运算的示例。首先,构建一个如下稀疏张量:

>>> i = [[0, 1, 1],
         [2, 0, 2]]
>>> v =  [[3, 4], [5, 6], [7, 8]]
>>> s = torch.sparse_coo_tensor(i, v, (2, 3, 2))

  对张量类型进行判断:

>>> isinstance(s, torch.Tensor)
>>> s.is_sparse
>>> s.layout == torch.sparse_coo

  获得其sparse_dimdense_dim()的值:

>>> s.sparse_dim(), s.dense_dim()
(2, 1)

  获得indicesvalues张量,coalesced和uncoalesced的方式不一样:

# coalesced
>>> s.indices()
tensor([[0, 1, 1],
        [2, 0, 2]])
>>> s.values()
tensor([[3, 4],
        [5, 6],
        [7, 8]])
# uncoalesced
>>> s._indices()
tensor([[0, 1, 1],
        [2, 0, 2]])
>>> s._values()
tensor([[3, 4],
        [5, 6],
        [7, 8]])

  也就是说uncoalesced需要前面加一个_,否则会报错。
  切片操作。COO稀疏张量仅支持dense维度上的切片操作,也即其实是将稀疏表示的张量转换成一般表示的张量之后再执行的切片操作的结果。举个例子:

>>> s[1]
tensor(indices=tensor([[0, 2]]),
       values=tensor([[5, 6],
                      [7, 8]]),
       size=(3, 2), nnz=2, layout=torch.sparse_coo)
>>> s[1, 0, 1]
tensor(6)
>>> s[1, 0, 1:]
tensor([6])

  下面分析一下上述代码的输出结果,只要将s转成dense表示的张量,就明白了:

>>> s.to_dense()
tensor([[[0, 0],
         [0, 0],
         [3, 4]],
        [[5, 6],
         [0, 0],
         [7, 8]]])

  其中,s[1]的values输出结果也即to_dense()之后结果的下半部分,当然是不包括空值的。然后indices输出结果0和2则是:(1, 0)对应值[5, 6]的坐标,(1, 2)对应值[7, 8]的坐标。
  

2 Sparse CSR Tensor

  CSR (Compressed Sparse Row)格式只支持二维张量的存取,不支持多维张量。但是,与COO格式相比,它有更高的空间存储效率以及更快的运算速度。它由三个一维张量组成:crow_indicescol_indicesvalues。其中,crow_indices用于存储行索引,它的大小为size[0]+1,其中最后一个数字记录了稀疏矩阵中包含多少个specified elements;col_indices记录了每个specified elements的列索引,其大小为nnz(即specified elements个数);values记录的则是每个specified elements的具体值,大小也为nnz。
  可以通过torch.sparse_csr_tensor()方法来构造一个CSR稀疏张量,需要给出crow_indicescol_indicesvalues三个张量,其中crow_indices最后一个指示specified elements个数的数字可以省略,将被自动计算。一个示例:

>>> crow_indices = torch.tensor([0, 2, 4])
>>> col_indices = torch.tensor([0, 1, 0, 1])
>>> values = torch.tensor([1, 2, 3, 4])
>>> csr = torch.sparse_csr_tensor(crow_indices, col_indices, values, dtype=torch.double)
tensor(crow_indices=tensor([0, 2, 4]),
      col_indices=tensor([0, 1, 0, 1]),
      values=tensor([1., 2., 3., 4.]), size=(2, 2), nnz=4,
      dtype=torch.float64)
>>> csr.to_dense()
tensor([[1., 2.],
        [3., 4.]], dtype=torch.float64)

  此外,也可以通过tensor.to_sparse_csr()方法将一个普通张量或者是COO稀疏张量转换为一个CSR稀疏张量,其中的0值将被视为fill elements,如下所示:

>>> a = torch.tensor([[0, 0, 1, 0], [1, 2, 0, 0], [0, 0, 0, 0]], dtype = torch.float64)
>>> sp = a.to_sparse_csr()
tensor(crow_indices=tensor([0, 1, 3, 3]),
      col_indices=tensor([2, 0, 1]),
      values=tensor([1., 1., 2.]), size=(3, 4), nnz=3, dtype=torch.float64)

  目前pytorch仅支持CSR稀疏张量的tensor.matmul()运算:

>>> vec = torch.randn(4, 1, dtype=torch.float64)
>>> sp.matmul(vec)
tensor([[0.9078],
        [1.3180],
        [0.0000]], dtype=torch.float64)

3 Appendix

3.1 Supported Linear Algebra operations

  下表总结了稀疏矩阵上支持的线性代数运算。这里,T[layout]表示具有给定布局的张量。类似地,M[layout]表示矩阵(2-D PyTorch张量),V[layout]表示向量(1-D PyTorch张量)。此外,f表示标量(float或0-D PyTorch张量),*表示元素乘法,@表示矩阵乘法。下表中,Sparse grad?一列指示了该运算是否支持稀疏张量的反向传播梯度计算。

3.2 Tensor methods and sparse

  下表是与稀疏张量相关的方法:

在这里插入图片描述
  下表是COO稀疏张量所特有的方法:

在这里插入图片描述
  下表是CSR稀疏张量所特有的方法:

在这里插入图片描述
  下述方法支持COO稀疏张量:

3.3 Torch functions specific to sparse Tensors

  下表是只支持稀疏张量的方法:

在这里插入图片描述
  下面是一些支持稀疏张量的方法:

在这里插入图片描述
  
  更详细内容可参考
pytorch官网

  torch.sparse是一个专门处理稀疏张量的模块。通常,张量会按一定的顺序连续地进行存取。但是,对于一个存在很多空值的稀疏张量来说,顺序存储的效率显得较为低下。因此,pytorch推出了稀疏张量的处理模块。在这里,有意义的值被称为specified elements,而无意义的值(空值,通常为0,但是也可以是其他值)则被称为fill value。只有足够稀疏的张量使用这种方式进行存储才能获得更高的效率。  Sparse COO tensors1 Coordinate format  这种格式
1.torch.expand 函数返回张量在某一个维度扩展之后的张量,就是将张量广播到新形状。函数对返回的张量不会分配新内存,即在原始张量上返回只读视图,返回的张量内存是不连续的。类似于numpy中的broadcast_to函数的作用。如果希望张量内存连续,可以调用contiguous函数。 import torch x = torch.tensor([1, 2, 3, 4]) xnew = x.expand(2, 4) print(xnew) tensor([[1, 2, 3, 4],         [1, 2, 3, 4]]) 2.torch.repeat
安装torch-cluster、torch-geometric、torch-scatter、torch-sparsetorch-spline-conv等库
torch 包 nn 中 Conv2d 的用法与 tensorflow 中类似,但不完全一样。 在 torch 中,Conv2d 有几个基本的参数,分别是 in_channels 输入图像的深度 out_channels 输出图像的深度 kernel_size 卷积核大小,正方形卷积只为单个数字 stride 卷积步长,默认为1 padding 卷积是否造成尺寸丢失,1为不丢失 与tensorflow不一样的是,pytorch中的使用更加清晰化,我们可以使用这种方法定义输入与输出图像的深度并同时指定使用的卷积核的大小。 而我们的输入则由经由 Conv2d 定义的参数传入
import torch i = torch.LongTensor([[0, 1, 1],[2, 0, 2]]) #row, col v = torch.FloatTensor([3, 4, 5]) #data torch.sparse.FloatTensor(i, v, torch.Size([2,3])).to_dense() #torch.Size tensor([[0., 0., 3.], [4., 0., 5.]]) 构造方法和 应该是库和库之间依赖的问题!版本要对应起来! pip install torch_geometric==1.4.1 pip install torch_sparse==0.4.4 pip install torch_sca torch.optim.Optimizer(params, defaults) params (iterable) —— Variable 或者 dict的iterable。指定了什么参数应当被优化。 defaults —— (dict):包含了优化选项默认值的字典(一个参数组没有指定的参数选项将会使用默认值)。 load_state_dict(stat... 稀疏张量稀疏矩阵的高维扩展,其中非零元素表示为一组索引和关联值。 Data Generation 可以通过提取非零元素直接生成数据。本文展示了一个简单的2D数组,其中心有5个非零元素。 data = [ [0, 0, 2.1, 0, 0], [0, 1, 1.4, 3, 0], [0, 0, 4.0, 0, 0] def to_sparse_coo(data): # An intuitive way to extract coordinates and features coords, ubuntu系统下执行sudo apt-get update报错无法连接上 127.0.0.1:58591 (127.0.0.1)。- connect (111: 拒绝连接)的踩坑历程及解决方案总结 31271