2.5 Tensor的API

Posted 王小小小草

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2.5 Tensor的API相关的知识,希望对你有一定的参考价值。

欢迎订阅本专栏:《PyTorch深度学习实践》
订阅地址:https://blog.csdn.net/sinat_33761963/category_9720080.html

  • 第二章:认识Tensor的类型、创建、存储、api等,打好Tensor的基础,是进行PyTorch深度学习实践的重中之重的基础。
  • 第三章:学习PyTorch如何读入各种外部数据
  • 第四章:利用PyTorch从头到尾创建、训练、评估一个模型,理解与熟悉PyTorch实现模型的每个步骤,用到的模块与方法。
  • 第五章:学习如何利用PyTorch提供的3种方法去创建各种模型结构。
  • 第六章:利用PyTorch实现简单与经典的模型全过程:简单二分类、手写字体识别、词向量的实现、自编码器实现。
  • 第七章利用PyTorch实现复杂模型:翻译机(nlp领域)、生成对抗网络(GAN)、强化学习(RL)、风格迁移(cv领域)。
  • 第八章:PyTorch的其他高级用法:模型在不同框架之间的迁移、可视化、多个GPU并行计算。

Pytorch提供了很多tensor的API, 如若逐条去记忆想必自然不是程序员的学习方法,更不是AI从业者的学习方法,API是帮助我们快速开发的工具,绝不能成为学习的压力,因此相对有效的方法是“类”–> “查” -->“熟”。

  • “类”:对纷繁众多的API进行归类,先归大类,再在大类中归小类,对知识形成一个系统的认知;

  • “查”:当在实际的应用中忽然想实现某个功能的时候,在“类”的帮助下马上知道了是什么类别下的API能帮助实现,于是去官方文档中迅速查找到函数名、使用方法及例子;

  • “熟”:这是自然而然的过程,在不断高效“查”的过程中对“类”也逐渐熟悉,于是根本无需死记硬背,就对海量的API熟悉了。

pytorch tensor API的官方文档地址:http://pytorch.org/docs

文档中给API归了大类,但是每个大类罗列的API是根据首字母的顺序,也不方便大家快速认知,因此本章的小节中不但按照官方的思路对大类进行讲解,也根据笔者的思路对大类再做了更细致的归类,方便大家对API的认知。

2.5.1 调用API的方式

方式一:操作后创建新的变量

方式一中也有两种不同的方式可以选用

  • (1)使用Torch module中的方法

用上文提到过的转置方法来举例, 可以调用torch.transpose(), 将原tensor:a作为参数传入, 生成新的tensor:a_t。

import torch

a = torch.ones(3, 2)
a_t = torch.transpose(a, 0, 1)
  • (2)使用tensor object的中方法

直接在tensor:a上调用transpose方法,结果同上,

a = torch.ones(3, 2)
a_t = a.transpose(0, 1)

因此两种方法可以互相替换使用,但注意目前(2)中tensor对象的方法数目相对较少,用(1)torch moduel中的功能更全面。

方式二:在原变量上做操作

对tensor调用带有下划线结尾的方法,即为对原tensor进行操作,无需赋值新的变量

a = torch.ones(3, 2)
a.zero_()
tensor([[0., 0.],
        [0., 0.],
        [0., 0.]])

2.5.2 索引、切块

(1)简单索引与切块

对tensor的索引和对python中list的索引是一样的,只是tensor是多维数组,list是一维的。对list的索引不再赘述,下面看几个tensor索引的例子。

points = torch.tensor([[1.0, 4.0],[2.0, 1.0],[3.0, 5.0]])
points[1:]  # 获取第2行开始的所有行 [[2.0, 1.0],[3.0, 5.0]]
points[1:, :]  # 获取第2行开始的所有行的所有列,结果与上同 [[2.0, 1.0],[3.0, 5.0]]
points[1:, 0]  # 获取第2行开始的所有行的第0列, [[2.0],[3.0]]
tensor([2., 3.])

(2)高级索引与切块

a.通过索引切割tensor: torch.index_select() & torch.take()

a = torch.rand(4,6)
b = torch.index_select(input=a, dim=1, index=torch.LongTensor([0,1,2,3]))

print(b)
tensor([[0.5042, 0.0299, 0.0558, 0.9661],
        [0.0617, 0.8948, 0.4300, 0.0555],
        [0.0531, 0.1290, 0.4504, 0.7697],
        [0.4639, 0.8179, 0.8393, 0.6148]])

对a, 在维度1上,切出索引在[0,3)之间的元素。
注意:index必须是LongTensor类型的。

torch.take()也是通过索引取数,只是将Input平铺成一维的:

b = torch.take(input=a, index=torch.LongTensor([0,1,2,3]))
print(b)
tensor([0.5042, 0.0299, 0.0558, 0.9661])

b.通过掩码切割tensor:torch.masked_select()

mask = torch.ByteTensor([[0,0,1,0],[1,1,0,1]])
a = torch.rand(2,4)

b = torch.masked_select(input=a, mask=mask)
print(b)
tensor([0.5985, 0.8829, 0.7861, 0.3360])

c.选择非0tensor: torch.nonzeros(a)

d.等分tensor: torch.split & torch.chunck

# 在维度1上将a等分成大小为2的小块
a_split = torch.split(a, 5, dim=0)
print(a_split)

# 在维度1上将a等分成2块
a_chunk = torch.chunk(input=a, chunks=2, dim=1)
print(a_chunk)
(tensor([[0.7681, 0.5095, 0.5985, 0.7944],
        [0.8829, 0.7861, 0.6407, 0.3360]]),)
(tensor([[0.7681, 0.5095],
        [0.8829, 0.7861]]), tensor([[0.5985, 0.7944],
        [0.6407, 0.3360]]))

e.通过tensor对象中的函数:

a.select(0,2):获取a的第1行第3列位置的元素

a.unfold(dim, size, step): 当step=1时返回a中维度为dim的所有大小为size的分片

2.5.3 连接、聚合、压缩、变形…

(1)tensor的连接:cat与stack

两个常用的连接tensor的方式:

torch.cat()拼接两个tensor, 但维度数量不变,如两个24的tensor在维度0上拼接,则生成一个44的新tensor,维度的数量仍然是2个;

torch.stack()将两个24的tensor拼接之后会产生22*4的新tensor, 总的维度数目变成了3.

a = torch.randn(2,4)
b = torch.randn(2,4)

a_b_cat = torch.cat(tensors=(a,b), dim=0)
a_b_stack = torch.stack(tensors=(a,b), dim=0)
print(a_b_cat.shape)
print(a_b_stack.shape)
torch.Size([4, 4])
torch.Size([2, 2, 4])

(2)tensor的压缩与扩张:sequenze与unbind

a = torch.rand(2,1,3,1)

# 不传入维度参数,则默认去掉所有大小为1的维度,变成(2,3)大小
a_squeeze = torch.squeeze(input=a)

# 传入指定维度参数,则只去掉该维度,如下变成(2,3,1)大小
a_squeeze = torch.squeeze(input=a, dim=1)

# 去掉0维
a_unbind = torch.unbind(a, 0) 

同理,可以用torch.unsqueeze()来扩张指定维度

(3)tensor的聚合:ganther

(4)tensor的转换:t, transpose

t只适用于二维的Tensor的转置,即矩阵;而transpose是适合多维Tensor的维度转换.

a = torch.rand(2,3)
a_t = torch.t(a)
print(a_t.shape)

a_ts = torch.transpose(a,1,0)
print(a_ts.shape)
torch.Size([3, 2])
torch.Size([3, 2])

(5)tensor的变形:view

a = torch.rand(2,3,4)
print(a.shape)

a = a.view(2, 12)
print(a.shape)

# 若参数为-1则表示自动计算该维度的大小
a = a.view(-1, 12)
print(a.shape)
torch.Size([2, 3, 4])
torch.Size([2, 12])
torch.Size([2, 12])

2.5.4 数学操作

数学操作的API可多呢~总共大概有90多,各下面几节依次分类介绍。

(1)逐点操作

逐点操作是对tensor中的每个值都进行相同的操作,如torch.abs(a), 是将tensor中的每个value都绝对值化,其他逐点操作大致罗列如下表。

类型功能功能api
三角函数余弦余弦.cos(a)
双曲余弦.cosh(a)
反余弦.acos(a)
正弦正弦.sin(a)
双曲正弦.sinh(a)
反正弦.asin(a)
正切正切.tan(a)
双曲正切.tanh(a)
反正切.atan(a)
2个张量的反正切.atan2(a,b)
加减乘除加法.add(a,b)
乘法.mul(a,b) #若张量标量:逐点都乘以标量; 若张量张量:对应元素乘
除法.div(a,b)
先除后加.addcdiv(t, 0.5, t1, t2) # t+0.5(t1/t2)
先乘后加.addcmul(t, 0.5, t1, t2) # t+0.5(t1·t2)
除法余数.reminder(a,2) # a%2
指数对数指数.exp(a)
自然对数.log(a)
幂与根.pow(a,2).pow(a,a)
平方根.sqrt(a)
平方根的倒数.rsqrt(a)
取整向上取整.ceil(a)
向下取整.floor(a)
四舍五入.round(a)
截断tranc(a)
激活函数sigmoid.sigmoid(a)
其他返回分数.frac(a)
取负.neg(a)
取倒.recipocal(a)
取正负号.sign(a)
夹紧到某个区域.clamp(a, max, min)
线性差值.lerp()

(2)缩减操作

缩减操作是指操作之后维度变小了,如求22的tensor每行的均值,得到的结果是21的新tensor。常用的缩减操作如下表罗列:

基本运算求均值.mean(a) # 求所有数值的均值,返回标量.mean(a, dim) # 求指定维度上的均值,返回数组
求中位数.media(a) #同上
求积.prod(a)
求标准差.std(a)
求方差.var(a)
求和.sum(a)
累计运算求累计和.cumsum(a, dim)
求累计积.cumprod(a,dim)
范数运算(a-b)的p范数dist(a, b, 3)
a的p范数.norm(a,p)

(3)比较操作

基本比较a == b ?.eq(a,b)
a>b ?.gt(a,b)
a <= b ?.le(a,b)
a < b ?.lt(a,b)
a != b ?.ne(a,b)

除了以上这些基本的比较操作,pytorch还封装了一些基于比较的常用方法:

取值取指定维度第K个最小值.kthvalue(a,k)
取指定维度K个最大/小值.topk(a,k)
取最大值.max(a, dim)
取最小值.min(a, dim)
排序排序, 返回排序后的tensor,与原index.sort(a)

(4)线性代数操作

点乘.dot(a,b)
特征值、特征向量.eig(a,True)
对满秩矩阵计算最小二乘和最小范数.gels(B,A)
OR分解gegrf(A)
AX=B的解gesv(B,A)
取逆invese(a)
矩阵*矩阵.mm(x,y)
矩阵*向量.mv(x,y)
向量积.cross(a,b)
获取对角矩阵.diag(a)
返回上三角.tril()
返回下三角.triu()

有必要讲以下最常用的点乘、矩阵与向量乘、矩阵与矩阵乘的例子:

  • 首先看点乘,调用torch.dot()实现向量与向量的点积,即将两个向量对应位置的元素相乘,并求和,其输出是标量。
a = torch.Tensor([1,2,3])
b = torch.Tensor([2,3,4])

c = torch.dot(a,b)  # 1*2 + 2*3 + 3*3 = 20
print(c)
tensor(20.)
  • torch.mv是实现矩阵与向量相乘,比如矩阵a:
    [ 1 2 3 4 5 6 7 8 9 ] (3) \\left[ \\beginmatrix 1 & 2 & 3 \\\\ 4 & 5 & 6 \\\\ 7 & 8 & 9 \\endmatrix \\right] \\tag3 147258369(3)
    和向量b:
    [ 1 2 3 ] (3) \\left[ \\beginmatrix 1 \\\\ 2 \\\\ 3 \\endmatrix \\right] \\tag3 123(3)
    相乘:
a = torch.Tensor([[1,2,3],[4,5,6],[7,8,9]])
b = torch.Tensor([1,2,3])

c = torch.mv(a, b)
print(c)
tensor([14., 32., 50.])

2.5.5 序列化

当我们想要把有价值的tensor保存在磁盘上以便下次读入使用时,就需要用到tensor的序列化操作。有两种方式可以操作,第一是使用torch 模块中的save/load功能,但是这样保存的结果只能适用于Pytorch读取,其他软件无法读取;因此第二种方法是使用HDF5格式, HDF5是一个接口,是很多软件支持的格式。

(1)使用torch.save/load存取tensor

保存:

points = torch.tensor([[1.0, 4.0],[2.0, 1.0],[3.0, 5.0]])

# 可以这样
torch.save(points, path)

# 也可以这样
with open(path, 'w') as f:
    torch.save(points, f)

读取:

# 可以这样
points = torch.load(path)

# 也可以这样
with open(path, 'r') as f:
    points = torch.load(f)

(2)使用hdf5存取tensor

先安装h5py

conda install h5py

保存:
注意,tensor是先转换成NumPy array, 再作为create_dataset的参数传入

import h5py

f = h5py.File(path, 'w')
dset = f.create_dataset('coords', data=points.numpy())
f.close()

读取:

f = h5py.File(path, 'r')
dset = f['coords']
last_points = dset[1:]  # 返回的是NumPyArray
last_points = torch.from_numpy(dset[1:])  # 要将NumPyArray转换成tensor
f.close()

2.5.6 设备

tensor可以放在CPU上计算,也可以放在GPU上计算,GPU能更快速,并行化地计算。有三种方式可以让tensor在设备之间转换。

(1)将tensor放到GPU上的方式

  • 创建时指定设备

point_gpu = torch.tensor([1,2], device=‘cuda’)

  • 创建后移动到某设备

point_gpu = torch.tensor([1,2]).to(device=‘cuda’)

  • 有多块GPU时可以使用’cuda:n’表示移动到第n块GPU上

point_gpu = torch.tensor([1,2]).to(device=‘cuda:1’)

(2)要注意的点

  • 当CPU上的tensor移动到了GPU上,原来的CPU类型也随之转变维GPU类型,如:torch.FloatTensor --> torch.cuda.FloatTensor
  • 当该tensor在已经在GPU上了,则在该Tensor上做的操作也都是在GPU上运算,运算后生成的新变量也是在GPU上的。

2.5.7 其他

(1)判断是否为张量或storage

  • is_tensor
  • is_storage

(2)设置打印

  • set_printoptions (参考numpy)

(3)设置/获取并行数

  • set_num_threads(n)
  • get_num_threads()

(4)随机取样

  • 设置随机种子: manel_seed()
  • 设置初始种子: inital_seed()
  • 设置/获取随机状态:set_rng_state() / get_rng_state()

以上是关于2.5 Tensor的API的主要内容,如果未能解决你的问题,请参考以下文章

pytorch笔记:pytorch的乘法

PyTorch框架学习 — Tensor(张量)详解

pytorch创建tensor数据

pytorch创建tensor数据

Pytorch基础知识

秩标量矢量矩阵