1.偏最小二乘法(NIPALS經典實作--未簡化)
2.偏最小二乘法 基本性質推導
3.OLS,PCA,CCA,PLS和CR的關系總結及幾何解釋
前面兩篇文章,實作了PLS最原始的NIPALS算法,并對PLS的基本性質做了一些推導,對PLS總算有一個基本認識。但是,那些隻是開胃小菜,最多隻能算站在PLS的大門口,還沒邁進去。這篇文章對SIMPLS算法進行實作和讨論,達到邁入半隻腳的境界。
原始的NIPALS算法可謂啰嗦,反複疊代,對E和F的成分提了又提,完了,還有對系數做一個反标準化,完全不理會别人受得了受不了。當然,NIPALS也有優化版本,比如F的成分提取完全是多餘,我們都知道通過投影軸w,我們已經可以推出q,v,r,隐隐中,又許多可以優化的地方。比如說F的成分有必要提取嗎?答案是沒有必要
以後,再實作優化的NIPALS再做詳細讨論。言歸正傳,目前先對SIMPLS開刀
看了SIMPLS的參考文獻,描述的符号總是不同,就是相同,代表的含義已經不同,看了總是有一種精神分裂的感覺。這裡按照軟體設計的開閉原則,盡量保留之前的所有變量意義不變,看看SIMPLS算法到底搞了什麼飛機。
首先,看一下SIMPLS的計算方式,先看看它的非簡化版本。
假定E,F已經中心化
SIMPLS的目标函數以及限制
SIMPLE不再直接計算E,F的殘差陣,直接計算從協方差矩陣S = E'T裡面提取資訊。投影軸w,v是正交的,就這一點,是否計算殘差E,F是沒有意義的,關鍵是S矩陣必須要調整,否則,無法保證w和v的正交性,提取結果的有效性。那麼如何保持正交性呢。先看一下限制,這幾項和NIPALS看上去并沒什麼差别,事實上,對NIPALS而言,這些限制隻是它的一個結果,并非前提。w,v正交比較容易了解,先看看要滿足最後一項需要具備什麼條件。
j>i,也就是說後續的wj對pi正交。對于
從PLS的性質推導裡面可以看到,
和
之間并沒有必然的正交關系,是以,需要對
進行調整
将
分為
正交部分和投影到
的部分,
,最後隻保留
(零空間)
被稱為投影矩陣,事實上,我們要找與
正交的部分
w是S的左奇異矩陣中的向量,w與v的關系是
令
是以,隻要令S按照和P的正交方向分解下去即可,就能保證上述的幾點限制。P矩陣對我來說一直有點尴尬,它并不等于W,以至于難以去解釋它的幾何意義。這裡就暫時看成W的一個加強版吧。
從幾何意義上來說,這個過程是将S進行空間上的分解,分為與P正交的部分和P上的投影,正交部分作為殘差。為什麼選擇P呢,因為P算是S的特征向量W的近親,能盡可能多提取到S中的成分,符合PLS的思路。為什麼P不能直接用W表示呢,讓人看着實在揪心,後續文章再揭曉。步子不能太大,會扯着0.
總結一下SIMPLS原始版本的計算過程,E,F已經經過中心化
上代碼
function [beta,W] = myplsregress_simpls1(X,Y,ncomp)
%{
Input:
X,Y --------輸入
ncomp-------潛在因子,提取的成分數量
output:
beta---------模型系數
中間變量解釋
E,F----------X,Y中心化後的資料
w ----------E的投影軸
t ----------E的得分
p------------E的回歸系數,載荷
大寫代表矩陣,小寫代表向量
同時也約定,W代表由w的矩陣
%}
MU_x = mean(X);
MU_y = mean(Y);
%資料标準化,變量記做 E和 F
E = bsxfun(@minus,X,MU_x);
F = bsxfun(@minus,Y,MU_y);
S = E'*F;
Si = S;
n=size(X,2);m=size(Y,2); %n 是自變量的個數,m 是因變量的個數
for i=1:ncomp
[U1,S1,V1]=svd(Si);
w=U1(:,1);
t = E*w;
p = E'*t/(t'*t);
W(:,i) = w;
T(:,i) = t;
P(:,i) = p;
Si = S-P*inv(P'*P)*P'*S;
end
B = W*pinv(T)*F;
%系數變換
for i=1:m
b = MU_y(1,i)-sum(B(:,i)'.*MU_x);
coeff = B(:,i)';
beta(:,i) =[b coeff]';
end
end
測試代碼
load spectra
[xl,yl,xs,ys,beta,pctvar,mse] = plsregress(NIR,octane,10);
plot(octane,'g');hold on;
plsfit = [ones(size(NIR,1),1) NIR]*beta;
plot(plsfit,'or');
[beta,W,P,T] = myplsregress_simpls1(NIR,octane,10);
myplsfit= [ones(size(NIR,1),1) NIR]*beta;
plot(myplsfit,'*b');
legend('original','plsregress','myplsregress-simpls1')
跟matlab自帶的plsregress算法結果基本重合,這個也很正常,它預設就是用simpls,後續會扒一扒它的simpls實作過程,不過先将SIMPLS算法再進一步完善一下,加入解釋比列等等,再跟matlab的simpls對比一下,後面想再讨論一下SIMPLS和NIPALS的差異。另外得吐槽一下,雖然代碼寫得醜,但我還是說一下,csdn的代碼段就不能支援一下matlab的高亮顯示,好讓我無需鼓足勇氣就能把代碼傳上來。
PS:我用C已經有十多年的時間,自從開始研究資料科學以來,我已經基本放棄C了,當年為了将一個稀疏版的pls從R語言轉為C,那代碼寫了幾天,調了一個多星期,從此對C這樣的語言痛心疾首。現在也沒有必須用到C的工作,是以對于我來說,Life is short, get far away from C。