最小二乘法是一種通過(guò)數(shù)值對(duì)曲線函數(shù)擬合的一種統(tǒng)計(jì)學(xué)方法,這里的最小是擬合誤差達(dá)到最小。我們可以根據(jù)擬合后的函數(shù)可以做一些預(yù)測(cè)或預(yù)報(bào)。它在數(shù)字信號(hào)處理、機(jī)器學(xué)習(xí)等領(lǐng)域廣泛的應(yīng)用。本文W君將和大家一起學(xué)習(xí)如何通過(guò)最小二乘法進(jìn)行線性回歸。
我們來(lái)用一個(gè)最簡(jiǎn)單的一元線性回歸模型的例子來(lái)理解最小二乘法。在生活中,我們知道人的身高和腳的大小是成正比的,這里我們假設(shè)身高和腳的大小是成一元線性關(guān)系的。那么我們?cè)趺慈ソ⑦@樣一個(gè)一元線性模型呢?我們從人群中隨機(jī)抽取幾個(gè)身高不同的人,分別測(cè)量他們的身高和腳長(zhǎng),假如下面的表格就是我們的統(tǒng)計(jì)數(shù)據(jù)。
將他們?cè)谧鴺?biāo)系上顯示,如下圖,可以看到這些數(shù)據(jù)是趨近于一條直線的。
那么如何擬合這個(gè)直線呢?早在1805年勒讓德就提出了最小二乘法。其方法就是根據(jù)已知的m個(gè)樣本特征值,列出一個(gè)目標(biāo)函數(shù)E,并求其最優(yōu)解,從而使得實(shí)際值與預(yù)估值達(dá)到最小。這里的目標(biāo)函數(shù)也叫損失函數(shù),它是可以表征回歸模型中估測(cè)值和真實(shí)值的不一致程度,其值越小越接近真實(shí)情況。它是由若干個(gè)預(yù)測(cè)值和真實(shí)之差的平方和構(gòu)成,所以我們稱之為最小二乘。
樣本特征值:
目標(biāo)函數(shù):
這里我們假設(shè)擬合函數(shù)為:
這時(shí)我們的目標(biāo)函數(shù)就為:
然后,通過(guò)最小二乘法使目標(biāo)函數(shù)最小,求出這時(shí)的θ0和θ1的值,就可以得出擬合曲線了。
那么,問(wèn)題來(lái)了?怎樣讓目標(biāo)函數(shù)最小,求出θ0和θ1這兩個(gè)參數(shù)呢? 這里我們有兩種方法,對(duì)其進(jìn)行求解,分別是代數(shù)法和矩陣法。
代數(shù)法
先高能預(yù)警一下,代數(shù)法公式看上去比較復(fù)雜,讓人看得不免有些枯燥,W君覺得這里還是有必要列一下,大家只要理解了就好。代數(shù)法的解法就是先分別對(duì)θ0和θ1分別求偏導(dǎo)數(shù),然后分別使導(dǎo)數(shù)為0,得到一個(gè)關(guān)于θ0和θ1的方程組,最后解方程組即可。
損失函數(shù)對(duì)θ0求導(dǎo),得到方程:
損失函數(shù)對(duì)θ1求導(dǎo),得到方程:
根據(jù)上面兩個(gè)方程組成一個(gè)二元一次方程組,求解θ0和θ1:
類似的,對(duì)于n元一次方程也需要對(duì)每個(gè)參數(shù)進(jìn)行求導(dǎo),得出一個(gè)n元一次方程組,并求解出這n個(gè)θ參數(shù)。
看到這里一大堆公式是不是已經(jīng)頭大了?確實(shí)W君也覺得讓人頭大,那么,還有沒(méi)有更加簡(jiǎn)潔的方法呀?這就是下面我們要學(xué)習(xí)的矩陣法了。
矩陣法
矩陣解法要比代數(shù)法簡(jiǎn)潔很多,也是大家比較習(xí)慣使用的方法。這里我們用n元一次函數(shù)作為擬合函數(shù)來(lái)求導(dǎo)矩陣法的公式,推導(dǎo)過(guò)程同樣是痛苦的,但是大家最后記住公式就好,我們來(lái)看公式推導(dǎo)過(guò)程。
擬合函數(shù):
其矩陣表示如下:
所以,損失函數(shù):
在利用矩陣的跡公式得出:
令上面的公式為0,得出:
(敲黑板,這個(gè)公式我們一定要記住!!!)
這里的θ就是我們需要求的參數(shù),不過(guò)這里的是向量形式。
C++實(shí)現(xiàn)矩陣法
下面我們用矩陣法求解本文開頭的例子,我們使用表格中的身高和腳長(zhǎng)作為樣本數(shù)據(jù),再利用開源庫(kù)Eigen來(lái)實(shí)現(xiàn)矩陣運(yùn)算。關(guān)于Eigen的安裝和使用可以參考W君的這篇文章《快速入門矩陣運(yùn)算——開源庫(kù)Eigen》。這里用到了轉(zhuǎn)置函數(shù)transpose()和求逆函數(shù)inverse()。
我們來(lái)看一下求解代碼,代碼中利用了矩陣法求θ向量的公式。
#include <iostream>
#include "eigen_3_3_7/Eigen/Eigen"
int main()
{
Eigen::MatrixXf X(8,2);
Eigen::VectorXf Y(8);
Eigen::VectorXf result;
X << 155, 1,
159, 1,
163, 1,
169, 1,
175, 1,
179, 1,
184, 1,
188, 1;
Y << 18.1, 19.7, 21.2, 24.2, 26.1, 27.8, 30.3, 32.4;
result = (X.transpose()*X).inverse() * X.transpose() * Y;
std::cout << "------ X ------" << std::endl << X << std::endl;
std::cout << "------ Y ------" << std::endl << Y << std::endl;
std::cout << "------ result ------" << std::endl << result << std::endl;
}
程序計(jì)算結(jié)果如下,θ向量?jī)蓚€(gè)參數(shù)分別為0.425896和-48.0662。所以,我們擬合出的曲線就是 y=0.425896x - 48.0062了 ,是不是很簡(jiǎn)單快捷?
最后,我們可以用Excel驗(yàn)算下我們的結(jié)果,如下圖,擬合出的曲線和我們代碼求出來(lái)的是一致的。
關(guān)于如何使用Excel進(jìn)行曲線擬合,W君后續(xù)計(jì)劃將會(huì)在Excel技巧里再為大家詳細(xì)介紹,敬請(qǐng)關(guān)注。