Numpy与Tensor是PyTorch的重要内容

Numpy的使用

Numpy是Python中科学计算的一个基础包,提供了一个多维度的数组对象,数组是由numpy.ndarray类来实现的,是Numpy的核心数据结构,其索引从0开始,和Python列表不同的是,Numpy没办法动态地改变,创建时就具有固定的大小,如果改变Numpy数组的长度,会创建一个新的数组并且删除原数组,并且数组中的数据类型必须是一样的,但消耗的内存更少,运行速度更快

创建数组

将一个任意维度的列表传入np.array()np.asarray()中就可以创建一个数组,如下创建一个一维数组:

import numpy as np

arr1 = np.asarray([1, 2, 3])
print(arr1)

输出:

[1, 2, 3]

如下创建一个二维数组:

import numpy as np

arr2 = np.asarray([[1, 2], [3, 4]])
print(arr2)

输出:

[[1 2]
 [3 4]]

np.ones()可以创建一个全1的数组,必须指定数组的形状,可选参数是数组的数据类型

arr = np.ones(shape=(2, 3))
print(arr)

输出:

[[1. 1. 1.]
 [1. 1. 1.]]

指定数据类型:

arr = np.ones(shape=(2, 3), dtype='int32')
print(arr)

输出:

[[1 1 1]
 [1 1 1]]

np.zeros()创建一个全0数组,用法与ones类似,使用np.ones()*n可以创建一个全n数组

np.arange()可以创建一个区间内的数组,格式为np.arrange([start,]stop,[step,]dtype=None),创建一个在[start,stop)区间的数组

arr1 = np.arange(5)
print(arr1)
arr2 = np.arange(2, 5)
print(arr2)
arr3 = np.arange(2, 9, 3)
print(arr3)

输出:

[0 1 2 3 4]
[2 3 4]
[2 5 8]

np.linerspace()可以创建一个从开始数值到结束数值的等差数列

参数

说明

start

必要参数,序列的起始值

stop

必须参数,序列的终点

num

序列中元素个数,默认50

endpoint

默认为True,如果为True则最后一个元素是stop(包含终点)

restep

默认为False,如果为True,返回数组与公差

arr = np.linspace(start=2, stop=10, num=3)
print(arr)

输出:

[ 2.  6. 10.]

数组属性

Numpy数组具有一些固有属性,如下是一些常用属性

nidm

表示数组维度的个数

打印上述代码中的arr2.ndim得到的就是2,arr1.ndim的结果就是1

shape

表示数组的形状,是一个整数的元组,长度为ndim

arr1 = np.asarray([1, 2, 3])
print(arr1.shape)
arr2 = np.asarray([[1, 2], [3, 4]])
print(arr2.shape)

上述这段代码的输出:

(3,)
(2, 2)

可以使用reshape对数组的形状进行变换,但是变换前和变换后的数组元素个数必须一样:

arr2 = np.asarray([[1, 2], [3, 4]])
print(arr2.shape)

arr2 = arr2.reshape((4, 1))
print(arr2)

输出:

(2, 2)
[[1]
 [2]
 [3]
 [4]]

还可以使用np.reshape(a,newshape,order)来对数组进行形状的改变,新的形状在newshape中指定,order参数指定以什么样的顺序读写元素,其中有几个参数:

名称

说明

C

默认参数,使用C-like语言(行优先)中的索引方式进行读写

F

使用类似Fortan-like语言(列优先)中的索引方式进行读写

A

原数组如果是按照'C'的方式存储数组,则用'C'的索引对数组进行reshape,否则使用'F'

示例:

a = np.arange(6).reshape((2, 3), order='C')
print(a)

输出:

[[0 1 2]
 [3 4 5]]

将数组a按照'C'的方式reshape成(3,2),方式首先将原数组展开,因为是行优先,所以最后一个维度优先改变

a = np.arange(6).reshape((2, 3), order='F')
print(a)

输出:

[[0 2 4]
 [1 3 5]]

size

表示数组元素的总数,等于shape属性中元素的乘积

a = np.arange(12).reshape((3, 4), order='C')
print(a.size)

输出12

dtype

描述数组中元素类型的对象,使用dtype可以查看数组所属的数据类型

a = np.arange(12).reshape((3, 4), order='C')
print(a.dtype)

输出int64

在创建数组时,Numpy会自动判断类型,然后给一个默认的数据类型,也直接可以指定:

arr = np.asarray([[1, 2], [3, 4]], dtype='float')
print(arr.dtype)

数组的数据类型可以用astype改变,但会创建一个新的数组,并不会改变原数组的数据类型:

arr = np.asarray([[1, 2], [3, 4]], dtype='float')
print(arr.dtype)
arr2 = arr.astype("int32")
print(arr2.dtype)
print(arr.dtype)

输出:

float64
int32
float64

事实上,不能通过直接修改数据类型来修改数组的数据类型,这样会改变数据

数组的轴

数组的轴即数组的维度,从0开始。对于二维数组来说有两个轴,分别是代表行的0轴与代表列的1轴

随机赋值一个3×4的数组,按0轴方向进行累加:

arr = np.random.randint(10, size=(4, 3))
print(arr)
ans = np.sum(arr, axis=0)  # 按0轴方向进行累加
print(ans)

输出:

[[0 0 2]
 [2 3 2]
 [7 8 6]
 [0 5 4]]
[ 9 16 14]

可以看到0轴方向元素的累加结果为[9 16 14],下面按1轴方向:

arr = np.random.randint(10, size=(4, 3))
print(arr)
ans = np.sum(arr, axis=1)
print(ans)

输出:

[[2 2 5]
 [5 8 2]
 [5 7 4]
 [7 0 4]]
[ 9 15 16 11]

如果是更高维度的数组,axis=i就意味着按照第i个轴的方向进行计算,第i个轴的数据将会被折叠或聚合到一起,示例:

a = np.arange(18).reshape((3, 2, 3))
print(a)
print("0 axis:\n", a.max(axis=0))
print("1 axis:\n", a.max(axis=1))
print("2 axis:\n", a.max(axis=2))

输出:

[[[ 0  1  2]
  [ 3  4  5]]

 [[ 6  7  8]
  [ 9 10 11]]

 [[12 13 14]
  [15 16 17]]]
0 axis:
 [[12 13 14]
 [15 16 17]]
1 axis:
 [[ 3  4  5]
 [ 9 10 11]
 [15 16 17]]
2 axis:
 [[ 2  5]
 [ 8 11]
 [14 17]]

Numpy在深度学习中的应用

深度学习的项目可以分为数据加载、训练与模型评估三部分,Numpy在数据加载和模型评估中经常会被使用到

数据加载

在这个阶段要做的是将模型训练要用的数据读取进来,训练数据无外乎: 图片、文本以及类似二维表那样的结构化数据

PyTorch中对于图片的操作大多基于Pillow,可以用Pillow或OpenCV读入图片然后转化为Numpy数组

numpy和python版本对应 numpy与pytorch_数据

下面就对如上的图片进行处理

Pillow加载图片

Pillow使用二进制形式读取图片,使用Numpy的asarray方法即可转换数据格式:

import numpy as np
from PIL import Image

image = Image.open("./apple.jpg")
image_pillow = np.asarray(image)
print(image_pillow.shape)

输出:

(424, 568, 3)

OpenCV加载图片

OpenCV读入图片后就是以Numpy数组形式保存的

import cv2

image = cv2.imread('./apple.jpg')
print(type(image))
print(image.shape)

输出:

<class 'numpy.ndarray'>
(424, 568, 3)

可以发现,数组的最后一个维度是3,这是因为图片的格式是RGB格式,表示有R、G、B三个通道

值得注意的是,Pillow读入后通道的顺序是R、G、B,而OpenCV读入后顺序是B、G、R

Numpy索引和切片

现在利用Numpy的索引和切片来分出图片的3个通道

image = Image.open("./apple.jpg")
image_pillow = np.asarray(image)
image_pillow_c1 = image_pillow[:, :, 0]
image_pillow_c2 = image_pillow[:, :, 1]
image_pillow_c3 = image_pillow[:, :, 2]

:表示全部选中,上述分别选中了第三个维度为0 1 2的数组,这样就获得了每个通道的数据

使用Numpy的contatenate函数可对数组进行拼接,现在将上述分离出的一个数据与全0数组进行拼接:

zeros = np.zeros(shape=(424, 568, 1), dtype='uint8')
image_pillow_c1 = image_pillow_c1[:, :, np.newaxis]
image_pillow_c1_3ch = np.concatenate((image_pillow_c1, zeros, zeros), axis=2)
print(image_pillow_c1_3ch.shape)

输出 (424, 568, 3)

代码中使用了np.newaxis,因为image_pillow_c1是一个二维的数据,要增加一个维度变成3维

利用直接赋值也可以对数组进行升维,生成一个和image_pillow形状一样的全0数组,然后每个通道的数值赋值为image_pillow_c1、image_pillow_c2与image_pillow_c3即可

按照这个方式将RGB三个通道都取出,和原图一起输出到一张图片上

import numpy as np
from PIL import Image
from matplotlib import pyplot as plt

image = Image.open("./apple.jpg")
image_pillow = np.asarray(image)
image_pillow_c1 = image_pillow[:, :, 0]
image_pillow_c2 = image_pillow[:, :, 1]
image_pillow_c3 = image_pillow[:, :, 2]
zeros = np.zeros(shape=(424, 568, 1), dtype='uint8')  # 创建全0数组
image_pillow_c1 = image_pillow_c1[:, :, np.newaxis]
image_pillow_c1_3ch = np.concatenate((image_pillow_c1, zeros, zeros), axis=2)   # 沿着2轴合并
image_pillow_c2 = image_pillow_c2[:, :, np.newaxis]
image_pillow_c2_3ch = np.concatenate((zeros, image_pillow_c2, zeros), axis=2)
image_pillow_c3 = image_pillow_c3[:, :, np.newaxis]
image_pillow_c3_3ch = np.concatenate((zeros, zeros, image_pillow_c2), axis=2)
plt.subplot(2, 2, 1)
plt.title('Origin Image')
plt.imshow(image_pillow)
plt.axis('off')
plt.subplot(2, 2, 2)
plt.title('Red Channel')
plt.imshow(image_pillow_c1_3ch)
plt.axis('off')
plt.subplot(2, 2, 3)
plt.title('Green Channel')
plt.imshow(image_pillow_c2_3ch)
plt.axis('off')
plt.subplot(2, 2, 4)
plt.title('Red Channel')
plt.imshow(image_pillow_c3_3ch)
plt.axis('off')
plt.savefig('./rgb_pillow.png', dpi=150)

最终得到的图片:

numpy和python版本对应 numpy与pytorch_numpy和python版本对应_02

深拷贝(副本)与浅拷贝(视图)

还有一种方式获得RGB 3个通道,只需将图片读入后,直接将其中两个通道赋值为0即可,但得使用深拷贝,完全复制原有数组,创建一个新的数组,这样修改新的数组才不会影响原有数组,因此就使用array来加载数据

import numpy as np
from PIL import Image
from matplotlib import pyplot as plt

image = Image.open("./apple.jpg")
image_pillow = np.array(image)
image_pillow_c1 = np.array(image)
image_pillow_c1[:, :, 1:] = 0
image_pillow_c2 = np.array(image)
image_pillow_c2[:, :, 0] = 0
image_pillow_c2[:, :, 2] = 0
image_pillow_c3 = np.array(image)
image_pillow_c3[:, :, 0:2] = 0

plt.subplot(2, 2, 1)
plt.title('Origin Image')
plt.imshow(image_pillow)
plt.axis('off')
plt.subplot(2, 2, 2)
plt.title('Red Channel')
plt.imshow(image_pillow_c1)
plt.axis('off')
plt.subplot(2, 2, 3)
plt.title('Green Channel')
plt.imshow(image_pillow_c2)
plt.axis('off')
plt.subplot(2, 2, 4)
plt.title('Red Channel')
plt.imshow(image_pillow_c3)
plt.axis('off')
plt.savefig('./rgb_pillow.png', dpi=150)

Tensor的使用

深度学习中,从数据的组织到模型内部的参数,都是一种叫做张量(Tensor)的数据结构进行表示和处理

Tensor是深度学习框架中极为基础的概念,是一种数据的存储和处理结构

标量、向量和矩阵都是张量,是秩不同的张量,标量是秩为0阶的Tensor,向量是秩为1阶的Tensor,矩阵是秩为2阶的Tensor

创建Tensor

PyTorch可以通过多种方式创建一个任意形状的Tensor

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

参数的含义:

data是要传入模型的数据,PyTorch支持通过list、tuple、numpy array、scalar等多种类型进行数据传入,并转换为Tensor,接着是dtype,它声明了要返回一个怎样的类型的Tensor,之后的device,这个参数指定了数据要返回到的设备,目前并不关心,最后一个参数是requires_grad,用于说明当前量是否需要在计算中保留对应的梯度信息,在PyTorch中,只有当一个Tensor设置requires_grad为True的情况下,才会对这个Tensor以及由这个Tensor计算出来的其他Tensor进行求导,然后将导数值存在Tensor进行求导,然后将导数值存在Tensor的grad属性中,便于优化器来更新参数

训练过程中设置为true,目的是方便求导和更新参数。而到了验证或者测试过程,目的是检查当前模型的泛化能力,那就要把requires_grad设置为False,避免这个参数根据loss自动更新

从Numpy中创建

实际应用中,在处理数据的阶段多用Numpy,而数据处理好之后想要传入PyTorch的深度学习模型中,则需要借助Tensor,所以PyTorch提供了一个从Numpy转到Tensor的语句:

torch.from_numpy(ndarry)

示例:

import numpy
import torch

arr = numpy.arange(6).reshape((2, 3))
res = torch.from_numpy(arr)
print(res)

输出:

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

创建特殊形式的Tensor

创建全0矩阵Tensor,其中用得比较多的size参数和dtype参数,size定义输出张量形状的整数序列:

res = torch.zeros((2,3))
print(res)

输出:

tensor([[0., 0., 0.],
        [0., 0., 0.]])

该函数的参数还有很多

创建单位矩阵Tensor,单位矩阵就是指主对角线上元素都为1的矩阵:

res = torch.eye(3, 4)
print(res)

输出:

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

创建全1矩阵Tensor,就是所有元素都为1的矩阵

res = torch.ones(3, 4)
print(res)

输出:

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

创建随机矩阵Tensor: 在PyTorch中有几种较为经常使用的随机矩阵创建方式,分别如下:

函数

说明

torch.rand(size)

生成浮点类型且维度指定的随机Tensor,数据在0~1间分布

torch.randn(size)

生成浮点类型且维度指定的随机Tensor,数据满足均值为0且方差为1的标准正态分布

torch.normal(mean,std,size)

生成浮点类型且维度制定的随机Tensor,可以指定均值和标准差

torch.randint(low,high,size)

生成随机整数的Tensor,其内部填充的是在[low,high)均匀生成的随机整数

转换Tensor数据类型

int与Tensor转换:

a = torch.tensor(1)
b = a.item()

上述代码先将一个数字转换为Tensor,又通过item()函数将Tensor转换为数字,item函数的作用就是将Tensor转换为一个python数字

list与Tensor转换:

a = [1, 2, 3]
b = torch.tensor(a)
c = b.numpy().tolist()

现将一个list转化为tensor,但要转化回来要先转为Numpy结构,之后在使用tolist得到list

Tensor常用操作

常用的操作包括: 获取形状、维度转换、形状变换以及增减维度

获取形状

使用shape或size来获取形状,shape是一个属性,size()是一个方法:

a = torch.zeros(2, 3, 5)
print(a.shape)
print(a.size())

输出:

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

知道了Tensor的形状,就可以通过所有维度相乘来计算出Tensor所包含的数量,也可以通过numel()函数直接统计元素数量

a.numel()

矩阵转秩

使用permute和transpose可以用来实现矩阵的转秩,或者说交换不同维度的数据

使用permute函数可以对任意高维矩阵进行转秩:

import torch

x = torch.rand(2, 3, 5)
print(x.shape)
x = x.permute(2, 1, 0)
print(x.shape)

输出:

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

permute中的0表示原来的第0个维度放在了现在的第二个维度,形状变成了[5, 3, 2]

而另外一个函数transpose不同于permute,每次只能转换两个维度,或者说交换两个维度的数据:

x = torch.rand(2, 3, 5)
print(x.shape)
x = x.transpose(1, 0)
print(x.shape)

输出:

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

经过transpose或者permute处理之后的数据,在内存中变得不再连续

形状变换

在PyTorch中有两个常用的改变形状的函数,分别是view和reshape

view:

x = torch.randn(4, 4)
print(x.shape)
x = x.view(2, 8)
print(x.shape)

先声明了一个[4,4]大小的Tensor,然后通过view函数,将其修改为[2,8]形状的Tensor,但是这个操作不能作用于内存不连续的Tensor(比如permute交换过的数据),这时使用reshape函数可以避免这个问题:

x = torch.randn(4, 4)
print(x.shape)
x = x.permute(1, 0)
x = x.reshape(2, 8)
print(x.shape)

输出:

torch.Size([4, 4])
torch.Size([2, 8])

增减维度

对Tensor增加或者删除某些维度,使用squeeze()或者unsqueeze()函数

x = torch.rand(2, 1, 3)
print(x.shape)
y = x.squeeze(1)
print(y.shape)
z = y.squeeze(1)
print(z.shape)

新建了一个维度[2, 1, 3]的Tensor,然后将第1维度的数据删除,得到y,squeeze执行成功是因为是第1维度的大小为1,,然而在y上打算进一步删除第1维度的时候,就会发现删除失败了,因为y此刻的第1维度的大小为3,所以suqeeze不能删除

unsqueeze(): 这个函数主要是对数据维度进行扩充。给指定位置加上维数为1的维度,同样结合代码例:

x = torch.rand(2, 1, 3)
y = x.unsqueeze(2)
print(y.shape)

输出:

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

Tensor的变形

Tensor连接和切分都是必不可少的操作

Tensor连接

连接操作函数:

torch.cat(tensors,dim=0,out=None)

cat是concatnate,意思为拼接和联系

第一个参数是tensors,是准备进行连接的Tensor

第二个参数是dim,Tensor的维度有多种情况,比如两个3维的Tensor,可以有几种不同的拼接方式

先看二维情况:

A = torch.ones(3, 3)
B = 2*torch.ones(3, 3)
print(A)
print(B)
C = torch.cat((A, B), 0)
print(C)
D = torch.cat((A, B), 1)
print(D)

输出:

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

上述展示了将dim指定为0或1,得到的结果,dim的数值是多少,两个矩阵就会按照相应维度进行连接

利用stack函数进行升维:

torch.stack(inputs, dim=0)

其中inputs表示要拼接的Tensor,dim表示新建立维度的方向

A = torch.arange(0, 4)
B = torch.arange(5, 9)
print(A)
print(B)
C = torch.stack((A, B), 0)
print(C)
D = torch.stack((A, B), 1)
print(D)

输出:

tensor([0, 1, 2, 3])
tensor([5, 6, 7, 8])
tensor([[0, 1, 2, 3],
        [5, 6, 7, 8]])
tensor([[0, 5],
        [1, 6],
        [2, 7],
        [3, 8]])

构建了4元素向量A和B,维度为1。在dim=0上建立一个维度,这样维度成了2,也就得到了C。而对于则是在dim=1,即列方向上建立维度

Tensor切分

切分就是连接的逆过程,切分的操作主要分为3种: chunk、split和unbind

chunk

torch.chunk(input, chunks, dim=0)

input是要做chunk操作的Tensor,chunks代表要被划分的块的数量,而不是每组的数量,chunks必须是整型,dim表示按照哪个维度进行chunk

A = torch.tensor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
B = torch.chunk(A, 2, 0)
print(B)

chunk函数将原来10位长度的Tensor,切分成了两个一样5位长度的向量

输出:

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

如果chunk参数不能够整除的话:

A = torch.tensor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
B = torch.chunk(A, 3, 0)
print(B)

输出:

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

先优先分出若干长度为n的向量(n为总长度除以个数再向上取整),最后不够分的就作为最后一个向量

如果chunk参数大于Tensor可以切分的长度:

A = torch.tensor([1, 2, 3])
B = torch.chunk(A, 5, 0)
print(B)

输出:

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

被切分的Tensor只能分成若干个长度为1的向量

对于二维矩阵Tensor也是类似的情形:

A = torch.ones(4, 4)
print(A)
B = torch.chunk(A, 2, 0)
print(B)

输出:

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

split

上述的chunk函数是按照"切分成确定的份数"来进行切分的,如果要按"每份的大小"来进行切分,则使用split函数

torch.split(tensor,split_size_or_sections,dim=0)

首先是tensor,也就是待切分的Tensor,对于split_size_of_sections参数,当它为整数时,表示将tensor按照每块大小为这个整数的数值来切割,当这个参数为列表时,则表示将此tensor切成和列表中元素一样大小的块,最后一个dim定义了按哪个维度切分

示例:

A = torch.rand(4, 4)
print(A)
B = torch.split(A, 2, 0)
print(B)

输出:

tensor([[0.0595, 0.3272, 0.8546, 0.0235],
        [0.9919, 0.7146, 0.8096, 0.0890],
        [0.5487, 0.9408, 0.4592, 0.3705],
        [0.4732, 0.8606, 0.9022, 0.3976]])
(tensor([[0.0595, 0.3272, 0.8546, 0.0235],
        [0.9919, 0.7146, 0.8096, 0.0890]]), tensor([[0.5487, 0.9408, 0.4592, 0.3705],
        [0.4732, 0.8606, 0.9022, 0.3976]]))

将一个4×4的Tensor从0轴按照每组两行的切分,得到2个2×4的Tensor

如果不能整除的话,如下:

A = torch.rand(4, 4)
print(A)
B = torch.split(A, 3, 0)
print(B)

输出:

tensor([[0.3061, 0.6316, 0.6293, 0.7099],
        [0.4281, 0.3687, 0.7680, 0.8685],
        [0.0606, 0.9907, 0.9463, 0.8688],
        [0.2345, 0.8496, 0.6657, 0.6714]])
(tensor([[0.3061, 0.6316, 0.6293, 0.7099],
        [0.4281, 0.3687, 0.7680, 0.8685],
        [0.0606, 0.9907, 0.9463, 0.8688]]), tensor([[0.2345, 0.8496, 0.6657, 0.6714]]))

显然在这种情况下,会尽量凑够每一个结果,剩下的不够凑了则作为一组

如果split_size_or_sections为列表:

A = torch.rand(5, 4)
print(A)
B = torch.split(A, (2, 3), 0)
print(B)

将 Tensor A,沿着第 0 维进行切分,每一个结果对应维度上的尺寸或者说大小,分别是 2(行),3(行)。

unbind

一种降维切分的方法,将原数组拆解

torch.unbind(input, dim=0)

input表示待处理的Tensor,dim依然表示切片的方向

A = torch.arange(0, 16).view(4, 4)
B = torch.unbind(A, 0)
print(B)

输出:

(tensor([0, 1, 2, 3]), tensor([4, 5, 6, 7]), tensor([ 8,  9, 10, 11]), tensor([12, 13, 14, 15]))

从第0维开始切分,因为有4行,所以切出4个结果,将unbind中第二个参数改成1则按列切分:

A = torch.arange(0, 16).view(4, 4)
B = torch.unbind(A, 1)
print(B)

输出:

(tensor([ 0,  4,  8, 12]), tensor([ 1,  5,  9, 13]), tensor([ 2,  6, 10, 14]), tensor([ 3,  7, 11, 15]))

索引

最常用的两个索引操作就是 index_select 和 masked_select

index_select

torch.index_select(tensor, dim, index)

示例:

A = torch.arange(0, 16).view(4, 4)
print(A)
B = torch.index_select(A, 0, torch.tensor([1, 3]))
C = torch.index_select(A, 1, torch.tensor([0, 3]))
print(B)
print(C)

输出:

tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15]])
tensor([[ 4,  5,  6,  7],
        [12, 13, 14, 15]])
tensor([[ 0,  3],
        [ 4,  7],
        [ 8, 11],
        [12, 15]])

第一次从0维按行选择,选择了第1行和第3行,第二次按从1维选择,选择了第0行和第3行

masked_select

还可通过一些判断条件来进行选择,比如提取深度学习网络中某一层中数值大于 0 的参数

torch.masked_select(input, mask, out=None)

input 表示待处理的 Tensor。mask 代表掩码张量,也就是满足条件的特征掩码。mask 须跟 input 张量有相同数量的元素数目,但形状或维度不需要相同。

A = torch.rand(5)
print(A)
B = torch.masked_select(A, A > 0.3)
print(B)

mask就是根据要筛选的条件,得到一个掩码张量,然后用这个张量去提取 Tensor 中的数据

输出:

tensor([0.2524, 0.4978, 0.9570, 0.0738, 0.5051])
tensor([0.4978, 0.9570, 0.5051])