Lo que está haciendo está mal: ¡no tiene sentido calcular PRESS para PCA así! Específicamente, el problema radica en su paso # 5.
Enfoque ingenuo de PRENSA para 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.
Por simplicidad, estoy ignorando los problemas de centrado y escalado aquí.
El enfoque ingenuo está mal
El problema anterior es que usamos para calcular la predicción , y eso es algo muy malo.x(i)x^(i)
Tenga en cuenta la diferencia crucial para un caso de regresión, donde la fórmula para el error de reconstrucción es básicamente la misma , pero la predicción se calcula usando las variables predictoras y no usando . Esto no es posible en PCA, porque en PCA no hay variables dependientes e independientes: todas las variables se tratan juntas.∥∥y(i)−y^(i)∥∥2y^(i)y(i)
En la práctica, significa que PRESS, como se calculó anteriormente, puede disminuir al aumentar el número de componentes y nunca alcanzar un mínimo. Lo que llevaría a pensar que todos los componentes son significativos. O tal vez en algunos casos alcanza un mínimo, pero aún tiende a sobreajustar y sobreestimar la dimensionalidad óptima.kd
Un enfoque correcto
Hay varios enfoques posibles, ver Bro et al. (2008) Validación cruzada de modelos de componentes: una mirada crítica a los métodos actuales para una visión general y comparación. Un enfoque es dejar de lado una dimensión de un punto de datos a la vez (es decir, lugar de ), para que los datos de entrenamiento se conviertan en una matriz con un valor faltante , y luego predecir ("imputar") este valor faltante con PCA. (Por supuesto, uno puede mantener al azar una fracción mayor de elementos de la matriz, por ejemplo, 10%). El problema es que computar PCA con valores faltantes puede ser computacionalmente bastante lento (depende del algoritmo EM), pero debe repetirse aquí muchas veces. Actualización: ver http://alexhwilliams.info/itsneuronalblog/2018/02/26/crossval/x(i)jx(i) para una discusión agradable y la implementación de Python (PCA con valores faltantes se implementa a través de mínimos cuadrados alternos).
Un enfoque que me pareció mucho más práctico es dejar de lado un punto de datos a la vez, calcular PCA en los datos de entrenamiento (exactamente como se indica arriba), pero luego recorrer las dimensiones de , déjelos fuera uno a la vez y calcule un error de reconstrucción usando el resto. Esto puede ser bastante confuso al principio y las fórmulas tienden a volverse bastante desordenadas, pero la implementación es bastante sencilla. Permítanme primero dar la fórmula (algo aterradora) y luego explicarla brevemente:x(i)x(i)
PRESSPCA=∑i=1n∑j=1d∣∣∣x(i)j−[U(−i)[U(−i)−j]+x(i)−j]j∣∣∣2.
Considere el bucle interno aquí. Dejamos un punto y calculamos componentes principales en los datos de entrenamiento, . Ahora mantenemos cada valor como prueba y utilizamos las dimensiones restantes para realizar la predicción . La predicción es la coordenada de "la proyección" (en el sentido de los mínimos cuadrados) de en el subespacio por . Para calcularlo, encuentre un punto en el espacio de la PC que esté más cerca dex(i)kU(−i)x(i)jx(i)−j∈Rd−1x^(i)jjx(i)−jU(−i)z^Rkx(i)−j calculando donde es con la fila expulsado, y significa pseudoinverso. Ahora asigne al espacio original: y tome su coordenada . z^=[U(−i)−j]+x(i)−j∈RkU(−i)−jU(−i)j[⋅]+z^U(−i)[U(−i)−j]+x(i)−jj[⋅]j
Una aproximación al enfoque correcto
No entiendo la normalización adicional utilizada en PLS_Toolbox, pero aquí hay un enfoque que va en la misma dirección.
Hay otra forma de mapear en el espacio de componentes principales: , es decir, simplemente tome la transposición en lugar de pseudo-inversa. En otras palabras, la dimensión que queda fuera para la prueba no se cuenta en absoluto, y los pesos correspondientes también se eliminan simplemente. Creo que esto debería ser menos preciso, pero a menudo podría ser aceptable. Lo bueno es que la fórmula resultante ahora se puede vectorizar de la siguiente manera (omito el cálculo):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,
donde escribí como para compacidad, y significa poner todos los elementos no diagonales a cero. ¡Tenga en cuenta que esta fórmula se ve exactamente como la primera (PRENSA ingenua) con una pequeña corrección! Tenga en cuenta también que esta corrección solo depende de la diagonal de , como en el código PLS_Toolbox. Sin embargo, la fórmula sigue siendo diferente de lo que parece implementarse en PLS_Toolbox, y no puedo explicar esta diferencia.U(−i)Udiag{⋅}UU⊤
Actualización (febrero de 2018): anteriormente llamé a un procedimiento "correcto" y otro "aproximado", pero ya no estoy tan seguro de que esto sea significativo. Ambos procedimientos tienen sentido y creo que ninguno es más correcto. Realmente me gusta que el procedimiento "aproximado" tenga una fórmula más simple. Además, recuerdo que tenía un conjunto de datos donde el procedimiento "aproximado" arrojó resultados que parecían más significativos. Desafortunadamente, ya no recuerdo los detalles.
Ejemplos
Así es como se comparan estos métodos para dos conjuntos de datos conocidos: el conjunto de datos Iris y el conjunto de datos de vino. Tenga en cuenta que el método ingenuo produce una curva decreciente monotónicamente, mientras que otros dos métodos producen una curva con un mínimo. Tenga en cuenta además que en el caso de Iris, el método aproximado sugiere 1 PC como el número óptimo, pero el método pseudoinverso sugiere 2 PC. (Y mirando cualquier diagrama de dispersión de PCA para el conjunto de datos de Iris, parece que ambas primeras PC tienen alguna señal). Y en el caso del vino, el método pseudoinverso apunta claramente a 3 PC, mientras que el método aproximado no puede decidir entre 3 y 5.
Código de Matlab para realizar validación cruzada y trazar los resultados
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
línea? ¿La línea anterior ya no garantiza que seatempRepmat(kk,kk)
igual a -1? Además, ¿por qué menos? El error se va a cuadrar de todos modos, ¿entiendo correctamente que si se eliminan las desventajas, nada cambiará?