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

din*_*eep 5 python numpy

我有三个数组RowIndex,ColIndex并且Entry在numpy中.实质上,这是来自矩阵的条目子集,分别具有行索引,列索引和这三个变量中该条目的值.我有两个numpy二维数组(矩阵)UM.设alphabeta是两个给定的常数.我需要通过,如果我遍历这是可能的矩阵的条目的子集进行迭代RowIndex,ColIndexValue.说,

i=RowIndex[0], j=ColIndex[0], value = Entry[0] 
Run Code Online (Sandbox Code Playgroud)

然后我需要更新i"行第j"的第n列U,并M根据一些方程分别.然后,我做

i=RowIndex[1], j=ColIndex[1], value = Entry[1]
Run Code Online (Sandbox Code Playgroud)

等等.详情如下.

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)
Run Code Online (Sandbox Code Playgroud)

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

PS:对于好奇的,这是着名的NetFlix百万奖金问题的获奖解决方案的变种.RowIndex对应于用户,ColIndex对应于与其评级对应的电影和值.大多数评级都缺失了.已知的评级在RowIndex,ColIndex和Entry中叠加.现在你试图找到矩阵U和M,这样,第一部电影的i'用户j' 的评级由下式给出np.dot(U[i,:],M[:,j]).现在,根据可用的评级,您尝试使用上面代码中所示的更新公式找到矩阵U和M(或它们的行和列).

Ima*_*ngo 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])
Run Code Online (Sandbox Code Playgroud)

这里,

e = values - np.dot(U[rows_idx, :], M[:, cols_idx]).diagonal()
Run Code Online (Sandbox Code Playgroud)

计算你的

e = value - np.dot(U[i,:],M[:,j])
Run Code Online (Sandbox Code Playgroud)

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

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


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

A(t) = A(t-1) +/* something
Run Code Online (Sandbox Code Playgroud)

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

因此,我提出的是独立更新的矢量化更新。

想象一下,您有M和各U10x10,并且有以下行索引和列索引:

rows_idx = [1, 1, 3, 4, 5, 0]
cols_idx = [7, 1, 7, 5, 6, 5]
Run Code Online (Sandbox Code Playgroud)

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

rows_idx = [1, 4, 5], [1, 3, 0]
cols_idx = [7, 5, 6], [1, 7, 5]
Run Code Online (Sandbox Code Playgroud)

请注意,独立集是由行和列中唯一的索引组成的。通过该定义,您可以将所需的循环数量从 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])
Run Code Online (Sandbox Code Playgroud)

因此,如果您有一种手动(或轻松)提取独立更新的方法,或者您通过使用搜索算法计算列表,则上述代码将对独立更新进行矢量


为了以防万一,在上面的例子中:

rows_idx = [1, 1, 3, 4, 5, 0]
cols_idx = [7, 1, 7, 5, 6, 5]
Run Code Online (Sandbox Code Playgroud)

第二行不能并行化,因为1之前已经出现过,第三行和最后一列也不能并行化,因为同样的原因(使用75)。因此,由于行和列都必须是唯一的,我们最终得到 2 组元组:

rows_idx = [1, 4, 5], [1, 3, 0]
cols_idx = [7, 5, 6], [1, 7, 5]
Run Code Online (Sandbox Code Playgroud)

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

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

如果您可以确保独立集的长度N远大于独立集的数量M(这或多或少意味着,如果您最终会M = {2,3,4}N = 100000, with N >> M行/列索引得到一些独立集),那么它可能值得寻找独立套装。

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

如果您没有办法在没有信息的情况下提取它们,则一些用于查找独立集的伪代码将如下所示:

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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,为了找到独立的集合,您已经需要迭代整个行/列索引列表。上面的伪代码不是最有效的伪代码,我很确定会有专门的算法来实现这一点。但是,如果您的更新可能依赖于以前的更新,则查找独立集的成本可能会高于执行所有顺序更新。

结束语:在整篇文章之后,这完全取决于您的数据。

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

  • 如果您可以确保大多数更新都是独立的(例如,不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)
    
    Run Code Online (Sandbox Code Playgroud)

    现在idx包含 rows_idx 和 cols_idx 的位置unique,希望这可以大大减少您的计算成本。您可以使用我的批量更新来快速更新那些索引对应的行和列。然后,您可以使用初始方法来更新在非唯一索引上重复迭代的少数条目

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