1. 程式人生 > 其它 >線性迴歸的從零實現(藉助pytorch)

線性迴歸的從零實現(藉助pytorch)

import torch
from torch import nn
import random
net=nn.Linear(2,1)
print(net.type)
print(torch.__version__)

za=[3,4,5,6,7,8,9,10]
random.shuffle(za)#把za打亂順序
bt=torch.tensor(za[1:7])
mt=list(range(20))
mt=torch.tensor(mt)#注意此處只有轉化為torch.tensor()後
#才能在後續的mt(bt執行)
print(bt,bt.type())
print(mt,type(mt))
print(mt[bt])#可繼續參考下面模組的程式碼

  

# %matplotlib inline #顯示圖示預設嵌入jupyter notebook中來顯示
import random
import torch
from d2l import torch as d2l
#import d2lzh_pytorch as d2l #有網友這樣說
#如果沒有d2l可以用pip安裝,或者在jupyter notebook中安裝的話用!pip install d2l命令安裝

 

'''
torch.normal(A, B ,size(C, D), requires_grad=True)
A表示均值,B表示標準差 ,C代表生成的資料行數,D表示列數,requires_grad=True表示對導數開始記錄,不需要記錄的話可以忽略。
'''
def synthetic_data(w,b,num_examples):
    """生成y=Xw+b+噪聲"""
    X=torch.normal(0,1,(num_examples,len(w)))#X為1000行2列的矩陣,0是均值,1是標準差。
    y=torch.matmul(X,w)+b#X與w相乘,並加上b,可以用廣播機制來加b,結果是1000行1列的列向量矩陣
    y+=torch.normal(0,0.01,y.shape)
#     print(y.shape,y.reshape(-1,1).shape)#torch.Size([1000]),torch.Size([1000, 1])說明y本身就是一個列向量,reshape(-1,1)後變成了若干行1列的二維矩陣
#     print(y.shape,y.shape[0],sep='\n')#torch.Size([1000]),1000,如果是y.shape[1]就報錯了,因為y只有行這個維度。
#     print(y.size())#torch.Size([1000])
#     print(y.reshape(-1,1).shape)#torch.Size([1000, 1])
#     print(y.reshape((-1,1)).shape)#torch.Size([1000, 1])
    return X,y.reshape((-1,1))

true_w=torch.tensor([2,-3.4]) #true_w是個2行1列的矩陣,是個列向量
true_b=4.2
features,labels=synthetic_data(true_w,true_b,1000)
# print(len(features),len(labels))
print(features.shape,labels.shape)
print('features:',features[0],'\nlabel:',labels[0])

  

d2l.set_figsize()
d2l.plt.scatter(features[:,1].detach().numpy(),labels.detach().numpy(),1)

  

'''每次從所給的樣本中選取batch個'''
def data_iter(batch_size,features,labels):
    num_examples=len(features)
    indices=list(range(num_examples))
    #這些樣本是隨機讀取的,沒有特定的順序
    random.shuffle(indices)#打亂順序後得到一個有打亂下標的list
    for i in range(0,num_examples,batch_size):
        '''此為構造一個隨機樣本,把樣本的順序打亂,然後間隔相同訪問,來達到隨機的目的'''
        batch_indices=torch.tensor(indices[i:min(i+batch_size,num_examples)])
        yield features[batch_indices],labels[batch_indices]
      

batch_size=10
for X,y in data_iter(batch_size,features,labels):
    print(X,'\n',y)
    break

  

num_examples=15
indices=list(range(15))
#這些樣本是隨機讀取的,沒有特定的順序
random.shuffle(indices)
for i in range(0,15,10):
    batch_indices=torch.tensor(indices[i:min(i+10,15)])
    print(batch_indices)


w=torch.normal(0,0.01,size=(2,1),requires_grad=True)
b=torch.ones(1,requires_grad=True)

def linreg(X,w,b):
    """線性迴歸模型"""
    return torch.matmul(X,w)+b
# print(w.size(),w.shape)

  

def squared_loss(y_hat,y):
    "均方誤差"
    return (y_hat-y.reshape(y_hat.shape))**2/2

  

def sgd(params,lr,batch_size):#params裡面包括w和b引數
    """小批量隨機梯度下降"""
    with torch.no_grad():
        for param in params:
            param-=lr*param.grad/batch_size#這個param又可能是w,也有可能是b;
            #此處的除以batch_size.是因為之前的loss沒有求均值,此處求均值。因為乘法對梯度來說是一個線性關係,
            #所以,除以batch_size除在上面和下面效果一樣。也因為我們計算的梯度是一個批量樣本的總和,所以用批量大小
            #來歸一化步長,這樣步長就不會取決於我們對批量大小的選擇。
            param.grad.zero_()#pytorch會不斷累加變數的梯度,故每更新一次引數,就要讓其對應的梯度清零。

  

lr=0.03
num_epochs=3
net=linreg
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)#X和y的小批量損失
        #因為1 形狀是(batch_size,1),而不是一個標量。1中的所有元素被加到
        #並一次計算關於[w,b]的梯度
        l.sum().backward()#求和之後算梯度;求和本身讓l以標量的形式變現出來;求梯度對於l中的每一個分量都是單獨求的;
        #求和之後後面會除batch_size可以計算均值。因為向量求梯度得矩陣;要同通過sum()轉化為標量,然後再求梯度。
        #不求和的話是個向量,梯度算下來就變成矩陣了,形狀沒法對應。這裡點backward()之後,你在sgd優化演算法裡就可以用
        #相應引數的點grad計算。
        sgd([w,b],lr,batch_size)#使用引數的梯度更新引數
    with torch.no_grad():
        train_l=loss(net(features,w,b),labels)
        print(f'epoch{epoch+1},loss{float(train_l.mean()):f}')

  

print('w的估計誤差:{}'.format({true_w-w.reshape(true_w.shape)}))
print('b的估計誤差:{}'.format({true_b-b}))

 

python列印的時候print(f"*******") 的括號裡的 f' ' 的意思 ?

python的print字串前面加f表示格式化字串,加f後可以在字串裡面使用用花括號括起來的變數和表示式,如果字串裡面沒有表示式,那麼前面加不加f輸出應該都一樣.Python3.6新增了一種f-字串格式化.

格式化的字串文字字首為’f’和接受的格式字串相似str.format()。它們包含由花括號包圍的替換區域。替換欄位是表示式,在執行時進行評估,然後使用format()協議進行格式化。

formatted string literals, 以 f 開頭,包含的{}表示式在程式執行時會被表示式的值代替。