Собственные векторы, рассчитанные с помощью numpy eigh и svd, не совпадают

Рассмотрим разложение по сингулярным значениям M = USV *. Тогда разложение на собственные значения M * M дает M * M = V (S * S) V * = VS * U * USV *. Я хочу проверить это равенство на numpy, показывая, что собственные векторы, возвращаемые функцией eigh, такие же, как те, которые возвращаются функцией svd:

import numpy as np
np.random.seed(42)
# create mean centered data
A=np.random.randn(50,20)
M= A-np.array(A.mean(0),ndmin=2)

# svd
U1,S1,V1=np.linalg.svd(M) 
S1=np.square(S1)
V1=V1.T  

# eig
S2,V2=np.linalg.eigh(np.dot(M.T,M))
indx=np.argsort(S2)[::-1]
S2=S2[indx]
V2=V2[:,indx]

# both Vs are in orthonormal form
assert np.all(np.isclose(np.linalg.norm(V1,axis=1), np.ones(V1.shape[0])))
assert np.all(np.isclose(np.linalg.norm(V1,axis=0), np.ones(V1.shape[1])))
assert np.all(np.isclose(np.linalg.norm(V2,axis=1), np.ones(V2.shape[0])))
assert np.all(np.isclose(np.linalg.norm(V2,axis=0), np.ones(V2.shape[1])))

assert np.all(np.isclose(S1,S2))
assert np.all(np.isclose(V1,V2))

Последнее утверждение терпит неудачу. Почему?

Ответы

Ответ 1

Просто играйте с небольшими номерами, чтобы отладить вашу проблему.

Начните с A=np.random.randn(3,2) вместо вашей гораздо большей матрицы с размером (50,20)

В моем случайном случае я обнаружил, что

v1 = array([[-0.33872745,  0.94088454],
   [-0.94088454, -0.33872745]])

и для v2:

v2 = array([[ 0.33872745, -0.94088454],
   [ 0.94088454,  0.33872745]])

они различаются только для знака и, очевидно, даже если нормализовать единичный модуль, вектор может отличаться для знака.

Теперь, если вы попробуете трюк

assert np.all(np.isclose(V1,-1*V2))

для вашей оригинальной большой матрицы, он терпит неудачу... снова, все в порядке. Случается, что некоторые векторы умножаются на -1, другие - нет.

Правильный способ проверки равенства между векторами:

assert allclose(abs((V1*V2).sum(0)),1.)

и действительно, чтобы понять, как это работает, вы можете напечатать это количество:

(V1*V2).sum(0)

который действительно равен либо +1, либо -1 в зависимости от вектора:

array([ 1., -1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
    1., -1.,  1.,  1.,  1., -1., -1.])

EDIT: Это произойдет в большинстве случаев, особенно если начать с случайной матрицы. Обратите внимание, однако, что этот тест, скорее всего, не сработает, если одно или несколько собственных значений имеют собственное пространство размером больше, чем 1, как указано @Sven Marnach в его комментарии ниже:

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