【學習筆記】深度學習 demo1 線性迴歸

本人近期開始嘗試基於pytorch框架,從原理上理解深度學習。在這幾個demo中將會展示一些基本的操作及其效果,並基於個人的一點粗淺理解進行原理描述,如有不當之處還請指正。

在本demo中,我們所使用的線性函數爲

\[f(x) = 3 x + 10 + rand \]

其中\(rand\)表示一個滿足標準正態分佈\(N\left(0, 1\right)\)的隨機數(平均值爲0,方差爲1)

基於Parameter手動構建

我們可以使用nn.Parameter工具,手動構建公式以實現線性迴歸。

一個線性函數的基本結構如下

\[g(x) = a x + b \]

其中包含兩個參數\(a\)\(b\)

代碼如下:

import matplotlib.pyplot as plt
import torch
from torch import nn
from torch.autograd import Variable


class SquareRegression(nn.Module):
    def __init__(self):
        nn.Module.__init__(self)
        self.a = nn.Parameter(torch.randn(1, 1), requires_grad=True)  # 1 x 1
        self.b = nn.Parameter(torch.randn(1, 1), requires_grad=True)  # 1 x 1

    def forward(self, x_):
        _t = x_.mm(self.a)                # n x 1
        return _t + self.b.expand_as(_t)  # n x 1


if __name__ == "__main__":
    n = 100
    x = torch.linspace(-2, 12, n).resize_((n, 1))   # n x 1 tensor
    y = 3 * x + 10 + torch.randn(x.size())          # n x 1 tensor

    model = SquareRegression()

    criterion = nn.MSELoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)

    num_epochs = 20000
    for epoch in range(num_epochs):
        inputs, targets = Variable(x), Variable(y)

        out = model(inputs)
        loss = criterion(out, targets)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if (epoch + 1) % 100 == 0:
            print('Epoch[{}/{}], loss:{:.6f}'.format(epoch + 1, num_epochs, loss.item()))

    for name, param in model.named_parameters():
        print(name, param.data)

    predict = model(x)

    plt.plot(x.numpy(), y.numpy(), 'ro', label='Original Data')
    plt.plot(x.numpy(), predict.data.numpy(), label='Fitting Line')
    plt.show()

輸出結果如下:

Epoch[100/20000], loss:23.441666
Epoch[200/20000], loss:20.163355
Epoch[300/20000], loss:17.363503
Epoch[400/20000], loss:14.972251
Epoch[500/20000], loss:12.929962
Epoch[600/20000], loss:11.185717
Epoch[700/20000], loss:9.696020
Epoch[800/20000], loss:8.423729
Epoch[900/20000], loss:7.337113
Epoch[1000/20000], loss:6.409071
Epoch[1100/20000], loss:5.616467
Epoch[1200/20000], loss:4.939533
Epoch[1300/20000], loss:4.361391
Epoch[1400/20000], loss:3.867616
Epoch[1500/20000], loss:3.445905

... ... ... ... 

Epoch[19000/20000], loss:0.977931
Epoch[19100/20000], loss:0.977931
Epoch[19200/20000], loss:0.977931
Epoch[19300/20000], loss:0.977931
Epoch[19400/20000], loss:0.977931
Epoch[19500/20000], loss:0.977931
Epoch[19600/20000], loss:0.977931
Epoch[19700/20000], loss:0.977931
Epoch[19800/20000], loss:0.977931
Epoch[19900/20000], loss:0.977931
Epoch[20000/20000], loss:0.977931
a tensor([[2.9913]])
b tensor([[10.1255]])

生成圖像如下:

可以發現,我們生成的擬合解如下:

\[\begin{cases} a &= 2.9913 \\ b &= 10.1255 \end{cases} \]

所得到的函數爲

\[\begin{aligned} g(x) &= a x + b \\ &= 2.9913 x + 10.1255 \end{aligned} \]

和原公式\(f(x) = 3x + 10\)相比已經十分接近,圖像上的擬合結果也與期望結果基本一致。

基於Linear構建

實際上,pytorch已經爲我們封裝了nn.Linear工具實現線性迴歸。

一個線性函數的基本結構如下

\[h(x) = w x + b \]

其中包含兩個參數\(w\)\(b\),分別對應nn.Linear中的weight和bias兩個核心參數。

代碼如下:

import matplotlib.pyplot as plt
import torch
from torch import nn
from torch.autograd import Variable


class SquareRegression(nn.Module):
    def __init__(self):
        nn.Module.__init__(self)
        self.linear = nn.Linear(1, 1)  # 1 x 1

    def forward(self, x_):
        return self.linear(x_)  # n x 1


if __name__ == "__main__":
    n = 100
    x = torch.linspace(-2, 12, n).resize_((n, 1))  # n x 1 tensor
    y = 3 * x + 10 + torch.randn(x.size())         # n x 1 tensor

    model = SquareRegression()

    criterion = nn.MSELoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)

    num_epochs = 20000
    for epoch in range(num_epochs):
        inputs, targets = Variable(x), Variable(y)

        out = model(inputs)
        loss = criterion(out, targets)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if (epoch + 1) % 100 == 0:
            print('Epoch[{}/{}], loss:{:.6f}'.format(epoch + 1, num_epochs, loss.item()))

    for name, param in model.named_parameters():
        print(name, param.data)

    predict = model(x)

    plt.plot(x.numpy(), y.numpy(), 'ro', label='Original Data')
    plt.plot(x.numpy(), predict.data.numpy(), label='Fitting Line')
    plt.show()

輸出結果如下:

Epoch[100/20000], loss:40.807762
Epoch[200/20000], loss:34.994335
Epoch[300/20000], loss:30.029305
Epoch[400/20000], loss:25.788853
Epoch[500/20000], loss:22.167242
Epoch[600/20000], loss:19.074156
Epoch[700/20000], loss:16.432467
Epoch[800/20000], loss:14.176275
Epoch[900/20000], loss:12.249363
Epoch[1000/20000], loss:10.603661
Epoch[1100/20000], loss:9.198134
Epoch[1200/20000], loss:7.997722
Epoch[1300/20000], loss:6.972489
Epoch[1400/20000], loss:6.096869
Epoch[1500/20000], loss:5.349041

... ... ... ...

Epoch[19000/20000], loss:0.972535
Epoch[19100/20000], loss:0.972535
Epoch[19200/20000], loss:0.972535
Epoch[19300/20000], loss:0.972535
Epoch[19400/20000], loss:0.972535
Epoch[19500/20000], loss:0.972535
Epoch[19600/20000], loss:0.972535
Epoch[19700/20000], loss:0.972535
Epoch[19800/20000], loss:0.972535
Epoch[19900/20000], loss:0.972535
Epoch[20000/20000], loss:0.972535
linear.weight tensor([[2.9816]])
linear.bias tensor([10.3128])

生成圖像如下:

可以發現,我們生成的擬合解如下

\[\begin{cases} w &= 2.9816 \\ b &= 10.3128 \end{cases} \]

所得到的函數爲

\[\begin{aligned} h(x) &= w x + b \\ & = 2.9816 x + 10.3128 \end{aligned} \]

和原公式\(f(x) = 3x + 10\)相比已經十分接近,圖像上的擬合結果也與期望結果基本一致。

其他

對機器學習的一些基本理解

  • 機器學習的本質是函數擬合。

  • 機器學習的最基本思路,就是通過一個預定義的損失函數(loss function,用於量化描述擬合結果與實際期望結果的差異),和預定義的計算模型(線性函數、二次函數、CNN等),利用反向傳播求導機制,通過梯度下降法對當前的擬合結果進行不斷地優化(準確的說,是對損失函數進行不斷地優化使之儘可能小)。

    • 簡單來說,有兩個最核心的基本要素:
      • 損失函數的定義
      • 計算模型的定義
  • 通過機器學習,我們可以獲得一個函數意義上的局部最優解,而無法保證所獲擬合結果爲複雜函數環境下的全局最優。

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