To, co robisz, jest złe: nie ma sensu obliczać PRESS dla PCA w ten sposób! W szczególności problem leży w kroku 5.
Naiwne podejście do PRESS dla PCA
ndx(i)∈Rd,i=1…nx(i)X(−i)kU(−i)∥∥x(i)−x^(i)∥∥2=∥∥x(i)−U(−i)[U(−i)]⊤x(i)∥∥2i
PRESS=?∑i=1n∥∥x(i)−U(−i)[U(−i)]⊤x(i)∥∥2.
Dla uproszczenia ignoruję tutaj kwestie centrowania i skalowania.
Naiwne podejście jest złe
Problem powyżej polega na tym, że używamy do obliczenia prognozy , i to jest bardzo zła rzecz.x(i)x^(i)
Zwróć uwagę na zasadniczą różnicę w przypadku regresji, w której wzór na błąd rekonstrukcji jest zasadniczo taki sam , ale predykcja jest obliczana przy użyciu zmiennych predykcyjnych, a nie przy użyciu . Nie jest to możliwe w PCA, ponieważ w PCA nie ma zmiennych zależnych i niezależnych: wszystkie zmienne są traktowane razem.∥∥y(i)−y^(i)∥∥2y^(i)y(i)
W praktyce oznacza to, że obliczone powyżej PRESS może się zmniejszać wraz ze wzrostem liczby składników i nigdy nie osiągać minimum. Co doprowadziłoby do wniosku, że wszystkie składniki są znaczące. A może w niektórych przypadkach osiąga minimum, ale nadal ma tendencję do przeładowywania i przeceniania optymalnej wymiarowości.kd
Prawidłowe podejście
Istnieje kilka możliwych podejść, patrz Bro i in. (2008) Walidacja krzyżowa modeli komponentów: krytyczne spojrzenie na obecne metody przeglądu i porównania. Jednym podejściem jest pominięcie jednego wymiaru jednego punktu danych na raz (tj. zamiast ), aby dane treningowe stały się macierzą z jedną brakującą wartością , a następnie przewidzieć („przypisać”) tę brakującą wartość za pomocą PCA. (Oczywiście można losowo wypuścić nieco większą część elementów macierzy, np. 10%). Problem polega na tym, że obliczanie PCA z brakującymi wartościami może być obliczeniowo dość powolne (opiera się na algorytmie EM), ale musi być tutaj wielokrotnie powtarzane. Aktualizacja: patrz http://alexhwilliams.info/itsneuronalblog/2018/02/26/crossval/x(i)jx(i) dla miłej dyskusji i implementacji w języku Python (PCA z brakującymi wartościami jest implementowany przez naprzemienne najmniejsze kwadraty).
Podejście, które uważam za bardziej praktyczne, polega na pomijaniu jednego punktu danych na raz, obliczaniu PCA na danych treningowych (dokładnie tak jak powyżej), ale następnie zapętlaniu wymiarów , pomiń je pojedynczo i oblicz resztę przy użyciu reszty. Na początku może to być mylące, a formuły stają się dość nieuporządkowane, ale implementacja jest raczej prosta. Pozwól mi najpierw podać (nieco przerażającą) formułę, a następnie krótko ją wyjaśnić:x(i)x(i)
PRESSPCA=∑i=1n∑j=1d∣∣∣x(i)j−[U(−i)[U(−i)−j]+x(i)−j]j∣∣∣2.
Rozważ tutaj wewnętrzną pętlę. Pominęliśmy jeden punkt i obliczyliśmy głównych składników danych treningowych, . Teraz trzymamy każdą wartość jako test i używamy pozostałych wymiarów do wykonania prognozy . Prognozowanie jest współrzędną „rzutu” (w sensie najmniejszych kwadratów) na podprzestrzeń rozpiętą autor: . Aby to obliczyć, znajdź punkt w przestrzeni komputera który jest najbliżejx(i)kU(−i)x(i)jx(i)−j∈Rd−1x^(i)jjx(i)−jU(−i)z^Rkx(i)−j obliczając gdzie to z -tym rzędem wyrzucony, a oznacza pseudoinwersję. Teraz mapuj powrotem do pierwotnego miejsca: i weź -tą współrzędną . z^=[U(−i)−j]+x(i)−j∈RkU(−i)−jU(−i)j[⋅]+z^U(−i)[U(−i)−j]+x(i)−jj[⋅]j
Przybliżenie prawidłowego podejścia
Nie do końca rozumiem dodatkową normalizację stosowaną w PLS_Toolbox, ale oto jedno podejście, które idzie w tym samym kierunku.
Istnieje inny sposób odwzorowania na przestrzeń głównych komponentów: , tzn. po prostu weź transpozycję zamiast pseudo-odwrotnej. Innymi słowy, wymiar, który jest pominięty do testowania, w ogóle nie jest liczony, a odpowiadające mu ciężary są po prostu wykreślane. Myślę, że powinno to być mniej dokładne, ale często może być do zaakceptowania. Dobrą rzeczą jest to, że wynikową formułę można teraz wektoryzować w następujący sposób (pomijam obliczenia):x(i)−jz^approx=[U(−i)−j]⊤x(i)−j
PRESSPCA,approx=∑i=1n∑j=1d∣∣∣x(i)j−[U(−i)[U(−i)−j]⊤x(i)−j]j∣∣∣2=∑i=1n∥∥(I−UU⊤+diag{UU⊤})x(i)∥∥2,
gdzie napisałem jako dla zwięzłości, a oznacza ustawienie wszystkich elementów niediagonalnych na zero. Zauważ, że ta formuła wygląda dokładnie tak samo jak pierwsza (naiwne PRASA) z małą korektą! Zauważ również, że ta korekta zależy tylko od przekątnej , jak w kodzie PLS_Toolbox. Jednak formuła wciąż różni się od tego, co wydaje się być zaimplementowane w PLS_Toolbox, a tej różnicy nie mogę wyjaśnić. U d i a g {⋅} U U ⊤U(−i)Udiag{⋅}UU⊤
Aktualizacja (luty 2018 r.): Powyżej nazwałem jedną procedurę „poprawną”, a drugą „przybliżoną”, ale nie jestem już tak pewien, czy to ma sens. Obie procedury mają sens i myślę, że żadna nie jest bardziej poprawna. Naprawdę podoba mi się, że procedura „przybliżona” ma prostszą formułę. Pamiętam też, że miałem zbiór danych, w którym procedura „przybliżona” dała wyniki, które wyglądały na bardziej znaczące. Niestety nie pamiętam już szczegółów.
Przykłady
Oto porównanie tych metod dla dwóch dobrze znanych zestawów danych: zestawu danych Iris i zestawu danych wina. Zauważ, że metoda naiwna tworzy krzywą monotonicznie malejącą, podczas gdy dwie pozostałe metody dają krzywą z minimum. Zauważ ponadto, że w przypadku Iris metoda przybliżona sugeruje 1 PC jako liczbę optymalną, ale metoda pseudoinwersyjna sugeruje 2 PC. (Patrząc na dowolny wykres rozrzutu PCA dla zestawu danych Iris, wydaje się, że oba pierwsze komputery PC niosą jakiś sygnał.) W przypadku wina metoda pseudoinwersyjna wyraźnie wskazuje na 3 komputery, podczas gdy metoda przybliżona nie może zdecydować między 3 a 5.
Kod Matlab do przeprowadzania weryfikacji krzyżowej i wykreślania wyników
function pca_loocv(X)
%// loop over data points
for n=1:size(X,1)
Xtrain = X([1:n-1 n+1:end],:);
mu = mean(Xtrain);
Xtrain = bsxfun(@minus, Xtrain, mu);
[~,~,V] = svd(Xtrain, 'econ');
Xtest = X(n,:);
Xtest = bsxfun(@minus, Xtest, mu);
%// loop over the number of PCs
for j=1:min(size(V,2),25)
P = V(:,1:j)*V(:,1:j)'; %//'
err1 = Xtest * (eye(size(P)) - P);
err2 = Xtest * (eye(size(P)) - P + diag(diag(P)));
for k=1:size(Xtest,2)
proj = Xtest(:,[1:k-1 k+1:end])*pinv(V([1:k-1 k+1:end],1:j))'*V(:,1:j)';
err3(k) = Xtest(k) - proj(k);
end
error1(n,j) = sum(err1(:).^2);
error2(n,j) = sum(err2(:).^2);
error3(n,j) = sum(err3(:).^2);
end
end
error1 = sum(error1);
error2 = sum(error2);
error3 = sum(error3);
%// plotting code
figure
hold on
plot(error1, 'k.--')
plot(error2, 'r.-')
plot(error3, 'b.-')
legend({'Naive method', 'Approximate method', 'Pseudoinverse method'}, ...
'Location', 'NorthWest')
legend boxoff
set(gca, 'XTick', 1:length(error1))
set(gca, 'YTick', [])
xlabel('Number of PCs')
ylabel('Cross-validation error')
tempRepmat(kk,kk) = -1
linii? Czy poprzednia linia nie zapewnia jużtempRepmat(kk,kk)
równości -1? Ponadto, dlaczego minusy? Błąd i tak zostanie wyrównany, więc czy rozumiem poprawnie, że jeśli minusy zostaną usunięte, nic się nie zmieni?