Estou tentando escrever minha própria função para análise de componentes principais, o PCA (é claro que já há muito escrito, mas estou interessado apenas em implementar coisas sozinho). O principal problema que encontrei é a etapa de validação cruzada e o cálculo da soma prevista dos quadrados (PRESS). Não importa qual validação cruzada eu uso, é uma questão principalmente sobre a teoria por trás, mas considere a validação cruzada de exclusão única (LOOCV). A partir da teoria, descobri que, para executar o LOOCV, você precisa:
- excluir um objeto
- escalar o resto
- executar PCA com algum número de componentes
- dimensione o objeto excluído de acordo com os parâmetros obtidos em (2)
- prever o objeto de acordo com o modelo PCA
- calcular PRESS para este objeto
- execute novamente o mesmo algoritmo para outros objetos
- resumir todos os valores PRESS
- lucro
Como sou muito novo em campo, para ter certeza de que estou certo, comparo os resultados com os resultados de alguns softwares que tenho (também para escrever um código, sigo as instruções do software). Recebo completamente os mesmos resultados calculando a soma residual dos quadrados e , mas calcular PRESS é um problema.
Você poderia me dizer se o que eu implemento na etapa de validação cruzada está certo ou não:
case 'loocv'
% # n - number of objects
% # p - number of variables
% # vComponents - the number of components used in CV
dataSets = divideData(n,n);
% # it is just a variable responsible for creating datasets for CV
% # (for LOOCV datasets will be equal to [1, 2, 3, ... , n]);'
tempPRESS = zeros(n,vComponents);
for j = 1:n
Xmodel1 = X; % # X - n x p original matrix
Xmodel1(dataSets{j},:) = []; % # delete the object to be predicted
[Xmodel1,Xmodel1shift,Xmodel1div] = skScale(Xmodel1, 'Center', vCenter,
'Scaling', vScaling);
% # scale the data and extract the shift and scaling factor
Xmodel2 = X(dataSets{j},:); % # the object to be predicted
Xmodel2 = bsxfun(@minus,Xmodel2,Xmodel1shift); % # shift and scale the object
Xmodel2 = bsxfun(@rdivide,Xmodel2,Xmodel1div);
[Xscores2,Xloadings2] = myNipals(Xmodel1,0.00000001,vComponents);
% # the way to calculate the scores and loadings
% # Xscores2 - n x vComponents matrix
% # Xloadings2 - vComponents x p matrix
for i = 1:vComponents
tempPRESS(j,i) = sum(sum((Xmodel2* ...
(eye(p) - transpose(Xloadings2(1:i,:))*Xloadings2(1:i,:))).^2));
end
end
PRESS = sum(tempPRESS,1);
No software ( PLS_Toolbox ), funciona assim:
for i = 1:vComponents
tempPCA = eye(p) - transpose(Xloadings2(1:i,:))*Xloadings2(1:i,:);
for kk = 1:p
tempRepmat(:,kk) = -(1/tempPCA(kk,kk))*tempPCA(:,kk);
% # this I do not understand
tempRepmat(kk,kk) = -1;
% # here is some normalization that I do not get
end
tempPRESS(j,i) = sum(sum((Xmodel2*tempRepmat).^2));
end
Portanto, eles fazem alguma normalização adicional usando essa tempRepmat
variável: a única razão pela qual descobri foi que eles aplicam LOOCV para PCA robusto. Infelizmente, a equipe de suporte não quis responder à minha pergunta, pois eu tenho apenas a versão demo de seu software.
fonte
tempRepmat(kk,kk) = -1
linha? A linha anterior já não está garantindo quetempRepmat(kk,kk)
seja igual a -1? Além disso, por que menos? O erro será quadrado de qualquer maneira, então eu entendi corretamente que se os menos foram removidos, nada mudará?Respostas:
O que você está fazendo está errado: não faz sentido calcular o PRESS para PCA dessa maneira! Especificamente, o problema está na etapa 5.
Abordagem ingênua do PRESS for PCA
Deixe o conjunto de dados constituído por pontos em espaço -dimensional: . Para calcular o erro de reconstrução para um único ponto de dados de teste , você executa o PCA no conjunto de treinamento com este ponto excluído, faça um determinado número de eixos principais como colunas de e localize o erro de reconstrução como . PRESS é então igual à soma de todas as amostras de tested x ( i ) ∈ R d ,n d x(i)∈Rd,i=1…n x(i) X(−i) k U(−i) i P∥∥x(i)−x^(i)∥∥2=∥∥x(i)−U(−i)[U(−i)]⊤x(i)∥∥2 i , portanto, a equação razoável parece ser:
Por simplicidade, estou ignorando os problemas de centralização e dimensionamento aqui.
A abordagem ingênua está errada
O problema acima é que usamos para calcular a previsão , e isso é uma coisa muito ruim.x(i) x^(i)
Observe a diferença crucial em um caso de regressão, onde a fórmula do erro de reconstrução é basicamente a mesma , mas a previsão é calculada usando as variáveis preditoras e não usando . Isso não é possível no PCA, porque no PCA não há variáveis dependentes e independentes: todas as variáveis são tratadas juntas.∥∥y(i)−y^(i)∥∥2 y^(i) y(i)
Na prática, isso significa que o PRESS, conforme calculado acima, pode diminuir com o aumento do número de componentes e nunca atingir um mínimo. O que levaria a pensar que todos os componentes são significativos. Ou talvez, em alguns casos, atinja um mínimo, mas ainda tende a superestimar e superestimar a dimensionalidade ideal.k d
Uma abordagem correta
Existem várias abordagens possíveis, veja Bro et al. (2008) Validação cruzada de modelos de componentes: uma visão crítica dos métodos atuais para uma visão geral e comparação. Uma abordagem é deixar de fora uma dimensão de um ponto de dados por vez (ou seja, vez de ), para que os dados de treinamento se tornem uma matriz com um valor ausente e, em seguida, prever ("imputar") esse valor ausente no PCA. (É claro que é possível armazenar aleatoriamente uma fração maior de elementos da matriz, por exemplo, 10%). O problema é que a computação do PCA com valores ausentes pode ser computacionalmente bastante lenta (depende do algoritmo EM), mas precisa ser iterada várias vezes aqui. Atualização: consulte http://alexhwilliams.info/itsneuronalblog/2018/02/26/crossval/x(i)j x(i) para uma boa discussão e implementação do Python (o PCA com valores ausentes é implementado por meio de mínimos quadrados alternados).
Uma abordagem que achei muito mais prática é deixar de fora um ponto de dados cada vez, computar o PCA nos dados de treinamento (exatamente como acima), mas depois percorrer as dimensões de , deixe-os de fora um de cada vez e calcule um erro de reconstrução usando o resto. Isso pode ser bastante confuso no começo e as fórmulas tendem a se tornar bastante confusas, mas a implementação é bastante direta. Deixe-me primeiro dar a fórmula (um pouco assustadora) e depois explicá-la brevemente:x(i) x(i)
Considere o loop interno aqui. Deixamos de lado um ponto e computamos componentes principais nos dados de treinamento, . Agora mantemos cada valor como teste e usamos as dimensões restantes para realizar a previsão . A previsão é a ésima coordenada da "projeção" (no sentido dos mínimos quadrados) de no subespaço estendido por . Para calcular, encontre um ponto no espaço do PC mais próximo de k U ( - i )x(i) k U(−i) x(i)j x(i)−j∈Rd−1 x^(i)j j x(i)−j U(−i) z^ Rk x(i)−j calculando onde é com -ésima linha expulso e significa pseudoinverso. Agora, mapeie volta ao espaço original: e assume a ésima coordenada . z^=[U(−i)−j]+x(i)−j∈Rk U(−i)−j U(−i) j [⋅]+ z^ U(−i)[U(−i)−j]+x(i)−j j [⋅]j
Uma aproximação à abordagem correta
Não entendo bem a normalização adicional usada no PLS_Toolbox, mas aqui está uma abordagem que segue na mesma direção.
Existe outra maneira de mapear no espaço dos componentes principais: , ou seja, simplesmente faça a transposição em vez de pseudo-inversa. Em outras palavras, a dimensão que é deixada de fora para o teste não é contada, e os pesos correspondentes também são simplesmente expulsos. Eu acho que isso deve ser menos preciso, mas muitas vezes pode ser aceitável. O bom é que a fórmula resultante agora pode ser vetorizada da seguinte forma (eu omito o cálculo):x(i)−j z^approx=[U(−i)−j]⊤x(i)−j
onde eu escrevi como para compacidade e significa definir todos os elementos não diagonais como zero. Observe que esta fórmula se parece exatamente com a primeira (IMPRENSA ingênua) com uma pequena correção! Observe também que essa correção depende apenas da diagonal de , como no código PLS_Toolbox. No entanto, a fórmula ainda é diferente do que parece ser implementado no PLS_Toolbox, e essa diferença não posso explicar. U d i a g {⋅} U U ⊤U(−i) U diag{⋅} UU⊤
Atualização (fev 2018): Acima, chamei um procedimento de "correto" e outro de "aproximado", mas não tenho mais tanta certeza de que isso seja significativo. Ambos os procedimentos fazem sentido e acho que nenhum dos dois é mais correto. Eu realmente gosto que o procedimento "aproximado" tenha uma fórmula mais simples. Além disso, lembro que tinha um conjunto de dados em que o procedimento "aproximado" produzia resultados que pareciam mais significativos. Infelizmente, não me lembro mais dos detalhes.
Exemplos
Aqui está como esses métodos se comparam para dois conjuntos de dados conhecidos: conjunto de dados Iris e conjunto de dados wine. Observe que o método ingênuo produz uma curva monotonicamente decrescente, enquanto outros dois métodos produzem uma curva com um mínimo. Observe ainda que, no caso Iris, o método aproximado sugere 1 PC como o número ideal, mas o método pseudo-inverso sugere 2 PCs. (E olhando para qualquer gráfico de dispersão de PCA para o conjunto de dados Iris, parece que os dois primeiros PCs transmitem algum sinal.) E no caso do wine, o método pseudoinverso aponta claramente para 3 PCs, enquanto o método aproximado não pode decidir entre 3 e 5.
Código Matlab para executar validação cruzada e plotar os resultados
fonte
i
Para adicionar um ponto ainda mais geral à boa resposta de @ amoeba:
Uma diferença prática e crucial entre os modelos supervisionados e não supervisionados é que, para os modelos não supervisionados, você precisa pensar muito mais sobre o que você consideraria equivalente e o que não.
Modelos supervisionadas sempre têm a sua última saída de uma forma onde você não precisa se preocupar muito com isso: por definição e construção, afirma ter o mesmo significado que , para que você possa compará-lo diretamente. y yy^ y^ y
Para construir medidas de desempenho significativas, você precisa pensar que tipos de liberdade do modelo são sem sentido para o seu aplicativo e quais não são. Isso levaria a uma IMPRENSA nas pontuações, possivelmente (geralmente?) Após algum tipo de rotação / inversão do tipo Procrustes.
IMPRENSA no x Meu palpite é (não tenho tempo agora para descobrir o que as duas linhas de código fazem - mas talvez você possa percorrer as linhas e dar uma olhada?):
Para obter uma medida que seja útil para determinar uma boa complexidade do modelo a partir de uma medida que ofereça uma qualidade de ajuste que normalmente aumentará até que o modelo de classificação completo seja atingido, você deve penalizar modelos muito complexos. O que, por sua vez, significa que essa penalização é a) crucial eb) o ajuste da penalidade ajustará a complexidade escolhida.
Nota lateral: gostaria de acrescentar que seria muito cuidadoso com esse tipo de otimização automatizada da complexidade do modelo. Na minha experiência, muitos desses algoritmos produzem apenas pseudo-objetividade e muitas vezes têm o custo de funcionar bem apenas para certos tipos de dados.
fonte