Те, що ви робите, не так: не має сенсу обчислювати PRESS для PCA так! Зокрема, проблема полягає у вашому кроці №5.
Наївний підхід до PRESS для PCA
Нехай набір даних складається з точок у -вимірному просторі: . Щоб обчислити помилку відновлення для однієї точки тестування даних , ви виконуєте PCA на навчальному наборі без цього пункту виключається певна кількість основних осей як стовпці , і знайдіть помилку відновлення як . Тоді PRESS дорівнює сумі для всіх тестових зразківdndx(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.
Для простоти я ігнорую питання центрування та масштабування тут.
Наївний підхід неправильний
Проблема вище полягає в тому, що ми використовуємо для обчислення прогнозу , і це дуже погана річ.x(i)x^(i)
Зверніть увагу на вирішальну відмінність випадку регресії, коли формула помилки відновлення в основному однакова , але прогнозування обчислюється за допомогою змінних предиктора та не використовує . Це неможливо в PCA, оскільки в PCA немає залежних і незалежних змінних: всі змінні обробляються разом.∥∥y(i)−y^(i)∥∥2y^(i)y(i)
На практиці це означає, що PRESS, як було обчислено вище, може зменшуватися зі збільшенням кількості компонентів і ніколи не досягати мінімуму. Що привело б до думки, що всі компоненти є важливими. Або, можливо, в деяких випадках це досягає мінімуму, але все ще має тенденцію перевищувати та завищувати оптимальні розміри.kd
Правильний підхід
Існує кілька можливих підходів, див. Бро та ін. (2008) Перехресна перевірка компонентних моделей: критичний погляд на сучасні методи огляду та порівняння. Один із підходів полягає в тому, щоб залишити один вимір однієї точки даних за раз (тобто замість ), щоб дані тренінгу стали матрицею з одним відсутнім значенням , а потім передбачити ("імпультувати") це відсутнє значення за допомогою PCA. (Звичайно, можна випадковим чином витримати більшу частину елементів матриці, наприклад, 10%). Проблема полягає в тому, що обчислення PCA з відсутніми значеннями може бути обчислювально досить повільним (воно покладається на алгоритм EM), але його тут потрібно багато разів повторювати. Оновлення: див. Http://alexhwilliams.info/itsneuronalblog/2018/02/26/crossval/x(i)jx(i) для приємного обговорення та реалізації Python (PCA з відсутніми значеннями реалізується через чергування найменших квадратів).
Підхід, який я вважав набагато практичнішим, полягає в тому, щоб залишити одну точку даних за один раз, обчислити PCA на навчальних даних (точно так, як вище), але потім перевести цикл на розміри , залиште їх по черзі та обчисліть помилку відновлення за допомогою решти. На початку це може бути досить заплутаним, і формули, як правило, стають досить безладними, але реалізація є досить простою. Дозвольте спочатку дати формулу (дещо страшно), а потім коротко пояснити:x(i)x(i)
PRESSPCA=∑i=1n∑j=1d∣∣∣x(i)j−[U(−i)[U(−i)−j]+x(i)−j]j∣∣∣2.
Розглянемо тут внутрішню петлю. Ми залишили одну точку і обчислили основні компоненти на навчальних даних . Тепер ми зберігаємо кожне значення як тест і використовуємо інші розміри для виконання прогнозування . Прогнозування - -та координата "проекції" (у значенні найменших квадратів) на підпростір. від . Для його обчислення знайдіть точку в просторі ПК яка найближча до k U ( - i ) x ( i ) j x ( i ) - j ∈ i ) - j ] + x ( i ) - j ∈ R k U (x(i)kU(−i)x(i)jx(i)−j∈Rd−1x^(i)jjx(i)−jU(−i)z^Rkx(i)−j шляхом обчислення де є з -м рядом вигнали, і означає псевдоінверс. Тепер поверніть карту назад у вихідний простір: і візьмемо його ю координату . z^=[U(−i)−j]+x(i)−j∈RkU(−i)−jU(−i)j[⋅]+z^U(−i)[U(−i)−j]+x(i)−jj[⋅]j
Наближення до правильного підходу
Я не зовсім розумію додаткову нормалізацію, що використовується в PLS_Toolbox, але ось один підхід, який йде в тому ж напрямку.
Є ще один спосіб відобразити на простір основних компонентів: , тобто просто перейміть замість псевдо-зворотного. Іншими словами, розмір, який залишається для тестування, взагалі не рахується, а відповідні ваги також просто витісняються. Я думаю, що це має бути менш точним, але часто може бути прийнятним. Хороша річ у тому, що отриману формулу тепер можна векторизувати наступним чином (я опускаю обчислення):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,
де я написав як для компактності, а означає встановлення всіх недіагональних елементів на нуль. Зауважте, що ця формула виглядає точно як перша (наївна ПРЕСА) з невеликою корекцією! Зауважте також, що ця корекція залежить лише від діагоналі , як у коді PLS_Toolbox. Однак формула все ще відрізняється від того, що, здається, реалізовано в PLS_Toolbox, і цю різницю я не можу пояснити. U d i a g {⋅} U U ⊤U(−i)Udiag{⋅}UU⊤
Оновлення (лютий 2018): Вище я назвав одну процедуру "правильною", а іншу - "приблизною", але я вже не впевнений, що це має сенс. Обидві процедури мають сенс, і я вважаю, що жодна з них не є більш коректною. Мені дуже подобається, що "приблизна" процедура має більш просту формулу. Крім того, я пам'ятаю, що у мене був деякий набір даних, де "приблизна" процедура давала результати, які виглядали більш значущими. На жаль, я вже не пам'ятаю деталей.
Приклади
Ось як порівняти ці методи для двох відомих наборів даних: набір даних Iris і набір даних про вино. Зауважимо, що наївний метод виробляє монотонно спадаючу криву, тоді як інші два методи дають криву з мінімумом. Далі зауважимо, що у випадку Іриса приблизний метод пропонує 1 ПК як оптимальне число, але псевдоінверсний метод пропонує 2 ПК. (І дивлячись на будь-який розсіювач PCA для набору даних Iris, здається, що обидва перші ПК несуть деякий сигнал.) А у винному випадку псевдоінверсний метод чітко вказує на 3 ПК, тоді як приблизний метод не може визначитись між 3 і 5.
Код Matlab для виконання перехресної перевірки та побудови результатів
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
лінії? Чи не попередній рядок вже гарантує, щоtempRepmat(kk,kk)
дорівнює -1? Також, чому мінуси? Помилка все одно буде зафіксована, тому я правильно розумію, що якщо мінуси будуть зняті, нічого не зміниться?