【機(jī)器學(xué)習(xí)基礎(chǔ)】線性回歸和梯度下降的初學(xué)者教程
編譯 | VK?
來(lái)源 | Towards Data Science

假設(shè)我們有一個(gè)虛擬的數(shù)據(jù)集,一對(duì)變量,一個(gè)母親和她女兒的身高:

考慮到另一位母親的身高為63,我們?nèi)绾晤A(yù)測(cè)她女兒的身高?
方法是用線性回歸。
首先,找到最合適的直線。然后用這條直線做預(yù)測(cè)。

?線性回歸是尋找數(shù)據(jù)集的最佳擬合線。這條線可以用來(lái)做預(yù)測(cè)。
?
「你如何找到最合適的?」
這就是使用梯度下降的原因。
?梯度下降是一種找到最佳擬合線的工具
?
在深入研究梯度下降之前,讓我們先看看另一種計(jì)算最佳擬合線的方法。
「最佳擬合線的統(tǒng)計(jì)計(jì)算方法:」
直線可以用公式表示:y=mx+b。
回歸線斜率m的公式為:
m = r * (SD of y / SD of x)
轉(zhuǎn)換:x和y值之間的相關(guān)系數(shù)(r),乘以y值的標(biāo)準(zhǔn)差(SD of y)除以x值的標(biāo)準(zhǔn)偏差(SD of x)。
以上數(shù)據(jù)中母親身高的標(biāo)準(zhǔn)差約為4.07。女兒身高的標(biāo)準(zhǔn)偏差約為5.5。這兩組變量之間的相關(guān)系數(shù)約為0.89。
因此,最佳擬合線或回歸線為:
y?=?0.89*(5.5?/?4.07)x?+?b
y?=?1.2x?+?b
我們知道回歸線穿過(guò)了平均點(diǎn),所以線上的一個(gè)點(diǎn)是(x值的平均值,y值的平均值),其中有(63.5,63.33)
63.33?=?1.2*63.5?+?b
b?=?-12.87
因此,使用相關(guān)系數(shù)和標(biāo)準(zhǔn)差計(jì)算的回歸線近似為:
y?=?1.2x?-?12.87
使用統(tǒng)計(jì)學(xué)的回歸線為y=1.2x-12.87
現(xiàn)在,讓我們來(lái)研究梯度下降。
「計(jì)算最佳擬合線的梯度下降法:」
在梯度下降中,你從一條隨機(jī)線開(kāi)始。然后一點(diǎn)一點(diǎn)地改變直線的參數(shù)(即斜率和y軸截距),以得到最佳擬合的直線。
你怎么知道你什么時(shí)候到達(dá)最合適的位置?
對(duì)于你嘗試的每一條直線——直線A、直線B、直線C等等——你都要計(jì)算誤差的平方和。如果直線B的值比直線A的誤差小,那么直線B更適合,等等。

誤差是你的實(shí)際值減去你的預(yù)測(cè)值。最佳擬合線使所有誤差平方和最小化。在線性回歸中,我們用相關(guān)系數(shù)計(jì)算出的最佳擬合線也恰好是最小平方誤差線。這就是回歸線被稱(chēng)為最小二乘回歸線的原因。
?最佳擬合線是最小二乘回歸線
?
在下面的圖像中,直線C比直線B更適合,直線B比直線A更適合。

這就是梯度下降的工作原理:
你從一條隨機(jī)線開(kāi)始,比如說(shuō)直線a,你計(jì)算這條線的誤差平方和。然后,調(diào)整斜率和y軸截距。重新計(jì)算新行的誤差平方和。繼續(xù)調(diào)整,直到達(dá)到局部最小值,其中平方誤差之和最小。
?梯度下降法是一種通過(guò)多次迭代最小化誤差平方和來(lái)逼近最小平方回歸線的算法。
?
梯度下降算法
在機(jī)器學(xué)習(xí)術(shù)語(yǔ)中,誤差平方和稱(chēng)為“成本”。這個(gè)成本公式是:

其中

因此,這個(gè)方程大致是“誤差平方和”,因?yàn)樗?jì)算的是預(yù)測(cè)值減去實(shí)際值平方的總和。
1/2m是“平均”數(shù)據(jù)點(diǎn)數(shù)量的平方誤差,這樣數(shù)據(jù)點(diǎn)的數(shù)量就不會(huì)影響函數(shù)。為什么除以2請(qǐng)看這個(gè)解釋?zhuān)╤ttps://datascience.stackexchange.com/questions/52157/why-do-we-have-to-divide-by-2-in-the-ml-squared-error-cost-function)。
在梯度下降中,目標(biāo)是使代價(jià)函數(shù)最小化。我們通過(guò)嘗試不同的斜率和截距值來(lái)實(shí)現(xiàn)這一點(diǎn)。但是應(yīng)該嘗試哪些值以及如何改變這些值?
我們根據(jù)梯度下降公式改變它們的值,這個(gè)公式來(lái)自于對(duì)代價(jià)函數(shù)的偏導(dǎo)數(shù)。確切的數(shù)學(xué)公式可以在這個(gè)鏈接中找到:https://www.ritchieng.com/one-variable-linear-regression/
通過(guò)偏導(dǎo)數(shù),得到:

這個(gè)公式計(jì)算每次迭代時(shí)θ的變化量。
α(α)被稱(chēng)為學(xué)習(xí)率。學(xué)習(xí)率決定了每次迭代的步驟有多大。有一個(gè)好的學(xué)習(xí)率是非常重要的,因?yàn)槿绻?,你的算法不?huì)達(dá)到最小值,如果它太小,你的算法會(huì)花很長(zhǎng)時(shí)間才能達(dá)到。對(duì)于我的例子,我選擇alpha為0.001
總而言之,步驟如下:
估計(jì)θ
計(jì)算成本
調(diào)整θ
重復(fù)2和3,直到你達(dá)到收斂。
這是我使用梯度下降實(shí)現(xiàn)簡(jiǎn)單線性回歸的方法。
斜率和截距都是0,0。
注:在機(jī)器學(xué)習(xí)中,我們使用θ來(lái)表示向量[y-截距,斜率]。θ=y軸截距。θ1=斜率。這就是為什么在下面的實(shí)現(xiàn)中將theta看作變量名。
#?x?=?[58,?62,?60,?64,?67,?70]?#?媽媽的身高
#?y?=?[60,?60,?58,?60,?70,?72]?#?女兒的身高
class?LinearRegression:
????def?__init__(self,?x_set,?y_set):
????????self.x_set?=?x_set
????????self.y_set?=?y_set
????????self.alpha?=?0.0001??#?alpha?是學(xué)習(xí)率
????def?get_theta(self,?theta):
????????intercept,?slope?=?theta
????????intercept_gradient?=?0
????????slope_gradient?=?0
????????m?=?len(self.y_set)
????????for?i?in?range(0,?len(self.y_set)):
????????????x_val?=?self.x_set[i]
????????????y_val?=?self.y_set[i]
????????????y_predicted?=?self.get_prediction(slope,?intercept,?x_val)
????????????intercept_gradient?+=?(y_predicted?-?y_val)
????????????slope_gradient?+=?(y_predicted?-?y_val)?*?x_val
????????new_intercept?=?intercept?-?self.alpha?*?intercept_gradient
????????new_slope?=?slope?-?self.alpha?*?(1/m)?*?slope_gradient
????????return?[new_intercept,?new_slope]
????def?get_prediction(self,?slope,?intercept,?x_val):
????????return?slope?*?x_val?+?intercept
????def?calc_cost(self,?theta):
????????intercept,?slope?=?theta
????????sum?=?0
????????for?i?in?range(0,?len(self.y_set)):
????????????x_val?=?self.x_set[i]
????????????y_val?=?self.y_set[i]
????????????y_predicted?=?self.get_prediction(slope,?intercept,?x_val)
????????????diff_sq?=?(y_predicted?-?y_val)?**?2
????????????sum?+=?diff_sq
????????cost?=?sum?/?(2*len(self.y_set))
????????return?cost
????def?iterate(self):
????????num_iteration?=?0
????????current_cost?=?None
????????current_theta?=?[0,?0]??#?初始化為0
????????while?num_iteration?500:
????????????if?num_iteration?%?10?==?0:
????????????????print('current?iteration:?',?num_iteration)
????????????????print('current?cost:?',?current_cost)
????????????????print('current?theta:?',?current_theta)
????????????new_cost?=?self.calc_cost(current_theta)
????????????current_cost?=?new_cost
????????????new_theta?=?self.get_theta(current_theta)
????????????current_theta?=?new_theta
????????????num_iteration?+=?1
????????print(f'After?{num_iteration},?total?cost?is?{current_cost}.?Theta?is?{current_theta}')
使用這個(gè)算法和上面的母女身高數(shù)據(jù)集,經(jīng)過(guò)500次迭代,我得到了3.4的成本。
500次迭代后的方程為y=0.998x+0.078。實(shí)際回歸線為y=1.2x-12.87,成本約為3.1。
用[0,0]作為[y-截距,斜率]的初始值,得到y(tǒng)=1.2x-12.87是不切實(shí)際的。為了在沒(méi)有大量迭代的情況下接近這個(gè)目標(biāo),你必須從一個(gè)更好的初始值開(kāi)始。
例如,[-10,1]在不到10次迭代后,大約得到y(tǒng)=1.153x-10,成本為3.1。
在機(jī)器學(xué)習(xí)領(lǐng)域,調(diào)整學(xué)習(xí)率和初始估計(jì)等參數(shù)是比較常見(jiàn)的做法。
這就是線性回歸中梯度下降的要點(diǎn)。
?梯度下降法是一種通過(guò)多次迭代最小化誤差平方和來(lái)逼近最小平方回歸線的算法。
?
到目前為止,我已經(jīng)討論過(guò)簡(jiǎn)單線性回歸,其中只有1個(gè)自變量(即一組x值)。理論上,梯度下降可以處理n個(gè)變量。
我已經(jīng)重構(gòu)了我以前的算法來(lái)處理下面的n個(gè)維度。
import?numpy?as?np
class?LinearRegression:
????def?__init__(self,?dataset):
????????self.dataset?=?dataset
????????self.alpha?=?0.0001??#?alpha?是學(xué)習(xí)率
????def?get_theta(self,?theta):
????????num_params?=?len(self.dataset[0])
????????new_gradients?=?[0]?*?num_params
????????m?=?len(self.dataset)
????????for?i?in?range(0,?len(self.dataset)):
????????????predicted?=?self.get_prediction(theta,?self.dataset[i])
????????????actual?=?self.dataset[i][-1]
????????????for?j?in?range(0,?num_params):
????????????????x_j?=?1?if?j?==?0?else?self.dataset[i][j?-?1]
????????????????new_gradients[j]?+=?(predicted?-?actual)?*?x_j
????????new_theta?=?[0]?*?num_params
????????for?j?in?range(0,?num_params):
????????????new_theta[j]?=?theta[j]?-?self.alpha?*?(1/m)?*?new_gradients[j]
????????return?new_theta
????def?get_prediction(self,?theta,?data_point):
????????#?使用點(diǎn)乘
????????#?y?=?mx?+?b?可以重寫(xiě)為?[b?m]?dot?[1?x]
????????#?[b?m]?是參數(shù)
????????#?代入x的值
????????values?=?[0]*len(data_point)
????????for?i?in?range(0,?len(values)):
????????????values[i]?=?1?if?i?==?0?else?data_point[i-1]
????????prediction?=?np.dot(theta,?values)
????????return?prediction
????def?calc_cost(self,?theta):
????????sum?=?0
????????for?i?in?range(0,?len(self.dataset)):
????????????predicted?=?self.get_prediction(theta,?self.dataset[i])
????????????actual?=?self.dataset[i][-1]
????????????diff_sq?=?(predicted?-?actual)?**?2
????????????sum?+=?diff_sq
????????cost?=?sum?/?(2*len(self.dataset))
????????return?cost
????def?iterate(self):
????????num_iteration?=?0
????????current_cost?=?None
????????current_theta?=?[0]?*?len(self.dataset[0])??#?initialize?to?0
????????while?num_iteration?500:
????????????if?num_iteration?%?10?==?0:
????????????????print('current?iteration:?',?num_iteration)
????????????????print('current?cost:?',?current_cost)
????????????????print('current?theta:?',?current_theta)
????????????new_cost?=?self.calc_cost(current_theta)
????????????current_cost?=?new_cost
????????????new_theta?=?self.get_theta(current_theta)
????????????current_theta?=?new_theta
????????????num_iteration?+=?1
????????print(f'After?{num_iteration},?total?cost?is?{current_cost}.?Theta?is?{current_theta}')
一切都是一樣的,唯一的例外是不用mx+b(即斜率乘以變量x加y截距)來(lái)獲得預(yù)測(cè)值,而是進(jìn)行矩陣乘法。參見(jiàn)上述的def get_prediction。

使用點(diǎn)積,你的算法可以接受n個(gè)變量來(lái)計(jì)算預(yù)測(cè)。
往期精彩回顧
獲取本站知識(shí)星球優(yōu)惠券,復(fù)制鏈接直接打開(kāi):
https://t.zsxq.com/qFiUFMV
本站qq群704220115。
加入微信群請(qǐng)掃碼:
