pytorch baisc
最近用Pytorch的时候发现对于pytorch的一些细节问题掌握不够充分。包括整个框架的使用流程,Tensor的一些初始化,类型转换的细节也理解的不是很深。为了方便以后能够更从容的使用Pytorch,重新学习以下Pytorch的一些基础内容,并对重点做一下记录。
目前大致按照官方tutorials的顺序记录,随着学习的深入和对文档的查阅慢慢补充。
Basic - Tensor
包: torch.Tensor
Tensor的创建
直接创建,不初始化:
torch.empty()
e.g.,
torch.empty(5, 3, dtype=torch.long)
其中内部的参数是tensor的形状
相应的,有如下的初始化方式,参数均和上述一致:
随机初始化:
torch.rand()
- 0值初始化:
torch.zeros()
- 列表初始化:
torch.tensor(A_List)
用已有Tensor初始化:
torch.new_ones()
(除非指定数据类型,否则和输入的tensor数据类型相同)x = x.new_ones(5, 3, dtype=torch.double) # new_* methods take in sizes print(x) x = torch.randn_like(x, dtype=torch.float) # override dtype! print(x)
操作
- 形状相同可以直接四则运算(
+
,-
,*
,/
),这样的运算是对应位置上的元素在运算 - 四则运算函数,返回一个运算结果,可以通过out参数将结果输出到给定tensor:
torch.add(x, y)
torch.sub(x, y)
torch.mul(x, y)
torch.div(x, y, out=result)
- 原地运算,函数名后缀为下划线的函数,运算结果直接在对应Tensor上改变。
y.add_(x)
x.copy_(y)
x.t_()
- 形状相同可以直接四则运算(
索引
- 可以用numpy数组的索引方式来索引tensor
- 如果一个tensor只有一个元素,可以使用
x.item()
获得该元素
改变Tensor形状
view()
:view()
函数接受的参数即是需要修改后的形状。x.view(16)
: 将x改为长度为16的向量x.view(-1, 8)
: -1表示其他不确定的维度,这个维度需要根据第二个参数8计算出来。例如., 此处x原始的形状为(4,4);那么第二个维度的大小为8,可以计算出第一个维度的大小为2,则新的x的形状为:(2,8)
numpy.ndarray -> tensor:
如何转化:
# converting numpy array to torch tensor import numpy as np a = np.ones(5) b = torch.from_numpy(a) np.add(a, 1, out=a) print(a) print(b) # tensor to numpy a = torch.ones(5) b = a.numpy()
可以通过
tensor_name.numpy()
方法将一个Tensor转化为numpy数组。通过Tensor转化的Numpy和原始的Tensor共享内存:
也就是说,如果你修改得到的Numpy数组,相应的Tensor内容也会变化。(类似指针操作)
同理,使用numpy数组生成的Tensor和原始numpy数组也有这样的关系。
使用GPU
使用
.to()
函数将变量转为GPU数据。eg.,
# let us run this cell only if CUDA is available # We will use ``torch.device`` objects to move tensors in and out of GPU if torch.cuda.is_available(): device = torch.device("cuda") # a CUDA device object y = torch.ones_like(x, device=device) # directly create a tensor on GPU x = x.to(device) # or just use strings ``.to("cuda")`` z = x + y print(z) print(z.to("cpu", torch.double)) # ``.to`` can also change dtype together!
参考官方文档
Basic - Autograd, 自动求导
包:torch.autograd
, torch.Function
Function: 参考文档
autograd
这个包为所有在张量上的操作提供了自动求导的功能。
该框架是在运行时定义的,因此反向传播的方式由代码运行的方式定义,每次迭代都可以不同。
requires_grad
和grad
属性requires_grad
属性,boolean
类型,True
表示需要自动计算梯度grad
:存储自动计算的梯度requires_grad_()
方法可以改变一个Tensor
的该属性的值
停止追踪历史计算
.detach()
:避免张量追溯历史记录或者被之后的计算追溯。(不太明白,后续修改补充。原句:To stop a tensor from tracking history, you can call .detach() to detach it from the computation history, and to prevent future computation from being tracked.)如果需要使用一个
.requires_grad
属性为True
的变量,但是又不想对这个变量做的操作被追溯(计算梯度),可以使用.detach()
方法得到该变量的副本,并且副本的该属性默认为False
避免追踪历史记录或者使用内存:
- 将代码块写在
with_torch.no_grad():
下。该方法下的代码块在执行时将不会自动计算梯度或者更新参数。适合在对已经训练好的模型测试,评估时使用。
- 将代码块写在
自动求导过程中最重要的类:
Function
Tensor
和Function
是相关联的,二者一起构建了一个非循环图以编码一个完整的计算过程。.grad_fn
:指向了生成这个Tensor
的Function
。当属性值为None
,表示这个Tensor
是由用户创建的
求导:
此处存疑,未能完全理解.backward()
使用
.backward()
求导需要注意的问题:- 如果该
Tensor
是一个标量,不需要特别指定参数 如果该
Tensor
含有不止一个元素,需要指定一个gradient
参数,这个参数是一个形状匹配的Tensor
autograd
是一个计算向量-雅可比矩阵乘积的引擎下面对此详细说明:
实际上,
.backward()
是需要参数的。只不过当Tensor
是一个标量的时候,默认方法实质上等价于.backward(torch.tensor(1.))
,autograd
引擎实际上计算的是$1.0*\dfrac{d(o)}{dx}$,其中1.0被理解为$\dfrac{dl}{do}=1.0$把
.backward()
的形参视为$v$,最终计算的值其实是$J^T\dotv$。(为什么不是$v^T\dotJ$, 因为前者得到的是列向量)
- 如果该
原始教程
Basic - Neural Network
包:torch.nn
重点内容:
- 定义模型
nn.Module
forward(input)
模型训练流程
- 定义神经网络,定义可学习的参数
- 遍历输入数据集
- 处理输入
- 计算损失值
- 计算梯度
- 更新权重
定义网络
- 继承
nn.Module
- 重写
__init__()
重写
forward()
模型的参数
model.parameters()
方法返回模型中的参数model(input)
直接调用forward()
方法,返回前馈网络的传播。model.zero_grad()
,在反向传播前,应该清空上一次迭代的梯度重点:
torch.nn
只支持mini-batches输入。也就是说,整个troch.nn
包中的方法都只支持使用mini-batches作为输入,不支持单个样本的输入。例如2为卷积层接收的输入应该是一个4维的张量,这个张量各个维度的意义是:样本数,通道数,高度,宽度。- 如果确实需要输入单个样本,可以考虑使用
.unsqueeze(0)
方法,为样本增加一个维度。
- 继承
计算loss值
Loss Function接受一个张量对
(output, target)
作为输入,并且计算输出和目标值的差距。有很多常用的Loss函数,这里不一一写出,详情参考Loss Function
关于Loss函数的一些细节问题之后补充反向传播
示例:
net.zero_grad() # zeroes the gradient buffers of all parameters print('conv1.bias.grad before backward') print(net.conv1.bias.grad) loss.backward() print('conv1.bias.grad after backward') print(net.conv1.bias.grad)