Tensor:张量,可以是标量(一个数)、向量(一维数组)、矩阵(二维数组)或者更高维的数组。

它与numpy和ndarrays相似,但是PyTorch的tensor支持GPU加速。

一、tensor分类

  • 从接口的角度分类

        

torch.function

如torch.save()

tensor.function

如tensor.view()

  • 从存储的角度分类

不会修改自身的数据

如a.add(b),加法的结果会返回一个新的tensor

会修改自身的数据

如a.add_(b),加法的结果仍然存储在a中,a被修改了

二、创建Tensor

在PyTorch中新建tensor的方法有很多,具体如下:

函数

功能

Tensor(*sizes)

基础构造函数

ones(*sizes)

全1Tensor

zeros(*sizes)

全0Tensor

eye(*sizes)

对角线为1,其他为0

arange(s,e,step)

从s到e,步长为step

linspace(s,e,steps)

从s到e,均匀切分成steps份

rand/randn(*sizes)

均匀/标准分布

normal(mean,std)/uniform(from,to)

正态分布/均匀分布

randperm(m)

随机排列

三、常用Tensor操作

       通过tensor.view方法调整tensor的形状,但必须保证调整前后元素总数一致。view不会修改自身的数据,

返回的新tensor与源tensor共享内存,即更改其中一个,另一个也随之改变。

主要用在经常需要添加或者减少某一维度,这时就需要squeeze和unsqueeze这两个函数。

       resize是另一种可以用来调整size的方法,但与view不同,它可以修改tensor的尺寸。如果新尺寸超过了原尺寸,会自动分配新的内存空间,但如果新尺寸小于原尺寸,则之前的数据依旧会被保存。

      Tensor支持numpy.ndarray类似的索引操作,语法也类似。如无特殊说明,索引出来的结果与原tensor共享内存。

from __future__ import print_function
import torch as t

a = t.randn(3, 4)
print(a)
print(a[0])  # 第0行(下标从0开始)
print(a[:, 0])  # 第0列

结果如下:

pytorch 统计Tensor大于零元素个数 pytorch.tensor_数据

四、常用的选择函数

函数

功能

index_select(input,dim,index)

在指定维度dim上选取,例如选取某些行或列

masked_select(input,mask)

例a[a>0],使用ByteTensor进行选取

non_zero(input)

非0元素的下标

gather(input,dim,index)

根据index,在dim维度上选取数据,输出的size与index一样

五、常见的逐元素操作

函数

功能

abs//sqrt/div/exp/fmod/log/pow

绝对值/平方根/除法/指数/求余/求幂

cos/sin/asin/atan2/cosh

三角函数

clamp(input,min,max)

超过min和max部分截断

ceil/round/floor/trunc

上取整/四舍五入/下取整/只保留整数部分

sigmod/tanh

激活函数

pytorch 统计Tensor大于零元素个数 pytorch.tensor_加载_02

     clamp常用在某些需要比较大小的地方,如取一个tensor的每个元素与另一个数的较大值。

a = t.arange(0, 6).view(2, 3)
print(a)
# 不转换为float,则会报错:RuntimeError: cos_vml_cpu not implemented for 'Long'
print(t.cos(a.float()))

      

pytorch 统计Tensor大于零元素个数 pytorch.tensor_数据_03

a = t.arange(0, 6).view(2, 3)
print(a)
print(a % 3)  # 等价于t.fmod(a, 3)
print(a ** 2)  # 等价于t.pow(a, 2)

       

pytorch 统计Tensor大于零元素个数 pytorch.tensor_数组_04

a = t.arange(0, 6).view(2, 3)
print(a)
# a中每个元素都与3相比,取较大的一个
print(t.clamp(a, min=3))

     

pytorch 统计Tensor大于零元素个数 pytorch.tensor_数据_05

 

六、归并操作

       归并操作会是输出形状小于输入形状,并可以沿着某一维度进行指定操作。

常用的归并操作:

函数

功能

mean/sum/median/mode

均值/和/中位数/众数

norm/dist

范数/距离

std/var

标准差/方差

cumsum/cumprod

累加/累乘

以上大多数函数都有一个参数dim,用来指定这些操作是在哪个维度上执行的。

假设输入的形状是(m, n, k)——m行n列

输入dim

输出的形状

0

(1, n, k)或者(n, k)——沿着列,变成一行

1

(m, 1, k)或者(m, k)——沿着行,变成一列

2

(m, n, 1)或者(m, n)

注意: size中是否有“1”,取决于参数keepdim,keepdim=True会保留维度1。默认为false

七、常用的比较函数

函数

功能

gt/lt/ge/le/eq/ne

大于/小于/大于等于/小于等于/等于/不等

topk

最大的k个数

sort

排序

max/min

比较两个tensor的最大值和最小值

max/min为例:

t.max(tensor)

返回tensor中最大的一个数

t.max(tensor,dim)

指定维上最大的数,返回tensor和下标

t.max(tensor1,tensor2)

比较两个tensor相比较的元素

 

 

 

 

八、常用的线性代数函数

函数

功能

trace

对角线元素之和(矩阵的迹)

diag

对角线元素

triu/tril

矩阵的上三角/下三角,可指定偏移量

mm/bmm

矩阵乘法/batch的矩阵乘法

addmm/addbmm/addmv

矩阵运算

t

转置

dot/cross

内积/外积

inverse

求逆矩阵

svd

奇异值分解

注意:矩阵的转置会导致存储空间不连续,需调用它的.contiguous方法将其转为连续

a = t.ones(2, 3)
print(a)
# 求a的转置
b = a.t()
# 查看a的转置矩阵b是否连续
print(b.is_contiguous())
# 将其转为连续
print(b.contiguous())

结果:

             

pytorch 统计Tensor大于零元素个数 pytorch.tensor_数据_06

九、对于Tensor和Numpy之间遇到的问题

需要注意的是:Numpy和Tensor共享内存。PyTorch已经支持了自动广播法则。

当遇到Tensor不支持的操作时,可以先转成Numpy数组,处理后再转为tensor,其转换开销很小。

  • 当输入数组的某个维度的长度为1时,计算时沿此维度复制扩充成一样的形状。
  • unsqueeze或view:为数据某一维的形状补1,实现法则1.
  • expand或expand_as,重复数组,实现法则3;该操作不会复制数组,所以不会占用额外的空间

     9.1、持久化

          tensor的保存:t.save

          tensor的加载:t.load

在load时还可以将GPU tensor映射到CPU或其他GPU上。

if t.cuda.is_available():
    a = a.cuda(1)  # 把a转为GPU1上的tensor
    t.save(a, 'a.path')
    # 加载为b,存储于GPU1上(因为保存时tensor就在GPU1上)
    b = t.load('a.path')
    # 加载为c,存储于CPU
    c = t.load('a.path', map_location=lambda storage, loc: storage)
    # 加载为d,存储于GPU0上
    d = t.load('a.path', map_location={'cuda:1':'cuda: 0'})

    9.2、向量化

           向量化计算是一种特殊的并行计算方式,一般程序在同一时间只执行一个操作的方式,它可以在同一时间执行多个操作,通常是对不同的数据执行同样的一个或一批指令,或者说把指令应用于一个数组/向量上。向量化可极大地提高科学运算的效率。

在科学计算程序中应当极力避免使用Python原生的for循环,尽量使用向量化的数值计算。

           还需要注意:

  • 大多数t.function都有一个参数out,这时产生的结果将保存在out指定的tensor之中
a = t.arange(0, 20000000)
b = t.LongTensor()
t.arange(0, 20000000, out=b)  # 64bit的LongTensor不会溢出
  • t.set_num_threads可以设置PyTorch进行CPU多线程并行计算时所占用的线程数,用来限制PyTorch所占用的CPU数目。
  • t.set_printoptions可以用来设置打印tensor时的数值精度和格式。

十、View的用法

a=torch.Tensor([[[1,2,3],[4,5,6]]])
b=torch.Tensor([1,2,3,4,5,6])

print(a.view(1,6))
print(b.view(1,6))

输出结果:tensor([[1., 2., 3., 4., 5., 6.]]) 
          tensor([[1., 2., 3., 4., 5., 6.]]) 
# -------------------------------------------------------
a=torch.Tensor([[[1,2,3],[4,5,6]]])
print(a.view(3,2))
输出结果:
tensor([[1., 2.],
        [3., 4.],
        [5., 6.]])
相当于就是从1,2,3,4,5,6顺序的拿数组来填充需要的形状
# --------------------------------------------------------
但是如果你想得到:
tensor([[1., 4.],
        [2., 5.],
        [3., 6.]])

就需要用到permute()
# -------------------------------------------------------
另外,参数不可为空。

参数中的-1就代表这个位置由其他位置的数字来推断,只要在不致歧义的情况的下,view参数就可以推断出来,也就是人可以推断出形状的情况下,view函数也可以推断出来。

比如a tensor的数据个数是6个,如果view(1,-1),我们就可以根据tensor的元素个数推断出-1代表6。而如果是view(-1,-1,2),人不知道怎么推断,机器也不知道。

还有一种情况是人可以推断出来,但是机器推断不出来的:view(-1,-1,6),人可以知道-1都代表1,但是机器不允许同时有两个负1。

如果没有-1,那么所有参数的乘积就要和tensor中元素的总个数一致了,否则就会出现错误。

十一、sequeeze和unsqueeze

 简介和用法

squeezeunsqueeze的作用与其翻译基本一致,被作用维度压缩和解压缩.用法相对简单,具体如下:

tensor_unsqueeze = tensor.unsqueeze(dim)

tensor存在n个维度,则dim的取值为[-n+1,n]区间的整数,且dim的取值不能为空.

tensor_squeeze = tensor.squeeze(dim)

tensor存在n个维度,则dim的取值为[-n,n-1]区间的整数,但dim的值可以为空

具体示例:

首先看unsqueeze,其中的参数dim不能为空。

f = torch.arange(0, 6).view(3, 2)
print(f, f.size())
# 参数表示在哪一维的前面增加一个维度
print(f.unsqueeze(0), f.unsqueeze(0).size())  # 1×3×2
print(f.unsqueeze(1), f.unsqueeze(1).size())  # 3×1×2
print(f.unsqueeze(2), f.unsqueeze(2).size())  # 3×2×1
# --------------------------------------------------
tensor([[0, 1],
        [2, 3],
        [4, 5]]) torch.Size([3, 2])

tensor([[[0, 1],
         [2, 3],
         [4, 5]]]) torch.Size([1, 3, 2])

tensor([[[0, 1]],

        [[2, 3]],

        [[4, 5]]]) torch.Size([3, 1, 2])

tensor([[[0],
         [1]],

        [[2],
         [3]],

        [[4],
         [5]]]) torch.Size([3, 2, 1])

再看squeeze,它是将被操作目标中维度为1的部分去除。

其中dim表示需要在哪一维去掉一个维度,如果不指定则自动寻找,如果指定则当指定的维度为1时去掉,如果不为1则不改变。

f = torch.arange(0, 6).view(3, 2, 1)
print(f, f.size())  # 3*2*1
print(f.squeeze(0), f.squeeze(0).size())  # 3*2*1,维度为3,不能去掉,所以不变
print(f.squeeze(1), f.squeeze(1).size())  # 3*2*1,维度为2,不能去掉,所有不变
print(f.squeeze(2), f.squeeze(2).size())  # 3*2  ,维度为1,可以去掉,所有改变
print(f.squeeze(), f.squeeze().size())    # 如果无参数,则自动寻找,找到则删除。
# -------------------------------------------------------------------------------
tensor([[[0],
         [1]],

        [[2],
         [3]],

        [[4],
         [5]]]) torch.Size([3, 2, 1])

tensor([[[0],
         [1]],

        [[2],
         [3]],

        [[4],
         [5]]]) torch.Size([3, 2, 1])

tensor([[[0],
         [1]],

        [[2],
         [3]],

        [[4],
         [5]]]) torch.Size([3, 2, 1])

tensor([[0, 1],
        [2, 3],
        [4, 5]]) torch.Size([3, 2])

tensor([[0, 1],
        [2, 3],
        [4, 5]]) torch.Size([3, 2])

 

十二、cat的用法

cat是用于将两个矩阵进行拼接,0表示按行拼接,1表示按列拼接

a = torch.ones(2, 3)
b = 2 * torch.ones(4, 3)
c = 4 * torch.ones(2, 4)
print(a)
print(b)
print(c)
d = torch.cat((a, b), 0)  # 0表示按行拼接
e = torch.cat((a, c), 1)  # 1表示按列拼接
print(d)
print(e)
# -----------------------------------------------
tensor([[1., 1., 1.],
        [1., 1., 1.]])

tensor([[2., 2., 2.],
        [2., 2., 2.],
        [2., 2., 2.],
        [2., 2., 2.]])

tensor([[4., 4., 4., 4.],
        [4., 4., 4., 4.]])

tensor([[1., 1., 1.],
        [1., 1., 1.],
        [2., 2., 2.],
        [2., 2., 2.],
        [2., 2., 2.],
        [2., 2., 2.]])

tensor([[1., 1., 1., 4., 4., 4., 4.],
        [1., 1., 1., 4., 4., 4., 4.]])