03_利用pytorch解決線性迴歸問題
一、引言
上一篇文章我們利用numpy解決了線性迴歸問題,我們能感覺到他的麻煩之處,很多數學性的方法都需要我們自己親手去實現,這對於數學不好的同學來說,簡直就是災難,讓你數學又好並且碼程式碼能力又強,臣妾做不到呀!因此我們說到,可以利用torch這個框架簡化其中的很多操作,接下來就讓我們提前體驗下torch的強大,由於直接上程式碼,有些程式碼可能你可能看不懂,但沒關係,能看懂torch的強大就行。
由於上一篇已經詳細的介紹了線性迴歸模型的流程,我們這裡就不再次羅嗦了,直接把線性迴歸的5個步驟貼過來:
- 初始化未知變數\(w=b=0\)
- 得到損失函式\(loss = \frac{1}{N}\sum_{i=1}^N{(\hat{y_i}-y_i)}^2\)
- 利用梯度下降演算法更新得到\(w', b'\)
- 重複步驟3,利用\(w', b'\)得到新的更優的\(w', b'\),直至\(w', b'\)收斂
- 最後得到函式模型\(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的用途及其強大,然後我們再慢慢的一步一步築基。