将NxD时间序列数据集转换为(N-T+1)xTxD的最佳方法



不幸的是,我想不出更好的标题了;我承认,我无法更好地解释这一事实,这可能阻碍了我寻找一个已经确定的答案的能力。

因此,我有一个时间序列数据集,其中有N1行和D列。递归神经网络需要N2xTxD格式的数据,因此如果序列长度T为2,则新的N2xTxD数据集ds2[0]的第一个元素将是原始数据集的前2行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张量(三维数组(时是如何缓慢而稳定地分配越来越多的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问题。

"[…]在其核心中,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.]]])

最新更新