深度学习主流经典框架PyTorch(day2)

五、Tensor数据转换

5.1张量转numpy

浅拷贝

调用numpy()方法可以把Tensor转换为Numpy,此时内存是共享的。

    #张量转numpy
    data_tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
    data_numpy = data_tensor.numpy()
    print(type(data_tensor), type(data_numpy))
    #他们内存是共享的
    data_numpy[0, 0] = 100
    print(data_tensor, data_numpy)

输出结果

 
tensor([[100,   2,   3],
        [  4,   5,   6]]) 
[[100   2   3]
 [  4   5   6]]

深拷贝

使用copy()方法可以避免内存共享:

    #张量转numpy
    data_tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])

    #使用copy()避免内存共享
    data_numpy = data_tensor.numpy().copy()
    print(type(data_tensor), type(data_numpy))

    #此时他们内存是不共享的
    data_numpy[0, 0] = 100
    print(data_tensor, data_numpy)

运行结果

 
tensor([[1, 2, 3],
        [4, 5, 6]]) 
[[100   2   3]
 [  4   5   6]]

 5.2numpy转张量

浅拷贝

from_numpy方法转Tensor默认是内存共享的

    #numpy转张量
    data_numpy = np.array([[1, 2, 3], [4, 5, 6]])
    data_tensor = torch.from_numpy(data_numpy)
    print(type(data_tensor), type(data_numpy))

    #他们内存是共享的
    data_tensor[0, 0] = 100
    print(data_tensor, data_numpy)

运行结果

 
tensor([[100,   2,   3],
        [  4,   5,   6]], dtype=torch.int32) [[100   2   3]
 [  4   5   6]]

深拷贝

使用传统的torch.tensor()则内存是不共享的~

    #numpy转张量
    data_numpy = np.array([[1, 2, 3], [4, 5, 6]])
    data_tensor = torch.tensor(data_numpy)
    print(type(data_tensor), type(data_numpy))

    #内存是不共享的
    data_tensor[0, 0] = 100
    print(data_tensor, data_numpy)

运行结果

 
tensor([[100,   2,   3],
        [  4,   5,   6]], dtype=torch.int32) 
[[1 2 3]
 [4 5 6]]

六、Tensor常见操作

在深度学习中,Tensor是一种多维数组,用于存储和操作数据,我们需要掌握张量各种运算。

6.1获取元素值

我们可以把单个元素tensor通过item()转换为Python数值,这是非常常用的操作

    x = torch.tensor(100)
    print(x)
    n = x.item()
    print(n, type(n))

    x2 = torch.tensor([100])
    print(x2, x2.item())
    #超过一个元素数据的张量无法通过item直接获取内部的数值

运行结果

tensor(100)
100 
tensor([100]) 100

注意:

  • 和Tensor的维度没有关系,都可以取出来!

  • 如果有多个元素则报错;

6.2元素值运算

常见的加减乘除次方取反开方等各种操作,带有_的方法则会替换原始值。

    x = torch.tensor([1, 2, 3])
    y1 = x + 100
    y2 = x * 2
    y3 = x % 2
    print(y1, y2, y3)
    # 张量之间的运算,张量与标量之间的运算,都是广播机制

    z1 = x.add(20)
    z2 = x.add_(30)
    print(z1, z2)#注意add和add_的区别,add_是inplace操作,直接修改x的值,add是返回一个新的张量

运行结果

tensor([101, 102, 103]) tensor([2, 4, 6]) tensor([1, 0, 1])
tensor([21, 22, 23]) tensor([31, 32, 33])

6.3阿达玛积

阿达玛积指的是矩阵对应位置的元素相乘,可以使用mul函数或者*来实现

    x = torch.tensor([1, 2, 3])
    y = torch.tensor([4, 5, 6])
    z1 = x*y #形状不一致,无法进行阿达玛积 # 效果和x.mul(y)一样
    z2 = torch.dot(x, y)#效果和x.dot(y)一样
    print(z1, z2)

运行结果

tensor([ 4, 10, 18]) tensor(32)

6.4Tensor相乘

点积运算将两个向量映射为一个标量,是向量之间的基本操作。

点积运算要求如果第一个矩阵的shape是 (N, M),那么第二个矩阵 shape必须是 (M, P),最后两个矩阵点积运算的shape为 (N, P)。

使用@或者matmul完成Tensor的乘法。

mm方法也可以用于矩阵相乘 但是只能用于2维矩阵即:m*k和k*n 得到m*n 的矩阵

    x = torch.tensor([[1, 2, 3], [4, 5, 6]])
    y = torch.tensor([[7, 8], [9, 10], [11, 12]])
    z = x@y#效果和x.matmul(y)一样,还有和x.mm(y)也一样,不过只适用到二维张量
    z2 = x.matmul(y)
    print(z, z2)

运行结果

tensor([[ 58,  64],
        [139, 154]]) 
tensor([[ 58,  64],
        [139, 154]])

6.5索引操作

掌握张量的花式索引在处理复杂数据时非常有用。花式索引可以让你灵活地访问、修改张量中的特定元素或子集,从而简化代码并提高操作效率。和numpy的索引类似。

6.5.1简单索引

索引,就是根据指定的下标选取数据

    torch.manual_seed(42)
    x = torch.randint(10, 30, (3, 4))

    #下标是数字
    print(x)
    print(x[0])#取到的数据,依然是张量 除非调用item()方法取得数据

    print(x[0][1])
    print(x[0][1].item())

    x2 = torch.randint(10, 30, (4, 3, 3))
    print(x2)
    print(x2[0,1,2])

    #下标是范围
    x3 = torch.randint(10, 30, (4, 3))
    print(x3)
    print(x3[1:3, 1:3])

运行结果

tensor([[12, 17, 26, 24],
        [16, 25, 10, 14],
        [20, 23, 28, 24]])
tensor([12, 17, 26, 24])
tensor(17)
17
tensor([[[20, 24, 21],
         [22, 25, 25],
         [17, 16, 19]],

        [[26, 23, 21],
         [29, 23, 11],
         [19, 17, 29]],

        [[22, 10, 25],
         [19, 23, 14],
         [29, 26, 22]],

        [[20, 16, 12],
         [27, 29, 17],
         [13, 13, 24]]])
tensor(25)
tensor([[23, 27, 20],
        [19, 20, 29],
        [16, 29, 25],
        [14, 28, 18]])
tensor([[20, 29],
        [29, 25]])

6.5.2列表索引

 使用list批量的制定要索引的元素位置,此时注意list的维度!

    x = torch.randint(1, 12, (4, 3))
    print(x)
    print(x[[1, 1, 2]])
    print(x[[1, 1, 2], [1, 2, 1]])#索引的维度必须与张量维度一致, 位置一一对应,类似于查找坐标

运行结果

tensor([[ 2,  2, 11],
        [ 7,  7,  8],
        [ 8,  6,  3],
        [ 7,  9,  4]])
tensor([[7, 7, 8],
        [7, 7, 8],
        [8, 6, 3]])
tensor([7, 8, 6])

6.5.3布尔索引

根据条件选择张量中的元素

    x = torch.randint(1, 12, (4, 3))
    print(x)
    print(x > 5)
    tensor = torch.tensor([[True, False, False],
                          [True, True, False],
                           [False, False, False],
                           [True, False, False]])
    print(x[tensor])#返回值 是一维,布尔数组中为true的位置的元素就取出来

运行结果

tensor([[ 5,  5,  6],
        [ 6, 10,  2],
        [ 3, 11,  1],
        [ 8,  6,  4]])
tensor([[False, False,  True],
        [ True,  True, False],
        [False,  True, False],
        [ True,  True, False]])
tensor([ 5,  6, 10,  8])

6.5.4索引赋值

使用花式索引轻松进行批量元素值修改~

    data = torch.eye(4)
    print(data)
    # 赋值
    data[:, 1:-1] = 0
    print(data)

运行结果

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

6.6张量拼接

在 PyTorch 中,cat 和 stack 是两个用于拼接张量的常用操作,但它们的使用方式和结果略有不同:

  • cat:在现有维度上拼接,不会增加新维度。

  • stack:在新维度上堆叠,会增加一个维度。

 6.6.1 torch.cat

元素级别的,torch.cat(concatenate 的缩写)用于沿现有维度拼接张量。换句话说,它在现有的维度上将多个张量连接在一起。

注意:要拼接的张量在除了指定拼接的维度之外的所有维度上的大小必须相同。

    x1 = torch.randint(1,10,(2,3))
    x2 = torch.randint(1,10,(1,3))
    print(x1)
    print(x2)
    x3 = torch.cat([x1,x2],dim=0)#0表示按行拼接(1表示按列拼接)列不一样,就会报错
    print(x3)

运行结果

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

6.6.2 torch.stack

张量级别的,torch.stack 用于在新维度上拼接张量。换句话说,它会增加一个新的维度,然后沿指定维度堆叠张量。

注意:要堆叠的张量必须具有相同的形状。 技巧:,堆叠指,沿着某个维度一人出一个交替添加 拼接指一人出完下个人在出完

    x1 = torch.randint(1,10,(2,3))
    x2 = torch.randint(1,10,(2,3))
    print(x1)
    print(x2)
    x3 = torch.stack([x1,x2],dim=0)#0表示按行堆叠(1表示按列堆叠)列不一样,就会报错
    print(x3)
    x4 = torch.stack([x1,x2],dim=1)#按列堆叠,2维张量
    print(x4)
    x5 = torch.stack([x1,x2],dim=2)#按深度堆叠,3维张量
    print(x5)

运行结果

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

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

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

        [[6, 7],
         [4, 7],
         [9, 5]]])

6.7形状操作

在 PyTorch 中,张量的形状操作是非常重要的,因为它允许你灵活地调整张量的维度和结构,以适应不同的计算需求。

6.7.1 reshape

可以用于将张量转换为不同的形状,但要确保转换后的形状与原始形状具有相同的元素数量。

    x = torch.randint(1, 10, (4,3))
    print(x)

    x2 = x.reshape(6,2)
    print(x2)

    x3 = x.reshape(2,2,3)
    print(x3)

运行结果

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

        [[1, 4, 1],
         [7, 7, 8]]])

6.7.2 view

view进行形状变换的特征:

  • 张量在内存中是连续的;

  • 返回的是原始张量视图,不重新分配内存,效率更高;

  • 如果张量在内存中不连续,view 将无法执行,并抛出错误。

    x = torch.randint(1, 10, (4,3))
    x2 = x.view(2, 6)# view和reshape的区别:view是按内存地址进行修改,reshape是重新分配内存
    print(x)
    print(x2)

运行结果

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

我们在进行变形或转置操作时,很容易造成内存的不连续性。可以通过tensor.is_contiguous()来判断内存是否连续。

view:高效,但需要张量在内存中是连续的;

reshape:更灵活,但涉及内存复制;

6.7.3转置

transpose 用于交换张量的两个维度,注意,是2个维度,它返回的是原张量的视图。

    x = torch.randint(1, 10, (2,3))#第一个维度有2个元素,第二个维度有3个元素
    print(x)
    x2 = x.transpose(0,1)# 0,1代表行和列,把0维和1维进行交换
    print(x2)
    x3 = x.t()#t()是特殊的转置,数学上的转置,维数小于等于2维
    print(x3)
    x4 = torch.randint(1, 10, (2, 3, 4, 5))
    print(x4.shape)
    x5 = x4.transpose(1,2)
    print(x5.shape)
    print(x5.is_contiguous())

运行结果

tensor([[8, 1, 4],
        [3, 3, 9]])
tensor([[8, 3],
        [1, 3],
        [4, 9]])
tensor([[8, 3],
        [1, 3],
        [4, 9]])
torch.Size([2, 3, 4, 5])
torch.Size([2, 4, 3, 5])
False

6.7.4 permute

permute 用于改变张量的所有维度顺序。与 transpose 类似,但它可以交换多个维度。

    x = torch.randint(1, 10, (2,3,4,5))
    print(x.shape)
    x2 = x.permute(3,0,2,1)#permute是交换维度 3,0,2,1代表把第0维和第3维交换,第1维和第2维交换
    print(x2.shape)#维度不能重复

运行结果

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

6.7.5 flatten

flatten 用于将张量展平为一维向量。

tensor.flatten(start_dim=0, end_dim=-1)
  • start_dim:从哪个维度开始展平。

  • end_dim:在哪个维度结束展平。默认值为 -1,表示展平到最后一个维度。

    x = torch.randint(1, 10, (8,4,10,9))
    print(x.shape)
    x2 = x.flatten()
    print(x2.shape)
    x3 = x.flatten(start_dim=1, end_dim=2)
    print(x3.shape)

运行结果

torch.Size([8, 4, 10, 9])
torch.Size([2880])
torch.Size([8, 40, 9])

6.7.5升维

    x = torch.randint(1, 10, (1,5))
    print(x.shape)
    x2 = torch.unsqueeze(x,-1)
    print(x2.shape, x2)

    y1 = torch.randint(1, 10, (1,5))
    y2 = torch.unsqueeze(y1.squeeze(), 1)
    print(y2)

运行结果

torch.Size([1, 5])
torch.Size([1, 5, 1]) tensor([[[4],
         [2],
         [3],
         [5],
         [7]]])
tensor([[8],
        [8],
        [3],
        [7],
        [8]])

6.7.6降维

    x = torch.randint(1, 10, (2,3,2,1,4))
    print(x.shape)
    x2 = x.squeeze()
    print(x2.shape)

运行结果

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

6.8张量分割

可以按照指定的大小或者块数进行分割。

6.8.1chunk分割

    x = torch.randint(1, 10, size=(21,))
    print(x)

    res = x.chunk(4)
    print(res)

    x = torch.randint(1, 10, size=(4,5,2))#在指定维上切,默认切在第0维上
    res = x.chunk(5, dim=1)#5代表切5份,dim=1代表切在第1维上
    print(res)

运行结果

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

        [[5, 5]],

        [[3, 6]],

        [[5, 9]]]), tensor([[[4, 9]],

        [[8, 6]],

        [[9, 2]],

        [[5, 2]]]), tensor([[[1, 7]],

        [[2, 6]],

        [[1, 1]],

        [[7, 1]]]), tensor([[[3, 8]],

        [[3, 4]],

        [[9, 4]],

        [[3, 3]]]), tensor([[[4, 7]],

        [[8, 3]],

        [[2, 1]],

        [[1, 3]]]))

6.8.2split分割

    x = torch.randint(1, 10, size=(21,))#
    res = x.split(4)#4代表切4份,dim=1代表切在第1维上
    print(res)

    x = torch.randint(1, 10, size=(2,4,3))
    res = x.split(3, dim=1)
    for el in res:
        print(el.shape)#第一次torch.Size([2, 3, 3]), 第二次torch.size([2, 1, 3])

运行结果

(tensor([6, 7, 1, 1]), tensor([9, 6, 5, 1]), tensor([6, 7, 1, 1]), tensor([4, 8, 5, 3]), tensor([8, 6, 9, 4]), tensor([1]))
torch.Size([2, 3, 3])
torch.Size([2, 1, 3])

6.9广播机制

广播机制需要遵循以下规则:

  • 每个张量的维度至少为1

  • 满足右对齐

    x = torch.randint(1, 10, (2,1,3))
    print(x)
    x2 = torch.randint(1, 10, (2,5,3))
    print(x2)
    x3 = x + x2
    print(x3)

    x4 = torch.randint(1, 10, (3,1))
    x5 = torch.randint(1, 10, (3,1,3))
    x6 = x4 + x5
    print(x6.shape)#广播机制是根据两个张量维度进行广播的,双向奔赴,所以输出的维度是(3,3,3)
    #参与广播机制的维度最小的维度只能是1维,其他维度可以不进行广播

运行结果

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

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

        [[3, 8, 7],
         [9, 9, 3],
         [9, 9, 6],
         [3, 8, 3],
         [4, 4, 4]]])
tensor([[[10, 11,  8],
         [ 7, 10,  8],
         [ 8,  6,  7],
         [10, 12, 11],
         [ 9,  8,  3]],

        [[ 6, 16, 13],
         [12, 17,  9],
         [12, 17, 12],
         [ 6, 16,  9],
         [ 7, 12, 10]]])
torch.Size([3, 3, 3])

2D 和 3D 张量广播

广播机制会根据需要对两个张量进行形状扩展,以确保它们的形状对齐,从而能够进行逐元素运算。广播是双向奔赴的。

6.10数学运算

  • floor: 向下取整;

  • ceil:向上取整;

  • round:四舍五入;

  • trunc:裁剪,只保留整数部分;

  • frac:只保留小数部分;

  • fix:向零方向舍入;

  • %:取余。

  • abs 取绝对值

代码示例

    x = torch.tensor([1,2,3,3.14,3.50,4.50,4.9,-2.5])
    print(x.ceil(),x)#向上取整,x不变
    print(x.floor(),x)#向下取整,x不变
    print(x.round(),x)#四舍五入,x不变
    print(x.trunc(),x)#取整,x不变,只是整数部分
    print(x.frac(),x)#取小数部分,x不变
    print(x.fix()) #向0方向取整数部分,x不变
    print(x.abs())#取绝对值,x不变

运行结果

tensor([ 1.,  2.,  3.,  4.,  4.,  5.,  5., -2.]) tensor([ 1.0000,  2.0000,  3.0000,  3.1400,  3.5000,  4.5000,  4.9000, -2.5000])
tensor([ 1.,  2.,  3.,  3.,  3.,  4.,  4., -3.]) tensor([ 1.0000,  2.0000,  3.0000,  3.1400,  3.5000,  4.5000,  4.9000, -2.5000])
tensor([ 1.,  2.,  3.,  3.,  4.,  4.,  5., -2.]) tensor([ 1.0000,  2.0000,  3.0000,  3.1400,  3.5000,  4.5000,  4.9000, -2.5000])
tensor([ 1.,  2.,  3.,  3.,  3.,  4.,  4., -2.]) tensor([ 1.0000,  2.0000,  3.0000,  3.1400,  3.5000,  4.5000,  4.9000, -2.5000])
tensor([ 0.0000,  0.0000,  0.0000,  0.1400,  0.5000,  0.5000,  0.9000, -0.5000]) tensor([ 1.0000,  2.0000,  3.0000,  3.1400,  3.5000,  4.5000,  4.9000, -2.5000])
tensor([ 1.,  2.,  3.,  3.,  3.,  4.,  4., -2.])
tensor([1.0000, 2.0000, 3.0000, 3.1400, 3.5000, 4.5000, 4.9000, 2.5000])

三角函数

  • torch.cos(input,out=None)

  • torch.cosh(input,out=None) # 双曲余弦函数

  • torch.sin(input,out=None)

  • torch.sinh(input,out=None) # 双曲正弦函数

  • torch.tan(input,out=None)

  • torch.tanh(input,out=None) # 双曲正切函数

统计学函数

  1. torch.mean(): 计算张量的平均值。

  2. torch.sum(): 计算张量的元素之和。

  3. torch.std(): 计算张量的标准差。

  4. torch.var(): 计算张量的方差。

  5. torch.median(): 计算张量的中位数。

  6. torch.max(): 计算张量的最大值。

  7. torch.min(): 计算张量的最小值。

  8. torch.sort(): 对张量进行排序。

  9. torch.topk(): 返回张量中的前 k 个最大值或最小值。

  10. torch.histc(): 计算张量的直方图。

  11. torch.unique(): 返回张量中的唯一值。

  12. torch.bincount(): 计算张量中每个元素的出现次数。

你可能感兴趣的:(深度学习,pytorch,人工智能)