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数组
下面就对如上的图片进行处理
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)
最终得到的图片:
深拷贝(副本)与浅拷贝(视图)
还有一种方式获得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])