MATLAB中用于矩阵归一化的快速技巧

10
我想在Matlab中将矩阵的每一列进行归一化处理。我尝试了两种实现方法:

选项A:

mx=max(x);
mn=min(x);
mmd=mx-mn;
for i=1:size(x,1)
    xn(i,:)=((x(i,:)-mn+(mmd==0))./(mmd+(mmd==0)*2))*2-1; 
end
Option B:
mn=mean(x);
sdx=std(x);
for i=1:size(x,1)
    xn(i,:)=(x(i,:)-mn)./(sdx+(sdx==0));
end

然而,对于我的数据来说,这些选项需要太多时间,例如在一个5000x53的矩阵上需要3-4秒钟。因此,是否有更好的解决方案?

7个回答

15

使用bsxfun代替循环。这样可能会快一些,但它可能也会使用更多的内存(如果你的情况需要分页,那么所有操作都会变得非常缓慢)。

要使用平均值和标准差进行归一化,您可以编写以下代码:

mn = mean(x);
sd = std(x);
sd(sd==0) = 1;

xn = bsxfun(@minus,x,mn);
xn = bsxfun(@rdivide,xn,sd);

为什么要使用 sd(sd==0) = 1; 而不是 sd(sd==0) = eps; ? - tashuhka
2
@tashuhkaпјҡеӣ дёәжҲ‘еҗҺйқўиҰҒйҷӨд»Ҙsdзҡ„еҖјгҖӮеҰӮжһңжҲ‘йҷӨд»Ҙ1пјҢз»“жһңдёҚдјҡж”№еҸҳпјӣеҰӮжһңжҲ‘йҷӨд»ҘepsпјҢз»“жһңдјҡд№ҳд»ҘдёҖдёӘеӨ§ж•°гҖӮ - Jonas
谢谢您的回复。我想这是一个偏好问题。操作0/eps总是返回零,因此除法没有问题。但是,如果我想保留s d矩阵进行进一步分析,则eps值比零更好地表示了实际变异性。 - tashuhka

8

请记住,在MATLAB中,向量化=速度。

如果A是一个M x N矩阵,

A = rand(m,n);
minA = repmat(min(A), [size(A, 1), 1]);
normA = max(A) - min(A);               % this is a vector
normA = repmat(normA, [length(normA) 1]);  % this makes it a matrix
                                       % of the same size as A
normalizedA = (A - minA)./normA;  % your normalized matrix

3

注意:此代码适用于Octave和MATLAB R2016b或更高版本。

function X_norm = normalizeMatrix(X)      
      mu = mean(X); %mean    
      sigma = std(X); %standard deviation   
      X_norm = (X - mu)./sigma;    
end

这在Matlab中不起作用,但在Octave中可以正常工作。 - Ulad Kasach
这在旧版本的MATLAB(2016b之前)中无法工作。从R2016b开始,自动广播功能已启用,因此可以在那些版本的MATLAB中使用。 - rayryeng
请注意,此代码执行的是零均值、单位方差的归一化。这不一定能实现原始贴子(OP)想要的目标,即将每个列归一化到[0,1]范围内。 - rayryeng

3

假设你有一个m x n的矩阵X,你想要对每一列进行归一化。

以下是用matlab实现的代码:

XMean = repmat(mean(X),m,1);
XStd = repmat(std(X),m,1);
X_norm = (X - XMean)./(XStd);

这里解释了逐元素的./运算符: http://www.mathworks.in/help/matlab/ref/arithmeticoperators.html

注意: 正如op所提到的,这只是一种更快的解决方案,并执行与循环矩阵相同的任务。这个内置函数的基本实现使它工作得更快。


这将使矩阵归一化,以便每列具有零均值和单位方差。原始问题只是想将每列归一化为[0,1]。可能会因人而异。 - rayryeng

3
注意:我并没有提供全新的答案,而是在比较已经提出的答案。
选项A:使用bsxfun()
function xn = normalizeBsxfun(x)

    mn = mean(x);
    sd = std(x);
    sd(sd==0) = eps;

    xn = bsxfun(@minus,x,mn);
    xn = bsxfun(@rdivide,xn,sd);

end

选项B:使用for循环
function xn = normalizeLoop(x)

    xn = zeros(size(x));

    for ii=1:size(x,2)
        xaux = x(:,ii);
        xn(:,ii) = (xaux - mean(xaux))./mean(xaux);
    end

end

我们将不同的矩阵大小进行比较:

expList = 2:0.5:5;
for ii=1:numel(expList)
    expNum = round(10^expList(ii));
    x = rand(expNum,expNum); 
    tic;
    xn = normalizeBsxfun(x);
    ts(ii) = toc; 
    tic;
    xn = normalizeLoop(x);
    tl(ii) = toc; 
end

figure;
hold on;
plot(round(10.^expList),ts,'b');
plot(round(10.^expList),tl,'r');
legend('bsxfun','loop');
set(gca,'YScale','log') 

结果显示,对于小矩阵,bsxfun更快。但是,随着维数的增加,差异可以忽略不计,就像在其他帖子中发现的那样。
图中的x轴是矩阵元素的平方根,而y轴是以秒为单位的计算时间。

1
如何使用?
normc(X)

这将使矩阵X按列归一化。但是,您需要在安装中包含神经网络工具箱。


这并没有为当前的答案列表增加任何有意义的内容...更不用说它在所有其他方法都没有的情况下依赖于工具箱了。 - rayryeng

-3

这个怎么样?

A = [7, 2, 6; 3, 8, 4]; %一个 2x3 的矩阵

Asum = sum(A); %对列求和

Anorm = A./Asum(ones(size(A,1),1),:); %对列进行归一化处理


使用 Asum(ones(...)) 复制元素对于较大的矩阵非常危险。如果矩阵的大小很大,您将会看到巨大的性能瓶颈。 - rayryeng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接