2.4 张量的相关函数

接下来开始学习各类张量的api,主要参考官方文档,通过右边目录栏可以看出有以下几个部分。

  • torchTensors
  • Generators
  • Random sampling
  • Serialization
  • Parallelism
  • Locally disabling gradient computation
  • Math operations
  • Utilities

里面有上百个函数,这里只挑高频使用的进行讲解,建议大家自行浏览一遍官方文档,看看都有哪些功能,便于今后使用到的时候不必重复造轮子。

张量的创建

直接创建

torch.tensor

torch.tensor(data, dtype=None, device=None, requires_grad=False, pin_memory=False)

  • data(array_like) - tensor的初始数据,可以是list, tuple, numpy array, scalar或其他类型。

  • dtype(torch.dtype, optional) - tensor的数据类型,如torch.uint8, torch.float, torch.long等

  • device (torch.device, optional) – 决定tensor位于cpu还是gpu。如果为None,将会采用默认值,默认值在torch.set_default_tensor_type()中设置,默认为 cpu。

  • requires_grad (bool, optional) – 决定是否需要计算梯度。

  • pin_memory (bool, optional) – 是否将tensor存于锁页内存。这与内存的存储方式有关,通常为False。

    import torch
    import numpy as np
    l = [[1., -1.], [1., -1.]]
    t_from_list = torch.tensor(l)
    arr = np.array([[1, 2, 3], [4, 5, 6]])
    t_from_array = torch.tensor(arr)
    print(t_from_list, t_from_list.dtype)
    print(t_from_array, t_from_array.dtype)
    

    tensor([[ 1., -1.],

    ​ [ 1., -1.]]) torch.float32

    tensor([[1, 2, 3],

    ​ [4, 5, 6]]) torch.int64

可以看到t_from_list是float32类型,而t_from_array是int64类型。如果想让tensor是其他数据类型,可以在创建tensor时使用dtype参数确定数据类型。

import torch
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
t_from_array = torch.tensor(arr, dtype=torch.uint8)
print(t_from_array)

tensor([[1, 2, 3],

​ [4, 5, 6]], dtype=torch.uint8)

torch.from_numpy

还有一种常用的通过numpy创建tensor方法是torch.from_numpy()。这里需要特别注意的是,创建的tensor和原array共享同一块内存(The returned tensor and ndarray share the same memory. ),即当改变array里的数值,tensor中的数值也会被改变。

import torch
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
t_from_numpy = torch.from_numpy(arr)
print("numpy array: ", arr)
print("tensor : ", t_from_numpy)
print("\n修改arr")
arr[0, 0] = 0
print("numpy array: ", arr)
print("tensor : ", t_from_numpy)
print("\n修改tensor")
t_from_numpy[0, 0] = -1
print("numpy array: ", arr)
print("tensor : ", t_from_numpy)

> >

numpy array: [[1 2 3]

[4 5 6]]

tensor : tensor([[1, 2, 3],

​ [4, 5, 6]])

修改arr

numpy array: [[0 2 3]

[4 5 6]]

tensor : tensor([[0, 2, 3],

​ [4, 5, 6]])

修改tensor

numpy array: [[-1 2 3]

[ 4 5 6]]

tensor : tensor([[-1, 2, 3],

​ [ 4, 5, 6]])

可以看到虽然只改变了arr的值,但是tensor中的data也被改变了,这一点在使用过程中需要注意。

依数值创建

torch.zeros

torch.zeros(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:依给定的size创建一个全0的tensor,默认数据类型为torch.float32(也称为torch.float)。

主要参数:

layout(torch.layout, optional) - 参数表明张量在内存中采用何种布局方式。常用的有torch.strided, torch.sparse_coo等。

out(tensor, optional) - 输出的tensor,即该函数返回的tensor可以通过out进行赋值,请看例子。

example:

import torch
o_t = torch.tensor([1])
t = torch.zeros((3, 3), out=o_t)
print(t, '\n', o_t)
print(id(t), id(o_t))

> >

tensor([[0, 0, 0],

​ [0, 0, 0],

​ [0, 0, 0]])

tensor([[0, 0, 0],

​ [0, 0, 0],

​ [0, 0, 0]])

4925603056 4925603056

可以看到,通过torch.zeros创建的张量不仅赋给了t,同时赋给了o_t,并且这两个张量是共享同一块内存,只是变量名不同。

torch.zeros_like

torch.zeros_like(input, dtype=None, layout=None, device=None, requires_grad=False)

功能:依input的size创建全0的tensor。

主要参数:

input(Tensor) - 创建的tensor与intput具有相同的形状。

example:

import torch
t1 = torch.tensor([[1., -1.], [1., -1.]])
t2 = torch.zeros_like(t1)
print(t2)

tensor([[0., 0.],

​ [0., 0.]])

除了创建全0还有创建全1的tensor,使用方法是一样的,这里就不赘述。

torch.ones(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:依给定的size创建一个全1的tensor。

torch.ones_like(input, dtype=None, layout=None, device=None, requires_grad=False)

功能:依input的size创建全1的tensor。

torch.full(size, fill_value, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:依给定的size创建一个值全为fill_value的tensor。

主要参数:

siz (int...) - tensor的形状。

fill_value - 所创建tensor的值

out(tensor, optional) - 输出的tensor,即该函数返回的tensor可以通过out进行赋值。

example:

import torch
print(torch.full((2, 3), 3.141592))

torch.full_like(input, fill_value, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

torch.full_like之于torch.full等同于torch.zeros_like之于torch.zeros,因此不再赘述。

torch.arange(start=0, end, step=1, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:创建等差的1维张量,长度为 (end-start)/step,需要注意数值区间为[start, end)。

主要参数:

start (Number) – 数列起始值,默认值为0。the starting value for the set of points. Default: 0.

end (Number) – 数列的结束值。

step (Number) – 数列的等差值,默认值为1。

out (Tensor, optional) – 输出的tensor,即该函数返回的tensor可以通过out进行赋值。

example:

import torch
print(torch.arange(1, 2.51, 0.5))

torch.range()函数就不推荐了,因为官网说了“This function is deprecated in favor of torch.arange().”

torch.linspace(start, end, steps=100, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:创建均分的1维张量,长度为steps,区间为[start, end]。

主要参数:

start (float) – 数列起始值。

end (float) – 数列结束值。

steps (int) – 数列长度。

example:

print(torch.linspace(3, 10, steps=5))
print(torch.linspace(1, 5, steps=3))

torch.logspace(start, end, steps=100, base=10.0, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:创建对数均分的1维张量,长度为steps, 底为base。

主要参数:

start (float) – 确定数列起始值为base^start

end (float) – 确定数列结束值为base^end

steps (int) – 数列长度。

base (float) - 对数函数的底,默认值为10,此参数是在pytorch 1.0.1版本之后加入的。

example:

torch.logspace(start=0.1, end=1.0, steps=5)
torch.logspace(start=2, end=2, steps=1, base=2)

torch.eye(n, m=None, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)**

功能:创建单位对角矩阵。

主要参数:

n (int) - 矩阵的行数

m (int, optional) - 矩阵的列数,默认值为n,即默认创建一个方阵

example:

import torch
print(torch.eye(3))
print(torch.eye(3, 4))

torch.empty(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False, pin_memory=False)

功能:依size创建“空”张量,这里的“空”指的是不会进行初始化赋值操作。

主要参数:

size (int...) - 张量维度

pin_memory (bool, optional) - pinned memory 又称page locked memory,即锁页内存,该参数用来指示是否将tensor存于锁页内存,通常为False,若内存足够大,建议设置为True,这样在转到GPU时会快一些。

torch.empty_like(input, dtype=None, layout=None, device=None, requires_grad=False)

功能:torch.empty_like之于torch.empty等同于torch.zeros_like之于torch.zeros,因此不再赘述。

torch.empty_strided(size, stride, dtype=None, layout=None, device=None, requires_grad=False, pin_memory=False)

功能:依size创建“空”张量,这里的“空”指的是不会进行初始化赋值操作。

主要参数:

stride (tuple of python:ints) - 张量存储在内存中的步长,是设置在内存中的存储方式。

size (int...) - 张量维度

pin_memory (bool, optional) - 是否存于锁页内存。

依概率分布创建

torch.normal(mean, std, out=None)

功能:为每一个元素以给定的mean和std用高斯分布生成随机数

主要参数:

mean (Tensor or Float) - 高斯分布的均值,

std (Tensor or Float) - 高斯分布的标准差

特别注意事项:

mean和std的取值分别有2种,共4种组合,不同组合产生的效果也不同,需要注意

mean为张量,std为张量,torch.normal(mean, std, out=None),每个元素从不同的高斯分布采样,分布的均值和标准差由mean和std对应位置元素的值确定;

mean为张量,std为标量,torch.normal(mean, std=1.0, out=None),每个元素采用相同的标准差,不同的均值;

mean为标量,std为张量,torch.normal(mean=0.0, std, out=None), 每个元素采用相同均值,不同标准差;

mean为标量,std为标量,torch.normal(mean, std, size, *, out=None) ,从一个高斯分布中生成大小为size的张量;

案例1

import 
mean = torch.arange(1, 11.)
std = torch.arange(1, 0, -0.1)
normal = torch.normal(mean=mean, std=std)
print("mean: {}, \nstd: {}, \nnormal: {}".format(mean, std, normal))

mean: tensor([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.]),

std: tensor([1.0000, 0.9000, 0.8000, 0.7000, 0.6000, 0.5000, 0.4000, 0.3000, 0.2000,

​ 0.1000]),

normal: tensor([ 1.3530, -1.3498, 3.0021, 5.1200, 3.9818, 5.0163, 6.9272, 8.1171,

​ 9.0623, 10.0621])

1.3530是通过均值为1,标准差为1的高斯分布采样得来,

-1.3498是通过均值为2,标准差为0.9的高斯分布采样得来,以此类推

torch.rand(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:在区间[0, 1)上,生成均匀分布。

主要参数:

size (int...) - 创建的张量的形状

torch.rand_like(input, dtype=None, layout=None, device=None, requires_grad=False)

torch.rand_like之于torch.rand等同于torch.zeros_like之于torch.zeros,因此不再赘述。

torch.randint(low=0, high, size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:在区间[low, high)上,生成整数的均匀分布。

主要参数:

low (int, optional) - 下限。

high (int) – 上限,主要是开区间。

size (tuple) – 张量的形状。

example

print(torch.randint(3, 10, (2, 2)))

torch.randint_like(input, low=0, high, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:torch.randint_like之于torch.randint等同于torch.zeros_like之于torch.zeros,因此不再赘述。

torch.randn(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:生成形状为size的标准正态分布张量。

主要参数:

size (int...) - 张量的形状

torch.randn_like(input, dtype=None, layout=None, device=None, requires_grad=False)

功能:torch.rafndn_like之于torch_randn等同于torch.zeros_like之于torch.zeros,因此不再赘述。

torch.randperm(n, out=None, dtype=torch.int64, layout=torch.strided, device=None, requires_grad=False)

功能:生成从0到n-1的随机排列。perm == permutation

torch.bernoulli(input, *, generator=None, out=None)

功能:以input的值为概率,生成伯努力分布(0-1分布,两点分布)。

主要参数:

input (Tensor) - 分布的概率值,该张量中的每个值的值域为[0-1]

example:

import torch
p = torch.empty(3, 3).uniform_(0, 1)
b = torch.bernoulli(p)
print("probability: \n{}, \nbernoulli_tensor:\n{}".format(p, b))

probability:

tensor([[0.7566, 0.2899, 0.4688],

​ [0.1662, 0.8341, 0.9572],

​ [0.6060, 0.4685, 0.6366]]),

bernoulli_tensor:

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

​ [1., 1., 1.],

​ [1., 1., 1.]])

张量的操作

熟悉numpy的朋友应该知道,Tensor与numpy的数据结构很类似,不仅数据结构类似,操作也是类似的,接下来介绍Tensor的常用操作。由于操作函数很多,这里就不一一举例,仅通过表格说明各个函数作用,详细介绍可查看官方文档

cat 将多个张量拼接在一起,例如多个特征图的融合可用。
concat 同cat, 是cat()的别名。
conj 返回共轭复数。
chunk 将tensor在某个维度上分成n份。
dsplit 类似numpy.dsplit()., 将张量按索引或指定的份数进行切分。
column_stack 水平堆叠张量。即第二个维度上增加,等同于torch.hstack。
dstack 沿第三个轴进行逐像素(depthwise)拼接。
gather 高级索引方法,目标检测中常用于索引bbox。在指定的轴上,根据给定的index进行索引。强烈推荐看example。
hsplit 类似numpy.hsplit(),将张量按列进行切分。若传入整数,则按等分划分。若传入list,则按list中元素进行索引。例如:[2, 3] and dim=0 would result in the tensors input[:2], input[2:3], and input[3:].
hstack 水平堆叠张量。即第二个维度上增加,等同于torch.column_stack。
index_select 在指定的维度上,按索引进行选择数据,然后拼接成新张量。可知道,新张量的指定维度上长度是index的长度。
masked_select 根据mask(0/1, False/True 形式的mask)索引数据,返回1-D张量。
movedim 移动轴。如0,1轴交换:torch.movedim(t, 1, 0) .
moveaxis 同movedim。Alias for torch.movedim().(这里发现pytorch很多地方会将dim和axis混用,概念都是一样的。)
narrow 变窄的张量?从功能看还是索引。在指定轴上,设置起始和长度进行索引。例如:torch.narrow(x, 0, 0, 2), 从第0个轴上的第0元素开始,索引2个元素。x[0:0+2, ...]
nonzero 返回非零元素的index。torch.nonzero(torch.tensor([1, 1, 1, 0, 1])) 返回tensor([[ 0], [ 1], [ 2], [ 4]])。建议看example,一看就明白,尤其是对角线矩阵的那个例子,太清晰了。
permute 交换轴。
reshape 变换形状。
row_stack 按行堆叠张量。即第一个维度上增加,等同于torch.vstack。Alias of torch.vstack().
scatter scatter_(dim, index, src, reduce=None) → Tensor。将src中数据根据index中的索引按照dim的方向填进input中。这是一个十分难理解的函数,其中index是告诉你哪些位置需要变,src是告诉你要变的值是什么。这个就必须配合例子讲解,请跳转到本节底部进行学习。
scatter_add 同scatter一样,对input进行元素修改,这里是 +=, 而scatter是直接替换。
split 按给定的大小切分出多个张量。例如:torch.split(a, [1,4]); torch.split(a, 2)
squeeze 移除张量为1的轴。如t.shape=[1, 3, 224, 224]. t.squeeze().shape -> [3, 224, 224]
stack 在新的轴上拼接张量。与hstack\vstack不同,它是新增一个轴。默认从第0个轴插入新轴。
swapaxes Alias for torch.transpose().交换轴。
swapdims Alias for torch.transpose().交换轴。
t 转置。
take 取张量中的某些元素,返回的是1D张量。torch.take(src, torch.tensor([0, 2, 5]))表示取第0,2,5个元素。
take_along_dim 取张量中的某些元素,返回的张量与index维度保持一致。可搭配torch.argmax(t)和torch.argsort使用,用于对最大概率所在位置取值,或进行排序,详见官方文档的example。
tensor_split 切分张量,核心看indices_or_sections变量如何设置。
tile 将张量重复X遍,X遍表示可按多个维度进行重复。例如:torch.tile(y, (2, 2))
transpose 交换轴。
unbind 移除张量的某个轴,并返回一串张量。如[[1], [2], [3]] --> [1], [2], [3] 。把行这个轴拆了。
unsqueeze 增加一个轴,常用于匹配数据维度。
vsplit 垂直切分。
vstack 垂直堆叠。
where 根据一个是非条件,选择x的元素还是y的元素,拼接成新张量。看案例可瞬间明白。

scater_

scater是将input张量中的部分值进行替换。公式如下:

self[index[i][j][k]][j][k] = src[i][j][k]  # if dim == 0
self[i][index[i][j][k]][k] = src[i][j][k]  # if dim == 1
self[i][j][index[i][j][k]] = src[i][j][k]  # if dim == 2

设计两个核心问题:

  1. input哪个位置需要替换?
  2. 替换成什么?

答:

  1. 从公式可知道,依次从index中找到元素放到dim的位置,就是input需要变的地方。
  2. 变成什么呢? 从src中找,src中与index一样位置的那个元素值放到input中。

案例1:

>>> src = torch.arange(1, 11).reshape((2, 5))
>>> src
tensor([[ 1,  2,  3,  4,  5],
        [ 6,  7,  8,  9, 10]])
>>> index = torch.tensor([[0, 1, 2, 0]])
>>> torch.zeros(3, 5, dtype=src.dtype).scatter_(0, index, src)
tensor([[1, 0, 0, 4, 0],
        [0, 2, 0, 0, 0],
        [0, 0, 3, 0, 0]])

dim=0, 所以行号跟着index的元素走。其它跟index的索引走。

第一步:找到index的第一个元素index[0, 0]是0, 那么把src[0, 0](是1)放到input[0, 0]
第二步:找到index的第二个元素index[0, 1]是1, 那么把src[0, 1](是2)放到input[1, 1]
第三步:找到index的第三个元素index[0, 2]是2, 那么把src[0, 2](是3)放到input[2, 2]
第四步:找到index的第四个元素index[0, 3]是0, 那么把src[0, 3](是4)放到input[0, 3]

案例2:


>>> src = torch.arange(1, 11).reshape((2, 5))
>>> src
tensor([[ 1,  2,  3,  4,  5],
        [ 6,  7,  8,  9, 10]])
>>> index = torch.tensor([[0, 2, 4], [1, 2, 3]])
>>> index
tensor([[0, 2, 4],
        [1, 2, 3]])
>>> torch.zeros(3, 5, dtype=src.dtype).scatter_(1, index, src)
tensor([[1, 0, 2, 0, 3],
        [0, 6, 7, 8, 0],
        [0, 0, 0, 0, 0]])

dim=1:告诉input(零矩阵)的索引,沿着列进行索引,行根据index走。 index:2*3,告诉input(零矩阵),你的哪些行是要被替换的。 src:input要替换成什么呢?从src里找,怎么找?通过index的索引对应的找。

第一步:找到index的第一个元素index[0, 0]是0, 那么把src[0, 0](是1)放到input[0, 0]
第二步:找到index的第二个元素index[0, 1]是2, 那么把src[0, 1](是2)放到input[0, 2]
第三步:找到index的第三个元素index[0, 2]是4, 那么把src[0, 2](是3)放到input[0, 4]
第四步:找到index的第四个元素index[1, 0]是1, 那么把src[1, 0](是6)放到input[1, 1]
第五步:找到index的第五个元素index[1, 1]是2, 那么把src[1, 1](是7)放到input[1, 2]
第六步:找到index的第六个元素index[1, 2]是3, 那么把src[1, 2](是8)放到input[1, 3]

这里可以看到

  • index的元素是决定input的哪个位置要变
  • 变的值是从src上对应于index的索引上找。可以看到src的索引与index的索引保持一致的

案例3:one-hot的生成

>>> label = torch.arange(3).view(-1, 1)
>>> label
tensor([[0],
        [1],
        [2]])
>>> torch.zeros(3, 3).scatter_(1, label, 1)
tensor([[1., 0., 0.],
        [0., 1., 0.],
        [0., 0., 1.]])

第一步:找到index的第一个元素index[0, 0]是0, 那么把src[0, 0](是1)放到input[0, 0]

第二步:找到index的第二个元素index[1, 0]是1, 那么把src[1, 0](是1)放到input[1, 1]

第三步:找到index的第三个元素index[2, 0]是2, 那么把src[2, 0](是1)放到input[2, 2]

(one-hot的案例不利于理解scater函数,因为它的行和列是一样的。。。其实input[x, y] 中的x,y是有区别的,x是根据index走,y是根据index的元素值走的,而具体的值是根据src的值。)

张量的随机种子

随机种子(random seed)是编程语言中基础的概念,大多数编程语言都有随机种子的概念,它主要用于实验的复现。针对随机种子pytorch也有一些设置函数。

seed 获取一个随机的随机种子。Returns a 64 bit number used to seed the RNG.
manual_seed 手动设置随机种子,建议设置为42,这是近期一个玄学研究。说42有效的提高模型精度。当然大家可以设置为你喜欢的,只要保持一致即可。
initial_seed 返回初始种子。
get_rng_state 获取随机数生成器状态。Returns the random number generator state as a torch.ByteTensor.
set_rng_state 设定随机数生成器状态。这两怎么用暂时未知。Sets the random number generator state.

以上均是设置cpu上的张量随机种子,在cuda上是另外一套随机种子,如torch.cuda.manual_seed_all(seed), 这些到cuda模块再进行介绍,这里只需要知道cpu和cuda上需要分别设置随机种子。

张量的数学操作

张量还提供大量数学操作,估计了一下,有快一百个函数,这里就不再一一分析,只需要知道有哪几大类,用到的时候来查吧。

  • Pointwise Ops: 逐元素的操作,如abs, cos, sin, floor, floor_divide, pow等
  • Reduction Ops: 减少元素的操作,如argmax, argmin, all, any, mean, norm, var等
  • Comparison Ops:对比操作, 如ge, gt, le, lt, eq, argsort, isnan, topk,
  • Spectral Ops: 谱操作,如短时傅里叶变换等各类信号处理的函数。
  • Other Operations:其它, clone, diag,flip等
  • BLAS and LAPACK Operations:BLAS(Basic Linear Algebra Subprograms)基础线性代数)操作。如, addmm, dot, inner, svd等。

小结

本节介绍了张量主要的操作函数,并归类到各个小结,这些仅是张量的部分操作,更多操作还请大家多多看官方文档。对于张量,主要是要理解2.3小节中张量的结构以及作用,对于它的操作就像numpy一样简单易用。

下一节就开始讲解pytorch的核心——autograd,autograd也是现代深度学习框架的核心,是实现自动微分的具体实现。

Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10

results matching ""

    No results matching ""