多处理时出现块状巨大矩阵点积

时间:2018-10-09 07:52:57

标签: python numpy

我正在实施EM-GMM的特殊情况。

X是形状为[1000000,900]的数据矩阵,是一个numpy mmap对象
Q是形状为[900,900]的精度矩阵,是一个ndarray

我还使用Multiprocessing库,使用相同的数据矩阵(X)在40个内核上同时遍历200个Q矩阵。

它适用于较小的尺寸,例如[1mil,196],[1mil,400],
但是当我尝试在进程的某个时刻运行[1mil,900]时会抛出异常:

  

OSError:[Errno 12]无法分配内存

我想这个问题是因为我有2个大计算,可能分配了大矩阵。

作为电子步骤的一部分,我需要计算:
np.sum(X.dot(Q) * X, axis=1)

作为M步的一部分,我需要计算(W是[1mil,1]权重向量):
(X.T * W).dot(X)

将来,我将不得不在更大尺寸(形状[2mil,2500]甚至[2mil,10k]的数据)上运行此EM-GMM
我该怎么做才能使计算效率更高?

编辑:

我注意到工作程序初始化使用的是pickle,因此X矩阵变成了ndarray,工作程序不共享它(这意味着X矩阵对所有工作程序都是重复的,并填满了我的RAM)

我有一个解决方法的想法,如果已解决,将会更新。
但是,如果有人对如何处理有个好主意,我将不胜感激。

1 个答案:

答案 0 :(得分:1)

原来,有2个不相关的问题导致RAM过度使用。

首先,为多处理工作者腌制时,已从磁盘完全读取memmap对象。
重复的数据为每个工作人员分配了6.7GB的额外RAM。
为了解决这个问题,我创建了一个共享的RawArray并将数据加载到其中,并且在每个使用了np.frombuffer的工作程序上。

第二,X.dot(Q)(X.T * W)导致numpy分配了另一个X形矩阵,这是另一个6.7GB RAM
我通过以下线程创建了答案的变体:https://stackoverflow.com/a/21096605/5572523
由于我的矩阵是瘦高的,所以将它们切成薄片:

def _block_slices(dim_size, block_size):
    count = 0
    while True:
        yield slice(count, count + block_size, 1)
        count += block_size
        if count >= dim_size:
            raise StopIteration

现在我可以遍历数据批次(在处理weight = 0时也增加了一些额外的加速)

我设置了max_elements = 2 ** 27,因为我使用的是float64,因此这将产生1GB的矩阵(如果我没记错的话)。

所以(X.T * W).dot(X)变成了:

def weighted_outer_prod(X, W):
    n, d = X.shape

    max_rows = max(1, int(max_elements / d))
    sigma = np.zeros([d, d])
    for mm in _block_slices(n, max_rows):
        sigma += batch_weighted_outer_prod(X[mm, :], W[mm])
    return sigma

def batch_weighted_outer_prod(batch, W):
    nz = W > 0
    buff = np.empty(batch[nz].shape)
    np.multiply(batch[nz], W[nz, np.newaxis], out=buff)
    sigma = buff.T.dot(batch[nz])
    del(buff)
    return sigma

然后np.sum(X.dot(Q) * X, axis=1)变成:(不用管函数名)

def calc_l_k(X, Q):
    max_rows = max(1, int(max_elements / d))
    l_k = np.empty(n)
    for mm in _block_slices(n, max_rows):
        l_k[mm] = batch_l_k(X[mm, :], Q)

    return l_k


def batch_l_k(batch, Q):
    buff = np.empty(batch.shape)
    np.dot(batch, Q, out=buff)
    np.multiply(buff, batch, out=buff)
    l_k = -0.5 * np.sum(buff, axis=1)
    del(buff)
    return l_k

现在它以X形状[1mil,900]运行,我希望它仍可以在更大的尺寸上使用。