首页 > 解决方案 > 有哪些方法可以加快 Pytorch 中大型稀疏数组(~100 万 x 100 万,密度~0.0001)的数据加载速度?

问题描述

我正在研究二进制分类问题。我有大约 150 万个数据点,特征空间的维数是 100 万。该数据集存储为稀疏数组,密度约为 0.0001。对于这篇文章,我将限制范围假设模型是浅层前馈神经网络,并假设维度已经优化(因此不能减少到 100 万以下)。从这些数据中创建小批量以馈送到网络的天真方法将花费大量时间(例如,从输入数组TensorDataset的表示创建(地图样式)并将其包裹起来的基本方法, 意味着大约 20 秒可以将 32 个小批量输入网络,而不是说大约 0.1 秒来执行实际训练)。我正在寻找加快速度的方法。torch.sparse.FloatTensorDataLoader

我试过的

  1. 我首先想到在每次迭代中从如此大的稀疏数组中读取DataLoader是计算密集型的,因此我将这个稀疏数组分解为更小的稀疏数组
  2. 为了DataLoader以迭代方式从这些多个稀疏数组中读取数据,我用 替换了我在其中的地图样式数据集DataLoaderIterableDataset并将这些较小的稀疏数组流式传输到这个 IterableDataset 中,如下所示:
from itertools import chain
from scipy import sparse

class SparseIterDataset(torch.utils.data.IterableDataset):
    
    def __init__(self, fpaths):
        super(SparseIter).__init__()
        self.fpaths = fpaths
    
    def read_from_file(self, fpath):
        data = sparse.load_npz(fpath).toarray()
        for d in data:
            yield torch.Tensor(d)
            
    def get_stream(self, fpaths):
        return chain.from_iterable(map(self.read_from_file, fpaths))
    
    def __iter__(self):
        return self.get_stream(self.fpaths)

使用这种方法,我能够将时间从大约 20 秒的幼稚基本情况减少到每 32 个小批量的大约 0.2 秒。但是,鉴于我的数据集有大约 150 万个样本,这仍然意味着在甚至通过数据集。(作为比较,即使它有点像橘子,但在原始稀疏数组上对 scikit-learn 运行逻辑回归,每次迭代整个数据集大约需要 6 秒。使用 pytorch,使用我刚刚概述的方法,它需要~3000s 只是为了在一个 epoch 中加载所有小批量)

我知道但尚未尝试的一件事是通过num_workersDataLoader. 我相信这在可迭代样式数据集的情况下有它自己的问题。另外,即使是 10 倍的加速,仍然意味着每个 epoch 加载小批量约 300 秒。我觉得我的速度太慢了!您是否可以建议任何其他方法/改进/最佳实践?

标签: pytorchbigdatasparse-matrix

解决方案


您的非稀疏形式的数据集将是 1.5M x 1M x 1 字节 = 1.5TB 作为 uint8,或 1.5M x 1M x 4 字节 = 6TB 作为 float32。在现代 CPU 上,简单地将 6TB 从内存读取到 CPU 可能需要 5-10 分钟(取决于架构),并且从 CPU 到 GPU 的传输速度会比这慢一些(PCIe 上的 NVIDIA V100 理论上有 32GB/s)。

方法:

  1. 单独对所有内容进行基准测试 - 例如在 jupyter

    %%timeit 数据 = sparse.load_npz(fpath).toarray()

    %%timeit dense = data.toarray() # un-sparsify 进行比较

    %%timeit t = torch.tensor(data) # 大概和上面那行差不多

还要打印出所有内容的形状和数据类型,以确保它们符合预期。我没有尝试运行你的代码,但我很确定(a)sparse.load_npz 非常快,不太可能成为瓶颈,但是(b)torch.tensor(data) 产生密集张量并且在这里也很慢

  1. 使用torch.sparse。我认为在大多数情况下,torch 稀疏张量可以用作常规张量。你必须做一些数据准备才能从 scipy.sparse 转换为 torch.sparse:

稀疏张量表示为一对密集张量:值的张量和索引的 2D 张量。可以通过 提供这两个张量以及稀疏张量的大小来构造一个稀疏张量

你提到torch.sparse.FloatTensor但我很确定你没有在你的代码中制作稀疏张量 - 没有理由期望这些会简单地通过将 scipy.sparse 数组传递给常规张量构造函数来构造,因为它们不是这样的通常制作。

如果你找到了一个好的方法,我建议你将它作为项目或 git 发布到 github 上,这将非常有用。

  1. 如果 torch.sparse 不起作用,请考虑其他方法将数据转换为仅在 GPU 上密集,或避免完全转换。

另见: https ://towardsdatascience.com/sparse-matrices-in-pytorch-be8ecaccae6 https://github.com/rusty1s/pytorch_sparse


推荐阅读