使用Pytorch實現簡單線性迴歸

我們的任務是給定一些數據點,這些數據點爲一次函數加噪聲構成。我們需要通過訓練得出一次函數的各個參數。
數據的公式表達爲
y=wx+b+cy = wx + b + c
其中,y是最終的數據,w和b是我們需要求的參數。

第一步:生成數據集

我們隨機一個x的列表,使用一個真實的w和b去計算它對應的y的值,再給y的值加上一個噪聲,並將噪聲後的數據作爲我們的訓練數據。

%matplotlib inline   
#此語句可以讓plot繪製的圖直接顯示在jupyter 的控制檯裏
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 = 3.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  #生成正確的數據,這裏是y

#給正確的數據加一點噪聲
labels += torch.from_numpy(np.random.normal(0,0.01,size = labels.size()))  #np.random.normal參數:均值  標準差  輸出的矩陣大小

plt.scatter(features[:,1].numpy(),labels.numpy(),1);

我們這裏生成了1000個值,並繪製它的圖形如下:
在這裏插入圖片描述

定義數據讀取函數

接下來我們來定義一個數據讀取的函數,作用是每次隨機從我們生成的數據集了讀取一個batch的數據

#數據讀取函數,每次從features和labels中隨機讀取一個批次的數據
def data_iter(batch_size,features,labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    random.shuffle(indices)  #隨機排列樣本
    for i in range(0,num_examples,batch_size):
        j = torch.LongTensor(indices[i:min(i + batch_size,num_examples)])   #將下標切片,並轉換成LongTensor類型賦給j
        
        yield features.index_select(0,j),labels.index_select(0,j)

初始化模型參數

這裏原書上的設置的類型是torch.float32,不過前面生成的數據均爲torch.float64的,在後面做矩陣乘法會因爲類型不一致報錯,這裏修改爲torch.float64

w = torch.tensor(np.random.normal(0,0.01,(num_inputs,1)),dtype = torch.float64)
b = torch.zeros(1,dtype = torch.float64)
# w.requires_grad = True
# b.requires_grad = False
#上面直接設置或者使用原地處理方式
w.requires_grad_(requires_grad = True)
b.requires_grad_(requires_grad = True)

定義模型

這裏實際上就是定義了一個計算函數,計算w和b取某個值時得到的y1y_1是多少,方便後面用來計算與真實值的誤差。

def linreg(x,w,b):#這裏使用的torch.mm  需要保證x w 和 b類型一致   比如均爲torch.float32 或者 均爲 torch.float64,如果不一致會報錯
    return torch.mm(x,w) + b  # 這裏返回的是x與w相乘再加b  實際上即計算y

定義損失函數和優化方法

def squared_loss(y_hat,y):
    return (y_hat - y.view(y_hat.size())) ** 2 / 2    #   a**b 即a的b次方,除2應該是爲了後面求導消參數 

#隨機梯度下降法
def sgd(params,lr,batch_size):
    for param in params:
        param.data -= lr * param.grad / batch_size   #讓其沿着梯度減少

開始訓練模型

實際上就是用w和b代進去算y1y_1,然後計算損失(y1yy_1與y的距離),用優化函數去降低這個損失


lr = 0.03   #學習率
num_epochs = 3 #訓練次數
net = linreg #設置網絡的操作,也就是前面說的那個計算y值的函數
loss = squared_loss #設置計算損失的方法
for epoch in range(num_epochs):
    for x,y in data_iter(batch_size,features,labels):
        l = loss(net(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()))

這是我這裏的輸出結果:

Epoch 1,loss 0.030724 
Epoch 2,loss 0.000125 
Epoch 3,loss 0.000049 

我們來比較一下真實的值和訓練的值:

print(true_w,'\n',w)
print(true_b,'\n',b)

結果爲:

[2, -3.4] 
 tensor([[ 1.9998],
        [-3.3995]], dtype=torch.float64, requires_grad=True)
3.2 
 tensor([3.1997], dtype=torch.float64, requires_grad=True)

我們真實的w設置的是2和-3.4,而訓練得出的是1.9998和-3.3995
真實的b設置的爲3.2,而訓練得到的b爲3.1997
可以看到,我們只訓練了三次,而得到的值基本是符合趨勢的,不過這只是一個很簡單的模型,僅僅有兩個參數,當我們模型很複雜時,我們就需要添加更多的層來訓練了。

PS.

以上代碼來源於Github《動手學深度學習》Pytorch版

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