03_利用pytorch解決線性迴歸問題

二十三歲的有德發表於2021-04-15

03_利用pytorch解決線性迴歸問題

一、引言

上一篇文章我們利用numpy解決了線性迴歸問題,我們能感覺到他的麻煩之處,很多數學性的方法都需要我們自己親手去實現,這對於數學不好的同學來說,簡直就是災難,讓你數學又好並且碼程式碼能力又強,臣妾做不到呀!因此我們說到,可以利用torch這個框架簡化其中的很多操作,接下來就讓我們提前體驗下torch的強大,由於直接上程式碼,有些程式碼可能你可能看不懂,但沒關係,能看懂torch的強大就行。

由於上一篇已經詳細的介紹了線性迴歸模型的流程,我們這裡就不再次羅嗦了,直接把線性迴歸的5個步驟貼過來:

  1. 初始化未知變數\(w=b=0\)
  2. 得到損失函式\(loss = \frac{1}{N}\sum_{i=1}^N{(\hat{y_i}-y_i)}^2\)
  3. 利用梯度下降演算法更新得到\(w', b'\)
  4. 重複步驟3,利用\(w', b'\)得到新的更優的\(w', b'\),直至\(w', b'\)收斂
  5. 最後得到函式模型\(f=w'*x+b'\)

但是這裡為了體現torch的強大,我們使用了神經網路全連線層的概念,但是用的是一層網路,相信不會難倒你。有人很好奇,為什麼我這樣做,我想說:如果不使用神經網路,我為什麼不用sklearn框架做個線性迴歸給你體驗下框架的強大呢?三行程式碼一套搞定。而且一層神經網路可以看做是感知機模型,而感知機模型無非就是線上性迴歸模型的基礎上加了一個sgn函式。

二、利用torch解決線性迴歸問題

2.1 定義x和y

在下面的程式碼中,我們定義的x和y都是ndarray資料的格式,所以在之後的處理之中需要通過torch的from_numpy()方法把ndarray格式的資料轉成tensor型別。

其中x為一維陣列,例如:[1,2,3,4,5,……]

其中y假設\(y=2*x+3\)

import numpy as np

# torch裡要求資料型別必須是float
x = np.arange(1, 12, dtype=np.float32).reshape(-1, 1)
y = 2 * x + 3

2.2 自定製線性迴歸模型類

由於torch內部封裝了線性迴歸演算法,我們只需要繼承它給我們提供的模型類即可,然後通過Python中類的繼承做出一些靈活的改動。(如果你對繼承不熟悉,強烈推薦回爐重造Python)下面給出程式碼:

import torch
import torch.nn as nn


# 繼承nn.module,實現前向傳播,線性迴歸直接可以看做是全連線層
class LinearRegressionModel(nn.Module):
    def __init__(self, input_dim, output_dim):
        super().__init__()  # 繼承父類方法
        self.linear = nn.Linear(input_dim, output_dim)  # 定義全連線層,其中input_dim和output_dim是輸入和輸出資料的維數

    # 定義前向傳播演算法
    def forward(self, inp):
        out = self.linear(inp)  # 輸入x後,通過全連線層得到輸入出結果out
        return out  # 返回被全連線層處理後的結果


# 定義線性迴歸模型
regression_model = LinearRegressionModel(1, 1)  # x和y都是一維的

2.3 指定gpu或者cpu

# 可以通過to()或者cuda()使用GPU進行模型的訓練,需要將模型和資料都轉換到GPU上,也可以指定具體的GPU,如.cuda(1)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
regression_model.to(device)

2.4 設定引數

epochs = 1000  # 訓練次數
learning_rate = 0.01  # 學習速率
optimizer = torch.optim.SGD(regression_model.parameters(), learning_rate)  # 優化器(未來會詳細介紹),這裡使用隨機梯度下降演算法(SGD)
criterion = nn.MSELoss()  # 使用均方誤差定義損失函式

2.5 訓練

for epoch in range(epochs):
    # 資料型別轉換
    inputs = torch.from_numpy(x).to(device)  # 由於x是ndarray陣列,需要轉換成tensor型別,如果用gpu訓練,則會通過to函式把資料傳入gpu
    labels = torch.from_numpy(y).to(device)

    # 訓練
    optimizer.zero_grad()  # 每次求偏導都會清零,否則會進行疊加
    outputs = regression_model(inputs)  # 把輸入傳入定義的線性迴歸模型中,進行前向傳播,得到預測結果
    loss = criterion(outputs, labels)  # 通過均方誤差評估預測誤差
    loss.backward()  # 反向傳播
    optimizer.step()  # 更新權重引數

    # 每50次迴圈列印一次結果
    if epoch % 50 == 0:
        print("epoch:", epoch, "loss:", loss.item())

predict = regression_model(torch.from_numpy(x).requires_grad_()).data.numpy()  # 通過訓練好的模型預測結果

2.6 儲存模型

torch.save(regression_model.state_dict(), "model.pk1")  # 儲存模型
result = regression_model.load_state_dict(torch.load("model.pk1"))  # 載入模型

三、程式碼彙總

# author : 'nickchen121';
# date: 14/4/2021 20:11

import numpy as np

# torch裡要求資料型別必須是float
x = np.arange(1, 12, dtype=np.float32).reshape(-1, 1)
y = 2 * x + 3

import torch
import torch.nn as nn


# 繼承nn.module,實現前向傳播,線性迴歸直接可以看做是全連線層
class LinearRegressionModel(nn.Module):
    def __init__(self, input_dim, output_dim):
        super().__init__()  # 繼承父類方法
        self.linear = nn.Linear(input_dim, output_dim)  # 定義全連線層,其中input_dim和output_dim是輸入和輸出資料的維數

    # 定義前向傳播演算法
    def forward(self, inp):
        out = self.linear(inp)  # 輸入x後,通過全連線層得到輸入出結果out
        return out  # 返回被全連線層處理後的結果


# 定義線性迴歸模型
regression_model = LinearRegressionModel(1, 1)  # x和y都是一維的

# 可以通過to()或者cuda()使用GPU進行模型的訓練,需要將模型和資料都轉換到GPU上,也可以指定具體的GPU,如.cuda(1)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
regression_model.to(device)

epochs = 1000  # 訓練次數
learning_rate = 0.01  # 學習速率
optimizer = torch.optim.SGD(regression_model.parameters(), learning_rate)  # 優化器,這裡使用隨機梯度下降演算法(SGD)
criterion = nn.MSELoss()  # 使用均方誤差定義損失函式

for epoch in range(epochs):
    # 資料型別轉換
    inputs = torch.from_numpy(x).to(device)  # 由於x是ndarray陣列,需要轉換成tensor型別,如果用gpu訓練,則會通過to函式把資料傳入gpu
    labels = torch.from_numpy(y).to(device)

    # 訓練
    optimizer.zero_grad()  # 每次求偏導都會清零,否則會進行疊加
    outputs = regression_model(inputs)  # 把輸入傳入定義的線性迴歸模型中,進行前向傳播
    loss = criterion(outputs, labels)  # 通過均方誤差評估預測誤差
    loss.backward()  # 反向傳播
    optimizer.step()  # 更新權重引數

    # 每50次迴圈列印一次結果
    if epoch % 50 == 0:
        print("epoch:", epoch, "loss:", loss.item())

predict = regression_model(torch.from_numpy(x).requires_grad_()).data.numpy()  # 通過訓練好的模型預測結果

# torch.save(regression_model.state_dict(), "model.pk1")  # 儲存模型
# result = regression_model.load_state_dict(torch.load("model.pk1"))  # 載入模型

四、總結

本篇文章從torch的角度去解決了線性迴歸問題,細節你可能不懂,但也可以發現它是非常簡單的,全程沒有讓你去實現優化器、去實現全連線層、去實現反向傳播,在這裡你就不需要去實現一個數學公式。你需要做的僅僅是成為一個優秀的調包俠,並且努力成為一個偉大的調參師即可。

至於為什麼直接上程式碼,而不是先講解torch的基礎,一定是有深意的。站得高看得遠,先帶你領略下torch的用途及其強大,然後我們再慢慢的一步一步築基。

相關文章