深度学习修炼(三)——自动求导机制
文章目錄
- 致謝
- 3 自動求導機制
- 3.1 傳播機制與計算圖
- 3.1.1 前向傳播
- 3.1.2 反向傳播
- 3.2 自動求導
- 3.3 再來做一次
- 3.4 線性回歸
- 3.4.1 回歸
- 3.4.2 線性回歸的基本元素
- 3.4.3 線性模型
- 3.4.4 線性回歸的實現
- 3.4.4.1 獲取數據集
- 3.4.4.2 模型搭建
- 3.4.4.3 損失函數
- 3.4.4.4 訓練模型
- 3.5 后記
致謝
Pytorch中常用的四種優化器SGD、Momentum、RMSProp、Adam - 簡書 (jianshu.com)
反向傳播算法(過程及公式推導)_好的嗡嗡嗡的博客-CSDN博客
動手學深度學習——矩陣求導之自動求導_時生丶的博客-CSDN博客
(1條消息) Pytorch入門(四)——計算圖與自動求導_xinye0090的博客-CSDN博客
(1條消息) pytorch 計算圖以及backward_coderwangson的博客-CSDN博客_pytorch清空計算圖
3 自動求導機制
要明白自動求導機制,我們就要先知道計算圖是什么。在后面的學習中,我們會學習到多層感知機,而為了計算多層感知機,我們求需要動用計算圖和利用自動求導。
3.1 傳播機制與計算圖
pytorch框架和TensorFlow框架在計算圖中最大的不同是:pytorch是動態的計算圖,它能邊搭建邊運行;而TensorFlow是靜態的計算圖,它必須先把圖搭建好后才能開始計算。
3.1.1 前向傳播
我們來看看什么是前向傳播。前向傳播英文是(forward propagation或forward pass),它指的是:按順序(從輸入層到輸出層)計算和存儲神經網絡中每層的結果。
如果你聽不懂的話,可以看一下下面前向傳播的計算圖。
上述的計算圖反映了一件事,如果把上述過程看做是神經網絡,那么前向傳播實際上是在計算每一層的值。
3.1.2 反向傳播
比較重要的是反向傳播。反向傳播(backward propagation或backpropagation)指的是計算神經網絡參數梯度的方法。簡而言之就是,該方法根據微積分中的鏈式規則,按相反的順序從輸出層到輸入層遍歷網絡。同理,反向傳播計算圖如下圖所示。
反向傳播實際上就是根據求導,來算出某一節點對于另外的某一節點所給的“回饋”。即導數,但是對于多個變量求偏導,我們叫其結果為梯度。
3.2 自動求導
pytorch為我們提供了自動求導機制,其機制用torch.autograd來實現。為了照顧新手,我們不直接給出API,而采取循循引誘的方式來講解。
對于pytorch的自動求導來說,由于版本的更新迭代,老版本的實現和新版本的實現大有差異,在老版本中,自動求導必須調用autograd.Variable來把變量包裝起來,而新版本則不需要了,只需在需要自動求導的張量里添加requires_grad = True即可。
# 方法1 x = torch.randn(3,4,requires_grad = True) xout:
tensor([[-0.1225, 0.5622, 0.3288, -1.2560],
[-0.7067, 0.2453, 1.8471, 0.9765],
[-0.7606, 0.8300, -0.9079, -0.2566]], requires_grad=True)
上述的方法是下面方法的簡化版。
# 方法2 x = torch.randn(3,4) x.requires_grad = True xout:
tensor([[-0.3050, 1.6089, 0.4765, 0.8169],
[-1.4941, -0.9640, 0.4670, -1.5811],
[ 0.1837, -0.5159, 0.4066, 1.8279]], requires_grad=True)
以上的兩個方法均可以使用。
我們單單只是構建張量可不夠,只要一個張量要求啥子導。我們在構建一個張量b。
b = torch.randn(3,4,requires_grad = True)做完上述的步驟后,我們構建一個沒有權重的線性模型。
# 構建一個線性模型 t = x + b當我們做完上述的步驟后,實際上一個比較簡單的神經網絡就搭建起來了。
如果我們把線性回歸描述成神經網絡。那么我們的輸入層就是所有的特征,而輸出層就是預測值。
在如圖所示的神經網絡中,輸入為x1,x2...xdx_1,x_2...x_dx1?,x2?...xd?,因此輸入層中的輸入數(由于我們常常把輸入的特征放入向量,實際上向量的長度就是維度,故我們把輸入數也叫特征維度)為d。
由于我們通常計算時發生在輸出層里,輸入層只是負責傳入數據,所以一般輸入層不算入層數,這么說下來,我們可以得出結論:這是一個單層神經網絡。
再啰嗦幾句,上面的輸入層把數據輸到輸出層,所以給人感覺就好像輸出層一下子要處理很多的輸入(笑。。。不知道你能不能get到那種感覺),所以這大概率為什么這種輸入到輸出的變化被叫做全連接層(fully_connected layer)或稱為稠密層(dense layer)的原因了。
好,回到我們的主題,既然已經搭好了一個線性回歸模型了,我們怎么使其反向傳播?我們知道,反向傳播的前提條件是要知道前向傳播中的輸出值。所以我們繼續往下:
y = t.sum yout:
tensor(-1.6405, grad_fn=)
在pytorch中,如果需要反向傳播,只需在每個張量里指定自動求導,而后在最后一步調用以上方法即可。如在本例中,調用y.backward即可進行反向傳播。
y.backward()而后,如果我們想看b的梯度,我們可以通過以下的方式:
b.gradout:
tensor([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
這里可能會有一個疑問哈,y已經進行反向傳播了,此時如果通過變量.grad調用的是什么梯度?因為你使用的是y的反向傳播,拿b來舉例,b.grad實際上就是求y對于b的偏導。
還有一個問題是,在上述的操作過程中,我們并沒有打開t張量的自動求導開關,那他是否也能自動求導得到梯度呢?
我們調用tensor.requires_grad即可查看該張量是否打開了自動求導開關。
x.requires_grad,b.requires_grad,t.requires_gradout:
(True, True, True)
以上的結果側面印證了一個問題,即使沒有指定某一張量可自動求導,但是和打開自動求導的張量進行運算時,其運算結果的自動求導開關也會被打開。
3.3 再來做一次
我相信經過上面的一次操作,你已經大概知道torch的自動求導機制了。我們趁熱打鐵,試著做一下下面的工作。
如果我們要對上述的計算圖計算的話,我們可以進行以下步驟:
# 計算流程 x = torch.rand(1) b = torch.rand(1,requires_grad = True) w = torch.rand(1,requires_grad = True) y = w * x z = y + b# 我們查看各參數的自動求導打開情況 x.requires_grad, b.requires_grad, w.requires_grad, y.requires_gradout:
(False, True, True, True)
實際上,我們還可以把計算圖看成計算樹來查看哪些參數是葉子結點。
# 我們還可以看一下哪些參數為計算圖(樹)的葉子結點 x.is_leaf, b.is_leaf, w.is_leaf, y.is_leaf, z.is_leafout:
(True, True, True, False, False)
當上述處理完畢后,我們對z進行反向傳播計算。
z.backward(retain_graph = True) # 保存計算圖然后我們分別查看下面參數的梯度。
w.gradout:
tensor([0.3843])
b.gradout:
tensor([1.])
你可以在jupyter notebook上執行完上述的兩個參數的梯度后再執行一次,第二次時,你發現它們居然是第一次運行結果的兩倍了。也是因為我們在z的反向傳播時指定保存計算計算圖,使得后面在計算某參數的梯度時,他都是根據上一次計算圖的結果繼續累加。
3.4 線性回歸
繼機器學習一別,我們已經許久未見狹義線性模型了。對此,我們希望在深度學習中能再次實現它。
3.4.1 回歸
回歸,英文名regression。在先修課機器學習中,我們經常能夠遇見兩個名詞:回歸和分類。這兩者的區別實際上就是:回歸是根據以往的經驗來預測未來的趨勢或走向,比如說經典的房價預測問題;而分類是根據根據以往的經驗來預測下一個東西是屬于什么,經典的就是二分類問題;從宏觀上來看,回歸是相對于連續的,而分類是相對于離散的。
3.4.2 線性回歸的基本元素
線性回歸基于幾個簡單的假設:首先,假設自變量x和因變量y之間的關系是線性的,即y可以表示為x中元素的加權和,這里通常允許包含觀測值的一些噪聲;其次,我們假設任何噪聲都比較正常,如噪聲遵循正態分布。
為了解釋線性回歸,我們舉一個實際的例子,我們希望根據房屋的面積和房齡來估算房屋的價格。接下來這里涉及到許多機器學習的術語;為了開發一個能夠預測房價的模型,我們需要收集一個真實的數據集,這個數據集包括了以往房屋的預售價格、面積和房齡,當然,如果你要收集更多的特征也不是不行,但是我們目前從最簡單的開始講起。
在機器學習的術語中,我們把這部分收集來用作訓練房價模型的數據集叫做訓練數據集(training data set)或訓練集(training set)。每行數據或者用數據庫的術語來說叫元組在這里稱為樣本,也可以稱為數據樣本(training instance)或者數據點(data point)。我們把試圖預測的目標(在這個例子中指的是房屋價格)稱為標簽(label)或者目標(target)。預測所依據的自變量(在這個例子中指的是面積和房齡)稱為特征(feature)或者協變量(covariate)。
通常,我們使用n來表示數據集中的樣本數。對索引為i的樣本,其輸入表示為:x(i)=[x1(i),x2(i)]Tx^{(i)} = [x_1^{(i)},x_2^{(i)}]^Tx(i)=[x1(i)?,x2(i)?]T,其對應的標簽是y(i)y^{(i)}y(i)。
3.4.3 線性模型
線性假設是指目標可以表示為特征的加權和,也就是我們高中所熟悉的一次函數y = kx+b,只是在深度學習中,我們換成了y=w1x1+w2x2+....+by = w_1x_1 + w_2x_2 +....+by=w1?x1?+w2?x2?+....+b。其中www叫做權重(weight),b叫做截距(intercept),b在高中數學中叫截距比較多一點,但是在深度學習中它通常被叫做偏置(bias)。偏置是指當前所有特征都取值為0時,預測值應該為多少。雖然特征取值為0可能在我們說的預測房價的例子中并不存在,但是我們仍然需要偏置,因為如果沒有偏置那我們的模型會受到限制。
嚴格來說,如果應用到房價預測的例子上的話,我們可以寫出這樣的式子:price=warea?area+wage?age+bprice = w_{area}·area+w_{age}·age+bprice=warea??area+wage??age+b
如果是單純地一個特征就寫一個x,那個式子就會變為:y=w1x1+w2x2+....wnxn+by = w_1x_1+w_2x_2+....w_nx_n+by=w1?x1?+w2?x2?+....wn?xn?+b,這樣的話實際上不利于我們計算,而且不簡潔。根據我們線性代數學過的知識,我們知道可以用向量存放特征,即x = {x1,x2,x3…xn},當然,這僅僅是一個樣本,如果是多樣本的話,我們可以用矩陣來放。X的每一行是一個樣本,每一列是一種特征
KaTeX parse error: Undefined control sequence: \ at position 115: … \end{bmatrix} \? ?
如同我們前面所說,下標表示第幾個特征,上標表示第幾個樣本。
同樣地我們也把權重w放進矩陣,那么模型簡化為:y^=wTx+b\hat{y} = w^Tx + by^?=wTx+b
其中w之所以加轉置是因為矩陣乘法就是ATBA^TBATB。而w和x兩個矩陣相乘后,由于b是標量,這個時候就會用到python的廣播機制去相加。
在我們給定訓練數據X和對應的已知標簽y后,線性回歸的目標就是找到一組權重向量w和偏置b,找到后這個模型就確定下來了;當有新的x進來后,這個模型預測的y能夠和真實的y盡可能的接近。
雖然我們相信給定x預測y的最佳模型會是線性的,但我們很難找到一個有n個樣本的真實數據集,然后算出來的結果真的是線性的,這根線一點彎曲都沒有,這是不可能的。因此,即使我們確信他們的潛在關系是線性,我們也需要加入一個噪聲項來考慮誤差所帶來的影響。
3.4.4 線性回歸的實現
3.4.4.1 獲取數據集
上面一直在吹理論,實際上,如果你看過我的機器學習中關于線性回歸的闡述,應該是或多或少了解的。而本小節的重點,我們將會放在如何用torch提供的API去實現一個線性回歸上。
繼上一小節最后說的,我們很難找到一個數據集用于構建線性模型,為此,我們自己捏一個出來。
import numpy as np import matplotlib.pyplot as pltx_values = [i for i in range(11)] x_train = np.array(x_values,dtype=np.float32) x_train = x_train.reshape(-1,1) x_trainy_values = [2*i +1 for i in x_values] y_train = np.array(y_values,dtype=np.float32) y_train = y_train.reshape(-1,1) y_train捏造出數據集后,我們可視化這些數據集看一下長啥樣。
plt.figure() plt.scatter(x_train,y_train) plt.show()out:
3.4.4.2 模型搭建
當我們獲取到數據集后,我們要做的第二步是搭建一個線性模型。
#導入模塊 import torch.nn as nn import torchclass LinearRegressionModel(nn.Module):def __init__(self,input_dim,output_dim):super(LinearRegressionModel,self).__init__()self.linear = nn.Linear(input_dim,output_dim) #全連接層# 前向傳播 def forward(self,x):out = self.linear(x)return out在上述的類中,我們需要去繼承torch.nn.Module包,以方便我們后續神經網絡層搭建。繼承完畢后,我們設定類中初始化方法,我們調用父類方法初始化,并且指定我們要搭建的線性回歸神經網絡的層,在這里,我們需要的僅僅是一個有輸入和輸出的全連接層,且其中不需要添加激活函數,我們調用nn.Linear(input_dim,output_dim)即可搭建,里面傳入的參數分別是輸入數據的維度和輸出數據的維度,在線性回歸中,通過輸入數據和輸出數據的維度全設為1即可。
除了初始化,我們還需要在類中添加一個前向傳播成員函數,前向傳播的計算實際上就是上面nn.Linear()輸入數據后得出的結果。
搭建好神經網絡后,我們需要實例化神經網絡類并且為其傳參。
# 實例化類 model = LinearRegressionModel(input_dim=1,output_dim=1) modelout:
LinearRegressionModel(
(linear): Linear(in_features=1, out_features=1, bias=True)
)
3.4.4.3 損失函數
搭建好模型后,我們還需要搭建損失函數,以此訓練模型。
epochs = 1000 #訓練次數 learning_rate = 0.01 #學習率 # 優化器 optimizer = torch.optim.SGD(model.parameters(),lr = learning_rate) # 優化器使用的誤差函數 criterion = nn.MSELoss()在上面的代碼中,我們調用torch.optim包下的SGD(隨機梯度下降法)來優化我們的訓練模型,并且我們采用均方誤差(Mean Square Error,MSE)進行梯度下降。SGD的有參構造器中需要傳入我們神經網絡對象中所有參數,并且還要傳入一個學習率。
定義好損失函數后,下一步,我們就該進行訓練了。
3.4.4.4 訓練模型
我們著重訓練我們的模型。
for epoch in range(epochs):# 每梯度下降一次,epoch+1epoch += 1# 前面的數據是array格式,torch只能處理tensor格式inputs = torch.from_numpy(x_train)labels = torch.from_numpy(y_train)# 梯度要清零每一次迭代optimizer.zero_grad()# 前向傳播outputs = model(inputs)# 計算損失loss = criterion(outputs,labels)# 反向傳播loss.backward()# 更新權重參數optimizer.step()# 打印每五十次梯度下降后對應損失值if epoch % 50 == 0:print(f'epoch {epoch}, loss {loss.item()}')out:
epoch 50, loss 0.23870129883289337
epoch 100, loss 0.13614629209041595
epoch 150, loss 0.07765268534421921
epoch 200, loss 0.04429023712873459
epoch 250, loss 0.02526141330599785
epoch 300, loss 0.014408227056264877
epoch 350, loss 0.008217886090278625
epoch 400, loss 0.00468717934563756
epoch 450, loss 0.0026733996346592903
epoch 500, loss 0.0015248022973537445
epoch 550, loss 0.0008696810691617429
epoch 600, loss 0.0004960428341291845
epoch 650, loss 0.00028292808565311134
epoch 700, loss 0.0001613689964869991
epoch 750, loss 9.204218804370612e-05
epoch 800, loss 5.2495626732707024e-05
epoch 850, loss 2.9940983949927613e-05
epoch 900, loss 1.7078866221709177e-05
epoch 950, loss 9.74200611381093e-06
epoch 1000, loss 5.556627456826391e-06
在第一講中我們不是說了tensor很重要嗎,現在可以告訴你為什么重要了,因為神經網絡只接收tensor格式。所以對于array的數據,我們必須將其先轉換為tensor再進行處理。
我們還要先注意在前面我們說過計算圖可以保存梯度,為了防止誤操作,我們在每次梯度下降時,我們都要清零迭代后的梯度數據。
接著我們需要計算前向傳播,而后根據前向傳播的輸出值,通過損失函數反向傳播,反向傳播后優化器內參數不會自動更新需要手動更新。
訓練完模型后,我們可以看看模型的預測結果,這里你照做就行,后面我再和你解釋這是怎么一回事。
predicted = model(torch.from_numpy(x_train).requires_grad_()).data.numpy() predicted訓練好的模型如果效果十分好,那我們可以選擇保存該模型,以便下次的使用。保存模型的方法如下:
torch.save(model.state_dict(),'model.pkl')需要知道的是,模型的保存是以字典形式進行保存,保存的僅僅是模型的參數,并且保存的名字為’model.pkl’且。
如果我們需要讀入模型,可以如下:
# 讀取模型 model.load_state_dict(torch.load('model.pkl'))最后,在整個訓練過程中,實際上我們并沒有用到GPU來加速我們的訓練過程,這是因為我們的模型十分簡單,所有的數據集也很少。在面對大數據的時候,我們通常需要GPU來加速我們模型的訓練,其方法是在實例化神經網絡類后,將該對象傳出cuda設備。并且在后面將訓練集傳入模型的時候,也要傳入cuda。
#將神經網絡對象傳給設備 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") model.to(device)#將數據傳入cuda inputs = torch.from_numpy(x_train).to(device) Labels = torch.from_numpy(y_train).to(device)3.5 后記
這一講中講的自動求導機制并沒有什么復雜,并不需要魔化它,你需要知道的僅僅是:在構建數據集張量的時候打開自動求導,并且先計算前向傳播后根據前向傳播的值,輸入損失函數然后反向傳播更新參數即可。其他的底層的東西如果你真想弄明白,何必呢,你搞底層就不會來看我這一篇筆記了不是。而且人家框架就是為了讓你避免重復造輪子。
至于3.4中線性回歸模型的搭建,這只是一個簡單到不能再簡單的torch的初體驗,實際上,里面的很多方法包含有很多的參數,都是我們不宜直接一波灌輸進去的,在后面的學習中,我們會逐步地去掌握它們。
總結
以上是生活随笔為你收集整理的深度学习修炼(三)——自动求导机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高新园区到大连计算机学校,大连高新区中心
- 下一篇: Visual Studio 2019 C