零基礎學習人工智慧—Python—Pytorch學習(二)

kiba518發表於2024-08-08

前言

數學的學習跟數學的計算是要分開的,現在回頭再去看大學的高數和線性代數,如果只是學習的話,其實一門課程3天,也就學完了。
學校的課程之所以上那麼久,其實是為了考試,也就是為計算準備的。計算是有意義的,但在有計算機的情況下,計算的意義並不是很大。
所以,如果大學數學沒學好,只要花一星期,就能補回來。甚至你沒上過大學,只要你上過初中,同樣,只需要一個星期就能學會高數和線性代數。
但,但,但,問題是,沒有人這樣給你上課,也沒有這樣資料讓你學習。至少國內是沒有這樣學習的資訊,國內全是耽誤我們學習效率的學習模式。
明明是一個星期的知識,非得浪費我們一年到一年半。而且大學數學學習的計算,也沒有法應對考研的題,你想考研還是得報班或者自己再繼續學習解題技巧。
總之,大學數學是個即浪費學習時間,又完全沒有目的性的扯淡課程。

Gradient

上一篇介紹了一點梯度,正向傳播,逆向傳播。這裡再詳細介紹一下。
不要被這些名詞嚇住了,名詞的本質都是總結,而總結的名詞,其實是最阻礙我們學習的,我們要討厭它,但不用害怕它。
先看一下requires_grad這個引數的使用,程式碼如下:

print("============求梯度1==============")
a = torch.randn(3) #這裡是randn 不是rand  torch.randn:生成服從標準正態分佈(均值為0,標準差為1)的隨機數。  torch.rand:生成服從均勻分佈(在區間 [0, 1) 之間)的隨機數。
print(a)
b=a+2
print(b) # 輸出tensor是a+2計算後的結果
x = torch.randn(3,requires_grad=True) #這裡是randn 不是rand
print(x)
y=x+2
print(y)  # 輸出tensor是x+2計算後的結果,同時記錄了函式,grad_fn=<AddBackward0> 表示是加法函式 grad=gradient  fn=Function

這裡a和x分別是開啟了requires_grad和沒有開始requires_grad的模式。如下圖:
image
開啟了requires_grad的x,多了一個屬性requires_grad=True。
經過y=x+2計算的y,多了一個屬性grad_fn=< AddBackward0 >,這裡grad=gradient fn=Function,就是梯度函式的意思;裡面的Add是加法的意思。
而這個y=x+2,這個計算就是前向傳播,前向傳播就是這一堆我們定義的函式。

正態分佈簡介

上面提到了正態分佈,這裡簡單解釋一下。
正態分佈 若以0為中心,稱為均值為0。若均值為0,標準差為1,資料點在不同區間內的分佈遵循68-95-99.7規則
均值0,標準差1(資料在68%[-1, 1]95%[-2, 2]99.7%[-3, 3])。即資料以0為中心,向兩邊擴散,68%的資料點位於均值的1個標準差範圍內(即[-1, 1]區間)95%的資料點位於均值的2個標準差範圍內(即[-2, 2]區間)。99.7%的資料點位於均值的3個標準差範圍內(即[-3, 3]區間)。
均值0,標準差2,資料在68%[-2, 2]95%[-4, 4]99.7%[-6, 6])。

標量函式和逆向傳播

上面我們使用了前向傳播,並設定y=x+2這樣的函式,現在我們在增加一個前向傳播函式:z=y * y * 2,然後再設定標量函式,最後在執行逆向傳播。
注1:在使用backward前,必須給一個scale value(標量值,即常數C) 比如z=z.mean(),或者給一個權度tensor,這裡先介紹傳遞標量函式。
注2:標量函式就是前向傳播中的計算損失的損失函式。
注3:標量函式其實是一個標量,或稱常量,或稱常數,或稱值,或者稱一個數。(這裡要是說傳遞的是一個數,那就low了,但要說傳了一個標量,就明顯較高大上了,這就是名詞阻礙我們學習的最完美體現了)。

x = torch.randn(3,requires_grad=True)
print(x)
y=x+2
z=y*y*2
print(z) # 這裡會增加屬性,grad_fn=<MulBackward0> ,這裡的mul表示是乘法
z=z.mean() # 指定標量函式
#這裡必須指定標量函式,如果刪除z=z.mean() 這句話會提示 grad can be implicitly created only for scalar outputs 
print(z) # 屬性grad_fn=<MeanBackward0>,Mean表示平均值函式
z.backward() #逆向傳播 如果requires_grad=False,則執行z.backward()回拋異常,因為沒有記錄grad_fn
print(x.grad)

執行如下圖:
image

程式碼簡介如下

x 是啟用了自動求導的張量。
y = x + 2,y 仍是一個啟用了自動求導的張量。
z = y * y * 2,z 是一個啟用了自動求導的張量。
z = z.mean(),z 是標量(因為 mean() 返回的是張量的平均值,結果是標量)。
呼叫 z.backward(),計算 z 對 x 的梯度,並將其儲存在 x.grad 中。
此時,x.grad 中儲存的是 z 對 x 的梯度,即 dz/dx;梯度的結構是跟x的結構一樣的。

梯度清0

在第二次計算梯度(呼叫backward())之前需要清零梯度。
如果在第二次呼叫 backward() 之前沒有清零梯度,那麼第二次呼叫 backward() 計算出的梯度會疊加在第一次計算出的梯度上。

print("============清零grad==============")
weights =torch.ones(4,requires_grad=True)
for epoch in range(3):
    model_output =(weights*3).sum()#設定標量值,這裡是連寫了,分開就是a=weight*3 model_output=a.sum()
    model_output.backward()
    print(model_output.grad)
    model_output.zero_()#可以註釋這一行,看看不清零的效果

加權

在計算梯度(呼叫backward())之前沒有設定標量或權度,就會報錯。
上面說過了標量,現在來介紹加權。
程式碼如下:

x = torch.randn(3,requires_grad=True)
print(x)
y=x+2
z=y*y*2
v = torch.tensor([1.0,2.0,3.0],dtype=torch.float32)
z.backward(v)
print(x.grad)

這裡在z.backward(v)呼叫前,增加了一個建立權度tensor[v]的操作,這個tensor的結構要求和x是一樣的,然後再把v做為引數給z.backward()。
像上面那樣呼叫, backward() 時,沒有傳遞引數,則預設會傳遞一個標量 1。
即:在使用backward時,要麼指定一個標量(使用標量函式)要麼指定一個權度tensor,如果不滿足這兩種情況,就會報錯。
標量與加權的區別是,標量會乘到張量的每個元素裡,而加權是把指定加權的tensor的元素乘到張量的對應行號和列號的元素裡。

計算邏輯

在計算梯度時,會把梯度的計算結果,x,權度tensor,全提出來,然後相乘。
因為梯度,x,權度tensor的結構是相同的,而計算方式就是對應元素相乘,這個又叫做鏈式法則(就是對應項乘以對應項)。

具體計算

y=x+2,dy/dx=y導=1
z=2y²,dz/dy=z導=4y
dz/dx=(dz/dy) * (dy/dx)=1 * 4y=4y
因為y=x+2所以4y=4(x+2)
加權後是三個元素分別是 1 * 4(x1+2) 2 * 4(x2+2) 3 * 4(x3+2)
帶入x即可得到梯度。
如下圖,4 * (0.8329+2)=11.3316,下圖是11.3317,這裡應該是有個進位。
image

傳送門:
零基礎學習人工智慧—Python—Pytorch學習(一)

基礎學習就先到這。


注:此文章為原創,任何形式的轉載都請聯絡作者獲得授權並註明出處!



若您覺得這篇文章還不錯,請點選下方的【推薦】,非常感謝!

https://www.cnblogs.com/kiba/p/18348414

相關文章