天天看點

利用PyTorch Autograd自動求導訓練線性回歸模型利用PyTorch Autograd自動求導訓練線性回歸模型

利用PyTorch Autograd自動求導訓練線性回歸模型

About-說明

第一篇文章中pytorch基礎入門 訓練原始的線性模型,我們用手動計算微分的方式,

實作了基于Python語言的線性回歸模型訓練;這雖然非常适合了解相關概念,但是實作方式是非常原始的.

在檢視PyTorch相關文檔時,很容易就發現它把 Autograd(自動求導) 作為一個突出的優勢,是以這篇文章旨在了解 Autograd的概念上,實作上一篇文章的線性回歸模型.

歸納而言,這篇會相對簡單,主要包含以下幾點:

  • PyTorch Autograd 概念介紹
  • 利用 Autograd 替代手動求解導數
  • 實作 Autograd 方式的線性回歸模型

PyTorch Autograd 概念介紹

簡單介紹就是:

PyTorch中設定 requires_grad 為True, PyTorch就會為你記錄表達式轉換過程的求導鍊,

當你執行backward()時,原始參數的grad就包含了對應參數的導數;

對應的官網入門講解位址

原理雖然簡單,但其中有不少細節需要注意:

  • 首先需要這個函數鍊是可導的
  • 一個 backward 對應一個 grad計算值,是以重複 backward 以及中間環節執行 backward 都會導緻 grad 值變化
  • 一個 grad 使用完之後,需要記得 設定為 0 ,再次 backward 才能得到正确的值

利用 Autograd 替代手動求解導數

# 設定 requires_grad=True ,告訴PyTorch需要記錄params上所有的操作
params = torch.tensor([1.0, 0.0], requires_grad=True)

# 目前為止 params.grad is None

# 執行 backward 後, params.grad 就儲存了微分值
loss = loss_fn(model(t_u, *params), t_c)
loss.backward()

# grad 使用完之後,需要記得 設定為 0
if params.grad is not None:
  params.grad.zero_()
           

實作 Autograd 方式的線性回歸模型

替代之前loop循環

def training_loop(n_epochs, learning_rate, params, t_u, t_c):
  for epoch in range(1, n_epochs + 1):
    if params.grad is not None:
       params.grad.zero_()
    t_p = model(t_u, *params)
    loss = loss_fn(t_p, t_c)
    loss.backward()
    params = (params - learning_rate * params.grad).detach().requires_grad_()
    # print('Epoch %d, Loss %f' % (epoch, float(loss)))
    # print('Params', params)
    if epoch % 500 == 0:
      print('Epoch %d, Loss %f' % (epoch, float(loss)))
  return params
           

需要着重解釋的是

detach().requires_grad_()

這裡的作用就是使 params 脫離之前的函數鍊,PyTorch重新記錄新的函數鍊

完整的代碼如下:

"""
PyTorch 基礎入門二: PyTorch 自動求導線性模型實作
線性回歸參數估計
問題: 華氏溫度轉換
"""
import torch

# 定義輸入資料
t_c = [0.5,  14.0, 15.0, 28.0, 11.0,  8.0,  3.0, -4.0,  6.0, 13.0, 21.0]
t_u = [35.7, 55.9, 58.2, 81.9, 56.3, 48.9, 33.9, 21.8, 48.4, 60.4, 68.4]

t_c = torch.tensor(t_c)
t_u = torch.tensor(t_u)

# 對應的線性模型為
# t_c = w * t_u + b

def model(t_u, w, b):
    return w*t_u + b

# 定義損失函數
# t_p 為模型估計值
# t_c 為實驗資料
def loss_fn(t_p, t_c):
    squared_diffs = (t_p - t_c)**2
    return squared_diffs.mean()
# 以上跟第一篇保持一緻
#########################################################################

# 設定 requires_grad=True ,告訴PyTorch需要記錄params上所有的操作
params = torch.tensor([1.0, 0.0], requires_grad=True)

# 目前為止 params.grad is None

# 執行 backward 後, params.grad 就儲存了微分值
# loss = loss_fn(model(t_u, *params), t_c)
# loss.backward()

if params.grad is not None:
  params.grad.zero_()

def training_loop(n_epochs, learning_rate, params, t_u, t_c):
  for epoch in range(1, n_epochs + 1):
    if params.grad is not None:
       params.grad.zero_()
    t_p = model(t_u, *params)
    loss = loss_fn(t_p, t_c)
    loss.backward()
    # 
    params = (params - learning_rate * params.grad).detach().requires_grad_()
    # print('Epoch %d, Loss %f' % (epoch, float(loss)))
    # print('Params', params)
    if epoch % 500 == 0:
      print('Epoch %d, Loss %f' % (epoch, float(loss)))
  return params

# 特征縮放處理
t_un = 0.1 * t_u
params = training_loop(
    n_epochs = 5000,
    learning_rate = 1e-2,
    params = torch.tensor([1.0, 0.0], requires_grad=True),
    t_u = t_un,
    t_c = t_c)

# 畫出圖示
import matplotlib.pyplot as plt
t_p = model(t_un, *params)

fig = plt.figure()
plt.title(u"PyTorch linear model")
plt.xlabel("Fahrenheit")
plt.ylabel("Celsius")
plt.plot(t_u.numpy(), t_p.detach().numpy())
plt.plot(t_u.numpy(), t_c.numpy(), 'o')
plt.show()

           

結果展示

結果其實同第一篇文章一緻:

利用PyTorch Autograd自動求導訓練線性回歸模型利用PyTorch Autograd自動求導訓練線性回歸模型