Я реализовал PCA в Python.Я использовал MNIST-Data и сократил данные до 2d.После этого я использовал KNN для классификации данных.То же самое я повторил с Scikit.В результате у меня с моим PCA точность намного ниже.Я сравнил ПК и вижу, что признаки некоторых компонентов отличаются от результатов SciKit.Я понятия не имею, как это исправить.Надеюсь, один из вас увидит мою ошибку или недоразумение.
class Dimension_reduction:
def PCA(self, X, dimensions):
covariance_matrix = self.find_covariance(X)
eigenvalues, eigenvectors = self.eigenvalue_decomposition(covariance_matrix)
eigenpairs = self.sort_eigenvalues(eigenvalues, eigenvectors)
projection_matrix = self.projection_matrix(eigenpairs, dimensions)
new_featurespace = self.project_reduced_featurespace(X, projection_matrix)
return new_featurespace
def find_covariance(self, X):
means = np.mean(X, axis = 0)
covariance_matrix = (X - means).T.dot((X - means)) / (len(X)-1)
return covariance_matrix
def eigenvalue_decomposition(self, covariance_matrix):
eigenvalues, eigenvectors = np.linalg.eig(covariance_matrix)
return eigenvalues, eigenvectors
def sort_eigenvalues(self, eigenvalues, eigenvectors):
eigenpairs = [(np.abs(eigenvalues[i]), eigenvectors[:,i]) for i in range(len(eigenvalues))]
eigenpairs.sort()
eigenpairs.reverse()
return eigenpairs
def projection_matrix(self, eigenpairs, dimensions):
projection_matrix = np.array(eigenpairs[0][1].reshape(len(eigenpairs[0][1]),1))
for i in range(1, dimensions, 1):
projection_matrix = np.concatenate((projection_matrix,
np.array(eigenpairs[i][1].reshape(len(eigenpairs[0][1]),1))), axis = 1)
return projection_matrix
def project_reduced_featurespace(self, X, projection_matrix):
return X.dot(projection_matrix)
def scatter_plot(self, new_X, label_number, labels):
partial_X = []
x = []
y = []
for i in range(label_number):
partial_X.append([])
x.append([])
y.append([])
for i in range(len(new_X)):
label = labels[i]
partial_X[int(label)].append(new_X[i])
colors = plt.cm.rainbow(np.linspace(0,1,label_number))
for i in range(label_number):
for j in range(len(partial_X[i])):
x[i].append(partial_X[i][j][0])
y[i].append(partial_X[i][j][1])
i = 0
for x,y,c in zip(x,y, colors):
plt.scatter(x,y,c, label = str(i))
return plt, colors
def eigenfaces(self, X):
covariance_matrix = self.find_covariance(X)
eigenvalues, eigenvectors = self.eigenvalue_decomposition(covariance_matrix)
eigenpairs = self.sort_eigenvalues(eigenvalues, eigenvectors)
return eigenpairs
Я также использовал разные реализации
def PCA(X):
PC = []
cov = np.cov(X.T)
w,v = np.linalg.eig(cov)
eig_pairs = [(np.abs(w[i]), v[:,i]) for i in range(len(w))]
eig_pairs.sort()
eig_pairs.reverse()
PC.append(eig_pairs[0][1])
PC.append(eig_pairs[1][1])
Y = X.dot(np.array(PC).T)
return Y
def PCA(data, dims_rescaled_data=2):
"""
returns: data transformed in 2 dims/columns + regenerated original data
pass in: data as 2D NumPy array
"""
import numpy as NP
from scipy import linalg as LA
m, n = data.shape
# mean center the data
data -= np.mean(data,axis=0)
# calculate the covariance matrix
R = NP.cov(data, rowvar=False)
# calculate eigenvectors & eigenvalues of the covariance matrix
# use 'eigh' rather than 'eig' since R is symmetric,
# the performance gain is substantial
evals, evecs = LA.eigh(R)
# sort eigenvalue in decreasing order
idx = NP.argsort(evals)[::-1]
evecs = evecs[:,idx]
# sort eigenvectors according to same index
evals = evals[idx]
# select the first n eigenvectors (n is desired dimension
# of rescaled data array, or dims_rescaled_data)
evecs = evecs[:, :dims_rescaled_data]
# carry out the transformation on the data using eigenvectors
# and return the re-scaled data, eigenvalues, and eigenvectors
return NP.dot(evecs.T, data.T).T, evals, evecs
def pca_svd(X, num = 2):
X = X-np.mean(X, axis = 0)
[u,s,v] = np.linalg.svd(X)
v = v.T[:,:num]
return np.dot(X,v)
В сумме есть 4 разных реализации.Результат первого:
# Training
array([[-1.01031446, 6.71282428],
[-3.03212724, 1.64169381],
[ 2.95288108, -1.44413258],
...,
[-1.15329784, -2.83978701],
[-8.02795144, 2.12452378],
[ 9.83911408, 3.2389573 ]])
# Testing
array([[ 5.15053345, 6.79771421],
[ 1.84247302, -0.58932415],
[ 1.66957196, 3.89696398],
...,
[ 5.22253275, 1.74628625],
[-8.2209684 , 0.32435677],
[11.00041468, -4.62978653]])
Для второго:
#train
array([[ -4.94267554, -6.22892054],
[ -6.96448832, -1.15779007],
[ -0.97948 , 1.92803631],
...,
[ -5.08565892, 3.32369075],
[-11.96031252, -1.64062004],
[ 5.906753 , -2.75505357]])
#test
array([[ 1.39046844, 7.2344142 ],
[ -1.91759199, -0.15262416],
[ -2.09049305, 4.33366397],
...,
[ 1.46246774, 2.18298624],
[-11.98103342, 0.76105676],
[ 7.24034966, -4.19308654]])
Для третьего:
#train
array([[ -4.94267554, -6.22892054],
[ -6.96448832, -1.15779007],
[ -0.97948 , 1.92803631],
...,
[ -5.08565892, 3.32369075],
[-11.96031252, -1.64062004],
[ 5.906753 , -2.75505357]])
#test
array([[-1.39046844, 7.2344142 ],
[ 1.91759199, -0.15262416],
[ 2.09049305, 4.33366397],
...,
[-1.46246774, 2.18298624],
[11.98103342, 0.76105676],
[-7.24034966, -4.19308654]])
для SVD:
#train
array([[ 4.94267554, -6.22892054],
[ 6.96448832, -1.15779007],
[ 0.97948 , 1.92803631],
...,
[ 5.08565892, 3.32369075],
[11.96031252, -1.64062004],
[-5.906753 , -2.75505357]])
#test
xt2
array([[ 1.39046844, 7.2344142 ],
[ -1.91759199, -0.15262416],
[ -2.09049305, 4.33366397],
...,
[ 1.46246774, 2.18298624],
[-11.98103342, 0.76105676],
[ 7.24034966, -4.19308654]])
Наконец-то ПК от scikit:
#train
array([[ 4.9426755 , -6.22891427],
[ 6.9644884 , -1.15779638],
[ 0.97948002, 1.92802868],
...,
[ 5.08565916, 3.32364585],
[11.96031245, -1.64060628],
[-5.90675305, -2.7550444 ]])
#test
array([[-1.39046854, 7.23440228],
[ 1.91759194, -0.1526275 ],
[ 2.09049303, 4.33366458],
...,
[-1.46246766, 2.18299325],
[11.98103337, 0.76105147],
[-7.24034972, -4.19309378]])
Видно, что результаты просто по-разному.Но для классификации это проблема.Кроме того, в первом коде должна быть ошибка.Но я не могу его найти.
У кого-нибудь есть идеи?
PS: Если я увеличу количество ПК в проекции, проблема со знаком станет хуже.