То, что вы делаете, неправильно: вычислять PRESS для PCA не имеет смысла! В частности, проблема заключается в вашем шаге № 5.
Наивный подход к PRESS для PCA
Пусть набор данных состоит из точек в d- мерном пространстве: x ( i ) ∈ R d ,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.
Для простоты я игнорирую проблемы центрирования и масштабирования здесь.
Наивный подход неправильный
Проблема выше в том, что мы используем для вычисления предсказания , и это очень плохо.x(i)x^(i)
Обратите внимание на принципиальное отличие от случая регрессии, где формула ошибки восстановления в основном такая же: , но прогноз вычисляется с использованием переменных-предикторов, а не с использованием . Это невозможно в PCA, потому что в PCA нет зависимых и независимых переменных: все переменные обрабатываются вместе.∥∥y(i)−y^(i)∥∥2y^(i)y(i)
На практике это означает, что PRESS, как вычислено выше, может уменьшаться с увеличением количества компонентов и никогда не достигать минимума. Что заставило бы думать, что все компоненты значимы. Или, может быть, в некоторых случаях он достигает минимума, но все же имеет тенденцию переоценивать и переоценивать оптимальную размерность.kd
Правильный подход
Существует несколько возможных подходов, см. Bro et al. (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.
Рассмотрим внутренний цикл здесь. Мы пропустили одну точку и вычислили основных компонентов на обучающих данных, . Теперь мы сохраняем каждое значение в качестве теста и используем оставшиеся измерения для выполнения прогноза , Предсказание - это координата "проекции" (в смысле наименьших квадратов) на подпространство, охватываемое по . Чтобы вычислить его, найдите точку в пространстве ПК которая ближе всего к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 и набор данных wine. Обратите внимание, что наивный метод создает монотонно убывающую кривую, тогда как два других метода дают кривую с минимумом. Отметим далее, что в случае радужной оболочки, приближенный метод предлагает 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? Кроме того, почему минусы? В любом случае ошибка будет возведена в квадрат, так что я правильно понимаю, что если убрать минусы, ничего не изменится?