参考资料:
- https://handbook.pytorch.wiki/chapter1/1.1-pytorch-introduction.html
- https://www.bookstack.cn/read/PyTorch-cn/README.md
张量的意思是一个多维数组,它是标量(0维)、向量(1维)、矩阵(2维),RGB 图像(3维)的高维扩展。
- data: 被包装的 Tensor。
- grad: data 的梯度。
- grad_fn: 创建 Tensor 所使用的 Function,是自动求导的关键,因为根据所记录的函数才能计算出导数。
- requires_grad: 指示是否需要梯度,并不是所有的张量都需要计算梯度。
- is_leaf: 指示是否叶子节点(张量),叶子节点的概念在计算图中会用到,后面详细介绍。
- dtype: 张量的数据类型,共有五种数据类型:- 32位浮点型:torch.FloatTensor。 (默认) - 64位整型:torch.LongTensor。 - 32位整型:torch.IntTensor。 - 16位整型:torch.ShortTensor。 - 64位浮点型:torch.DoubleTensor。具体可看官方文档:https://pytorch.org/docs/stable/tensors.html
- shape: 张量的形状(Size()一样)。如 (64, 3, 224, 224)
- device: 张量所在设备 (CPU/GPU),GPU 是加速计算的关键
简单生成一个张量
import torch
import numpy as np
x= torch.rand(2,3)
print(x.size())
print(x.dtype)
print(x.requires_grad)
print(x.grad)
print(x.data)
print(x.grad_fn)
print(x.device)
结果: torch.Size([2, 3]) torch.float32 False None tensor([[0.2630, 0.8515, 0.0415], [0.3337, 0.2660, 0.2949]]) None cpu
利用torch.Tensor()生成张量
x = [2,3,2,4]
torch.tensor(x)
结果: tensor([2, 3, 2, 4])
从NumPy数组生成:共享内存
x= np.zeros((2,3,4))
torch.from_numpy(x)
结果: tensor([[[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]], [[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]]], dtype=torch.float64)
Tensor数据的CPU和GPU转换
#使用torch.cuda.is_available()来确定是否有cuda设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
#使用设备
x = torch.rand((2,3,4),device = device)
print(x.type())
x=x.cpu() #转为CPU
print(x.type())
结果: cuda torch.cuda.FloatTensor torch.FloatTensor
初始化为0:torch.ones()
# torch.ones(*size, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
#size: 张量的形状
#out: 输出的张量,如果指定了 out,那么torch.zeros()返回的张量和 out 指向的是同一个地址
#layout: 内存中布局形式,有 strided,sparse_coo 等。当是稀疏矩阵时,设置为 sparse_coo 可以减少内存占用。
#device: 所在设备,cuda/cpu
#requires_grad: 是否需要梯度
torch.ones((2,2,2),device="cpu")
结果: tensor([[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]])
**初始化为单位矩阵:**torch.eye()
#初始化一个单位矩阵,即对角线为1 其他为0
#eye(n, m=None, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
eye=torch.eye(2,2)
eye
结果: tensor([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
数据正态分布(高斯分布):torch.normal()
#normal(mean, std, *, generator=None, out=None)
#mean:平均值
#std:标准差
#generator:随机数种子
normal_t = torch.normal(mean=torch.arange(2,10,dtype=torch.float),std=2)
print(normal_t)
结果 tensor([ 2.2514, 3.6211, 4.0486, 4.8381, 6.1706, 7.6390, 10.2755, 5.9701])
标准正态分布:torch.randn()
沿行取最大值:max_value, max_idx = torch.max(x, dim=int)
x=torch.randn(3,3,3)
print(x)
max_value, max_idx=torch.max(x, dim=2) # dim:要求和的维度,可以是一个列表
print(max_value)
print(max_idx)
结果: tensor([[[ 0.5244, -0.1050, 1.0411], [ 0.3610, 0.8447, -1.5066], [-0.6421, -0.1181, -0.1401]], [[ 0.5994, -1.5200, -1.8082], [ 1.7824, -2.1836, -1.3482], [ 1.4337, 0.6371, 1.6609]], [[-0.3236, -0.4225, -1.5040], [ 0.9013, 0.0914, 1.3402], [-1.0523, -1.7614, -0.7179]]]) tensor([[ 1.0411, 0.8447, -0.1181], [ 0.5994, 1.7824, 1.6609], [-0.3236, 1.3402, -0.7179]]) tensor([[2, 1, 1], [0, 0, 2], [0, 2, 2]])
行和:torch.sum(input, *, dtype=None)
sum_x=torch.sum(x,dim=2)
print(sum_x)
结果: tensor([[ 1.4605, -0.3009, -0.9004], [-2.7288, -1.7494, 3.7317], [-2.2501, 2.3328, -3.5316]])
Autograd模块实现了深度学习的算法中自动求导,得到张量的梯度,方便计算参数的权值,简化了手动计算导数的复杂过程。
创建张量时,通过设置 requires_grad 标识为Ture来记录该张量的每一步操作历史并自动计算梯度。
例如:
x= torch.rand(5,5,requires_grad=True)
y= torch.rand(5,5,requires_grad=True)
z = x.add(y) #.add()和.add_()都能把两个张量加起来,但.add_是in-place操作,比如x.add_(y),x+y的结果会存储到原来的x中。Torch里面所有带"_"的操作,都是in-place的。
k = torch.sum(x+y)
print(x)
print(y)
print(z)
print(k)
结果: tensor([[0.7720, 0.3047, 0.7918, 0.8146, 0.4110], [0.6372, 0.2570, 0.1504, 0.1811, 0.5916], [0.3155, 0.1520, 0.1405, 0.5431, 0.3443], [0.0485, 0.4622, 0.8489, 0.3135, 0.3817], [0.5740, 0.1225, 0.5853, 0.3158, 0.4517]], requires_grad=True) tensor([[0.2287, 0.9599, 0.8266, 0.8068, 0.8266], [0.9077, 0.5955, 0.0568, 0.7390, 0.7779], [0.1036, 0.2468, 0.2675, 0.4490, 0.8680], [0.0307, 0.0600, 0.1917, 0.2553, 0.4924], [0.3012, 0.2574, 0.8915, 0.9823, 0.2243]], requires_grad=True) tensor([[1.0007, 1.2646, 1.6184, 1.6214, 1.2375], [1.5448, 0.8525, 0.2071, 0.9201, 1.3695], [0.4192, 0.3988, 0.4080, 0.9921, 1.2123], [0.0792, 0.5221, 1.0407, 0.5688, 0.8742], [0.8752, 0.3799, 1.4768, 1.2981, 0.6760]], grad_fn=<AddBackward0>) tensor(22.8580, grad_fn=<SumBackward0>) # grad_fngrad_fn被赋予了一个新的函数,这个函数引用了一个创建了这个Tensor类的Function对象。
对以上结果求梯度:
为了避免 Tensor 对 Tensor 求导,pytorch中:
假设 x 经过一番计算得到 y,torch.autograd.backward(y, w), 或者说 y.backward(w) 的含义是:先计算 l = torch.sum(y * w),然后求 l 对(能够影响到 y 的)所有变量 x 的导数。这里,y 和 w 是同型 Tensor,w 可以视为 y 的各分量的权重,也可以视为遥远的损失函数 l 对 y 的偏导数(这正是函数说明文档的含义)。也就是说,可以理解成先按照 w 对 y 的各个分量加权,加权求和之后得到真正的 loss,再计算这个 loss 对于所有相关变量的导数。 参考
torch.autograd.backward(z,x) #如果使用z.backward()的话,那么就会报如下的错:RuntimeError: grad can be implicitly created only for scalar outputs.这是因为pytorch禁止tensor对tensor求导,而是只允许标量 Scalar 对张量 Tensor 求导,求导结果是和自变量同型的 Tensor。
print(x.grad)
k.backward() #执行z.backward()方法会更新x.grad和y.grad
print(x.grad,"\n",y.grad)
tensor([[5.3160, 3.9142, 5.3753, 5.4437, 4.2329], [4.9115, 3.7709, 3.4511, 3.5434, 4.7747], [3.9466, 3.4560, 3.4216, 4.6292, 4.0329], [3.1454, 4.3865, 5.5468, 3.9406, 4.1452], [4.7220, 3.3675, 4.7559, 3.9473, 4.3552]]) tensor([[6.3160, 4.9142, 6.3753, 6.4437, 5.2329], [5.9115, 4.7709, 4.4511, 4.5434, 5.7747], [4.9466, 4.4560, 4.4216, 5.6292, 5.0329], [4.1454, 5.3865, 6.5468, 4.9406, 5.1452], [5.7220, 4.3675, 5.7559, 4.9473, 5.3552]]) tensor([[6.3160, 4.9142, 6.3753, 6.4437, 5.2329], [5.9115, 4.7709, 4.4511, 4.5434, 5.7747], [4.9466, 4.4560, 4.4216, 5.6292, 5.0329], [4.1454, 5.3865, 6.5468, 4.9406, 5.1452], [5.7220, 4.3675, 5.7559, 4.9473, 5.3552]])
求导的流程:
- 当我们执行z.backward()的时候。这个操作将调用z里面的grad_fn这个属性,执行求导的操作。
- 这个操作将遍历grad_fn的next_functions,然后分别取出里面的Function(AccumulateGrad),执行求导操作。这部分是一个递归的过程直到最后类型为叶子节点。
- 计算出结果以后,将结果保存到他们对应的variable 这个变量所引用的对象(x和y)的 grad这个属性里面。
- 求导结束。所有的叶节点的grad变量都得到了相应的更新
自定义autograd扩展新的功能,就需要扩展Function类。因为Function使用autograd来计算结果和梯度,并对操作历史进行编码。 在Function类中最主要的方法就是forward()
执行向前传播和backward()
执行反向传播。
详细可参考
举个例子:
# 引入Function便于扩展
from torch.autograd.function import Function
class MulConstant(Function): #继承Function
@staticmethod #定义为静态方法
def forward(ctx, tensor, constant):
# ctx 用来保存信息这里类似self,并且ctx的属性可以在backward中调用
ctx.constant=constant
return tensor *constant
@staticmethod
def backward(ctx, grad_output):
# 返回的参数要与输入的参数一样.
# 第一个输入为3x3的张量,第二个为一个常数
# 常数的梯度必须是 None.
return grad_output, None
a=torch.rand(3,3,requires_grad=True)
b=MulConstant.apply(a,5) # b为a的元素乘以5
print("a:"+str(a))
print("b:"+str(b))
b.backward(torch.ones_like(a))
a.grad
结果:
a:tensor([[0.5850, 0.1941, 0.9270],
[0.7013, 0.6022, 0.4268],
[0.0101, 0.8201, 0.4765]], requires_grad=True)
b:tensor([[2.9248, 0.9706, 4.6352],
[3.5064, 3.0112, 2.1338],
[0.0505, 4.1007, 2.3823]], grad_fn=)
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
未完待续!
欢迎关注个人公众号【智能建造小硕】(分享计算机编程、人工智能、智能建造、日常学习和科研经验等,欢迎大家关注交流。)