有没有更快的方法在python numpy中有效地执行此伪代码?

时间:2015-05-20 05:46:44

标签: python numpy

我在numpy中有三个名为RowIndexColIndexEntry的数组。实质上,这是来自矩阵的条目子集,分别具有行索引,列索引和这三个变量中该条目的值。我有两个numpy二维数组(矩阵)UM。让alphabeta为两个给定的常量。我需要遍历矩阵的条目子集,如果我遍历RowIndexColIndexValue,这是可能的。说,

i=RowIndex[0], j=ColIndex[0], value = Entry[0] 

然后我需要根据某个等式分别更新ij的{​​{1}}和U列。然后,我做

M

等等。详情如下。

i=RowIndex[1], j=ColIndex[1], value = Entry[1]

问题是代码非常慢。是否有任何代码部分可以加快速度?

PS:对于好奇的人来说,这是着名的NetFlix百万奖金问题的获奖解决方案的变种。 RowIndex对应于用户,ColIndex对应于与其评级对应的电影和值。大多数评级都缺失了。已知的评级在RowIndex,ColIndex和Entry中叠加。现在,您尝试查找矩阵U和M,这样for iter in np.arange(length(RowIndex)): i = RowIndex[iter] j = ColIndex[iter] value = Entry[iter] e = value - np.dot(U[i,:],M[:,j]) OldUi = U[i,:] OldMj = M[:,j] U[i,:] = OldUi + beta * (e*OldMj - alpha*OldUi) M[:,j] = OldMj + beta * (e*OldUi - alpha*OldMj) '用户对i'电影的评分由j给出。现在,根据可用的评级,您尝试使用上面代码中所示的更新公式找到矩阵U和M(或它们的行和列)。

1 个答案:

答案 0 :(得分:4)

我认为如果我没有理解错误,那么您的代码可以按照以下方式进行矢量化:

import numpy as np

U, M = # two 2D matrices
rows_idx = # list of indexes
cols_idx = # list of indexes
values   = # np.array() of values

e = values - np.dot(U[rows_idx, :], M[:, cols_idx]).diagonal()
Uo = U.copy()
Mo = M.copy()
U[rows_idx, :] += beta * ((e * Mo[:, cols_idx]).T - alpha * Uo[rows_idx, :])
M[:, cols_idx] += beta * ((e * Uo[rows_idx, :].T) - alpha * Mo[:, cols_idx])

下面,

e = values - np.dot(U[rows_idx, :], M[:, cols_idx]).diagonal()

计算你的

e = value - np.dot(U[i,:],M[:,j])

请注意,您想要的结果位于矩阵之间的点积的对角线上。

这不会处理顺序更新(因为没有可用的矢量化),但它将允许您以矢量化和更快的方式执行一批独立更新。

如上所述,我向你建议的代码不能处理顺序更新,因为根据定义,顺序更新方案无法进行矢量化。任何形式

A(t) = A(t-1) +/* something

t定义时间,不能并行更新。

所以,我提出的是独立更新的矢量化更新。

想象一下,每个MU都有10x10行,并且您拥有以下行和列索引:

rows_idx = [1, 1, 3, 4, 5, 0]
cols_idx = [7, 1, 7, 5, 6, 5]

您可以从中识别出两个独立的集合(考虑到索引是有序的):

rows_idx = [1, 4, 5], [1, 3, 0]
cols_idx = [7, 5, 6], [1, 7, 5]

请注意,独立集由行和列中唯一的索引构成。根据该定义,您可以将所需的循环数从6(在本例中)减少到2:

for i in len(rows_idx):
    ridx = rows_idx[i]
    cidx = cols_idx[i]
    # Use the vectorized scheme proposed above the edit
    e = values - np.dot(U[ridx, :], M[:, cidx]).diagonal()
    Uo = U.copy()
    Mo = M.copy()
    U[ridx, :] += beta * ((e * Mo[:, cidx]).T - alpha * Uo[ridx, :])
    M[:, cidx] += beta * ((e * Uo[ridx, :].T) - alpha * Mo[:, cidx])

因此,无论您是手动(或轻松)提取独立更新,还是使用搜索算法计算列表,上述代码都会对独立更新进行矢量化。 / p>

为了澄清以下情况,在上面的例子中:

rows_idx = [1, 1, 3, 4, 5, 0]
cols_idx = [7, 1, 7, 5, 6, 5]

第二行无法并行化,因为1之前已出现过,并且第三列和最后一列无法并行化,原因相同(7和{{1 }})。因此,行和列都必须是唯一的,我们最终得到2组元组:

5

从这里开始,将取决于您的数据。找到独立集合的问题可能非常昂贵,特别是如果它们中的大多数都依赖于之前的某些更新。

如果您从数据中获得了一种方法(比如说您按时记录了数据)来提取独立集,那么批量更新将对您有所帮助。另一方面,如果您将所有数据放在一起(这很常见),则取决于一个因素:

如果你可以确保独立集rows_idx = [1, 4, 5], [1, 3, 0] cols_idx = [7, 5, 6], [1, 7, 5] 的长度远远大于独立集N的数量(这或多或少意味着,如果你最终会有一些M行/列索引的M = {2,3,4}个独立集合,那么可能值得寻找独立集合。

换句话说,如果您要以10000种不同的组合更新30位作者和30部电影,那么您的数据可能会依赖于以前的更新,但是,如果您要更新100000位作者和100000部电影30种组合,那么您的数据可能是独立的。

某些伪代码可以找到独立的集合,如果你没有办法在没有信息的情况下提取它们,那就是这样的:

N = 100000, with N >> M

如您所见,为了找到独立的集合,您需要迭代整个行/列索引列表。上面的伪代码不是最有效的伪代码,我非常确定会有特定的算法。但是,如果您的更新可能依赖于以前的更新,那么查找独立集的成本可能高于执行所有顺序更新。

完成:在整篇文章之后,它完全取决于您的数据。

  • 如果您可以事先获得要更新的行/列的方式提取独立集,那么您可以轻松地更新它们的矢量化。

  • 如果您能确保 大多数您的更新将是独立的(例如,independent_sets = [] # list with sets for row, col in zip(rows_idx, cols_idx): for iset in independent_sets: if row and col DONT exist in iset: insert row and col break if nothing inserted: add new set to independent set add current (row, col) to the new set 中的990将是独立的,可能值得尝试找到10000集。一种近似集合的方法是使用990

    np.unique

    现在# Just get the index of the unique rows and columns _, idx_rows = np.unique(rows_idx, return_index=True) _, idx_cols = np.unique(cols_idx, return_index=True) # Get the index where both rows and columns are unique idx = np.intersection1d(idx_rows, idx_cols) 包含idx的rows_idx和cols_idx的位置,希望这可以大大降低您的计算成本。您可以使用我的批量更新来快速更新与这些索引对应的行和列。然后,您可以使用初始方法更新希望几个重复迭代非唯一索引的条目。

  • 如果您有相同演员或电影的多个更新,那么......保留您的顺序更新方案,因为查找独立集将比迭代更新更难。