PyTorch_h
Tensor & autograd
Tensor
支持gpu加速,可以在ipython/notebook使用<function>? 查看帮助文档,也可以在代码中用help(function)这里的function就不用加小括号了,如
1 | torch.ones? #仅用于notebook/ipython |
基础操作
从接口,对tensor操作分为两类(torch.sum(a, b), a.sum(b)):
- torch.function, 如torch.save (torch 是导入的模块名)
- tensor.function, 如tensor.view (tensor代表的是创建的tensor对象名,)
从存储,对tensor操作分为两类:
- 不会修改自身数据,如a.add(b),会返回一个新的tensor
- 会修改自身数据,如a.add_(b)
创建tensor方式:
Tensor函数是最复杂多变的方式,可以接收list,可以指定形状,可以传入其他的tensor,或者torch.Size类别。
1 | a = t.Tensor(2, 3) |
tensor.size() 返回torch.Size 对象,可查看tensor大小,也可以作为Tensor创建函数的参数。tensor.shape等同于tensor.size()
常用tensor操作
tensor.view
改变tensor的形状,返回的tensor与原tensor共享内存(相当于只是变了映射关系)
torch.squeeze(a, [N]),代表去除a中维度为一的维度,不输入N代表去除所有维度为一的维度,为具体数值表示如果该维度为1,则将其去掉。
torch.unsqueeze(a, [N])对数据进行扩充,在指定位置加上维度为一的维度
resize是另一种调整size的办法,但如果新尺寸超过了原尺寸,会分配新的空间,如果新尺寸小于了原尺寸,之前的数据会被保留
1 | a = t.randn(2, 3) |
索引操作
无特殊说明,索引出来的结果与原tensor共享内存,
一般来说可以这么用 a[1:2, 4:6, 1:3 ...]
以逗号分割的代表不同维度,可以小于tensor的维度,以冒号分割的代表该维度上下限,左开又闭。
注意后两者区别
1 | print(a.size()) |
一个tensor,可以用 a > 1返回一个bytetensor,也可以作为tensor的索引,等同于a.mask_select(a>1)
1 | a = t.randn(3, 4) |
其他的选择函数:
对一个二维gather,输出的每个元素如下
与gather对应的逆操作是scatter_。
高级索引
可以看成普通索引的拓展,但是结果一般不和原始Tensor共享内存。
1 | x = t.arange(0, 27).view(3, 3, 3) |
这与之前带 冒号的索引很相似,逗号分割的是不同维度,可以小于tensor的维度,只不过原来是i:j ,现在是[a, b],表示a或b,不同维度之间的匹配次数不愿意,原来是i*j,现在是各维度的元素数量(应该,有点不好描述)
Tensor类型
tensor有不同类型,每种类型分别对应有CPU和GPU版本的,默认tensor是floattensor类型,可以通过t.set_default_tensor_type 修改tensor类型,(如果默认类型为gpu tensor,那么所有操作都将在GPU上进行),
各类型之间可以进行转换,**type(new_type)**是通用的做法,除此之外,还有float,long,half等快捷方式。GPU tensor 和 CPU tensor之间的相互转换可以通过 tensor.cuda 和 tensor.cpu的方法实现,Tensor还有一个new 方法,可以创建该tensor类型的tensor
1 | a = t.Tensor(2, 3) |
逐元素操作
element-wise,操作输入与输出形状一致。
归并操作
使输出形状小于输入形状,并可以沿着某一维度指定操作。如加法sum,可以计算整个tensor的和,也可以计算每一行/每一列的和。
以上函数都有一个参数dim,用来指定是在那个维度上操作的,另外一个注意点:(wuenda视频里也经常有keepdims=True)
比较
有些是逐元素比较,有些类似于归并操作。常用函数如下:
第一行实现了运算符重载可以用a>=b, a!=b
等,返回结果是一个bytetensor。max/min有三种用法,
- t.max(tensor) 返回tensor中最大的一个数
- t.max(tensor, dim) 指定维度上最大的数,返回tensor和下标(如二维tenser,dim=1,代表每一行的最大值)
- t.max(tensor, tensor) 比较两个tensor比较大的元素
维度,整个多维矩阵可以看出一棵树,从第一维到第n维,max的第二种用法需要指定dim为第几维度,对应到树上就是第几层树,然后在该层间找最大值(同一个父节点)
例子:
x = numpy.array([[[1, 2], [3, 4]], [[4, 3], [2, 1]]]) (pytorch也一样)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 print(numpy.max(x, axis=0))
print(all(numpy.max(x, axis=0)==numpy.max(x, axis=-3)))
----------------------------------
[[4, 3],
[3, 4]]
True
print(numpy.max(x, axis=1))
print(all(numpy.max(x, axis=1)==numpy.max(x, axis=-2)))
-----------------------------------
[[3, 4],
[4, 3]]
True
print(numpy.max(x, axis=2))
print(all(numpy.max(x, axis=2)==numpy.max(x, axis=-1)))
------------------------------------
[[2, 4],
[4, 2]]
True
线性代数
pytorch的线性函数主要封装了blas和lapack,常用的函数如下表所示
Tensor & Numpy
两者相似性很高,之间共享内存。可以相互转换再使用。
虽然pytorch有自动广播,还是可以通过以下函数手动实现,不易出错
- unsqueeze或view,为某一维度补上1
- expand或expand_as, 重复数组,将单维度扩大。(不会复制数组,所有不会占用额外空间,repeat有类似的功能,但会复制数据,会占用额外空间)
内部结构
tensor分为头信息区和存储区。信息区主要保存着tensor形状,步长,数据类型等信息。真正的数据连续成组,信息区占用内存较少,主要内存占用取决于tensor中元素的数目,即存储区的大小。
可以通过对象的storage函数访问,将其传入id中获取对象id, 可以通过对象的data_ptr 返回tensor的首元素的内存地址。
1 | print(b.storage()) |
other
持久化
tensor加载和保存很简单,使用t.save和t.load,在save/load时还可以指定使用到pickle模块,load时可将gpu tensor映射到其他cpu或gpu上。(不太理解,之后看吧)
向量化
避免原生for循环,使用向量化的数值计算。
疑问,pytorch 的tensor变量在被赋值时可以自动更改类型吗
1
2
3
4
5
6
7
8 # 创建一个整数类型的张量
a = t.tensor([1, 2, 3])
# 尝试将浮点数赋值给整数类型的张量
a = t.tensor([1.0, 2.0, 3.0])
# 查看最终张量的类型
print(a.dtype)
小试牛刀-线性回归
%matplotlib inline
将使得 matplotlib 的图表直接嵌入到 notebook 中的输出单元格中,而不是弹出一个新的图形窗口。这对于在 notebook 中进行数据分析和可视化非常方便。
autograd
方便用户使用,专门开发的自动求导引擎,根据输入和前向传播过程自动构建计算图,执行反向传播。了解基本计算图知识很有必要(Christopher Olah)
Variable(部分可能是老版本的)
Pytorch在autograd模块中实现了计算图的相关功能,autograd中的核心计算结构是Variable,Variable封装了tensor。Variable包含三个属性,data(保存variable包含的tensor),grad(保存data对应的梯度),grad_fn(指向一个function,记录variable的操作历史),
Variable的构造参数需要传入tensor,同时有两个可选参数。
requires_grad(bool) ,是否需要对该variable进行求导。
vaolatile(bool) ,设置为true,构建在该variable上的图都不会求导。
Variable支持大部分tensor支持的函数,但不支持部分inplace函数,因为这些函数会修改tensor本身,在反向传播中,variable需要缓存之前的tensor来计算梯度。要计算各个Variable的梯度,只需要调用根节点variable的backward方法。autograd会自动沿着计算图反向传播,计算每一个叶子结点的梯度。
计算图
由于中间变量的梯度计算后会被清空,可以使用autograd.grad和hook来获取中间变量的梯度
1 | t.autograd.grad(z, y) #代表求z对y的梯度,隐式调用backward |
1 | def variable_hook(grad): |
假如y = x**2 + x*2, z = y.sum()
可以从z开始backward,也可以从y开始,只是需要传入y当前的梯度,之所以z不用传,是因为z对自己的梯度就是1,被省略了。
1 | x = t.arange(0.0, 3.0, requires_grad=True) |
对variable的操作才可以使用autograd,如果对variable的data直接操作,将无法使用反向传播。一般不会修改data的值。
神经网络工具箱nn
针对前向传播网络,每次都写很复杂的forward函数含麻烦,有两种简化方式,ModuleList和Sequential。Sequential是一个特殊的Module,它包含几个module,前向传播会将输入一层一层的传递下去,Module也是一个特殊的Module,可以包含几个module,向使用list一样使用。
两种创建
优化器
常用优化方法封装到torch.optim中,所有优化方法都是继承基类optim.Optimizer,
nn.module 和nn.fucntional, 模型有可学习的参数,最好用第一个,否则都可。激活函数(relu,sigmoid,tanh,)池化没有可学习的参数,可以用对应的functional函数代替。卷积,全连接等网络建议用nn.module。有参数也可以用funtional,只是需要手动定义参数,如。
常用工具
数据,可视化,GPU加速
数据
数据加载,通过自定义数据集实现,数据集对象被抽象为dataset类,实现自定义的数据集需要继承dataset,并实现两个魔法方法。
- __getitem__, 返回一个数据
- len ,返回样本的数量
此外,pytorch提供了torchvision,是一个视觉工具包,提供了很多视觉图像处理的工具。transforms模块提供了对PIL Image对象和Tensor对象的常用操作。
对PIL iMAGE的常用操作:
- Resize
- centerCrop
- pad
- toTensor,并且自动归一化[0,1]
- normalize
- topilimage
torchvision 视觉工具包,常用模型,数据集加载方式,预处理操作
可视化工具,Tensorboard和visdom