作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客
本文网址:https://blog.csdn.net/HiWangWenBing/article/details/120251332
目录
第2章 Tensorflow与Pytorch的Varialbe对象的比较
4.5 非求导属性的对象不能求导:requires_grad = False
4.6 多次求导,保留上下文:torch.autograd.grad(y,x1, retain_graph=True)
第6章 Pytorch Varialbe对象与Tensor对象的合并与统一
第1章 Varialbe变量求导概述
1.1 Varialbe变量基础
[Pytorch系列-19]:Pytorch基础 - Variable变量的使用方法与 Tensor变量的比较_文火冰糖(王文兵)的博客-CSDN博客
1.2 求导方法概述
(1)手工求导
这种求导方法,需要程序员预先根据原函数定义好导函数,然后直接利用导函数求导。
这种求导方法,对变量没有限制,可以是普通的tensor对象,也可以是Varialbe对象。
- tensor对象的手工求导
- Varialbe对象的手工求导
(2)自动求导
自动求导就是不需要程序员手工定义导函数,而是利用框架提供的求导工具进行求导。
自动求导由分为:
- 半自动求导:这种求导与
- 全自动求导
1.3 准备条件
#环境准备
import numpy as np
import math
import matplotlib.pyplot as plt
%matplotlib inline
import torch
from torch.autograd import Variable
print("Hello World")
print(torch.__version__)
print(torch.cuda.is_available())
Hello World 1.8.0 False
第2章 Tensorflow与Pytorch的Varialbe对象的比较
2.1 相同点
(1)封装性
他们都是在普通的tensor对象的基础上的进一步封装。
(2)原理相同
都是基于复合函数链式求导的基本原理
2.2 不同点
(1)变量导数/梯度的存放位置
- Pytorch的Varialbe对象:除了存放Varialbe对象的自身的参数值外,还增加了一个grad属性,全启动求导时存放该变量对应导数值。
- Tensorflow的Varialbe对象:只能存放Varialbe对象的自身的参数值,导数值需要定义其他Varialbe对象存放导数值。
(2)Varialbe对象与普通的tensor对象的关系
- Pytorch已经把Varialbe对象与普通的tensor对象进行了整合,统一成tensor对象,并通过requires_grad属性来标识是否需要进行自动求导,存放导数值。
- Tenorflow中的Varialbe对象与 普通的Tensor对象,还没有合并,是相互独立的。
(3)自动求导的方式
- Pytorch支持半自动求导和全自动求导
- Tensorflow只支持半自动求导。
(4)前向数据流图与反向数据流图的组织方式不同
- Pytorch把上图中的所有信息,都存放在函数的数据流图中,包括每个参数的当前的梯度值,包括Yx和Wx,Bx。也就是说Pytorch的前向计算的数据流图和反向求导的数据流图示统一的,不需要显式的为反向求导建立数据流图和相应的上下文。
- Tensorflow的正向计算的数据流图和反向求导的数据流图示分离的,在自动求导时,必须通过显式的方式建立反向求导建立数据流图和相应的上下文,使用完后,该上下文的信息释放。
第3章 手工求导
3.1 Tensor自变量的手工求导
这种方式,必须人工预先根据导数的公式,写出原函数的导函数。
并通过导函数计算导数,这种方法,虽然说是求导,实际上就是普通的正向函数计算。、
这种情形下,其实并不一定需要Varialbe变量,普通的Tensor变量也是可以的。
# 一元函数在某点(x_i)处对一元Tensor变量求导
# Tensor张量与手工求导函数
print("自变量:张量tensor => 自变量值")
x_tensor = torch.Tensor([1.0])
print("x_tensor =", x_tensor)
print("x_tensor.grad =", x_tensor.grad)
print("\n因变量:一元原函数 => 原函数值")
y_tensor = x_tensor ** 2 + 1 # y = x^2 + 1
print("y_tensor =",y_tensor)
print("\n对原函数的所有变量分别手工求偏导(通过手工定义的导函数)")
y_tensor_grad_x = 2 * x_tensor # dy = 2*x
print("\n因变量:手工求导后 => 导函数值")
print("y_tensor_grad_x =", y_tensor_grad_x)
print("y_tensor_grad_x.grad =", y_tensor_grad_x.grad)
自变量:张量tensor => 自变量值 x_tensor = tensor([1.]) x_tensor.grad = None 因变量:一元原函数 => 原函数值 y_tensor = tensor([2.]) 对原函数的所有变量分别手工求偏导(通过手工定义的导函数) 因变量:手工求导后 => 导函数值 y_tensor_grad_x = tensor([2.]) y_tensor_grad_x.grad = None
3.2 Varialbe因变量的手工求导
这个过程与Tensor自变量的手工求导完全相同,无非就先定义导函数,然后正向函数计算。
因此,如果是手工求导,是否是Varialbe变量还是Tensor变量,其实并不重要。
只有需要利用平台提供的自动求导的工具,就行自动求导时,Varialbe变量的作用才体现出来。
# 一元函数在某点(x_i)处对一元Variable变量求导
# Tensor张量与手工求导函数
print("自变量:张量tensor => 自变量值")
# Variable的参数必须是tensor
x_var = torch.autograd.Variable(torch.Tensor([1]), requires_grad = True)
print("x_var =", x_var)
print("x_var.grad =", x_var.grad)
print("\n因变量:一元原函数 => 原函数值")
y_var = x_var ** 2 + 1 # y = x^2 + 1
print("y_var =",y_var)
print("\n对原函数的所有变量分别手工求偏导(通过手工定义的导函数)")
y_var_grad_x = 2 * x_var # dy = 2*x
print("\n因变量:手工求导后 => 导函数值")
print("y_var_grad_x =", y_var_grad_x)
自变量:张量tensor => 自变量值
x_var = tensor([1.], requires_grad=True)
x_var.grad = None
因变量:一元原函数 => 原函数值
y_tensor = tensor([2.], grad_fn=<AddBackward0>)
对原函数的所有变量分别手工求偏导(通过手工定义的导函数)
因变量:手工求导后 => 导函数值
y_var_grad_x = tensor([2.], grad_fn=<MulBackward0>)
备注:
- Varialbe是一种tensor
- tensor也是一种Varialbe
- 未自动求导前,Varialbe对象和tensor对象的梯度grad都是None
Varialbe对象与Tensor对象,在手工求导方面,其实是相同的。
第4章 Varialbe变量的半自动求导
4.1 半自动求导概述
在引入Variable后,在forward时,自动生成了计算图,
backward就不需要我们手工计算了,pytorch将根据计算图自动计算梯度。
自动求导不支持一连串数值点的的自动求导,只支持单个节点。
但自动求导支持多元函数(多元参数)的自动一次性求所有参数的偏导。
所谓半自动求导:就是导数或梯度时,需要程序员指定对哪些参数进行参数进行求导,而不是所有参数根据trainable属性,自动全部求导。
4.2 torch.autograd.grad半自动求导
这种方法,使用torch的全局函数,需要指定求导的函数以及相应的偏导数对象。
如:y = wx + b
dy_dw, dy_db = torch.autograd.grad(y, [w,b], retain_graph=True)
备注:
- 这种方式获得的梯度,直接通过函数返回,并没有存放到w,b的tensor中。
- 这种方式与Tensorflow的半自动求导方式类似
- 这种方式与Tensorflow的半自动求导不同的是:pytorch不需要通过手工with语句建立求导上下文。
4.3 代码示例1:
# 一元Variable变量与自动求导
print("自变量:Variable对象 => 自变量值")
x_variable = torch.autograd.Variable(torch.Tensor([3.0]), requires_grad = True)
print("x_variable =", x_variable)
print("\n因变量:一元原函数 => 函数值")
y_variable = x_variable ** 2
print("y_variable =", y_variable)
print("\n对指定的数据流对象下的指定的参数求导数")
dy_dx = torch.autograd.grad(y_variable, x_variable)
print("\n因变量:自动求导后 => 导数值 ")
print("x_variable =", x_variable)
print("x_variable.grad =", x_variable.grad)
print("dy_dx =", dy_dx)
自变量:Variable对象 => 自变量值
x_variable = tensor([3.], requires_grad=True)
因变量:一元原函数 => 函数值
y_variable = tensor([9.], grad_fn=<PowBackward0>)
对指定的数据流对象下的指定的参数求导数
因变量:自动求导后 => 导数值
x_variable = tensor([3.], requires_grad=True)
x_variable.grad = None
dy_dx = (tensor([6.]),)
4.4 代码示例2:
# 多元Variable变量与自动求导
print("创建变量")
x1 = torch.autograd.Variable(torch.Tensor([1.0]), requires_grad = True)
x2 = torch.autograd.Variable(torch.Tensor([2.0]), requires_grad = True)
x3 = torch.autograd.Variable(torch.Tensor([3.0]), requires_grad = True)
x4 = torch.autograd.Variable(torch.Tensor([4.0]), requires_grad = False)
#x4 = torch.Tensor([4.0])
print(x1)
print(x2)
print(x3)
print(x4)
y1 = 1*x1 + 2*x2 + 3*x3 + 4*x4
y = (y1 - 1)**2
print("单独求偏导")
# torch.autograd.grad能够自动更加y函数,建立求导上行文,
# 不需要像Tensorflow那样,通过with语句建立tape对象
dy_dx1 = torch.autograd.grad(y,x1, retain_graph=True)
dy_dx2 = torch.autograd.grad(y,x2, retain_graph=True)
dy_dx3 = torch.autograd.grad(y,x3, retain_graph=True)
dy_dx4 = torch.autograd.grad(y,x4, retain_graph=True)
print(dy_dx1)
print(dy_dx2)
print(dy_dx3)
print(dy_dx4)
print("批量求偏导")
dy_dx1,dy_dx2,dy_dx3,dy_dx4 = torch.autograd.grad(y,[x1, x2, x3, x4])
print(dy_dx1)
print(dy_dx1.grad)
print(dy_dx2)
print(dy_dx2.grad)
print(dy_dx3)
print(dy_dx3.grad)
print(dy_dx4)
print(dy_dx4.grad)
创建变量 tensor([1.], requires_grad=True) tensor([2.], requires_grad=True) tensor([3.], requires_grad=True) tensor([4.], requires_grad=True) 单独求偏导 (tensor([58.]),) (tensor([116.]),) (tensor([174.]),) (tensor([232.]),) 批量求偏导 tensor([58.]) None tensor([116.]) None tensor([174.]) None tensor([232.]) None
备注:
torch.autograd.grad半自动求导,不会自动更新variable对象的grad成员变量。
4.5 非求导属性的对象不能求导:requires_grad = False
# 一元Variable变量与自动求导
print("自变量:Variable对象 => 自变量值")
x_variable = torch.autograd.Variable(torch.Tensor([3.0]), requires_grad = False)
print("x_variable =", x_variable)
print("\n因变量:一元原函数 => 函数值")
y_variable = x_variable ** 2
print("y_variable =", y_variable)
print("\n对指定的数据流对象下的指定的参数求导数")
dy_dx = torch.autograd.grad(y_variable, x_variable)
print("\n因变量:自动求导后 => 导数值 ")
print("x_variable =", x_variable)
print("x_variable.grad =", x_variable.grad)
print("dy_dx =", dy_dx)
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn
出错原因:
x_variable = torch.autograd.Variable(torch.Tensor([3.0]), requires_grad = False)
x_variable不具备求导的能力。
4.6 多次求导,保留上下文:torch.autograd.grad(y,x1, retain_graph=True)
# 一元Variable变量与自动求导
print("自变量:Variable对象 => 自变量值")
x_variable = torch.autograd.Variable(torch.Tensor([3.0]), requires_grad = True)
print("x_variable =", x_variable)
print("\n因变量:一元原函数 => 函数值")
y_variable = x_variable ** 2
print("y_variable =", y_variable)
print("\n对指定的数据流对象下的指定的参数求导数")
dy_dx = torch.autograd.grad(y_variable, x_variable)
dy_dx = torch.autograd.grad(y_variable, x_variable)
print("\n因变量:自动求导后 => 导数值 ")
print("x_variable =", x_variable)
print("x_variable.grad =", x_variable.grad)
print("dy_dx =", dy_dx)
RuntimeError: Trying to backward through the graph a second time, but the saved intermediate results have already been freed. Specify retain_graph=True when calling .backward() or autograd.grad() the first time.
错误原因:
dy_dx = torch.autograd.grad(y_variable, x_variable)这行完一次求导后,就释放求导的上下文。
解决方法1:retain_graph=True
# 一元Variable变量与自动求导
print("自变量:Variable对象 => 自变量值")
x_variable = torch.autograd.Variable(torch.Tensor([3.0]), requires_grad = True)
print("x_variable =", x_variable)
print("\n因变量:一元原函数 => 函数值")
y_variable = x_variable ** 2
print("y_variable =", y_variable)
print("\n对指定的数据流对象下的指定的参数求导数")
dy_dx = torch.autograd.grad(y_variable, x_variable, retain_graph=True)
dy_dx = torch.autograd.grad(y_variable, x_variable)
print("\n因变量:自动求导后 => 导数值 ")
print("x_variable =", x_variable)
print("x_variable.grad =", x_variable.grad)
print("dy_dx =", dy_dx)
解决方法2:重选构建动图图(包括正向与反向)
# 一元Variable变量与自动求导
print("自变量:Variable对象 => 自变量值")
x_variable = torch.autograd.Variable(torch.Tensor([3.0]), requires_grad = True)
print("x_variable =", x_variable)
print("\n因变量:一元原函数 => 函数值")
y_variable = x_variable ** 2
print("y_variable =", y_variable)
print("\n对指定的数据流对象下的指定的参数求导数")
dy_dx = torch.autograd.grad(y_variable, x_variable)
y_variable = x_variable ** 2
dy_dx = torch.autograd.grad(y_variable, x_variable)
print("\n因变量:自动求导后 => 导数值 ")
print("x_variable =", x_variable)
print("x_variable.grad =", x_variable.grad)
print("dy_dx =", dy_dx)
y_variable = x_variable ** 2 =》表明重选构建动态图,包括正向与反向。
4.7 对pytorch动图图的几点说明
(1)重选构建动态图:
y_variable = x_variable ** 2
(2)释放动态图:
torch.autograd.grad()
torch.autograd.grad(retain_graph=False)
(3)不释放动态图,继续保留动态图信息
torch.autograd.grad(retain_graph=True)
第5章 半自动求导的应用:梯度下降迭代
5.0 环境转变
#环境准备
import numpy as np
import math
import matplotlib.pyplot as plt
%matplotlib inline
import torch
from torch.autograd import Variable
print("Hello World")
print(torch.__version__)
print(torch.cuda.is_available())
Hello World 1.8.0 False
5.1 梯度下降迭代
print("定义迭代的自变量参数以及初始值")
x1_variable = torch.autograd.Variable(torch.Tensor([2.0]), requires_grad = True)
x2_variable = torch.autograd.Variable(torch.Tensor([2.0]), requires_grad = True)
print("x1_variable =", x1_variable)
print("x2_variable =", x2_variable)
# 定义原函数
def loss_fun(x1, x2):
y = x1**2 + x2**2 + 1
return (y)
# 获取y初始值
y_variable = loss_fun (x1_variable, x2_variable)
print("y_variable =", y_variable)
#定义学习率
learnning_rate = 0.1
#定义迭代次数
iterations = 30
#定义存放迭代过程数据的列表
x1_data = []
x2_data = []
y_data = []
# 保存初始值:需转换成numpy,便于matlab可视化
x1_data.append(x1_variable.data.numpy())
x2_data.append(x2_variable.data.numpy())
y_data.append(y_variable.data.numpy())
print("\n初始点:", x1_data[0], x2_data[0], y_data[0])
while(iterations):
# 创建自动求导的数据流图和上下文对象tape
y_variable = loss_fun (x1_variable, x2_variable)
# 指定参数,半自动求导
dy_dx1,dy_dx2 = torch.autograd.grad(y_variable, [x1_variable, x2_variable])
# 梯度下降迭代
x1_variable.data = x1_variable.data - (learnning_rate * dy_dx1)
x2_variable.data = x2_variable.data - (learnning_rate * dy_dx2)
#计算当前最新的y值
y_variable = loss_fun (x1_variable, x2_variable)
#保存数据
x1_data.append(x1_variable.data.numpy())
x2_data.append(x2_variable.data.numpy())
y_data.append(y_variable.data.numpy())
#下次迭代做准备
iterations = iterations -1
print("\n迭代后的数据:")
print("x1_variable =", x1_variable.data.numpy())
print("x2_variable =", x2_variable.data.numpy())
print("y_variable =", y_variable.data.numpy())
定义迭代的自变量参数以及初始值 x1_variable = tensor([2.], requires_grad=True) x2_variable = tensor([2.], requires_grad=True) y_variable = tensor([9.], grad_fn=<AddBackward0>) 初始点: [2.] [2.] [9.] 迭代后的数据: x1_variable = [0.00247588] x2_variable = [0.00247588] y_variable = [1.0000123]
备注:
(1) 函数 y = x1**2 + x2**2 + 1
- 其理论极小值为1
- 位于(x1=0, x2=0)处。
(2)自动求导,梯度下降过程
- 起始点坐标(x=2, y=2)
- 学习率=0.1
- 迭代次数=30
- 迭代后坐标: x1=0.00247588, x2_variable=0.00247588
- 迭代后的最小值:y_variable = 1.0000123
5.2 自动求导、梯度下降迭代的关键点
# 指定参数,半自动求导
dy_dx1,dy_dx2 = tape.gradient(y_variable, [x1_variable, x2_variable])
# 梯度下降迭代
x1_variable.assign_sub(learnning_rate * dy_dx1)
x2_variable.assign_sub(learnning_rate * dy_dx2)
(1)偏导/梯度的存放空间
tensorflow的半自动求导后的导数值,并不是与variable对象存放在一起的,而是通过函数返回,因此需要dy_dx1,dy_dx2存放返回的导数值。
(2)偏导/梯度的更新或迭代
通过variable.assign_sub()函数进行梯度迭代。
5.3 可视化迭代过程
fig = plt.figure()
ax1 = plt.axes(projection='3d') #使用matplotlib.pyplot创建坐标系
ax1.scatter3D(x1_data, x2_data, y_data, cmap='Blues') #绘制三维散点图
plt.show()
5.4 可视化原函数
# 可视化原函数的图形
x1_sample = np.arange(-10, 10, 1)
x2_sample = np.arange(-10, 10, 1) #X,Y的范围
x1_grid, x2_grid = np.meshgrid(x1_sample,x2_sample) #空间的点序列转换成网格点
y_grid = loss_fun(x1_grid,x2_grid) #生成z轴的网格数据
figure = plt.figure()
ax1 = plt.axes(projection='3d') #创建三维坐标系
ax1.plot_surface(x1_grid, x2_grid, y_grid ,rstride=1,cstride=1,cmap='rainbow')
第6章 Pytorch Varialbe对象与Tensor对象的合并与统一
在最新的Pytorch中, Varialbe对象已经与Tensor对象合并与统一
设置了 requires_grad=True的tensor对象的功能与varaible对象完全一致。
设置了 requires_grad=False的tensor对象的功能与传统的tensor对象完全一致。
后续讨论将不再使用专有的Varialbe对象,而直接使用统一的Tensor对象。
print("定义样本tensor")
x = torch.Tensor([2.])
y = torch.Tensor([2.])
print("定义参数tensor")
w = torch.Tensor([1.])
b = torch.tensor([1.], requires_grad=True) # 使能自动梯度计算方法1
print(x)
print(w)
print(b)
print("\n使能tensor的梯度属性,替代variable变量")
w.requires_grad_() # 使能自动梯度计算方法2
print(w)
print(b)
print("\n生成loss函数的动态数据流图")
f_pred = w * x + b
loss = (f_pred - y)**2
print("\n自动计算梯度")
torch.autograd.grad(loss, [w,b], retain_graph=True)
print(loss)
print(w.grad) #autograd.grad的计算结果被函数返回,并为存放到w和b的grad参数中!!!
print(b.grad)
print("\n生成loss函数的动态数据流图")
f_pred = w * x + b
loss = (f_pred - y)**2
print("自动反向求梯度")
loss.backward()
print(loss)
print(w.grad)
print(b.grad)
定义样本tensor 定义参数tensor tensor([2.]) tensor([1.]) tensor([1.], requires_grad=True) 使能tensor的梯度属性,替代variable变量 tensor([1.], requires_grad=True) tensor([1.], requires_grad=True) 生成loss函数的动态数据流图 自动计算梯度 tensor([1.], grad_fn=<PowBackward0>) None None 生成loss函数的动态数据流图 自动反向求梯度 tensor([1.], grad_fn=<PowBackward0>) tensor([4.]) tensor([2.])
第7章 Varialbe对象的全自动求导
待续。。。。。。。
作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客
本文网址:https://blog.csdn.net/HiWangWenBing/article/details/120251332