Почему результат SVD Armadillo отличается от NumPy?

В моем коде Python я вычислял SVD некоторых данных, используя numpy.linalg.svd:

from numpy import linalg
(_, _, v) = linalg.svd(m)

Матрица V, возвращенная этим, была:

[[ 0.4512937  -0.81992002 -0.35222884]
 [-0.22254721  0.27882908 -0.93419863]
 [ 0.86417981  0.4999855  -0.05663711]]

При переносе кода на C++ я переключился на использование Armadillo для вычислений SVD:

#include <armadillo>

arma::fmat M; // Input data
arma::fmat U;
arma::fvec S;
arma::fmat V;
arma::svd(U, S, V, M);

Результирующий V для тех же данных:

  0.4513  -0.2225  -0.8642
 -0.8199   0.2788  -0.5000
 -0.3522  -0.9342   0.0566

Мы видим, что транспонирование V из Armadillo соответствует V из NumPy. Разве что для последнего столбца V от Armadillo. Эти значения имеют противоположный знак значений в последней строке результата NumPy.

Что здесь происходит? Почему результаты SVD для двух популярных библиотек так различаются? И какой из двух правильный результат?


person Ashwin Nanjappa    schedule 21.11.2013    source источник
comment
На какую исходную матрицу вы наносите свою СВД? Можете ли вы гарантировать, что это идентично между двумя системами? Обычно единственная разница может заключаться в масштабе собственных векторов...   -  person Alexander L. Belikoff    schedule 21.11.2013
comment
@AlexanderL.Belikoff: Да, входные данные идентичны. В C++ это тип float, а в Python, наверное, double. Но такая разница в точности не должна иметь значения?   -  person Ashwin Nanjappa    schedule 21.11.2013
comment
Технически разложение SVD не является уникальным (хотя я не уверен в этом изменении знака). Кроме того, вероятно, один из двух возвращает V* вместо V, отсюда и транспонирование. Более того, в SVD реальной квадратной матрицы создаются U и V, которые являются матрицами вращения, armadillo в этом правильно. Можешь выложить исходную матрицу?   -  person sbabbi    schedule 21.11.2013
comment
@sbabbi: входная матрица не квадратная. Он размещен здесь: pastebin.com/bPgKER7E   -  person Ashwin Nanjappa    schedule 21.11.2013
comment
Armadillo возвращает V, а numpy возвращает V*. Что касается смены знака, я понятия не имею, но, вероятно, оба результата верны, и эта смена знака в V уравновешивается другой в U. Вы можете вычислить USV и проверить, что он равен вашей исходной матрице.   -  person sbabbi    schedule 21.11.2013


Ответы (1)


Оба верны... Строки v, которые вы получили из numpy, являются собственными векторами M.dot(M.T) (в сложном случае транспонирование будет сопряженным транспонированием). Собственные векторы в общем случае определяются только с точностью до мультипликативной константы, поэтому вы можете умножить любую строку v на другое число, и это все равно будет матрицей собственных векторов.

Существует дополнительное ограничение на v, заключающееся в том, что это унитарная матрица, что приблизительно означает, что ее строки являются ортонормированными. Это уменьшает доступные варианты выбора для каждого собственного вектора до двух: нормализованный собственный вектор, указывающий в любом направлении. Но вы все равно можете умножить любую строку на -1 и все еще иметь действительное v.

Если вы хотите проверить это на своей матрице, которую я загрузил как a:

>>> u, d, v = np.linalg.svd(a)
>>> D = np.zeros_like(a)
>>> idx = np.arange(a.shape[1])
>>> D[idx, idx] = d
>>> np.allclose(a, u.dot(D).dot(v))
True
>>> v[2] *= -1
>>> np.allclose(a, u.dot(D).dot(v))
True

На самом деле вы можете умножать строки v только на -1 в реальном домене, но в сложном случае вы можете умножать их на любое комплексное число с абсолютным значением 1:

>>> vv = v.astype(np.complex)
>>> vv[0] *= (1+1.j)/np.sqrt(2)
>>> np.allclose(a, u.dot(D).dot(v))
True
person Jaime    schedule 21.11.2013
comment
Спасибо Хайме! Я использую v для поворота точек, как в PCA. Итак, я думаю, нет ничего плохого в том, чтобы использовать для этого V, который я получил от Armadillo? - person Ashwin Nanjappa; 21.11.2013
comment
Да, выравнивание ваших осей не изменится, единственная разница заключается в ориентации вашего базиса, что в большинстве случаев не должно иметь значения. Обратите внимание, что, по крайней мере, в этом случае именно numpy дает обычную правую ориентацию основы. Опять же, я не думаю, что разница должна иметь значение для выравнивания ваших данных. - person Jaime; 21.11.2013