我知道这听起来很明显,但是,如果你能负担得起内存,我会首先检查你获得的性能,只需将数据填充为统一大小,即简单地添加零并执行操作。有时,更简单的解决方案比具有更多 Python/C 往返次数的更理想的解决方案更快。
如果这不起作用,那么您最好的选择,正如Tom Wyllie建议的那样,可能是一种分桶策略。假设X
你的列表列表alpha
是一个数组,你可以从收集第二个索引的大小开始(也许你已经有了这个):
X_sizes = np.array([len(x_i) for x_i in X])
并对它们进行排序:
idx_sort = np.argsort(X_sizes)
X_sizes_sorted = X_sizes[idx_sort]
然后你选择一个桶数,这就是你工作的分区数。假设您选择BUCKETS = 4
. 您只需要划分数据,以便每个部分或多或少都具有相同的大小:
sizes_cumsum = np.cumsum(X_sizes_sorted)
total = sizes_cumsum[-1]
bucket_idx = []
for i in range(BUCKETS):
low = np.round(i * total / float(BUCKETS))
high = np.round((i + 1) * total / float(BUCKETS))
m = sizes_cumsum >= low & sizes_cumsum < high
idx = np.where(m),
# Make relative to X, not idx_sort
idx = idx_sort[idx]
bucket_idx.append(idx)
然后为每个存储桶进行计算:
bucket_results = []
for idx in bucket_idx:
# The last index in the bucket will be the biggest
bucket_size = X_sizes[idx[-1]]
# Fill bucket array
X_bucket = np.zeros((len(X), bucket_size, len(X[0][0])), dtype=X.dtype)
for i, X_i in enumerate(idx):
X_bucket[i, :X_sizes[X_i]] = X[X_i]
# Compute
res = np.einsum('ijp,ipk->ijk',X, alpha[:, :bucket_size, :])
bucket_results.append(res)
在这部分填充数组X_bucket
可能会很慢。X
同样,如果你能负担得起内存,那么在单个填充数组中然后只使用 slice会更有效X[idx, :bucket_size, :]
。
最后,您可以将结果放回列表中:
result = [None] * len(X)
for res, idx in zip(bucket_results, bucket_idx):
for r, X_i in zip(res, idx):
result[X_i] = res[:X_sizes[X_i]]
抱歉,我没有提供正确的功能,但我不确定您的输入或预期输出到底如何,所以我只是将这些部分放在一起,您可以根据需要使用它们。