zoukankan      html  css  js  c++  java
  • 深度学习与Pytorch入门实战(二)Pytorch张量高阶操作

    笔记摘抄

    1. Broadcasting

    • Broadcasting能够实现 Tensor自动维度增加(unsqueeze)维度扩展(expand)

    • 使两个Tensor的shape一致,从而完成某些操作,主要步骤:

      • 从最后面的维度开始匹配(一般后面理解为小维度);

      • 在前面插入若干维度,进行unsqueeze操作;

      • 将维度的size从 1 通过expand变到 和 某个Tensor相同的维度。

    • 举例:

      • Feature maps:[4, 32, 14, 14]

      • Bias:[32, 1, 1](Tip:后面的两个1是手动unsqueeze插入的维度)-> [1, 32, 1, 1] -> [4, 32, 14, 14]

    为什么使用broadcasting?

    • 举例:

      • [class, students, scores]

      • Add bias for every students: +5 score

      • [4, 32, 8] + [4, 32, 8]

      • [4, 32, 8] + [1] ([1].unsqueeze(0).unsqueeze(0).expand_as(A): [1]->[1,1,1]->[4,32,8])

    • memory consumption (与repeat相比)

      • [4, 32, 8] => 1024

      • [5.0] => 1

    匹配规则(从最后面的维度开始匹配):(符合boardcast规则,则自动完成)

    • if current dim=1,expand to same

    • if either has no dim,insert one dim and expand to same

    • otherwise,NOT broadcasting-able

    A的维度[4, 32, 8],B的维度[1],[1]->[1, 1, 1]->[4, 32, 8],对应情况1

    A的维度[4, 32, 8],B的维度[8],[1]->[1, 1, 8]->[4, 32, 8],对应情况2

    A的维度[4, 3, 32, 32],B的维度[3, 1, 1], [3, 1, 1]->[1, 3, 1, 1]->[4,3,32,32]

    A的维度[4, 32, 8],B的维度[4],对应情况3,不能broadcasting

    2. 拼接与拆分

    2.1 cat拼接操作

    • 功能:通过dim指定维度,在当前指定维度上直接拼接 (concat)

    • 默认是dim=0

    • 指定的dim上,维度可以不相同,其他dim上维度必须相同,不然会报错

    a1=torch.rand(4,3,32,32)
    a2=torch.rand(5,3,32,32)
    print(torch.cat([a1,a2],dim=0).shape)    #torch.Size([9, 3, 32, 32])
    
    a3=torch.rand(4,1,32,32)
    print(torch.cat([a1,a3],dim=1).shape)    #torch.Size([4, 4, 32, 32])
    
    a4=torch.rand(4,3,16,32)
    print(torch.cat([a1,a4],dim=2).shape)    #torch.Size([4, 3, 48, 32])
    

    2.2 stack拼接操作

    • 与cat不同的是,stack是在拼接的同时,在指定dim处插入维度后拼接create new dim

    • stack需要 保证两个Tensor的shape是一致的,这就像是有两类东西,它们的其它属性都是一样的(比如男的一张表,女的一张表)。

    • 使用stack时候要指定一个维度位置,在那个 位置前 会插入一个新的维度

    • 因为是 两类东西 合并过来所以这个新的维度size是2,通过指定这个维度是0或者1来选择性别是男还是女。

    • 默认dim=0

    a1 = torch.rand(4,3,32,32)
    a2 = torch.rand(4,3,32,32)
    
    # 左边起第二个维度取0时,取上半部分即a1; 左边起第二个维度取1时,取下半部分即a2
    print(torch.stack([a1, a2],dim=1).shape)  # torch.Size([4, 2, 3, 32, 32])  
    
    print(torch.stack([a1,a2],dim=2).shape)  #torch.Size([4, 3, 2, 32, 32])
    
    a1 = torch.rand(4,3,16,32)
    a2 = torch.rand(4,3,16,32)
    
    print(torch.cat([a1, a2], dim=2).shape)    # torch.Size([4, 3, 32, 32])
    print(torch.stack([a1, a2], dim=2).shape)  # torch.Size([4, 3, 2, 16, 32])
    
    a = torch.rand(32, 8)
    b = torch.rand(32, 8)
    print(torch.stack([a, b], dim=0).shape)    # torch.Size([2, 32, 8])
    

    2.3 split分割操作

    • 指定拆分dim

    • 按长度拆分,给定拆分后的数据大小

    c = torch.rand(3,32,8)
    
    aa,bb = c.split([1,2],dim=0)
    print(aa.shape,bb.shape)            # torch.Size([1, 32, 8]) torch.Size([2, 32, 8])
    
    aa,bb,cc = c.split([1,1,1],dim=0)   # 或者写成aa,bb,cc=c.split(1,dim=0)
    print(aa.shape,bb.shape,cc.shape)   # torch.Size([1, 32, 8]) torch.Size([1, 32, 8]) torch.Size([1, 32, 8])
    

    2.4 chunk分割操作

    • chunk是在指定dim下 按个数拆分 ,给定平均拆分的个数

    • 如果给定个数不能平均拆分当前维度,则会取 比给定个数小的,能平均拆分数据的,最大的个数

    • dim默认是0

    c = torch.rand(3, 32, 8)
    d = torch.rand(2, 32, 8)
    
    aa, bb = c.chunk(2, dim=0)
    print(aa.shape, bb.shape)       # torch.Size([2, 32, 8]) torch.Size([1, 32, 8])
    
    aa, bb = d.chunk(2, dim=0)
    print(aa.shape, bb.shape)       # torch.Size([1, 32, 8]) torch.Size([1, 32, 8])
    

    3. 基本运算

    3.1 加减乘除

    • 加法(a+b、torch.add(a,b))

    • 减法(a-b、torch.sub(a,b))

    • 乘法(*、torch.mul(a,b))对应元素相乘

    • 除法(/、torch.div(a,b))对应元素相除,//整除

    a = torch.rand(3, 4)
    b = torch.rand(4)
    
    c1 = a + b
    c2 = torch.add(a, b)
    print(c1.shape, c2.shape)                # torch.Size([3, 4]) torch.Size([3, 4])
    print(torch.all(torch.eq(c1, c2)))       # tensor(True)
    
    print(torch.all(torch.eq(a - b, torch.sub(a, b))))  # 减
     
    print(torch.all(torch.eq(a * b, torch.mul(a, b))))  # 乘
    
    print(torch.all(torch.eq(a / b, torch.div(a, b))))  # 除
    

    3.2 矩阵乘法

    • torch.mm(only for 2d,不推荐使用)

    • torch.matmul(推荐)

    • @

    a = torch.rand(2,1)
    b = torch.rand(1,2)
    
    print(torch.mm(a, b).shape)          # torch.Size([2, 2])
    
    print(torch.matmul(a, b).shape)      # torch.Size([2, 2])
    
    print((a @ b).shape)                 # torch.Size([2, 2])
    
    • 应用于矩阵降维
    x = torch.rand(4,784)
    
    w = torch.rand(512,784)         # channel-out对应512,channel-in对应784
    
    print((x@w.t()).shape)          # (4,784)x(784,512) torch.Size([4, 512]) Tip:.t()只适用于二维
    

    多维矩阵相乘

    • 对于高维的Tensor(dim>2)

    • 定义其矩阵乘法 仅在最后的两个维度上,要求前面的维度必须保持一致,就像矩阵的索引一样,并且运算操作符只有torch.matmul()。

    a = torch.rand(4, 3, 28, 64)
    b = torch.rand(4, 3, 64, 32)
    print(torch.matmul(a,b).shape)    # torch.Size([4, 3, 28, 32])
    
    c = torch.rand(4, 1, 64, 32)
    print(torch.matmul(a,c).shape)    # 符合broadcast机制,torch.Size([4, 3, 28, 32])
    
    # d = torch.rand(4,64,32)
    # print(torch.matmul(a,d).shape)    # 报错
    

    Tip:这种情形下的矩阵相乘,"矩阵索引维度" 如果符合Broadcasting机制,也会自动做广播,然后相乘。

    3.3 次方pow(**操作)

    a = torch.full([2, 2], 3)  
    b = a.pow(2)                 
    c = a ** 2
    
    print(a)
    print(b)
    print(c)
    
    #tensor([[9., 9.],
    #        [9., 9.]])
    

    3.4 开方sqrt

    #接上面
    c = b.sqrt()   # 也可以a**(0.5)
    print(c)
    #tensor([[3., 3.],
    #        [3., 3.]])
    
    d = b.rsqrt()  # 平方根的倒数
    print(d)
    #tensor([[0.3333, 0.3333],
    #        [0.3333, 0.3333]])
    

    3.5 指数exp与对数log运算

    a = torch.exp(torch.ones(2, 2))  #得到2*2的全是e的Tensor
    print(a)
    #tensor([[2.7183, 2.7183],
    #        [2.7183, 2.7183]])
    
    print(torch.log(a))              #取自然对数
    #tensor([[1., 1.],
    #        [1., 1.]])
    

    3.6 近似值运算

    a = torch.tensor(3.14)
    print(a.floor(), a.ceil(), a.trunc(), a.frac())  # 取下,取上,取整数部分,取小数部分
    
    # tensor(3.) tensor(4.) tensor(3.) tensor(0.1400)
    
    b = torch.tensor(3.49)
    c = torch.tensor(3.5)
    print(b.round(), c.round())                      #四舍五入tensor(3.) tensor(4.)
    

    3.7 裁剪运算clamp

    • 对Tensor中的元素进行范围过滤,不符合条件的可以把它变换到范围内部(边界)上

    • 常用于梯度裁剪(gradient clipping)

      • 即在发生梯度离散 或 者梯度爆炸时对梯度的处理

      • (min, max): 小于min的都设置为min,大于max的都设置成max

      • 实际使用时,可以查看梯度的(L2范数)模来看看需不需要做处理:w.grad.norm(2)

    grad = torch.rand(2, 3) * 15      #0~15随机生成
    print(grad.max(), grad.min(), grad.median())  
    # tensor(12.9533) tensor(1.5625) tensor(11.1101)
    
    print(grad)
    # tensor([[12.7630, 12.9533,  7.6125],
    #         [11.1101, 12.4215,  1.5625]])
    
    print(grad.clamp(10))             # 最小是10,小于10的都变成10
    # tensor([[12.7630, 12.9533, 10.0000],
    #         [11.1101, 12.4215, 10.0000]])
    
    print(grad.clamp(3, 10))          # 最小是3,小于3的都变成3; 最大是10,大于10的都变成10
    # tensor([[10.0000, 10.0000,  7.6125],
    #         [10.0000, 10.0000,  3.0000]])
    

    4. 统计属性

    4.1 范数norm

    • Vector norm 和matrix norm区别

    a = torch.full([8],1)
    b = a.view(2,4)
    c = a.view(2,2,2)
    print(b)
    #tensor([[1., 1., 1., 1.],
    #        [1., 1., 1., 1.]])
    
    print(c)
    #tensor([[[1., 1.],
    #         [1., 1.]],
    #        [[1., 1.],
    #         [1., 1.]]])
    
    # 求L1范数(所有元素绝对值求和)
    print(a.norm(1),b.norm(1),c.norm(1))   # tensor(8.) tensor(8.) tensor(8.)
    
    # 求L2范数(所有元素的平方和再开根)
    print(a.norm(2),b.norm(2),c.norm(2))   # tensor(2.8284) tensor(2.8284) tensor(2.8284)
    
    # 在b的1号维度上求L1范数
    print(b.norm(1, dim=1))            # tensor([4., 4.])
    # 在b的1号维度上求L2范数
    print(b.norm(2, dim=1))            # tensor([2., 2.])
    
    # 在c的0号维度上求L1范数
    print(c.norm(1, dim=0))
    #tensor([[2., 2.],
    #        [2., 2.]])
    
    # 在c的0号维度上求L2范数
    print(c.norm(2, dim=0))
    #tensor([[1.4142, 1.4142],
    #        [1.4142, 1.4142]])
    

    4.2 均值/累加/最大/最小/累积

    • 均值mean、累加sum、最小min、最大max、累积prod

    • 最大值最小值索引argmax、argmin

    b = torch.arange(8).reshape(2, 4).float()
    print(b)
    
    # tensor([[0., 1., 2., 3.],
    #         [4., 5., 6., 7.]])
    
    # 均值,累加,最小,最大,累积
    print(b.mean(), b.sum(), b.min(), b.max(), b.prod())    
    # tensor(3.5000) tensor(28.) tensor(0.) tensor(7.) tensor(0.)
    
    # 不指定维度,输出打平后的最小最大值索引
    print(b.argmax(), b.argmin())                           # tensor(7) tensor(0)
    
    # 指定维度1,输出每一行最大值所在的索引
    print(b.argmax(dim=1))                                  # tensor([3, 3])
    
    # 指定维度0,输出每一列最大值所在的索引
    print(b.argmax(dim=0))                                  # tensor([1, 1, 1, 1])
    

    Tip:

    • 上面的argmax、argmin操作默认会将Tensor打平后取最大值索引和最小值索引

    • 如果不希望Tenosr打平,而是求给定维度上的索引,需要指定在哪一个维度上求最大值或最小值索引。

    4.3 dim/keepdim

    • 例:shape=[4,10],dim=1时,保留第0个维度,即max输出会有4个值。

    • 使用keepdim=True:可以 保持应有的dim

      • 即,仅仅是将求最值的那个dim的size变成了1,返回的结果是符合原Tensor语义的。
    a=torch.rand(4,10)
    print(a.max(dim=1))                                  # 返回结果和索引
    # torch.return_types.max(
    # values=tensor([0.9770, 0.8467, 0.9866, 0.9064]),
    # indices=tensor([4, 2, 2, 4]))
    
    print(a.argmax(dim=1))                               # tensor([4, 2, 2, 4])
    
    # 如:这里使用keepdim=True,保持应有的dim=1
    print(a.max(dim=1, keepdim=True))
    # torch.return_types.max(
    # values=tensor([[0.9770],
    #         [0.8467],
    #         [0.9866],
    #         [0.9064]]),
    # indices=tensor([[4],
    #         [2],
    #         [2],
    #         [4]]))
    
    print(a.argmax(dim=1,keepdim=True))
    # tensor([[4],
    #         [2],
    #         [2],
    #         [4]])
    
    • 这里保持应有的dim=0
    # 这里使用keepdim=True,保持应有的dim=0
    print(a.max(dim=0, keepdim=True))
    # torch.return_types.max(
    # values=tensor([[0.8339, 0.7886, 0.8641, 0.9699, 0.7194, 0.7754, 0.9818, 0.8987, 0.8183,
    #          0.9588]]),
    # indices=tensor([[0, 0, 0, 3, 2, 0, 2, 3, 0, 0]]))
    
    print(a.argmax(dim=0,keepdim=True))
    # tensor([[0, 0, 0, 3, 2, 0, 2, 3, 0, 0]])
    

    4.4 topk 和 kthvalue

    • 前k大topk(largest=True) / 前k小(largest=False) 的概率值及其索引

    • 第k小(kthvalue) 的概率值及其索引

    # 2个样本,分为10个类别的置信度
    d = torch.randn(2, 10)
    # 最大概率的3个类别
    print(d.topk(3, dim=1))
    # torch.return_types.topk(
    # values=tensor([[2.0577, 0.9995, 0.9206],
    #         [1.6206, 1.4196, 0.5670]]),
    # indices=tensor([[3, 6, 9],
    #         [2, 6, 3]]))
    
    # 最小概率的3个类别
    print(d.topk(3, dim=1, largest=False))
    # torch.return_types.topk(
    # values=tensor([[-0.6407, -0.3367, -0.3064],
    #         [-1.6083, -0.7407,  0.0508]]),
    # indices=tensor([[7, 8, 0],
    #         [7, 1, 9]]))
    
    # 求第8小概率的类别(一共10个那就是第3大,正好对应上面最大概率的3个类别的第3列)
    print(d.kthvalue(8, dim=1))
    # torch.return_types.kthvalue(
    # values=tensor([0.9206, 0.5670]),
    # indices=tensor([9, 3]))
    

    4.5 比较操作

    • ,>=,<,<=,!=,==

    • torch.eq(a,b)、torch.equal(a,b)

    print(torch.equal(a, a))        # True
    print(a == a)
    print(torch.eq(a,a))
    # tensor([[True, True, True],
    #         [True, True, True]])
    # tensor([[True, True, True],
    #         [True, True, True]])
    

    5. 高阶操作

    5.1 where

    • 使用 C = torch.where(condition, A, B)

    • 其中 A,B,C,condition是shape相同的Tensor

    • C中的某些元素来自A,某些元素来自B,这由 condition中 对应位置的元素是1还是0来决定。

    • 如果condition对应位置元素是 1,则C中的该位置的元素来自 A 中的该位置的元素

    • 如果condition对应位置元素是 0,则C中的该位置的元素来自 B 中的该位置的元素。

    例: C[0,0,1]=A[0,0,1], C[0,0,2]=B[0,0,2], C[0,0,3]=B[0,0,3]....

    cond = torch.tensor([[0.6,0.1],[0.8,0.7]])
    
    a = torch.tensor([[1,2],[3,4]])
    b = torch.tensor([[4,5],[6,7]])
    print(cond>0.5)
    # tensor([[ True, False],
    #         [ True,  True]])
    
    print(torch.where(cond>0.5, a, b))
    # tensor([[1, 5],        
    #         [3, 4]])
    # [A[0], B[1]]
    # [A[0], A[1]]
    

    5.2 gather

    • torch.gather(input, dim, index, out=None) 对元素实现一个查表映射的操作

    prob = torch.randn(4,10)
    idx = prob.topk(dim=1,k=3)
    print(idx)
    # torch.return_types.topk(
    # values=tensor([[0.9902, 0.3165, 0.3033],
    #         [1.7028, 0.5323, 0.1279],
    #         [2.2629, 1.6216, 0.8855],
    #         [1.8379, 1.1718, 0.8398]]),
    # indices=tensor([[1, 0, 9],
    #         [4, 0, 9],
    #         [8, 2, 1],
    #         [6, 9, 4]]))
    
    # 得到索引
    idx = idx[1]
    print(idx)
    # indices=tensor([[1, 0, 9],
    #         [4, 0, 9],
    #         [8, 2, 1],
    #         [6, 9, 4]]))
    
    label = torch.arange(10)+100
    print(label.expand(4, 10))
    # tensor([[100, 101, 102, 103, 104, 105, 106, 107, 108, 109],
    #         [100, 101, 102, 103, 104, 105, 106, 107, 108, 109],
    #         [100, 101, 102, 103, 104, 105, 106, 107, 108, 109],
    #         [100, 101, 102, 103, 104, 105, 106, 107, 108, 109]])
    
    print(torch.gather(label.expand(4,10), dim=1, index=idx.long()))
    # tensor([[101, 100, 109],
    #         [104, 100, 109],
    #         [108, 102, 101],
    #         [106, 109, 104]])
    
    label=[[100, 101, 102, 103, 104, 105, 106, 107, 108, 109],
          [100, 101, 102, 103, 104, 105, 106, 107, 108, 109],
            [100, 101, 102, 103, 104, 105, 106, 107, 108, 109],
            [100, 101, 102, 103, 104, 105, 106, 107, 108, 109]]
    
    index=[[8, 5, 4],
          [6, 9, 8],
            [1, 3, 8],
            [6, 1, 3]]
    
    • gather:利用 index 来 索引input特定位置 的数值。
  • 相关阅读:
    linux shell 脚本显示执行的命令与关闭显示执行的命令
    docker-compose 文件参考
    django 关系字段一对多的筛选
    深度学习-tensorflow学习笔记(2)-MNIST手写字体识别
    opencv-学习笔记(6)图像梯度Sobel以及canny边缘检测
    opencv-学习笔记(5)形态学转变
    opencv-学习笔记(4)-模糊
    opencv-学习笔记(3)
    opencv-学习笔记(2)
    opencv-学习笔记(1)常用函数和方法。
  • 原文地址:https://www.cnblogs.com/douzujun/p/13285149.html
Copyright © 2011-2022 走看看