Vectorization in Machine Learning

對於有些問題,如果使用了合適的向量化方法,代碼就會變得簡單得多而且有效得多。

我們來看一些例子:

這是一個常見的線性迴歸假設函數:

如果想要計算h(x),注意右邊是一個求和公式。那麼可以自己來計算 j = 0到 j = n的和。

但換另一種方式來想一想,把h(x)看做是θ的轉置乘以x,那麼就可以表示成兩個向量的內積。假設這裏有兩個特徵向量(也就是n等於2)的話,那麼θ就是[θ_0; θ_1; θ_2],並且把x看做一個向量,x = [x_0; x_1; x_2]。

這兩種思考角度,會給你兩種不同的實現方式。

如下圖所示,左邊是未向量化的代碼來計算h(x)。這樣的話,我們可能首先要初始化變量prediction的值爲0.0,而這個變量prediction的最終結果就是h(x)。然後要用一個for循環,j取值1到n+1,變量prediction每次更新爲自身加上θ(j) * x(j),這個就是算法的代碼實現。

順便提醒一下,這裏的向量用的下標是從0開始的,但因爲MATLAB下標從1開始,在MATLAB中可能會用θ1來表示θ0,θ1用θ2來表示,而第三個元素就用θ3表示。

儘管實際的θ和x,下標從0開始,但MATLAB中的向量下標從1開始。這就是爲什麼這裏的for循環中j的取值是從1到n+1,而不是從0到n。

這是一個未向量化的代碼實現方式,我們要用一個for循環對n個元素進行加和。

作爲比較,接下來是向量化的代碼實現。

將x和θ看做向量,然後只需要令prediction等於θ的轉置乘以x。與其寫for循環的多行代碼,而現在只需要一行代碼,這行代碼就是利用Octave高度優化的數值線性代數算法,來計算θ和x兩個向量的內積。向量化的實現方式不僅代碼更簡單,也會讓運行更高效。

這就是Octave所做的,而向量化的方法,在其他編程語言中同樣可以實現。

來看一個用C++實現的例子:

這就是未向量化的代碼。同樣地先初始化變量prediction爲0.0,然後是一個for循環,j等於從0到n,然後變量prediction每次等於自身加上 θ[j] * x[j]。

這樣也是自己寫了一個for循環。與此相反,利用C++數值線性代數庫,就可以用下圖這個方程,這取決於你的數值線性代數庫的內容,或許可以有一個C++對象向量theta和另一個C++對象向量x,然後只需要用 theta.transpose() * x  ,然後讓C++來實現運算。只需要在C++中讓兩個向量相乘。根據你所使用的數值線性代數庫,代碼表達方式可能會有些許不同,但通過一個內置庫來計算內積,可以得到一段更簡單更有效的代碼。

 

再來看一個更爲複雜的例子:

先提醒一下線性迴歸梯度下降算法的更新規則,對 j 等於 0、1、2等等的所有值,更新對象θ_j。假設只有兩個特徵量,也就是n等於2。然後羅列一些方程,用θ_0、θ_1、θ_2做例子。並且這些更新應該是同步更新的。

 

下面是同樣的三個方程:

可以想象,實現這三個方程的方法就是用一個for循環,讓 j 等於 0、1、2來更新θ_j。

但下面來用向量化的方式實現。看看是否有更簡單的方法來壓縮這三行代碼,或者說是這個for循環,一次來實現這三個方程。

接下來給出將這三步壓縮成一行的向量化的代碼。思路是這樣的:將θ看做一個向量,然後更新向量θ爲θ減去α乘以某個向量δ,這裏的 δ等於1/m 乘以 從i=1到m的一個表達式的總和,如下圖所示。

下面來解釋一下,把θ看做一個向量,一個n+1維的向量。α是一個實數,而δ是一個n+1維的向量,所以這個減法是一個向量減法。因爲α乘以δ是一個向量,所以θ更新爲θ - α乘以 δ得到的向量。那麼向量δ是什麼呢?其實就是下圖紅色框內的部分:

 

具體地說,δ是n+1維向量,向量δ的第一個元素就是下圖綠色框中的部分:

如果δ下標從0開始,δ就是下圖這樣子,其中δ_0就是上一張圖的綠色框內的部分:

 

如果在實施線性迴歸的時候,使用兩個以上的特徵量,有時我們在線性迴歸中使用幾十、幾百個、甚至幾千個特徵,當使用向量化實現線性迴歸時,相比過去使用for循環來更新θ_0、θ_1、θ_2,通常運行速度會更快。使用向量化實現方式,應該能夠得到一個高效得多的線性迴歸算法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章