認(rèn)為octave - 矩陣算法的便捷性
????????很多人認(rèn)為Octave帶來(lái)很便捷的矩陣算法,所以通常計(jì)算矩陣的時(shí)候不再用普通的點(diǎn)乘求和再遍歷的方式,直接就是兩個(gè)矩陣相乘就可以求和。殊不知常常沒(méi)搞懂兩個(gè)矩陣的行列數(shù)是否對(duì)應(yīng),就直接相乘,結(jié)果是錯(cuò)漏百出,各種調(diào)試,絞盡腦汁,都不知道出錯(cuò)在哪。 ??
? ? ? ? 我在做machina learning第六章的作業(yè)----machina-learning-ex1使用Octave計(jì)算梯度算法的時(shí)候就吃過(guò)虧:我想一步到位直接就用訓(xùn)練集和相關(guān)參數(shù)的矩陣運(yùn)算就能得出最優(yōu)解,然而終不可得。后來(lái)當(dāng)我拿出草稿紙,一步一步,循序漸進(jìn)地寫出梯度算法的細(xì)化的每個(gè)步驟的時(shí)候,又可以得出個(gè)正確答案,雖然沒(méi)有達(dá)到牛人那種簡(jiǎn)潔而又一句到位的寫法,但是想起以前學(xué)習(xí)太極拳的老師傅的一句話:基礎(chǔ)要慢慢打好,不要想著一步就想把高深的拳術(shù)學(xué)習(xí)到位;就算讓一個(gè)新人馬上學(xué)習(xí)到怎么打好太極拳,他的基礎(chǔ)也是很薄弱的,站樁都站不穩(wěn),別人一推就倒。學(xué)習(xí)算法也是這樣,當(dāng)有兩條路給你選擇,一條是捷徑帶有風(fēng)險(xiǎn),另一條是崎嶇難行,走的路比較長(zhǎng),但是一定能到達(dá)終點(diǎn),那你一定要選擇難走的那一條。百分之200的人會(huì)選擇容易走的那一條,當(dāng)他們遇到危險(xiǎn)挫折的時(shí)候,由于潛意識(shí)決定他們寧愿走容易走的路,所以他們會(huì)避開繞開崎嶇,從而不能體會(huì)到解決困難的過(guò)程,甚至缺乏相關(guān)的經(jīng)驗(yàn)。所以對(duì)于新人來(lái)說(shuō),一定要時(shí)時(shí)刻刻保持良好的思維習(xí)慣,多選擇走正確的路,choose the right way , but not the easy one.以后的路都會(huì)好走很多。
? ? ? ? 話不多說(shuō),先看公式:


????????第一章其實(shí)已經(jīng)講過(guò)梯度算法求解theta最優(yōu)解和h(θ)的公式,我把這個(gè)公式在草稿紙一畫——這不正是兩矩陣相乘得出的結(jié)果嗎?后來(lái)我就把X * theta - y先寫上去,然后到了Xj(i)這一步,想來(lái)想去不知道怎么寫,因?yàn)閄 * theta - y得出的結(jié)果是100行1列的矩陣,而X是取j列向量相乘的,后面還要求和乘以a/m,theta還要減去他們的和,要知道theta是3行1列的向量,怎么減去一百行1列的矩陣,感覺(jué)不對(duì)啊,昨晚想了一個(gè)晚上。最后還是先睡覺(jué)。
【深入淺出,從丑陋的寫法開始】
? ? ? ? 今天早上想到一個(gè)辦法,就從最容易理解的for循環(huán)多重循環(huán)入手,不要想著一步到位,雖然這是一個(gè)很丑的寫法,但是this is the right way,對(duì)于新人的我,可以深入淺出地理解梯度算法的過(guò)程,又可以得出正確答案,這不是一舉兩得嗎?
1. for循環(huán)梯度算法(i,j)
首先我們知道for循環(huán)是先對(duì)

以i為參數(shù)求和,(j暫時(shí)等于1先不管它)
先結(jié)合上面的h(θ)公式做個(gè)轉(zhuǎn)換 Σ(θ1x(i,1)+θ1x(i,1)-y(i)) * x(i,j)?
再拆解
i = 1 ?, ? ?((θ1*x(1,1)+θ1*x(1,1)) - y1) * x(1,1)
i = 2 ?, ? ?((θ1*x(2,1)+θ1*x(2,1)) - y2) * x(2,1)
i = 3 ?, ? ?((θ1*x(3,1)+θ1*x(3,1)) - y3) * x(3,1)
...
這不就是可以理解為x的第i行于θ向量相乘,再減去y的第i個(gè),再誠(chéng)意x的第i行,第j個(gè)
Octave寫法為 ? ?(X(i,:) * theta - y(i)) * X(i,j);
然后就是加個(gè)for循環(huán), 用sumTemp作為求和的臨時(shí)變量
sumTemp = 0;
for i = 1:m,
sumTemp = sumTemp + (X(i,:) * theta - y(i)) * X(i,j);? %theta is column vector
end;
theta公式的右半部分的delta好解決:
deltaTemp = sumTemp * a/m
然后就是theta(j) = theta(j) - deltaTemp;
外面再嵌套一個(gè)For循環(huán)to j 整個(gè)代碼就是:
for j = 1:size(theta),
?sumTemp = 0;
?for i = 1:m,
? sumTemp = sumTemp + (X(i,:) * theta - y(i)) * X(i,j);? %theta is column vector
?end;
?deltaTemp = sumTemp * alpha / m
?theta(j) = theta(j) - deltaTemp;
end;
2.for循環(huán)(i)
再看看我們上面的代碼有什么可以再優(yōu)化的地方,
對(duì)于(X(i,:) * theta - y(i))各i行求和,其實(shí)就是
X的逐個(gè)行和theta相乘,X是百行三列,theta,三行1列,得出來(lái)回是一個(gè)百行1列的數(shù)據(jù),正好y得百行一列可以相減,得出一個(gè)新的列向量,與X(:,j)的百行一列相點(diǎn)乘
(X * theta - y ) .* X(:,j)
再求和就是一個(gè)值,再得出delta
delta =(a/m) *?sum((X * theta - y ) .* X(:,j))
然后
theta(j) = theta(j) - delta
跑一下Octave驗(yàn)證一下(你不想Octave不斷打印delta最后句末加個(gè);)
% ====================== For loop to j ======================
?for j = 1:size(theta),
?deltaTemp = (alpha/m) * sum((X * theta - y ) .* X(:,j));
?theta(j) = theta(j) - deltaTemp;
?end;
3.去掉所有for循環(huán)
再看看有什么可以改進(jìn)的
theta是一個(gè)3行1列向量,deltaTemp也可以做成一個(gè)三行1列的向量,
deltaTemp(j) =??(alpha/m) * sum((X * theta - y ) .* X(:,j));
((X * theta - y ) 它就是一個(gè)百行一列,跟X逐個(gè)逐個(gè)列相乘,會(huì)得出百行三列的數(shù)據(jù),此時(shí)對(duì)這個(gè)百行三列分別列求和,就可以得出一個(gè)新的一行三列矩陣,
再分別被theta減去。那么可以
((alpha / m) * sum((X * theta - y) .* X))'
最后
theta = theta - ((alpha / m) * sum((X * theta - y) .* X))';
(sum()方法對(duì)所有矩陣來(lái)說(shuō),都只對(duì)各列元素分別求和,并不是所有元素求和)
? ? ? ? 總結(jié):我分別用了三個(gè)方法,深入淺出,最終才能得出真正的一步到位,希望以后的學(xué)習(xí)過(guò)程中再難也能夠choose the right way, not the easy way.
P.S. : sum求和是對(duì)各列分別求和哦;另外如果不想頻繁打印,記得要再代碼后加;分號(hào),如果是沒(méi)有,Octave會(huì)默認(rèn)時(shí)刻輸出數(shù)據(jù)。