注:本系列所有博客將持續更新並發布在github上,您可以通過github下載本系列所有文章筆記文件
1 引言
線性回歸算法應該是大多數人機器學習之路上的第一站,因為線性回歸算法原理簡單清晰,但卻囊括了擬合、優化等等經典的機器學習思想。去年畢業時參加求職面試就被要求介紹線性回歸算法,但由於當初過於追求神經網絡、SVN、無監督學習等更加高大尚的算法,反而忽略了線性回歸這些基礎算法,當時給出的答案實在是差強人意。
這一篇關於線性回歸的總結我也猶豫過要不要寫,如果你看了我上兩篇關於最小二乘和梯度下降算法介紹的博客,你就會發現,關於最小二乘法和梯度下降算法的介紹都是以線性回歸為例展開,所以,綜合來說,之前兩篇博文對一元線性回歸還是多元線性回歸算法都已經有了還算全面的總結,再寫一篇關於線性回歸的文章總感覺有些多余。不過,為了讓我對機器學習的總結更加系統,還是決定專門總結一下。
2 什么是線性回歸
說到線性回歸,我們得先說說回歸與分類、線性與非線性這些概念的區別。
回歸和分類都是有監督學習(機器學習分有監督學習和無監督學習)中的概念。從算法的目標或者作用上看,分類的目標就如同字面上的意思,就是分類,將數據分門別類,而且類別是有限的(數學上稱為離散的)。但回歸算法不同,回歸算法的目標是預測,說詳細些就是從已有數據和結果中獲取規律,對其它數據的位置結果進行預測,預測結果的數量數無限的(數學上叫連續的)。舉個例子我們可以根據樓房的建材、面積、用途將茅草房、普通住房、廠房等等有限的幾類,這就是分類;另外,我們可以根據房屋面積、地段、裝修等因素對樓房房價進行預測,預測結果有無限種可能,可能是10000元/平米,也能是10001.1元/平米——這就是回歸的過程。
對於線性和非線性的解釋,我至今還沒有見到過一個讓我覺得滿意的數學定義,所以,我也從直觀認識上說說我的看法。我們高中時學習函數就是從一元一次函數學起的,一元一次函數在數學上可以表示為:
在二維坐標軸上,其表現為一條直線。如果是三維空間中的二元一次函數,數學上表示為:
其在空間上圖形為一平面。以此類推,在更高維(n維)的平面上,多元一次函數表達式為:
\[f(x)={{\theta }_{0}}+{{\theta }_{1}}{{x}_{1}}+{{\theta }_{2}}{{x}_{2}}+\ldots +{{\theta }_{n}}{{x}_{n}}\]
在多維空間上,其圖形我們稱為為超平面。
如果一個數據模型能夠用上述二維直線、三維平面或者更多維空間上的超平面去擬合,我們就可以說這個模型是線性模型。
最后,綜合上面內容總結一下什么是線性回歸:
線性回歸(Linear Regression)是一種通過屬性的線性組合來進行預測的回歸模型,其目的是找到一條直線或者一個平面或者更高維的超平面,使得預測值與真實值之間的誤差最小化。
3 最優解
上面提到,線性回歸的目的就是找到一個線性模型,是的預測值與真實值之間的誤差最小,這就是一個優化的問題。為什么要優化呢?看看下圖:

正如你眼前所見,圖中的點代表數據,能夠對這些數據點進行大概擬合的直線不止一條,到底哪一條才是最好呢?以上圖中數據為例,假設此時模型為${y}'=f(x)={{\theta }_{0}}+{{\theta }_{1}}{{x}_{1}}$,圖像如下所示,我們要對其擬合程度進行評價:

點$A({{x}_{i}},{{y}_{i}})$為數據集中某一點,使用模型預測時,結果為${A}'({{x}_{i}},{{{{y}'}}_{i}})$。我們用高中時學過,對於${A}$與${A}'$之間的誤差,我們可以用它們之間的歐氏距離的平方來表示:
這是對一個點的擬合程度評價,但數據集中可不止$A({{x}_{i}},{{y}_{i}})$一個數據點,所以,我們需要對每個點進行誤差評估,然后求和,也就是誤差平方和作為這個線性模型對數據集的總體擬合程度評估:
我們將$J({{\theta }_{0}},{{\theta }_{1}})$作為損失函數,也有資料中稱為目標函數。這里,${{\theta }_{0}},{{\theta }_{1}}$是模型中$x$的參數。在不同的模型中,${{\theta }_{0}},{{\theta }_{1}}$取不同值,我們要做的就是求目標函數$J({{\theta }_{0}},{{\theta }_{1}})$取最小值時的${{\theta }_{0}},{{\theta }_{1}}$確切值,換句話說就是求$J({{\theta }_{0}},{{\theta }_{1}})$的最優解問題。
關於求最優解問題,最常用的算法就是最小二乘法和梯度下降法,這兩種方法在前兩篇博文中都有詳細介紹,如果你尚不清楚其中原理和過程,還是去看看吧,這很重要,也很基礎。
上面說的是一元線性回歸模型,對於多元線性回歸模型,原理也是一樣的,只不過需要求解的參數不止${{\theta }_{0}},{{\theta }_{1}}$兩個了就是,這里不再多說。下面我們用代碼實現一元線性回歸模型。
4 代碼實現
以下代碼參考《深度學習之Pytorch》一書中內容,代碼采用Python的pytorch實現。
在實現模型之前,首先我們要制造一個數據集,在[0, 10]區間范圍內,隨機生成100個數:
import torch x = torch.unsqueeze(torch.linspace(0, 10, 100), dim=1) print(x)
輸出的內容有100行,這就是一個列向量,我們以則100個數作為數據集中的自變量,也就是X,假設我們需要擬合的模型為:$y=f(x)=0.5x+15$,為了更加符合真實數據集中隨機誤差的效果,我們在生成y的時候,加上一些服從高斯分布隨機誤差:
y = 0.5 * x + 15 + torch.rand(x.size()) print(y)
輸出結果為:
tensor([[15.0477],
[15.0557],
[15.3653],
……
[20.7046],
[20.1751]])
在二維坐標軸上,這些點如下所示:

數據有了,我們可以開始搭建我們的模型了:
class LinearRegression(nn.Module): def __init__(self): super(LinearRegression, self).__init__() self.linear = nn.Linear(1, 1) # 因為是一元線性回歸,所以輸入和輸出的維度都是1 def forward(self, x): out = self.linear(x) return out if torch.cuda.is_available(): model = LinearRegression().cuda() else: model = LinearRegression() criterion = nn.MSELoss() # 這里選擇使用誤差平方和作為損失函數,就是上文中說的函數J optimizer = torch.optim.SGD(model.parameters(), lr=1e-2) # 定義梯度下降算法進行優化 num_epochs = 1000 # 定義迭代次數 for epoch in range(num_epochs): if torch.cuda.is_available(): inputs = Variable(x).cuda() target = Variable(y).cuda() else: inputs = Variable(x) target = Variable(y) # 向前傳播 out = model(inputs) loss = criterion(out, target) # 向后傳播 optimizer.zero_grad() # 注意每次迭代都需要清零 loss.backward() optimizer.step() if (epoch + 1) % 20 == 0: print('Epoch[{}/{}], loss:{:.6f}'.format(epoch + 1, num_epochs, loss.item())) model.eval() if torch.cuda.is_available(): predict = model(Variable(x).cuda()) predict = predict.data.cpu().numpy() else: predict = model(Variable(x)) predict = predict.data.numpy() plt.plot(x.numpy(), y.numpy(), 'ro', label='Original Data') plt.plot(x.numpy(), predict, label='Fitting Line') plt.show() print(str(model.state_dict()['linear.weight']) + "\t" + str(model.state_dict()['linear.bias']))
訓練出來的模型效果如下所示:

代碼最后一行輸出為:
tensor([[0.5206]], device='cuda:0') tensor([15.3803], device='cuda:0')
也就是說,模型訓練結果中,${{\theta }_{0}},{{\theta }_{1}}$分別為15.3803和0.5206.