動手學習深度學習-梯度

# tensor的屬性改爲.requires_grad設置爲true,它將開始追蹤在其上的所有操作(這樣就可以利用鏈式法則進行梯度傳播)
# 完成計算後,可以調用.backward()來完成所有梯度計算,此Tensor的梯度將累積到.grad屬性中
"""
注意:在y.backward()時,如果y是標量,則不需要爲backward()傳入任何參數;否則,需要傳入一個與y同形的tensor

如果不想被繼續追蹤,可以調用.detach()將其從追蹤記錄中分離開來,這樣就可以防止將來的計算被追蹤,這樣梯度就傳不過去了,此外還可以
使用with torch.no_grad()將不想被追蹤的操作代碼塊包裹起來,這種方法在評估模型的時候很常用

Function是另外一個很重要的類,tensor和function互相結合就可以構建一個記錄有整個計算過程的有向無環圖,每個tensor都有一個
.grad_fn()屬性,該屬性創建該tensor的function類,就是說該tensor是不是通過某些運算得到的,若是,則grad_fn返回一個與這些運算
相關的對象,否則爲none
"""

# 創建一個tensor並設置requires_grad= True
import torch
x = torch.ones(2,2,requires_grad=True)
print(x)
"""
tensor([[1., 1.],
        [1., 1.]], requires_grad=True)
"""
print(x.grad_fn)  # None

# 再做些運算得到
y = x + 2
print(y)
"""
tensor([[3., 3.],
        [3., 3.]], grad_fn=<AddBackward0>)
"""
print(y.grad_fn)  # <AddBackward0 object at 0x000001EEC0110BA8>
# 注意:x是直接創建的,所以它沒有grad_fn,y是通過一個加法操作所創建的,所以它有一個grad_fn
# 像x這種直接創建的稱爲葉子節點,葉子節點對應的grad_fn是None
print(x.is_leaf,y.is_leaf)  # True False

# 再來點複雜的操作
z = y * y * 3
out = z.mean()
print(z,out)
"""
tensor([[27., 27.],
        [27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)
"""

# 通過.requires_grad_()來用in-place的方式改變requires_grad屬性
a = torch.randn(2,2)  # 默認requires_grad= False
a = ((a * 3) / (a - 1))
print(a.requires_grad)  # False
a.requires_grad_(True)
print(a.requires_grad)  # True
b = (a * a).sum()
print(b.grad_fn)  # <SumBackward0 object at 0x000001AAA624D240>



# 梯度
# 因爲out是一個標量,值爲27,所以調用backward()時不需要指定求導變量
out.backward()  # 等價於out.backward(torch.tensor(1.))
print(x.grad)
"""
tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])
"""
"""
例如:假設y由自變量x計算而來,w和y是同形的張量,則y.backward(w)的含義是:先計算1 = torch.sum(y * w),則1是一個標量,然後求1
對自變量x的導數
"""
x = torch.tensor([1.0,2.0,3.0,4.0],requires_grad=True)
y = 2 * x
z = y.view(2,2)
print(z)
print(y)
"""
tensor([[2., 4.],
        [6., 8.]])
"""
# 現在y不是一個標量,所以在調用backward時需要傳入一個和y同形的權重向量進行加權求和得到一個標量
v = torch.tensor([[1.0,0.1],[0.01,0.001]],dtype=torch.float)
z.backward(v)
print(x.grad)  # tensor([2.0000, 0.2000, 0.0200, 0.0020])


# 注意:grad在反向傳播的過程中是累加的,這意味着每一次運行着反向傳播梯度都會累加之前的梯度,所以一般在反向傳播之前需要把梯度
# 清零
# out2 = x.sum()
# out2.backward()
# print(x.grad)
"""
tensor([[5.5000, 5.5000],
        [5.5000, 5.5000]])
"""

# out3 = x.sum()
# x.grad.data.zero_()
# out3.backward()
# print(x.grad)
"""
tensor([[1., 1.],
        [1., 1.]])
"""

# 梯度中斷
x = torch.tensor(1.0,requires_grad=True)
y1 = x ** 2
with torch.no_grad():
    y2 = x ** 3

y3 = y1 + y2

print(x.requires_grad)  # True
print(y1,y1.requires_grad)  # tensor(1., grad_fn=<PowBackward0>) True
print(y2,y2.requires_grad)  # tensor(1.) False
print(y3,y3.requires_grad)  # tensor(2., grad_fn=<AddBackward0>) True

y3.backward()
print(x.grad)  # tensor(2.),這裏等於2是因爲y2不能求導

# 如果我們想要修改tensor的數值,但是又不希望autograd記錄,我們可以對tensor.data進行操作
x = torch.ones(1,requires_grad=True)
print(x.data)  # 還是一個tensor ,tensor([1.])
print(x.data.requires_grad)  # 但是已經獨立於計算圖之外,False

y = 2 * x
x.data *= 100
y.backward()
print(x)  # 更改data的值也會影響tensor的值  # tensor([100.], requires_grad=True)
print(x.grad)  # tensor([2.])

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