Scikit-Learn PCA

Я использую входные данные из здесь (см. раздел 3.1).

Я пытаюсь воспроизвести их матрицу ковариации, собственные значения и собственные векторы, используя scikit-learn. Однако я не могу воспроизвести результаты, представленные в источнике данных. Я также видел эти входные данные в другом месте, но я не могу различить, есть ли проблема с scikit-learn, моими шагами или источником данных.

data = np.array([[2.5,2.4],
                 [0.5,0.7],
                 [2.2,2.9],
                 [1.9,2.2],
                 [3.1,3.0],
                 [2.3,2.7],
                 [2.0,1.6],
                 [1.0,1.1],
                 [1.5,1.6],
                 [1.1,0.9],
                 ]) 

centered_data = data-data.mean(axis=0)
pca = PCA()
pca.fit(centered_data)
print(pca.get_covariance()) #Covariance Matrix

array([[ 0.5549,  0.5539],
   [ 0.5539,  0.6449]])

print(pca.explained_variance_ratio_) #Eigenvalues (normalized)

[ 0.96318131  0.03681869]

print(pca.components_) #Eigenvectors

[[-0.6778734  -0.73517866]
 [ 0.73517866 -0.6778734 ]]

Удивительно, что проекции соответствуют результатам из источника данных, описанного выше.

print(pca.transform(centered_data)) #Projections

array([[-0.82797019,  0.17511531],
   [ 1.77758033, -0.14285723],
   [-0.99219749, -0.38437499],
   [-0.27421042, -0.13041721],
   [-1.67580142,  0.20949846],
   [-0.9129491 , -0.17528244],
   [ 0.09910944,  0.3498247 ],
   [ 1.14457216, -0.04641726],
   [ 0.43804614, -0.01776463],
   [ 1.22382056,  0.16267529]])

Вот что я не понимаю:

  • Почему матрица ковариации отличается?
  • Обновлено. Как получить собственные значения из scikit-learn, которые еще не нормализованы?

Ответ 1

Корректная ковариационная матрица этих данных:

numpy.cov(data.transpose())
array([[ 0.61655556,  0.61544444],
       [ 0.61544444,  0.71655556]])

Предвзятый (т.е. "неверный", с использованием неправильного термина нормализации и недооценка дисперсии в наборе данных): ковариационная матрица:

numpy.cov(data.transpose(), bias=1)
array([[ 0.5549,  0.5539],
       [ 0.5539,  0.6449]])

Numpy знает, что вам нужно центрировать свои данные, поэтому вам не нужно centered_data.

Компоненты PCA не являются 1:1 собственными значениями.

Правильное разложение на собственные значения:

numpy.linalg.eig(numpy.cov(data.transpose()))
(array([ 0.0490834 ,  1.28402771]),
 array([[-0.73517866, -0.6778734 ],
        [ 0.6778734 , -0.73517866]]))

Использование смещенной оценки дает разные собственные значения (опять же, недооценка дисперсии), но те же собственные векторы:

(array([ 0.04417506,  1.15562494]), ...

Заметим, что собственные векторы еще не отсортированы по наибольшим собственным значениям.

Как указывает название pca.explained_variance_ratio_, это не собственные значения. Это соотношение. Если мы возьмем (предвзятые, недооцененные) собственные значения и нормируем их на сумму 1, получим

s/sum(s)
array([ 0.03681869,  0.96318131])

Кроме того, метод pca.transform scipy, по-видимому, не применяется масштабирование. IMHO, при использовании PCA, также довольно распространено масштабирование каждого компонента, чтобы иметь дисперсию единиц. Это явно не выполняется для этого вывода. Тогда результат был бы (при замене двух столбцов я не стал бы это менять)

s, e = numpy.linalg.eig(numpy.cov(data.transpose()))
o=numpy.argsort(s)[::-1]
(data-mean).dot(e[:,o]) / numpy.sqrt(s[o])
array([[-0.73068047, -0.79041795],
       [ 1.56870773,  0.64481466],
       [-0.87561043,  1.73495337],
       [-0.24198963,  0.58866414],
       [-1.47888824, -0.94561319],
       [-0.80567404,  0.79117236],
       [ 0.08746369, -1.57900372],
       [ 1.01008049,  0.20951358],
       [ 0.38657401,  0.08018421],
       [ 1.08001688, -0.73426743]])

(Как вы можете видеть, PCA - это всего лишь три строки в numpy, поэтому для этого вам не нужна функция.)

Почему я думаю, что это правильный результат? Поскольку полученный набор данных обладает свойством, что его ковариационная матрица (за исключением ошибок округления) является идентификационной матрицей. Без масштабирования ковариационная матрица numpy.diag(s[o]). Но можно также утверждать, что, применяя масштабирование, я "потерял" информацию о дисперсии, которая была бы сохранена в противном случае.

По-моему, scipy использует неправильную (предвзятую) ковариацию. numpy верен.

Но чаще всего это не имеет большого значения. В приведенном выше соотношении смещение отменяется. И если у вас большой набор данных, разница между использованием наивного 1/n и непредвзятого 1/(n-1) в конечном итоге становится небрежным. Но также разница достигается при нулевой стоимости процессора, поэтому вы можете также использовать объективную оценку дисперсии.

Ответ 2

Краткий ответ на (1) заключается в том, что когда вы применяли PCA к вашим данным, вы повернули его, а новое векторное пространство выражало новые случайные переменные с различной ковариацией. Ответ на (2) заключается в том, что если вы хотите не нормированные собственные значения, просто представьте ковариационную матрицу ваших данных.

Дополнительная информация:

Чтобы вычислить собственные значения с помощью scipy: http://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.eigvals.html

Вместо этого вы можете вычислить SVD матрицы данных (а не ковариацию) и посмотреть на особые значения: http://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.svd.html

По-видимому, scikit-learn имеет разные вкусы SVD, которые вы можете попробовать.