有哪些方法可以加快Pytorch中大型稀疏阵列(约100万x 100万,密度约0.0001)的数据加载速度?

有哪些方法可以加快Pytorch中大型稀疏阵列(约100万x 100万,密度约0.0001)的数据加载速度?,pytorch,bigdata,sparse-matrix,Pytorch,Bigdata,Sparse Matrix,我正在研究一个二进制分类问题。我有150万个数据点,特征空间的维数是100万。此数据集存储为稀疏数组,密度为~0.0001。在这篇文章中,我将限制范围,假设该模型是一个浅层前馈神经网络,并且还假设维度已经过优化(因此不能降低到100万以下)。从这些数据中创建小批量以输入网络的任何方法都需要花费大量时间(例如,创建TensorDataset(地图样式)的基本方法)从输入数组的torch.sparse.FloatTensor表示,并在其周围包装一个DataLoader,意味着要20秒才能将一小批32

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

我试过的
  • 我首先想到,在
    DataLoader
    的每次迭代中,从如此大的稀疏数组中读取数据是计算密集型的,所以我将这个稀疏数组分解为更小的稀疏数组
  • 为了让
    DataLoader
    以迭代方式从这些多个稀疏数组中读取数据,我将
    DataLoader
    中的映射样式数据集替换为
    IterableDataset
    ,并将这些较小的稀疏数组流化到这个IterableDataset中,如下所示:
  • 通过这种方法,我能够将NAIVE基本情况下的时间从~20秒减少到~0.2秒/小批量32。然而,考虑到我的数据集有150万个样本,这仍然意味着要花费大量的时间,即使只通过一个数据集。(作为比较,尽管这有点像苹果和橙子,但在原始稀疏数组上的scikit learn上运行逻辑回归在整个数据集上每次迭代大约需要6秒。使用Pytork,使用我刚才概述的方法,一个历元中加载所有小批量需要大约3000秒)


    我知道但还需要尝试的一件事是通过在
    DataLoader
    中设置
    num\u workers
    参数来使用多进程数据加载。不过,我相信这在iterable风格的数据集中也有自己的特点。此外,即使是10倍的加速,也意味着在装载小批量时,每一个历元大约需要300秒。我觉得我的速度太慢了!您还有其他方法/改进/最佳做法吗?

    您的非稀疏形式的数据集在uint8中为1.5M x 1M x 1字节=1.5TB,在浮动32中为1.5M x 1M x 4字节=6TB。在现代CPU上,仅从内存到CPU读取6TB数据可能需要5-10分钟(取决于体系结构),而从CPU到GPU的传输速度将略慢于此(PCIe上的NVIDIA V100的理论传输速度为32GB/s)

    方法:

  • 对每件事单独进行基准测试-例如在jupyter中

    %%timeit data=sparse.load\u npz(fpath.toarray())

    %%timeit density=data.toarray()#不稀疏进行比较

    %%timeit t=torch.tensor(数据)#可能与上面的线大致相同

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

  • 使用。我认为火炬稀疏张量在大多数情况下可以用作正则张量。要从scipy.sparse转换为torch.sparse,您必须做一些数据准备:
  • 稀疏张量表示为一对稠密张量,即 值和索引的二维张量。稀疏张量可以由 提供这两个张量以及稀疏张量的大小

    您提到了
    torch.sparse.FloatTensor
    ,但我很确定您的代码中没有生成稀疏张量-没有理由期望这些张量仅通过将scipy.sparse数组传递给常规张量构造函数来构造,因为它们通常不是这样生成的

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

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

    感谢您抽出时间回复!对于第2点,我没有将scipy.sparse数组传递到张量构造函数中,而是传递它的值和索引(这是推荐的方式)——为了简洁起见,我省略了这一点。你发布的链接看起来很有趣。在接下来的几天里,我将继续探讨这个问题。@AbhimanyuSahai-最重要的部分是如何构造
    torch.sparse.FloatTensor
    ,请添加该代码。您显示的代码示例将稀疏转换为稠密,这听起来不像是您想要的。坦白说,这并不是太多的数据,而是150米的数值?最大的瓶颈将是
    toarray()
    ,它将不得不分配大量的内存来容纳所有这些零。你为什么不直接把coo指数推到一个
    火炬上。稀疏的coo张量
    ?我看不出有任何理由要加密这个数组。
    
    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)