關於什麼是線性迴歸,很少作介紹了.能夠參考我之前的博客http://www.javashuo.com/article/p-towfilsl-ha.htmlhtml
分爲如下幾個部分:python
咱們構造一個簡單的人工訓練數據集,它可使咱們可以直觀比較學到的參數和真實的模型參數的區別。設訓練數據集樣本數爲1000,輸入個數(特徵數)爲2。給定隨機生成的批量樣本特徵 \(\boldsymbol{X} \in \mathbb{R}^{1000 \times 2}\),咱們使用線性迴歸模型真實權重 \(\boldsymbol{w} = [2, -3.4]^\top\) 和誤差 \(b = 4.2\),以及一個隨機噪聲項 \(\epsilon\) 來生成標籤
\[ \boldsymbol{y} = \boldsymbol{X}\boldsymbol{w} + b + \epsilon \]算法
其中噪聲項 \(\epsilon\) 服從均值爲0、標準差爲0.01的正態分佈。噪聲表明了數據集中無心義的干擾。網絡
%matplotlib inline import torch from IPython import display from matplotlib import pyplot as plt import numpy as np import random num_inputs = 2 num_examples = 1000 true_w = [2, -3.4] true_b = 4.2 features = torch.from_numpy(np.random.normal(0, 1, (num_examples, num_inputs))) print(type(features),features.shape) labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b print(type(labels),labels.shape) labels += torch.from_numpy(np.random.normal(0, 0.01, size=labels.size())) def use_svg_display(): # 用矢量圖顯示 display.set_matplotlib_formats('svg') def set_figsize(figsize=(3.5, 2.5)): use_svg_display() # 設置圖的尺寸 plt.rcParams['figure.figsize'] = figsize set_figsize() plt.scatter(features[:, 1].numpy(), labels.numpy(), 1);
每次讀取batch_size個樣本.注意亂序讀取.以使得每一個batch的樣本多樣性足夠豐富.數據結構
def data_iter(batch_size, features, labels): num_examples = len(features) #print(num_examples) indices = list(range(num_examples)) random.shuffle(indices) # 樣本的讀取順序是隨機的 #print(indices) for i in range(0, num_examples, batch_size): j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # 最後一次可能不足一個batch #print(j) yield features.index_select(0, j), labels.index_select(0, j) batch_size = 10 for X, y in data_iter(batch_size, features, labels): #print(X, y) #break pass
關於yiled用法參考:http://www.javashuo.com/article/p-wykkoqyz-hb.html中yield部分.
關於torch的index_select用法參考:https://pytorch-cn.readthedocs.io/zh/latest/package_references/torch/#torchindex_select
features是[1000,2]的Tensor。因此features.index_select(0, j)即在第0維度上對索引爲j的輸入進行切片.也即選取第j(j爲一個長度爲batch_size的tensor)個樣本.閉包
權重值有2個.因此咱們初始化一個shape爲[2,1]的Tensor.咱們將其隨機初始化爲符合均值0,標準差0.01的正態分佈隨機數,bias初始化爲0.dom
w=torch.from_numpy(np.random.normal(0,0.01,(num_inputs,1))) b = torch.zeros(1, dtype=torch.float64) print(w.dtype,b.dtype)
ndarray的類型是float64,因此w的類型是float64,在生成b的時候咱們指定dtype=float64.svg
以後的模型訓練中,須要對這些參數求梯度來迭代參數的值,所以咱們要讓它們的requires_grad=True
。函數
w.requires_grad_(requires_grad=True) b.requires_grad_(requires_grad=True)
下面是線性迴歸的矢量計算表達式的實現。咱們使用mm
函數作矩陣乘法。
在咱們的例子中,X是[1000,2]的矩陣,w是[2,1]的矩陣,相乘獲得[1000,1]的矩陣.工具
def linreg(X, w, b): # 本函數已保存在d2lzh_pytorch包中方便之後使用 return torch.mm(X, w) + b
咱們使用平方損失來定義線性迴歸的損失函數。在實現中,咱們須要把真實值y
變造成預測值y_hat
的形狀。如下函數返回的結果也將和y_hat
的形狀相同。
def squared_loss(y_hat, y): # 注意這裏返回的是向量, 另外, pytorch裏的MSELoss並無除以 2 return (y_hat - y.view(y_hat.size())) ** 2 / 2
如下的sgd
函數實現了上一節中介紹的小批量隨機梯度降低算法。它經過不斷迭代模型參數來優化損失函數。這裏自動求梯度模塊計算得來的梯度是一個批量樣本的梯度和。咱們將它除以批量大小來獲得平均值。均值反映了平均而言,對單個樣本,朝着哪一個梯度方向去更新參數可使得loss最小
def sgd(params, lr, batch_size): for param in params: param.data -= lr * param.grad / batch_size # 注意這裏更改param時用的param.data
這裏的params傳入的即w,b
咱們建立一個循環,每次傳入batch_size個樣本,計算損失.反向傳播,計算w,b的梯度,而後更新w,b.循環往復.注意每次方向傳播後清空梯度. 以及l是一個向量. 調用.sum()將其轉換爲標量,再計算梯度.
一個epoch即全部樣本均計算一次損失.
代碼以下:
lr = 0.03 num_epochs = 3 net = linreg loss = squared_loss batch_size=10 for epoch in range(num_epochs): for X,y in data_iter(batch_size,features,labels): l = loss(linreg(X,w,b),y).sum() l.backward() sgd([w,b],lr,batch_size) w.grad.data.zero_() b.grad.data.zero_() train_l = loss(net(features,w,b),labels) print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item())) print(true_w,'\n',w) print(true_b,'\n',b)
輸出以下:
epoch 1, loss 0.051109 epoch 2, loss 0.000217 epoch 3, loss 0.000049 [2, -3.4] tensor([[ 1.9996], [-3.3993]], dtype=torch.float64, requires_grad=True) 4.2 tensor([4.1995], dtype=torch.float64, requires_grad=True)
能夠看到獲得的w和b都已經很是接近true_w,true_b了.
以前咱們是手寫代碼構建模型,建立損失函數,定義隨機梯度降低等等.用pytorch裏提供的類和函數,能夠更方便地實現線性迴歸.
與前面沒有區別.
num_inputs = 2 num_examples = 1000 true_w = [2, -3.4] true_b = 4.2 features = torch.from_numpy(np.random.normal(0, 1, (num_examples, num_inputs))) labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b labels += torch.from_numpy(np.random.normal(0, 0.01, size=labels.size()))
用torch.utils.data模塊,主要使用TensorDataset類和DataLoader類
import torch.utils.data as Data batch_size=10 dataset = Data.TensorDataset(features,labels) data_iter = Data.DataLoader(dataset,batch_size=batch_size,shuffle=True) for X,y in data_iter: print(X,y) break
在上一節從零開始的實現中,咱們須要定義模型參數,並使用它們一步步描述模型是怎樣計算的。當模型結構變得更復雜時,這些步驟將變得更繁瑣。其實,PyTorch提供了大量預約義的層,這使咱們只需關注使用哪些層來構造模型。下面將介紹如何使用PyTorch更簡潔地定義線性迴歸。
首先,導入torch.nn
模塊。實際上,「nn」是neural networks(神經網絡)的縮寫。顧名思義,該模塊定義了大量神經網絡的層。以前咱們已經用過了autograd
,而nn
就是利用autograd
來定義模型。nn
的核心數據結構是Module
,它是一個抽象概念,既能夠表示神經網絡中的某個層(layer),也能夠表示一個包含不少層的神經網絡。在實際使用中,最多見的作法是繼承nn.Module
,撰寫本身的網絡/層。一個nn.Module
實例應該包含一些層以及返回輸出的前向傳播(forward)方法。下面先來看看如何用nn.Module
實現一個線性迴歸模型。
class LinearNet(nn.Module): def __init__(self, n_feature): super(LinearNet, self).__init__() self.linear = nn.Linear(n_feature, 1) # forward 定義前向傳播 def forward(self, x): y = self.linear(x) return y net = LinearNet(num_inputs) print(net) # 使用print能夠打印出網絡的結構
輸出:
LinearNet( (linear): Linear(in_features=2, out_features=1, bias=True) )
事實上咱們還能夠用nn.Sequential
來更加方便地搭建網絡,Sequential
是一個有序的容器,網絡層將按照在傳入Sequential
的順序依次被添加到計算圖中。
# 寫法一 net = nn.Sequential( nn.Linear(num_inputs, 1) # 此處還能夠傳入其餘層 ) # 寫法二 net = nn.Sequential() net.add_module('linear', nn.Linear(num_inputs, 1)) # net.add_module ...... # 寫法三 from collections import OrderedDict net = nn.Sequential(OrderedDict([ ('linear', nn.Linear(num_inputs, 1)) # ...... ])) print(net) print(net[0])
輸出:
Sequential( (linear): Linear(in_features=2, out_features=1, bias=True) ) Linear(in_features=2, out_features=1, bias=True)
能夠經過net.parameters()
來查看模型全部的可學習參數,此函數將返回一個生成器。
for param in net.parameters(): print(param)
輸出:
Parameter containing: tensor([[-0.2956, -0.2817]], requires_grad=True) Parameter containing: tensor([-0.1443], requires_grad=True)
做爲一個單層神經網絡,線性迴歸輸出層中的神經元和輸入層中各個輸入徹底鏈接。所以,線性迴歸的輸出層又叫全鏈接層。
注意:
torch.nn
僅支持輸入一個batch的樣本不支持單個樣本輸入,若是隻有單個樣本,可以使用input.unsqueeze(0)
來添加一維。
在使用net
前,咱們須要初始化模型參數,如線性迴歸模型中的權重和誤差。PyTorch在init
模塊中提供了多種參數初始化方法。這裏的init
是initializer
的縮寫形式。咱們經過init.normal_
將權重參數每一個元素初始化爲隨機採樣於均值爲0、標準差爲0.01的正態分佈。誤差會初始化爲零。
from torch.nn import init init.normal_(net[0].weight, mean=0, std=0.01) init.constant_(net[0].bias, val=0) # 也能夠直接修改bias的data: net[0].bias.data.fill_(0)
一樣,咱們也無須本身實現小批量隨機梯度降低算法。torch.optim
模塊提供了不少經常使用的優化算法好比SGD、Adam和RMSProp等。下面咱們建立一個用於優化net
全部參數的優化器實例,並指定學習率爲0.03的小批量隨機梯度降低(SGD)爲優化算法。
import torch.optim as optim optimizer = optim.SGD(net.parameters(), lr=0.03) print(optimizer)
輸出:
SGD ( Parameter Group 0 dampening: 0 lr: 0.03 momentum: 0 nesterov: False weight_decay: 0 )
咱們還能夠爲不一樣子網絡設置不一樣的學習率,這在finetune時常常用到。例:
optimizer =optim.SGD([ # 若是對某個參數不指定學習率,就使用最外層的默認學習率 {'params': net.subnet1.parameters()}, # lr=0.03 {'params': net.subnet2.parameters(), 'lr': 0.01} ], lr=0.03)
有時候咱們不想讓學習率固定成一個常數,那如何調整學習率呢?主要有兩種作法。
optimizer.param_groups
中對應的學習率# 調整學習率 for param_group in optimizer.param_groups: param_group['lr'] *= 0.1 # 學習率爲以前的0.1倍
全部的optimizer都實現了step()方法,這個方法會更新全部的參數。它能按兩種方式來使用:
for input, target in dataset: optimizer.zero_grad() output = model(input) loss = loss_fn(output, target) loss.backward() optimizer.step()
for input, target in dataset: def closure(): optimizer.zero_grad() output = model(input) loss = loss_fn(output, target) loss.backward() return loss optimizer.step(closure)
具體參考https://pytorch-cn.readthedocs.io/zh/latest/package_references/torch-optim/
num_epochs = 3 for epoch in range(1, num_epochs + 1): for X, y in data_iter: output = net(X) l = loss(output, y.view(-1, 1)) optimizer.zero_grad() # 梯度清零,等價於net.zero_grad() l.backward() optimizer.step() print('epoch %d, loss: %f' % (epoch, l.item())) dense = net[0] print(true_w, dense.weight) print(true_b, dense.bias)
輸出:
epoch 1, loss: 0.000227 epoch 2, loss: 0.000160 epoch 3, loss: 0.000136 [2, -3.4] Parameter containing: tensor([[ 2.0007, -3.4010]], requires_grad=True) 4.2 Parameter containing: tensor([4.1998], requires_grad=True)
總結:
torch.utils.data
模塊提供了有關數據處理的工具,torch.nn
模塊定義了大量神經網絡的層,torch.nn.init
模塊定義了各類初始化方法,torch.optim
模塊提供了模型參數優化的各類方法。