PyTorch编程基础01--PyTorch中的张量

PyTorch是一个Python包,用于将数据封装成张量(Tensor)来进行运算。张量是向量和矩阵的推广。PyTorch中的张量就是元素为同一种数据类型的多维矩阵。

目录

1.定义张量的方法

1.1将已有数值转换成张量

函数torch.tensor()可以将传入的对象转换成张量。该函数不仅支持Python中的原生类型,还支持Numpy类型。

import torch
import numpy as np
a = torch.tensor(5)
print(a)  # 输出:tensor(5)
anp = np.asarray([4])
a = torch.tensor(anp)
print(a)  # 输出:tensor([4]. dtype=torch.int32)

1.2根据指定形状、类型生成张量

张量类型函数dtype
系统的默认类型torch.Tensor()torch.float32 or torch.float
浮点型torch.FloatTensor()torch.float32 or torch.float
整型torch.IntTensor()torch.int32 or torch.int
双精度浮点型torch.DoubleTensor()torch.float64 or torch.double
长整型torch.LongTensor()torch.int64 or torch.long
字节型torch.ByteTensor()torch.uint8
字符型torch.CharTensor()torch.int8
短整型torch.ShortTensor()torch.int16 or torch.short

如果没有特殊要求,则直接用函数torch.Tensor()定义的张量是32位浮点型。这与调用torch.FloatTensor()函数定义张量的效果是一样的。
张量类型是由PyTorch中的默认类型来控制的。也可以修改用torch.Tensor生成的张量类型

import torch
print(torch.get_default_dtype())  # 输出默认类型:torch.float32
print(torch.Tensor([1, 3]).dtype)  # 输出torch.Tensor()函数返回的类型:torch.float32
torch.set_default_dtype(torch.float64)
print(torch.get_default_dtype())  # 输出默认类型:torch.float64
print(torch.Tensor([1, 3]).dtype)  # 输出torch.Tensor()函数返回的类型:torch.float64

在使用上表中的函数定义张量时,可以指定张量的形状,也可以指定张量的内容。以torch.Tensor()函数为例

a = torch.Tensor(2)  # 定义一个指定形状的张量
print(a)  # 输出:tensor([1.1210e-43, 4.7265e-01])
b = torch.Tensor(1, 2)  # 定义一个指定形状的张量
print(b)  # 输出:tensor([-1.4754e+04, 4.5909e-41])
c = torch.Tensor([2]) # 定义一个指定内容的张量
print(c)  # 输出:tensor([2.])
d = torch.Tensor([1, 2]) # 定义一个指定内容的张量
print(d)  # 输出:tensor([1., 2.])

注意:在以指定形状的方式调用torch.Tensor()函数时,得到的张量是没有初始化的。


1.3根据指定形状生成固定值的张量

  • torch.ones()函数生成指定形状、值为1的张量数组
  • torch.zeros()函数生成指定形状、值为0的张量数组
  • torch.ones_like()函数生成与目标张量形状相同、值为1的张量数组
  • torch.zeros_like()函数生成与目标张量形状相同、值为0的张量数组
  • torch.eye()函数生成对角矩阵的张量
  • torch.full()函数生成全为1的矩阵的张量

2.生成随机值张量

2.1设置随机值种子

所有的随机数都是基于种子参数进行生成的。使用torch.initial_seed()函数可以查看当前系统中的随机数种子,使用torch.manual_seed()函数可以设置随机数种子

torch.initial_seed()  # 输出:1
torch.manual_seed(2)
torch.initial_seed()  # 输出:2

2.2通过指定形状生成随机值

  • torch.rand()函数生成指定形状的随机值,随机区间[0,1)
  • torch.randn()函数生成指定形状的随机值,随机值的分布式为均值为0,方差为1
  • torch.randint(low, high, size)函数生成指定形状的随机值,随机区间[low, high)

2.3生成线性空间的随机值

print(torch.arange(1, 10, step=2))  # 输出:tensor([1, 3, 5, 7, 9])
print(torch.linspace(1, 9, steps=5))  # 输出:tensor([1, 3, 5, 7, 9])
  • arange():取值范围只包括起始值,不包括结束值,通过步长来控制取值的个数
  • linespace():取值范围既包括起始值,又包括结束值,可以直接指定取值的个数

2.4生成对数空间的随机值

torch.logspace()函数

print(torch.logspace(1, 9, steps=5))  # 输出:rensor([1.0000e+01, 1.0000e+03, 1.0000e+05, 1.0000e+07, 1.0000e+09])

2.5生成未初始化的矩阵

torch.empty()函数

print(torch.empty(1, 2))  # 输出:tensor([[6.9518e-310, 0.0000e+00]])

3.张量的操作

3.1获得张量中元素的个数

torch.numel()函数

a = torch.Tensor(2)
print(torch.numel(a))  # 获得a中元素的个数,输出:2

3.2张量的判断

torch.is_tensor()函数

a = torch.Tensor(2)
print(torch.is_tensor(a))  # 判断a是否为张量,输出:True

3.3张量的类型转换

type()方法

a = tensor.FloatTensor([4])
print(a.type(torch.IntTensor))  # 输出:tensor([4], dtype=torch.int32)
print(a.type(torch.DoubleTensor))  # 输出:tensor([4.], dtype=torch.float64)

3.4张量类中的重载操作符函数

张量之间的运算于Python的基本运算完全一样,除此之外,在张量类中还有很多其他的运算函数。

a = torch.FloatTensor([4])
b = torch.add(a, a)
print(b)  # 输出:tensor([8.])
torch.add(a, a, out=b)
print(b)  # 输出:tensor([8.])

注意:在以指定输出的方式调用运算函数时,需要确保输出变量已经定义,否则会报错


3.5张量类中的自变化运算符

自变化运算函数是指,在变量本身的基础上做运算,其结果直接作用在变量自身。

a.add_(b)
print(b)  # 输出:tensor([12.])

注意:在PyTorch中,所有的自变化运算都会带有一个下划线


3.6张量与Numpy间的相互转换

a = torch.FloatTensor([4])
print(a.numpy())  # 将张量转换成Numpy类型的对象,输出:[4.]
anp = np.asarray([4])
# 将Numpy类型的对象转换成张量,两种方法
print(torch.from_numpy(anp))
print(torch.tensor(anp))  # 输出:tensor([4], dtype=torch.int32)

张量与Numpy类型数据的转换是基于零复制技术实现的。在转换过程中,张量与Numpy数值对象共享同一个内存区域,张量会保留一个指向内部Numpy数组的指针,而不是直接复制Numpy的值。


3.7张量与Numpy各自的形状获取

x = torch.rand(2, 1)
print(x.shape)  # 打印张量形状,输出:torch.Size([2 1])
print(x.size())  # 打印张量大小,输出:torch.Size([2 1])
anp = np.asarray([4, 2])
print(anp.shape, anp.size)  # 打印Numpy变量的形状和大小,输出:(2,) 2

二者也都可以通过reshape()属性函数进行变形

print(x.reshape([1, 2]).shape)  # 输出:torch.Size([1 2])
print(anp.reshape([1, 2]).shape)  # 输出:(1 2)

3.8张量与Numpy各自的切片操作

张量与Numpy的切片操作几乎完全一样

x = torch.rand(2, 1)
print(x[:])  # 输出:tensor([[0.1273], [0.3797]])
anp = np.asarray([4, 2])
print(anp[:])  # 输出:[4 2]

3.9张量与Numpy相互转换的陷阱

在将Numpy转换成向量时,只是简单地给指针赋值,并不会发生复制现象。然而这种快捷的方式会带来安全隐患:由于两个变量共享一块内存,所以一旦修改了其中一个变量,则会影响另一个变量的值。
其实PyTorch考虑到了这一点,在Numpy被转成张量后,如果对张量进行修改,则其内部会触发复制机制,额外开辟一块内存并将值复制过去,不会影响原来Numpy的值。
但是在将Numpy转换成张量后,如果对Numpy进行修改,则结果就不一样了,因为Numpy没有PyTorch这种共享内存的设置,这会导致在对Numpy修改时使得张量的值偷偷发生了变化

nparray = np.array([1, 1])
x = torch.from_numpy(nparray)
print(x)  # 输出:tensor([1, 1], dtype=toech.int32)
nparray += 1
print(x)  # 输出:tensor([2, 2], dtype=toech.int32)

需要又替换内存的运算操作

nparray = np.array([1, 1])
x = torch.from_numpy(nparray)
print(x)  # 输出:tensor([1, 1], dtype=toech.int32)
nparray = nparray + 1
print(x)  # 输出:tensor([1, 1], dtype=toech.int32)

因为nparray = nparray + 1表示系统会额外复制一份内存将nparray + 1的结果赋值给nparray变量,并没有在nparray的原有内存中进行改变。


4.在CPU和GPU控制的内存中定义张量

Pytorch默认将张量定义在CPU控制的内存中

4.1将CPU内存中的张量复制到GPU内存中

cuda()方法

a = torch.FloatTensor([4])
b = a.cuda()
print(b)  # 输出:tensor([4.], device='cuda:0')

同样,如果要将GPU中的张量创建到CPU中,需要cpu()方法

print(b.cpu())  # 输出:tensor([4.])

4.2直接在GPU内存中定义张量

通过调用函数torch.tensor()并指定device参数为cuda

a = torch.tensor([4], device="cuda")
print(q)  # 输出:tensor([4], device='cuda:0')

4.3使用to()方法来指定设备

将张量的cpu()和cuda()两种方法合并到一起,通过张量的to()方法来实现

a = torch.FloatTensor([4])
print(a)  # 输出:tensor([4.])
print(a.to("cuda:0"))  # 输出:tensor([4.], device='cuda:0')

在计算机中,多块GPU卡的编号是从0开始的。"cuda:0"是指使用计算机的第一块GPU卡


4.4使用环境变量CUDA_VISIBLE_DEVICES来指定设备

使用环境变量CUDA_VISIBLE_DEVICES来为代码指定所运行的设备,时最常见的方式。它不需要对代码中的各个变量依次设置,只需要在运行python程序时统一设置依次即可。
在命令行输入如下启动命令:

CUDA_VISIBLE_DEVICES=0 python 自己的代码.py

也可以在代码的最前端加入如下语句:

import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0"

5.张量间的数据操作

5.1用reshape()函数实现数据维度的变换

reshape()函数可以在保证张量矩阵数据不变的前提下改变数据的维度,使其转换成指定的形状。在神经网络的上下层连接时,经常会用到reshape()函数,用于调节数据的形状,使其与下层网络的输入相匹配。

a = torch.tensor([[1, 2], [3, 4]])
print(torch.reshape(a, (1, -1)))  # 将其转为只有一行数据的张量,输出:tensor([[1, 2, 3, 4]])

在使用reshape函数时,要求指定的目标形状必须与原有的输入张量的元素个数一致,否则会报错。在指定形状过程中,可以使用-1来代表该维度由系统自动计算。
另外,还可以调用张量的reshape()或view()方法

print(a.reshape((1, -1)))  # 将其转为只有一行数据的张量,输出:tensor([[1, 2, 3, 4]])
print(a.view((1, -1)))  # 将其转为只有一行数据的张量,输出:tensor([[1, 2, 3, 4]])

5.2用squeeze()函数实现数据的压缩

a = torch.tensor([[1, 2], [3, 4]])
print(torch.squeeze(torch.reshape(a, (1, -1))))  # 输出:tensor([1, 2, 3, 4])

squeeze()函数默认在变形过程中,将输入张量中所有值为1的维度去掉。如果一个张量中值为1的维度有很多,但又不想全部去掉,则可以在函数中通过设定dim参数去掉某一个维度(dim参数所指定的维度必须满足值为1)
如果要删掉一个不为1的维度,则可以使用torch.unbind()函数。
还有一个与torch.squeeze()函数功能相反的函数-----torch.unsqueeze(),它可以为输入张量增加一个值为1的维度。函数定义如下:
torch.unsqueeze(input, dim, out=None)
其中,dim参数用于指定要增加维度的位置,其默认值为1,即在索引维度为1的位置加1


5.3实现张量数据的矩阵转置

torch.t()和torch.transpose()函数都可以实现张量的矩阵转置运算,其中torch.t()函数使用起来比较简单,而torch.transpose()函数功能更为强大,但使用起来较为复杂

b = torch.tensor([[5, 6, 7], [2, 8, 0]])
print(torch.t(b))  # 输出:tensor([[5, 2], [6, 8], [7, 0]])
print(torch.transpose(b, dim0=1, dim1=0))  # 输出:tensor([[5, 2], [6, 8], [7, 0]])

可以看到,torch.transpose()函数接收两个参数----dim0和dim1,分别用于指定原始的维度和转换后的目标维度。
另外,还可以使用张量的permute()方法实现转置:
b.permute(1, 0)
注意:permute可以处理超过二维


5.4view()方法与contiguous()方法

view()方法比reshape()方法更底层,也更不智能,只能作用于整块内存中的张量。
在PyTorch中,有些张量是由不同的数据块组成的,它们并没有分布在相同的整块内存中。view()方法无法对这样的张量数据进行变形处理。同样,view()方法也无法对已经用过transpose()、permute()等方法改变形状后的张量进行变形处理。通过张量的is_contiguous()方法可以判断该张量的内存是否连续。
如果要使用view()方法,则最好和contiguous()方法一起使用。contiguous()方法可以把张量复制到连续的整块内存中。

b = torch.tensor([[5, 6, 7], [2, 8, 0]])
print(b.is_contiguous())  # 输出:True
c = b.transpose(1, 0)
print(c.is_contiguous())  # 输出:False
print(c.contiguous().is_contiguous())  # 输出:True
print(c.contiguous().view(-1))  # 输出:tensor([5, 2, 6, 8, 7, 0])

5.5用torch.cat()函数实现数据连接

用torch.cat()函数可以将两个张量数据按照指定的维度连接起来。

a = torch.tensor([[1, 2], [3, 4]])
b = torch.tensor([[5, 6], [7, 8]])
print(torch.cat([a, b], dim=0))  # 输出:tensor([[1, 2], [3, 4], [5, 6], [7, 8]])
print(torch.cat([a, b], dim=1))  # 输出:tensor([[1, 2, 5, 6], [3, 4, 7, 8]])

还可以使用torch.stack()函数对列表中的多个元素进行合并。该函数于cat()的作用非常相似,只不过要求列表中的张量元素的维度必须一致。


5.6用torch.chunk()函数实现数据均匀分割

用torch.chunk()函数可以将一个多维张量按照指定的维度和拆分个数进行分割。chunks
参数用于指定拆分的个数,dim参数用于指定连接的维度,返回值是一个元组类型。

a = torch.tensor([[1, 2], [3, 4]])
print(torch.chunk(a, chunks=2, dim=0))  # 输出:(tensor([[1, 2]]), tensor([[3, 4]]))
print(torch.chunk(a, chunks=2, dim=1))  # 输出:(tensor([[1], [3]]), tensor([[2], [4]]))

5.7用torch.split()函数实现数据不均匀分割

b = torch.tensor([[5, 6, 7], [2, 8, 0]])
print(torch.split(b, split_size_or_sections=(1, 2), dim=1))  # 输出:(tensor([[5], [2]]), tensor([[6, 7], [8, 0]]))

注意:split_size_or_sections参数为一个具体的数值,代表系统将按照指定的元素个数对张量数据进行拆分。在分割过程中,“不满足指定个数的剩余数据”将被作为分割数据的最后一部分。例如:
torch.split(b, split_size_or_sections=2, dim=1) # 输出:(tensor([[5, 6], [2, 8]]), tensor([[7], [0]]))


5.8用torch.gather()函数对张量数据进行检索

torch.gather()函数与TensorFlow中的gather()函数意义相近,但用法截然不同。该函数的作用是,使张量数据中的值按照指定的索引和顺序进行排列。

b = torch.tensor([[5, 6, 7], [2, 8, 0]])
print(torch.gather(b, dim=1, index=torch.tensor([[1, 0], [1, 2]])))  # 输出:tensor([[6, 5], [8, 0]])
print(torch.gather(b, dim=0, index=torch.tensor([[1, 0, 0]])))  # 输出:tensor([[2, 6, 7]])

在torch.gather()函数中,index参数必须是张量类型,而且要与输入的维度相同。index参数中的值是输入数据中的索引。
如果要从多维张量中取出整行或整列的数据,则可以使用torch.index_select()函数。具体如下:
torch.index_select(b, dim=0, index=torch.tensor(1)) # 输出:tensor([[2, 8, 0]])
其中,index参数可以是一个一维数组,代表所选取数据的索引值。


5.9按照指定的阈值对张量进行过滤

可以通过PyTorch中的逻辑比较函数和掩码取值函数(torch.masked_select())来实现。

a = torch.tensor([[1, 2], [3, 4]])
mask = a.ge(2)  # 找出大于或等于2的数
print(mask)  # 输出掩码,输出:tensor([[0, 1], [1, 1]], dtype=torch.unit8)
print(torch.masked_select(a, mask))  # 按照掩码取值,输出:tensor([2, 3, 4])

常见的逻辑比较函数有:大于(gt())、大于或等于(ge())、等于(eq())、小于(lt())、小于或等于(le())、不等(ne())


5.10用torch.nonzero()函数找出张量中的非0值索引

eye = torch.eye(3)
print(eye)  # 输出:tensor([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
print(torch.nonzero(eye))  # 输出:tensor([[0, 0], [1, 1], [2, 2]])

5.11用torch.where()函数根据条件对多个张量取值

b = torch.tensor([[5, 6, 7], [2, 8, 0]])
c = torch.ones_like(b)
print(c)  # 输出:tensor([[1, 1, 1], [1, 1, 1]])
print(torch.where(b>5, b, c))  # 将b中值大于5的取出,否则取c中的值

5.12用torch.clamp()函数根据阈值进行数据截断

该功能常用在梯度的计算过程中:通过一个固定的阈值限制梯度的变化,从而避免出现训练过程中的“梯度爆炸”(模型每次训练的调整至都变得很大,从而导致最终训练过程难以收敛)现象。

a = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(torch.clamp(a, min=2, max=5))  # 输出:tensor([[2, 2, 3], [4, 5, 5]])

5.13获取数据中最大值、最小值的索引

torch.argmax()函数用于返回最大值的索引,torch.argmin()函数用于返回最小值索引。

a = torch.tensor([[1, 2], [3, 4]])
print(torch.argmax(a, dim=0))  # 输出:tensor([1, 1])
print(torch.argmin(a, dim=1))  # 输出:tensor([0 ,0])

还可以使用功能更为强大的torch.max()和torch.min()函数,这两个函数在输出张量数据中最大值、最小值的同时,还会输出其对应的索引。

a = torch.tensor([[1, 2], [3, 4]])
print(torch.max(a, dim=0))  # 输出:(tensor([3, 4]), tensor([1, 1]))
print(torch.min(a, dim=1))  # 输出:(tensor([1, 3]), tensor([0 ,0]))
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值