Tensor数据转换为稀疏矩阵
一、稀疏矩阵
原文链接
常用的稀疏矩阵存储格式有COO,CSR/CSC,LIL
1.COO
COO(Coordinate format )是最为简单的格式,以三元组的形式存储稀疏矩阵。记录矩阵中非零元素的数值和所在的行序号和列序号。形式为(行号,列号,数值)。这种存储方式的主要优点是灵活、简单。但是缺点是不可以直接进行矩阵的相关运算

2.CSR/CSC
CSR(Compressed Sparse Row)格式实现了用于存储二维张量的 CSR 格式。尽管不支持 N 维张量,但与 COO 格式相比的主要优势是更好地利用存储和更快的计算操作。目前尚不存在 CUDA 支持

3.LIL
LIL (List-of-List) 每行存储一个列表,每个条目包含列索引和值。通常,这些条目按列索引进行排序,以便更快地查找
4.稀疏矩阵的处理
Pytorch中,处理稀疏矩阵的有效工具torch.sparse。Torch 支持 COO(rdinate) 格式的稀疏张量,可以有效地存储和处理大多数元素为零的张量
二、Tensor数据转换为稀疏矩阵
1.torch.spares_coo_tenso
1
| torch.spares_coo_tensor(indices, values, siez=None,*, dtype=None, requires_grad=False)->Tensor
|
参数:
indices: 一个2D张量,其中每一列都代表一个非零元素的坐标。
values: 一个1D张量,其中每个值都是与indices对应坐标中的非零元素对应的值。
size: (可选) 一个表示稀疏张量大小的元组
假设一个2D的tensor数据
非零元素的坐标(indices)和对应的值(values)为:
1 2 3 4 5
| indices = [[0, 3], [1, 2], [2, 1]]
values = [5, 3, 2]
|
可以使用torch.sparse_coo_tensor来创建这个稀疏张量:
1 2 3 4 5 6 7 8
| import torch
indices = torch.tensor([[0, 1, 2], [3, 2, 1]]) values = torch.tensor([5, 3, 2]) size = (3, 4)
sparse_tensor = torch.sparse_coo_tensor(indices, values, size)
|
2.将一个2D的Tensor数据变为COO稀疏张量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import torch
# 示例张量 tensor = torch.tensor([[0, 2, 1], [0, 0, 3], [4, 0, 0]])
# 寻找非零元素的索引 non_zero_indices = torch.nonzero(tensor).t() print(non_zero_indices[0]) # tensor([0, 0, 1, 2]) print(non_zero_indices[1]) # tensor([1, 2, 2, 0])
print(tensor.dim()) # 2
# 获取非零元素的值 values = tensor[tuple(non_zero_indices[i] for i in range(tensor.dim()))]
print(values) # tensor([2, 1, 3, 4])
print(tensor.size()) # torch.Size([3, 3])
# 创建稀疏张量 sparse_tensor = torch.sparse_coo_tensor(non_zero_indices, values, tensor.size())
print(sparse_tensor)
|
最后得到的稀疏矩阵
1 2 3 4
| tensor(indices=tensor([[0, 0, 1, 2], [1, 2, 2, 0]]), values=tensor([2, 1, 3, 4]), size=(3, 3), nnz=4, layout=torch.sparse_coo)
|
3.将一个3Dtensor数据转换为COO稀疏张量
仍然可以使用torch.sparse_coo_tensor函数将其转换为稀疏表示。与2D张量相似,你需要确定非零元素的位置和它们的值。对于3D张量,每个非零元素的坐标将由三个值表示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import torch
# 示例3D张量 tensor = torch.tensor([ [[0, 2, 0], [0, 0, 3], [4, 0, 0]], [[0, 0, 0], [0, 5, 0], [0, 0, 6]] ])
print(tensor.dim()) # 3
# 寻找非零元素的索引 non_zero_indices = torch.nonzero(tensor).t() print(non_zero_indices[0]) # tensor([0, 0, 0, 1, 1]) print(non_zero_indices[1]) # tensor([0, 1, 2, 1, 2]) print(non_zero_indices[2]) # tensor([1, 2, 0, 1, 2])
# 获取非零元素的值 values = tensor[tuple(non_zero_indices[i] for i in range(tensor.dim()))]
# 创建稀疏张量 sparse_tensor = torch.sparse_coo_tensor(non_zero_indices, values, tensor.size())
print(sparse_tensor)
|
4.将一个未知维度的张量数据转换为COO稀疏张量,并且存储到硬盘
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| """ 将tensor 数据转换为COO 稀疏张量函数 """ def tensor_to_sparse(dense_tensor): size = dense_tensor.size() # 寻找非零元素的索引 non_zero_indices = torch.nonzero(dense_tensor).t() # 获取非零元素的值 values = dense_tensor[tuple(non_zero_indices[i] for i in range(dense_tensor.dim()))] # 创建稀疏张量 sparse_tensor = torch.sparse_coo_tensor(non_zero_indices, values, size)
return sparse_tensor,size # 随机产生一个4D张量数据 dense_tensor = torch.randn((2,3,3,3)) print(dense_tensor) print(dense_tensor.dim()) # 4
sparse_tensor,size = tensor_to_sparse(dense_tensor) print(sparse_tensor) print(size) # torch.Size([2, 3, 3, 3]) # 保存稀疏张量到硬盘 torch.save(sparse_tensor, "spare_tensor.npz")
|
5.读取硬盘上存储的COO稀疏张量,并且转换为原来的tensor数据(dense_tensor)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| # 从硬盘上加载稀疏张量 loaded_sparse_tensor = torch.load("spare_tensor.npz") """ COO 稀疏张量转换为密集张量 """ def sparse_to_tensor(loaded_sparse_tensor): # 将稀疏张量复原为原始的密集张量 dense_tensor = loaded_sparse_tensor.to_dense() return dense_tensor # 调用函数 sparse_to_tensor(loaded_sparse_tensor)
|
5.使用scipy包完成上述操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| import scipy.sparse import torch import scipy.sparse
def tensor_to_sparse(dense_tensor): # 将dense_tensor转化为2D shape = dense_tensor.shape tensor_2d = dense_tensor.view(-1, shape[-1])
# 将2D tensor转化为numpy array array_2d = tensor_2d.numpy()
# 从numpy array创建sparse matrix sparse_matrix = scipy.sparse.coo_matrix(array_2d)
return sparse_matrix,shape
def sparse_to_tensor(sparse_matrix, original_shape): # 从稀疏矩阵转换为2D array array_2d = sparse_matrix.toarray()
# 将2D array转换为original_shape_array original_shape_array = array_2d.reshape(original_shape)
# 将3D array转换为3D tensor dense_tensor = torch.from_numpy(original_shape_array)
return dense_tensor
# 随机产生一个dense_tensor dense_tensor = torch.randn((2,3,3,3))
# 转化为sparse matrix sparse_matrix,original_shape = tensor_to_sparse(dense_tensor) print(original_shape)
# 将sparse matrix保存到硬盘上 scipy.sparse.save_npz('sparse_matrix.npz', sparse_matrix)
# 使用scipy.sparse.load_npz从硬盘加载保存的稀疏张量 loaded_sparse_matrix = scipy.sparse.load_npz('sparse_matrix.npz')
# 稀疏张量复原为原来的tensor数据 restored_tensor = sparse_to_tensor(loaded_sparse_matrix, original_shape)
print(restored_tensor)
# 判断restored_tensor与原来的tensor数据是否一致 print(dense_tensor==restored_tensor)
|