【问题标题】:How to avoid large matrix multiplication in MatlabMatlab中如何避免大矩阵乘法
【发布时间】:2013-12-04 16:46:05
【问题描述】:

我的代码中有两个大矩阵,它们的列数相同,行数不同。喜欢A(20000X4000)B(30000X4000)。两者都是 0-1 稀疏的。

我应该检查 A 的每一行和 B 的所有行,并计算常见 1 的数量。例如A(1,:)=[0 1 0 1 1]B([1 2],:)=[1 1 1 1 1;0 0 0 1 1],我需要得到32的结果。

假设有一个大的 0-1 矩阵 C(50000X4000),并且它的行被标记为类型 A 或类型 B。我应该比较AB 的所有行并枚举1。如果 A 和 B 的每一行中 1 的数量大于某些界限,那么我将使用 A 和 B 的那些行进行其余的计算。所以,我什至不需要存储AB,我只需要一个行索引对的列表。像[(3,2),(3,5),...] 这样的东西表明我应该使用A 的第三行和B 的第二行,A 的第三行和B 的第五行等等。

我首先想到的是A*B',它给出了正确的结果,但实际上它非常昂贵,在某些情况下不可能进行这种乘法运算。

我将矩阵转换为单一数据类型,它变得有点快。稀疏没有帮助。

任务看起来很简单,只计算A 的每一行和B 的所有行的公共1,但并不容易实现。考虑到代码应该像 1000 次一样完成这个任务,那么这实际上是不可能的。

知道如何在不乘法的情况下枚举常见的吗? (顺便说一句,循环也没有帮助)。

谢谢。

【问题讨论】:

  • 矩阵有多稀疏,即大约有多少个?
  • 一般来说,A*B' 无法改进。甚至切换到 C++ 也可能不会带来显着的改进。因此,您唯一的希望是 1. 以不同的方式解决问题,或 2. 使用更多有用的信息。它是一个逻辑矩阵的事实就是这种信息,但不幸的是它还不够。你可以问自己,矩阵有多稀疏,它们有特定的结构吗?你的结果矩阵有多稀疏?
  • 无论如何,结果将是一个 20000 x 30000 的矩阵。你真的需要这个矩阵吗?当你拥有它时,你打算用它做什么?
  • 其实我不需要这些矩阵,我想列举两个矩阵的公因数。就像假设有一个大的 0-1 矩阵 C(50000X4000) 并且它的行被标记为类型 A 或类型 B。我应该将 A 和 B 的所有行一起比较并枚举 1。如果 A 和 B 的每一行中 1 的数量大于某些界限,那么我将使用 A 和 B 的这些行进行其余的计算。
  • @Alef,我认为如果您编辑您的问题以包含此信息,并在评论中比此处更详细地解释它,这真的会帮助您获得有用的答案。也许有一种方法可以在没有这种计算的情况下达到您的目标。

标签: matlab matrix matrix-multiplication enumerate large-scale


【解决方案1】:

我不知道这是否真的比你所拥有的更好,因为它仍然有一个 for 循环,但如果有人能弄清楚如何删除那个 for 循环,你应该很高兴。

%  create temp data
A = rand(20000,4000) < 0.5;
B = rand(30000,4000) < 0.5;
counts = zeros(size(A,1),size(B,1),'uint8');
for i = 1:size(A,1)
    counts(i,:) = sum(bsxfun(@eq,A(i,:),B),2);
end

无论哪种方式,该过程都需要很长时间,因为您要比较 30000 行,每行 4000 个元素,20000 次,或大约 2.4e+12 比较。这是一项艰巨的任务,而且肯定需要很长时间。如果需要更快,可以尝试使用并行计算。

【讨论】:

  • 谢谢!然而,矩阵的大小不允许任何循环;它变得更加耗时,我无法运行代码。
  • @Alef 我不确定你是否理解这样一个事实,无论哪种方式,这都会是一个巨大的比较。至少需要 30000*4000*20000 = 2.4e12 次比较。那是巨大的。即使您可以“优化”它,也需要进行相同数量的比较
  • 这是正确的,这就是我被卡住的原因。我一直在尝试重新定义问题,或者重新表述它,但仍然没有进展。我主要担心的是,我在想可能有一些枚举方法可以应用于我不知道的特定问题(0-1 矩阵)。
  • 我真的不认为有任何简单的方法可以解决这个问题。对不起。我的意思是它会运行,它只需要一段时间。如果您真的不耐烦,或者遇到 RAM 问题,您可以将矩阵分解成更小的块
  • 在我只有 Intel i5 CPU 的笔记本电脑上运行这个程序大约需要一个小时。但是一旦完成,您可以保存结果,而不必再次运行该程序。或者,您始终可以使用 parfor 和并行计算工具箱来加快进程
【解决方案2】:

我做了一些基准测试;在我的机器上(i7-3770 @ 3.40GHz),无论内容如何,​​将大小为 30000 x 4000 和 4000 x 20000 的完整矩阵相乘大约需要 55 秒,这与 Dennis Jaheruddin 发现的相同。但是使用稀疏矩阵可以使计算更快,这取决于稀疏性。如果我将稀疏度r 定义为1s 的数量与矩阵元素的比率,我会得到以下结果:

r      time / s 
0.001   0.07
0.002   0.3
0.005   2.1
0.01    8.3
0.02   25

这是用于确定这些数字的代码:

m = 20000;
n = 4000;
o = 30000;

r = 0.001;

N = round(r * m * n);
A = sparse(randi(m, N, 1), randi(n, N, 1), 1, m, n);

N = round(r * n * o);
B = sparse(randi(o, N, 1), randi(n, N, 1), 1, o, n);

tic
C = A * B';
toc

【讨论】:

  • 这是正确的,MatLab/Octave 不计算 0(你甚至有一个特殊的函数 spfun() 来制作你自己的自定义稀疏函数!)。我认为它甚至可以完全跳过整个列,因为它知道该列是否完全为空。我目前在使用大型矩阵的项目中使用稀疏矩阵,它确实降低了网络密度的 CPU 时间(1 的数量 = r 的 Donda 稀疏度)。
【解决方案3】:

如果无法完成整个矩阵的乘法运算,一种想法是一次处理一个垂直条纹。对于每个条带,您计算所需的结果,并将其与前面的条带累加:

A = double(rand(5,300)<.5); %// random data
B = double(rand(4,300)<.5); %// random data

S = 10; %// stripe size
result = zeros(size(A,1),size(B,1)); %// initialize to 0
for s = 1:10:size(A,2) %// each vertical stripe, of width S
    ind = s+(0:S-1);
    result = result +  A(:,ind)*(B(:,ind)).';
end

检查:

>> result

result =

    73    72    62    72
    84    70    79    71
    83    84    76    77
    77    80    77    74
    71    71    70    74

>> A*B.'

ans =

    73    72    62    72
    84    70    79    71
    83    84    76    77
    77    80    77    74
    71    71    70    74

【讨论】:

  • 感谢您的建议,但它不适用于我的情况!这个问题现在让我很恼火。
  • 我的意思是它比我从 Matlab 中的常规矩阵乘法获得的时间要多得多。这种方法当然是正确的,但对速度没有帮助。谢谢大佬!
【解决方案4】:

您尝试的解决方案是最优或接近最优的。

当我尝试这个时,不到一分钟:

A = round(rand(30000,4000));
B = round(rand(20000,4000));
tic,A*B';toc;

如果你真的需要这样做上千次,我只能想象两种情况:

  1. 你不需要经常这样做,那样的话就让它运行,明天就会完成
  2. 您希望经常这样做并加快速度,但根本不可能找到更快的解决方案。除非您对要相乘的矩阵有一些非常有用的信息。

如果您发现此样本乘法超过一分钟(例如超过 10 分钟),您可能正在低效地使用内存。在这种情况下,尝试获得更多的内存。

【讨论】:

  • 我应该在算法中使用这个枚举,它应该被“每个人”(我的目标群体中的人)使用,所以它不是一个数据分析项目,它更多的是一个算法开发任务。我希望它只是运行一次,然后我可以在大学的 IT 服务中使用/租用计算服务,并在合理的时间内完成所有事情。谢谢!
猜你喜欢
  • 2011-07-24
  • 2018-12-17
  • 2012-07-28
  • 2017-08-16
  • 2017-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-29
相关资源
最近更新 更多