无论查多少资料看tensor总是从头说起,我觉得无外乎,如何定义,如何计算,如果转换。
直接将常规用法写为一个文件,直接运行看结果和注释就可以知道tensor几乎全部的用法了。这个是参考侠之大者_7d3f的简书文章:https://www.jianshu.com/p/be67a948eebb。
具体代码如下:
当然,顺利运行的前提是import torch和numpy两个模块。需要前期建立好对应的环境,对应torch环境建立,以后再写一篇总结吧。由于代码较长,可以使用目录的链接进行跳过哈。
# 导入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
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的导数看看与结果是否相同:
以上的例子只有一个通路,反向逐步计算即可得到与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((w1∗data+w2)∗(w1∗data∗w3))其中 l 1 = w 1 ∗ d a t a l_1=w_1*data l1=w1∗data, l 2 = w 1 ∗ d a t a + w 2 l_2=w_1*data+w_2 l2=w1∗data+w2, l 3 = w 1 ∗ d a t a ∗ w 3 l_3=w_1*data*w_3 l3=w1∗data∗w3, 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=(w1∗data+w2)∗(w1∗data∗w3)。
好了,背景交代完了,下面按照惯例,用代码描述上边式子:
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计算过程。我们用网络图进行描述:
下边进行反向传播求导:
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的节点,下边来手动计算一下: