动手学深度学习-预备知识-数据操作

发布时间 2023-11-07 09:30:14作者: AndreaDO

动手学深度学习,笔记

第一章:预备知识

第一节数据操作:

(1)入门操作:

1.首先导入torch库,我们使用pytorch主要使用这个库的函数

import torch

张量表示一个由数值组成的数组,这个数组可能有多个维度。具有一个轴的张量对应数学上的向量(vector); 具有两个轴的张量对应数学上的矩阵(matrix);具有两个轴以上的张量没有特殊的数学名称。

我们来创建一个张量,可以使用 arange 创建一个行向量 x。

这个行向量包含以0开始的前12个整数,它们默认创建为整 数。也可指定创建类型为浮点数。张量中的每个值都称为张量的 元素(element)。

x = torch.arange(12)
print(x)
## tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

可以通过张量的shape属性来访问张量(沿每个轴的长度)的形状

print(x.shape)
#结果:torch.Size([12])

如果只想知道张量中元素的总数,即形状的所有元素乘积,可以检查它的大小(size)。因为这里在处理的是 一个向量,所以它的shape与它的size相同。

print(x.numel())
#12

 要想改变一个张量的形状而不改变元素数量和元素值,可以调用reshape函数。例如,可以把张量x从形状为 (12,)的行向量转换为形状为(3,4)的矩阵。这个新的张量包含与转换前相同的值,但是它被看成一个3行4列 的矩阵。要重点说明一下,虽然张量的形状发生了改变,但其元素值并没有变。注意,通过改变张量的形状, 张量的大小不会改变。

X = x.reshape(3, 4)
print(X)

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

有时,我们希望使用全0、全1、其他常量,或者从特定分布中随机采样的数字来初始化矩阵。我们可以创建 一个形状为(2,3,4)的张量,其中所有元素都设置为0。代码如下:

x2 = torch.zeros((2, 3, 4))
print(x2)
'''
tensor([[[0., 0., 0., 0.],
         [0., 0., 0., 0.],
         [0., 0., 0., 0.]],

        [[0., 0., 0., 0.],
         [0., 0., 0., 0.],
         [0., 0., 0., 0.]]])
'''

同样,我们可以创建一个形状为(2,3,4)的张量,其中所有元素都设置为1。代码如下:

x3 = torch.ones((2, 3, 4))
print(x3)
# tensor([[[1., 1., 1., 1.],
#          [1., 1., 1., 1.],
#          [1., 1., 1., 1.]],

#         [[1., 1., 1., 1.],
#          [1., 1., 1., 1.],
#          [1., 1., 1., 1.]]])

我们想要随机获取一些值,以下代码创建一个形状为(3,4)的张量。其中的 每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样。

x4 = torch.randn(3, 4)
print(x4)
# tensor([[-0.2421, -1.4333,  0.1154, -0.2187],
#         [-0.6590, -0.1766,  1.9008, -0.7222],
#         [ 1.2284, -0.5664,  0.6310, -1.3181]])

创建确定的张量内容,为张量内每个元素确定值

x5 = torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(x5)
# tensor([[1, 2, 3],
#        [4, 5, 6],
#        [7, 8, 9]])

(2)运算符

我们可以对张量进行加减乘除和幂运算

import torch

x = torch.tensor([1.0, 2, 4, 8])
y = torch.tensor([2, 2, 2, 2])

print("x+y=", x + y)
print("x-y=", x - y)
print("x*y=", x * y)
print("x/y=", x / y)
print("x^y=", x**y)
# x+y= tensor([ 3.,  4.,  6., 10.])
# x-y= tensor([-1.,  0.,  2.,  6.])
# x*y= tensor([ 2.,  4.,  8., 16.])
# x/y= tensor([0.5000, 1.0000, 2.0000, 4.0000])
# x^y= tensor([ 1.,  4., 16., 64.])

使用自然对数e^(x)的函数

print("exp(x)=", torch.exp(x))
# exp(x)= tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03])

将两个张量进行拼接在一起,cat函数

比如(1,4)和(1,4)的张量,cat中dim=0,拼接后的张量1+1=2 shape=(2,4)

比如(1,4)和(1,4)的张量,cat中dim=1,拼接后的张量4+4=8 shape=(1,8)

x1 = torch.cat((x, y), dim=0)
x2 = torch.cat((x, y), dim=1)
print("dim = 0 ", x1)
print("dim = 1", x2)
print("cat前的大小 x.shape=", x.shape, "y.shape=", y.shape)
print("cat后的大小 x1.shape=", x1.shape, "x2.shape=", x2.shape)
# dim = 0  tensor([[1, 2, 4, 8],
#         [2, 2, 2, 2]])
# dim = 1 tensor([[1, 2, 4, 8, 2, 2, 2, 2]])
# cat前的大小 x.shape= torch.Size([1, 4]) y.shape= torch.Size([1, 4])
# cat后的大小 x1.shape= torch.Size([2, 4]) x2.shape= torch.Size([1, 8])

对于张量的逻辑操作

比如x==y ,x!=y

 x =[1, 2, 4, 8]
  y = [2, 2, 2, 2]
x1 = x == y
x2 = x != y
print("x==y", x1)
print("x!=y", x2)
# x==y tensor([[False,  True, False, False]])
# x!=y tensor([[ True, False,  True,  True]])

张量的降维,求和,计算张量的总和,变成一个标量 sum函数

x1 = x.sum()
print(x1)
print(type(x1))
# tensor(15)
# <class 'torch.Tensor'>

(3) 广播机制

 通过适当复制元素来扩展一个或者两个数组,以便转换之后,两个张量具有相同的形状,方便元素操作

例如x 和y张量 ,x张量复制出额外的列,元素012,变成3行2列。

y张量复制出额外的行,元素01,变成3行2列。

然后张量按照行列相加。

总而言之,矩阵x复制列,矩阵y复制行,然后按照元素相加。

x = torch.tensor([[0], [1], [2]])
y = torch.tensor([[0, 1]])

print(x + y)
# tensor([[0, 1], 
#         [1, 2], 
#         [2, 3]])

(4) 节省内存

在pytorch中进行新的操作会为新结果分配内存,例如,如果我们用Y =Y+1,我们将取消引用Y指向的张量, 而是指向新分配的内存处的张量。

import torch

Y = torch.tensor([[1], [2]])
before = id(Y)
Y = Y + 1
flag = id(Y) == before
print(flag)

这里的结果是False,表示内存位置不一样,Python的id()函数演示了这一点,它给我们提供了内存中引用对象的确切地址。运 行Y = Y + 1后,我们会发现id(Y)指向另一个位置。这是因为Python首先计算Y + 1,为结果分配新的内存, 然后使Y指向内存中的这个新位置。

这可能是不可取的,原因有两个: 1. 首先,我们不想总是不必要地分配内存。在机器学习中,我们可能有数百兆的参数,并且在一秒内多次 更新所有参数。通常情况下,我们希望原地执行这些更新; 2. 如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧 的参数。

执行原地址操作的代码

Z = torch.zeros_like(Y)
print(id(Z))
Z[:] = Y + 1
print(id(Z))
# 2104847078848
# 2104847078848

或者

before = id(Y)
Y += 1
print(before == id(Y))
# True

或者

before = id(Y)
Y[:] = Y + 1
print(before == id(Y))
# True

这样就会在原内存地址操作,减少内存的开销。