【问题标题】:Best way to transform NxD timeseries dataset to (N-T+1)xTxD?将 NxD 时间序列数据集转换为 (N-T+1)xTxD 的最佳方法?
【发布时间】:2021-12-20 03:37:30
【问题描述】:

很遗憾,我想不出一个更好的标题;我承认,我无法更好地解释这一事实可能会妨碍我寻找已经确定的答案。

所以,我有一个包含 N1 行和 D 列的时间序列数据集。 循环神经网络需要 N2xTxD 格式的数据,因此如果序列长度 T 为 2,则新 N2xTxD 数据集ds2[0] 的第一个元素将是原始数据集的前两行ds[0:2, :]。第二个元素ds2[1] 将是ds[1:3, :] 以此类推,直到ds2[N2] = ds[N-2:N, :]

我现在的做法是使用这些函数:

import numpy as np

#Shift Array arr's elements by num positions
def NpShift(arr, num, fill_value = np.nan):
    result = np.empty_like(arr)
    result[:num] = fill_value
    result[num:] = arr[:-num]
    return result


def TemporalTransformation(ds, T):
    tmp = ds
    ds = ds.reshape(-1, 1, ds.shape[1]) #By definition ds is NxD, so Nx1xD is -1x1xshape[1]
    
    for t in range(T):
        ds = np.concatenate((NpShift(tmp, t+1)[:, np.newaxis, :], ds), axis = 1) #Adding the shifted matrices one by one
    ds = ds[T-1:, 1:, :] #The 1st T-1 elements contain the shifted values so they have to be discarded; same goes for the 1st element on axis=1
    
    return ds

您可以使用以下方法对其进行测试以查看结果是否正确:

t = 2
xall = np.array([[1,1,1], [2,2,2], [3,3,3], [4,4,4], [5,5,5]], dtype = float)
print(f"ds shape:\n{xall.shape}")
print(f"ds:\n{xall}\n")
ds2 = TemporalTransformation(xall, t)
print("ds2 shape:\n", ds2.shape)
print(f"ds2:\n{ds2}")

哪个输出:

ds shape:
(5, 3)
ds:
[[1. 1. 1.]
 [2. 2. 2.]
 [3. 3. 3.]
 [4. 4. 4.]
 [5. 5. 5.]]

ds2 shape:
 (4, 2, 3)
ds2:
[[[1. 1. 1.]
  [2. 2. 2.]]

 [[2. 2. 2.]
  [3. 3. 3.]]

 [[3. 3. 3.]
  [4. 4. 4.]]

 [[4. 4. 4.]
  [5. 5. 5.]]]

现在,它完美地工作并完成了我想要的,但是,对于大型数据集(数十万行)上的大量 T(例如 700),完成转换需要大量时间(30分钟左右)。

我可以观察到这段(当前)单线程代码在创建最终 (N-T-1)xTxD 张量(3 维数组)时如何缓慢而稳定地分配越来越多的 RAM。

有没有办法在不分配如此大量内存的情况下更快地完成它? 我的意思是,在其核心中,ds2 的值与 ds1 相同,所以我认为应该存在一种使用指针的方法(我只是想不出如何)。

任何可能的解决方案最好在 windows 和 linux 上都可以使用 最后一个值得注意的事情是,最终,这个 N2xTxD numpy 数组将被分批调用(因此一次迭代将调用前 b 行,然后是下 b 行),这批将成为 PyTorch 张量。

现在,我熟悉了 torch.utils.data.Dataset,并且我尝试通过继承它来扩展它以制作我自己的迭代器:

import numpy as np
from torch.utils.data import Dataset
class TemporalTransformation_Dataset(Dataset):
    def __init__(self, data, T):
        self.data = data
        self.T = T

    def __getitem__(self, index):
            Xi = self.data[index : index + self.T]
            return Xi

    def __len__(self):
        return self.data.shape[0] - self.T + 1

t = 2
ds = torch.from_numpy(np.array([[1,1,1], [2,2,2], [3,3,3], [4,4,4], [5,5,5]]))
print(f"ds shape:\n{ds.shape}")
print(f"ds:\n{ds}\n")
ds2 = TemporalTransformation_Dataset(ds, t)
ds2_loader = torch.utils.data.DataLoader(dataset = ds2, batch_size = len(ds2), shuffle = False)
print("W/o Y:\n", next(iter(ds2_loader)))

但是,与我的 numpy 实现相比,它的训练速度要慢得多。我们正在谈论两倍左右的时间,因此这并不有趣。 话虽如此,与我的 numpy 的解决方案相当快的 pytorch 解决方案也是我可以使用的 - 我只是不知道如何使它更快.. 似乎这是一个 pytorch 问题。

【问题讨论】:

    标签: python arrays numpy machine-learning


    【解决方案1】:

    “[...] 在其核心中,ds2 的值与 ds1 相同,所以我认为应该存在一种使用指针的方法” 您的直觉是正确的。 这是使用 NumPy 的 as_strided 函数的一种方法。它创建了数组的一个新的视图,而不复制底层数据:

    from numpy.lib.stride_tricks import as_strided
    
    def transformed_view(ds, T):
        ds = np.asarray(ds)
        if ds.ndim != 2:
            raise ValueError('ds must be a 2-d array.')
        shp = ds.shape
        if T < 1 or T > shp[0]:
            raise ValueError('Must have 1 <= T <= ds.shape[0]')
    
        strides = ds.strides
        return as_strided(ds, shape=(shp[0] - T + 1, T, shp[1]),
                          strides=(strides[0], strides[0], strides[1]))
    

    例如,

    In [49]: xall = np.array([[1,1,1], [2,2,2], [3,3,3], [4,4,4], [5,5,5]], dtype=float)
    
    In [50]: xall
    Out[50]: 
    array([[1., 1., 1.],
           [2., 2., 2.],
           [3., 3., 3.],
           [4., 4., 4.],
           [5., 5., 5.]])
    
    In [51]: transformed_view(xall, 2)
    Out[51]: 
    array([[[1., 1., 1.],
            [2., 2., 2.]],
    
           [[2., 2., 2.],
            [3., 3., 3.]],
    
           [[3., 3., 3.],
            [4., 4., 4.]],
    
           [[4., 4., 4.],
            [5., 5., 5.]]])
    

    【讨论】:

    • 这就像一个魅力。我根本不熟悉“strides”和“as_strided”,查看了文档,结果发现 strides 给了你每个维度占用的字节数,这样你就可以操纵 RAM。 “as_strided”更难让我理解 - 玩了一下,但我还没有完全理解它。例如,在我的原始(未在此处共享)代码中,我可以有一个负 T,但我无法使用它来重现它。不过我很高兴,因为我目前需要的是正 T。
    猜你喜欢
    • 2010-10-04
    • 2017-12-15
    • 2013-01-03
    • 2011-06-25
    • 2012-03-10
    • 1970-01-01
    • 2018-07-15
    • 2018-04-21
    • 2011-01-24
    相关资源
    最近更新 更多