pytorch学习随笔记录一——tensor,Autograd与数学求导结果计算对比

文章目录

  • tensor
    • tensor常用操作对应代码
  • Autograd
    • 简单实例
    • 稍微复杂的例子

tensor

无论查多少资料看tensor总是从头说起,我觉得无外乎,如何定义,如何计算,如果转换。
直接将常规用法写为一个文件,直接运行看结果和注释就可以知道tensor几乎全部的用法了。这个是参考侠之大者_7d3f的简书文章:https://www.jianshu.com/p/be67a948eebb。
具体代码如下:
当然,顺利运行的前提是import torch和numpy两个模块。需要前期建立好对应的环境,对应torch环境建立,以后再写一篇总结吧。由于代码较长,可以使用目录的链接进行跳过哈。

tensor常用操作对应代码

# 导入pytorch
import torch
import numpy as np

# 创建一个3x3全1矩阵,Tensor
x1 = torch.ones(3, 3)
print(x1)

# 5x5 全0矩阵
x2 = torch.zeros(5, 5)
print(x2)

# 与x1同维0矩阵
x4 = torch.zeros_like(x1)
print(x4)

# 与x1同维全1矩阵
x5 = torch.ones_like(x1)
print(x5)

# 对角矩阵
x6 = torch.diag(torch.from_numpy(np.array([1, 2, 3, 4, 5])))
print(x6)

# 5x5 随机矩阵
x7 = torch.rand(5, 5)
print(x7)

# 5x5 norm分布矩阵
x8 = torch.randn(5, 5)
print(x8)

# 创建一个empty Tensor
x9 = torch.empty(3, 3)
print("x9 = ", x9)

# 创建一个Tensor,给定数值
x10 = torch.Tensor([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
print(x10)

# 根据已有的Tensor创建一个新的Tensor
x11 = torch.rand_like(x10, dtype=torch.float)
print(x11)

# 获取Tensor的size,  Tensor.Size 实际上是一个Tuple
print(x11.size())

# Tensor的in place 操作,会改变Tensor本身
x12 = torch.rand(3, 3)
print(x12.t_())
print(x12.copy_(x11))

# Tensor  resize/reshape操作
x13 = torch.randn(4, 4)
x14 = x13.view(16)          #16*1
print(x14)
x15 = x13.view(-1, 8)       # -1, 此维度根据另一个维度计算得到
print(x15)

# 只有一个数的Tensor,  使用xxx.item() 得到python 数值
x16 = torch.rand(1)
print(x16)
print(x16.item())

# 获取Tensor中元素的个数
x17 = torch.randn(1, 2, 3, 4, 5)
print(torch.numel(x17))
print(torch.numel(torch.zeros(4, 5)))

# 判断一个对象是否是Tensor
print(torch.is_tensor(x17))
print(torch.is_tensor(np.array([1, 2, 3, 4])))

# 判断一个对象是否为Pytorch storage object
print(torch.is_storage(torch.empty(3, 3)))          #False???
print(torch.is_storage(np.zeros(shape=(3, 3))))     #False

# 设置Tensor的数据类型,初始默认为torch.float32
print(torch.Tensor([1.2, 3]).dtype)                 #float32
torch.set_default_dtype(torch.float64)
print(torch.Tensor([1.2, 3]).dtype)                 #float64

# 获取默认的Tensor数据类型
print(torch.get_default_dtype())                    #float64
torch.set_default_dtype(torch.float32)
print(torch.get_default_dtype())                    #float32

# rom
# __future__
# import print_function
# import torch

'''
pytorch矩阵操作
var = torch.Tensor()  返回一个Tensor

tensor1 = torch.Tensor(3, 3)
tensor2 = torch.Tensor(3, 3)
 var2 = torch.add(tensor1, tensor2)     # 矩阵加
 var3 = torch.sub(tensor1, tensor2)     # 减
 var4 = torch.mul(tensor1, tensor2)     # 乘
 var5 = torch.div(tensor1, tensor2)     # 矩阵点除
 var6 = torch.mm(tensor1, tensor2)      # 矩阵乘

'''

print(dir(torch.Tensor))
print(help(torch.tensor))

x1 = torch.Tensor(5, 3)  # 构造一个5x3的Tensor
x2 = torch.rand(5, 3)  # 构造一个随机初始化的Tendor
print(x1)
print("x1", x1.size())
print(x2.size())

# #####################Tensor相加################################
# 2个Tensor相加
y = torch.rand(5, 3)
var1 = torch.add(x1, y)

# 2个Tensor相加
var2 = x2 + y

var3 = torch.rand(5, 3)
# 2个Tensor相加
torch.add(x1, y, out=var3)
print(var3)

# Tensor相加,y.add_() 会改变y的值
y.add_(x2)
print(y)

# #####################Tensor相减###############################
x3 = torch.rand(5, 5)
x4 = torch.rand(5, 5)

y2 = x3 - x4
y3 = torch.sub(x3, x4)
print(x3.sub_(x4))

# ###################Tensor相乘################################
x5 = torch.rand(3, 3)
x6 = torch.rand(3, 3)
y4 = x5 * x6  # 矩阵元素点乘
y5 = torch.mul(x5, x6)
print(x5.mul_(x6))

# ###################Tensor相除################################
x7 = torch.rand(5, 5)
x8 = torch.rand(5, 5)
y6 = x7 / x8
y7 = torch.div(x7, x8)
print(x7.div_(x8))

# #################Tensor矩阵乘################################
x9 = torch.rand(3, 4)
x10 = torch.rand(4, 3)
y8 = torch.mm(x9, x10)
print("y8", y8)
# print(x9.mm_(x10))    # 错误用法


# ###################矩阵切片###################################
# 矩阵切片
var4 = x2[:, 1]
print(var4)


'''
torch.Tensor与numpy.ndarray的相互转换

'''


x1 = torch.Tensor(5, 5)

# 将Tensor转为numpy的ndarray
var1 = x1.numpy()
print(var1)

# 将numpy的ndarray转为Tensor
x2 = np.ndarray(shape=(5, 5), dtype=np.int32)
var2 = torch.from_numpy(x2)
print(var2)

另外这里还有一篇讲的比较全的博文:https://blog.csdn.net/V_lq6h/article/details/88086712
还有在具体使用过程中发现,torch.Tensor和torch.tensor是存在区别的,上文用法都是tensor的用法。只与两者区别,我语言功底浅,还没彻底弄懂,留下查到的博文:玄云飘风对这个问题的讨论:https://blog.csdn.net/tfcy694/article/details/85338745

Autograd

PyTorch 中所有神经网络的核心是 autograd。我们先简单介绍一下这个包,然后训练一个神经网络。

autograd 为张量上的所有操作提供了自动求导。它是一个在运行时定义的框架,这意味着反向传播是根据你的代码来确定如何运行。torch.Tensor 是这个包的核心类。如果设置 .requires_grad 为 True,那么将会追踪所有对于该张量的操作。当完成计算后通过调用 .backward() 会自动计算所有的梯度,这个张量的所有梯度将会自动积累到 .grad 属性。这也就完成了自动求导的过程。

要阻止张量跟踪历史记录,可以调用 .detach() 方法将其与计算历史记录分离。为了防止跟踪历史记录(和使用内存),可以将代码块包装在 with torch.no_grad(): 语句中。这一点在评估模型时特别有用,因为模型可能具有 requires_grad=True 的可训练参数,但是我们并不需要计算梯度。

自动求导中还有另外一个重要的类 Function。Tensor 和 Function 互相连接并生成一个非循环图,其存储了完整的计算历史。

如果需要计算导数,你可以在 Tensor 上调用 .backward()。 如果 Tensor 是一个标量(即它包含一个元素数据)则不需要为 backward() 指定任何参数。但是,如果它有多个元素,你需要指定一个 gradient 参数来匹配张量的形状。

以上段落主要参考:https://huhuhang.com/post/machine-learning/pytorch-basic-tutorials链接。

简单实例

这里我们做一个简单的示范,在手动求导与autograd对应,看明白autograd的具体过程。
我们要计算: o u t = m e a n ( 2 ∗ ( x + 2 ) 2 + ( x + 2 ) ) out=mean(2*(x+2)^2+(x+2)) out=mean(2(x+2)2+(x+2))这个式子中,x的导数,其中x为两行两列行列式。
我们先用程序定义这个函数:

import torch

x = torch.tensor([[1., 4.], [3., 10.]], requires_grad = True)
y = x+2
z = 2*y*y+y
out = z.mean()

这里由于要求x的导数,所以设置其requires_grad = True。这里再给一种设置方式:x.requires_grad_(True)注意其后有“_”。
下面很简单,直接求导:

out.backward()
print(x.grad)
#tensor([[ 3.2500,  6.2500],
#       [ 5.2500, 12.2500]])

那我们一步一步来计算一下x的导数看看与结果是否相同:

  1. 由于out是对x(两行两列求均值): ∂ o u t ∂ z = 1 4 ∗ [ 1 1 1 1 ] \frac{\partial out}{\partial z}=\frac 1 4*\begin{bmatrix} 1&1\\1&1 \end{bmatrix} zout=41[1111]得到其对应结果。
  2. 之后,对y求偏导,首先有z和y的关系式: z = 2 y 2 + y z=2y^2+y z=2y2+y可以得到其计算out对y的偏导公式:
    ∂ o u t ∂ y = ∂ o u t ∂ z ∂ z ∂ y = 1 4 ∗ ( 4 y + 1 ) \frac{\partial out}{\partial y}=\frac{\partial out}{\partial z}\frac{\partial z}{\partial y}=\frac1 4*(4y+1) yout=zoutyz=41(4y+1)
  3. 再然后,结合 y = x + 2 y=x+2 y=x+2可计算out对x的偏导: ∂ o u t ∂ x = ∂ o u t ∂ z ∂ z ∂ y ∂ y ∂ x = 1 4 ∗ ( 4 ( x + 2 ) + 1 ) = x + 9 4 \frac{\partial out}{\partial x}=\frac{\partial out}{\partial z}\frac{\partial z}{\partial y}\frac{\partial y}{\partial x}=\frac1 4*(4(x+2)+1)=x+\frac 9 4 xout=zoutyzxy=41(4(x+2)+1)=x+49
    对照backward结果,可以看到是完全相同的,从而粗略的了解了backward的计算过程。

稍微复杂的例子

以上的例子只有一个通路,反向逐步计算即可得到与backward相同的结果。
但是,在实际训练网络过程中,我们不是为了求取输入数据的导数,而是为了计算网络参数的导数,从而实现对参数的调整。
下一步呢,我们来计算一下一个稍微复杂一点的式子的每个环节的偏导数。
这里参考AlanBupt的博文。
此博文对叶子节点又进行了详细的阐述,内容清晰,读之受益匪浅,推荐阅读!
o u t = m e a n ( ( w 1 ∗ d a t a + w 2 ) ∗ ( w 1 ∗ d a t a ∗ w 3 ) ) out=mean((w_1*data+w_2)*(w_1*data*w_3)) out=mean((w1data+w2)(w1dataw3))其中 l 1 = w 1 ∗ d a t a l_1=w_1*data l1=w1data l 2 = w 1 ∗ d a t a + w 2 l_2=w_1*data+w_2 l2=w1data+w2 l 3 = w 1 ∗ d a t a ∗ w 3 l_3=w_1*data*w_3 l3=w1dataw3 l 4 = ( w 1 ∗ d a t a + w 2 ) ∗ ( w 1 ∗ d a t a ∗ w 3 ) l_4=(w_1*data+w_2)*(w_1*data*w_3) l4=(w1data+w2)(w1dataw3)
好了,背景交代完了,下面按照惯例,用代码描述上边式子:

import torch
data = torch.tensor([1., 4.], requires_grad=True)
#为了看计算结果,顺便求了输入数据的导数
w1 = torch.tensor(2.0, requires_grad=True)
w2 = torch.tensor(3.0, requires_grad=True)
w3 = torch.tensor(4.0, requires_grad=True)
l1 = data * w1
l2 = l1 + w2
l3 = l1 * w3
l4 = l2 * l3
out = l4.mean()
print("out",out)

其实描述式子的过程就是神经网络中forward计算过程。我们用网络图进行描述:

data
L1
L2
L3
L4
out
w1
w2
w3

下边进行反向传播求导:

l4.register_hook(lambda grad: print('l4 grad: ', grad))
l3.register_hook(lambda grad: print('l3 grad: ', grad))
w3.register_hook(lambda grad: print('w3 grad: ', grad))
l1.register_hook(lambda grad: print('l1 grad: ', grad))
# 以上是保留中间参数求导结果的方法。
out.backward()
# l4 grad:  tensor([0.5000, 0.5000])
# l3 grad:  tensor([2.5000, 5.5000])
# w3 grad:  tensor(49.)
# l1 grad:  tensor([14., 38.])

中间记录几个有别于示例1的节点,下边来手动计算一下:

  1. 最终点还是一个均值求导,此次为一行两列,所以结果为1/2: ∂ o u t ∂ l 4 = 1 2 ∗ [ 1 1 ] \frac{\partial out}{\partial l_4}=\frac 1 2*\begin{bmatrix} 1&1 \end{bmatrix} l4out=21[11]得到其对应结果。
  2. 进而计算 l 3 l_3 l3的求导结果: l 4 = l 2 ∗ l 3 l_4=l_2*l_3 l4=l2l3所以,求导结果为: ∂ o u t ∂ l 3 = ∂ o u t ∂ l 4 ∂ l 4 ∂ l 3 = 1 2 ∗ l 2 \frac{\partial out}{\partial l_3}=\frac{\partial out}{\partial l_4}\frac{\partial l_4}{\partial l_3}=\frac1 2*l_2 l3out=l4outl3l4=21l2由于 l 2 = [ 5 11 ] l_2=\begin{bmatrix} 5&11 \end{bmatrix} l2=[511],可得上式结果与程序输出一致。
  3. 进一步计算 w 3 w_3 w3方法与上述一致,但是由于 w 3 w_3 w3为一个值,需要将两个偏导结果求和。 ∂ o u t ∂ w 3 = ∂ o u t ∂ l 4 ∂ l 4 ∂ l 3 ∂ l 3 ∂ w 3 = Σ ( 1 2 ∗ l 2 ∗ l 1 ) = 49 \frac{\partial out}{\partial w_3}=\frac{\partial out}{\partial l_4}\frac{\partial l_4}{\partial l_3}\frac{\partial l_3}{\partial w_3}=\Sigma (\frac1 2*l_2*l_1)=49 w3out=l4outl3l4w3l3=Σ(21l2l1)=49计算结果依旧与程序输出结果相同。
  4. 首先计算 o u t out out l 1 l_1 l1的函数关系: l 4 = ( l 1 + w 2 ) ( l 1 ∗ w 3 ) = w 3 ∗ l 1 2 + w 2 ∗ w 3 ∗ l 1 l_4=(l_1+w_2)(l_1*w_3)=w_3*l_1^2+w_2*w_3*l_1 l4=(l1+w2)(l1w3)=w3l12+w2w3l1计算 l 1 l_1 l1求导可得: ∂ o u t ∂ l 1 = 1 2 ∗ ( 2 w 3 ∗ l 1 + w 2 ∗ w 3 ) = [ 14 38 ] \frac{\partial out}{\partial l_1}=\frac1 2*(2w_3*l_1+w_2*w_3)=\begin{bmatrix} 14&38 \end{bmatrix} l1out=21(2w3l1+w2w3)=[1438]结果依旧与程序输出结果一致。
    到此,经过手动计算,基本明确了autograd的计算过程和方法。
    再次强调,此处例子参考了AlanBupt的博文:https://blog.csdn.net/byron123456sfsfsfa/article/details/92210253。

你可能感兴趣的:(deep,learning,神经网络,深度学习,pytorch)