精华内容
下载资源
问答
  • pytorch

    2021-05-15 03:35:03
    文章目录Pytorch 框架笔记1.深度学习的核心——梯度2.张量数据类型3....自动求导10.1torch.autograd10.2逻辑回归11.DataLoader和DataSet12.transforms12.1概述12.2数据增强12.3剪裁12.4翻转12.5图像变

    Pytorch 框架笔记

    主流框架

    在这里插入图片描述

    PyTorch能做什么?

    • GPU加速

    • 自动求导

    • 常用网络层

    1.深度学习的核心——梯度

    在这里插入图片描述

    learn rate:学习率,迭代速度的限制因素。

    设置不同的梯度下降的求解器
    l o s s = ( W X + b − y ) 2 w ′ = w − l r × ▽ l o s s ▽ w loss=(WX+b-y)^2\\ w'=w-lr\times \frac{\bigtriangledown loss }{\bigtriangledown w} loss=(WX+by)2w=wlr×wloss

    import numpy as np
    def compute_error_for_line_given_points(b,w,points):
        totalError=0
        for i in range(0,len(points)):
            x=points[i,0]#取x值
            y=points[i,1]#取y值
            totalError+=(y-(w*x+b))**2
        return totalError/float(len(points))#做平均
    def step_gradient(b_current,w_current,points,learningRate):
        b_gradient=0
        w_gradient=0
        N=float(len(points))
        for i in range(0,len(points)):
            x = points[i, 0]  # 取x值
            y = points[i, 1]  # 取y值
            #通过数学方法计算出求导公式,然后代入计算。
            b_gradient+=-(2/N)*(y-((w_current*x)+b_current))#对b求导
            w_gradient+=-(2/N)*x*(y-((w_current*x)+b_current))#对w求导
        new_b=b_current-(learningRate*b_gradient)
        new_w=w_current-(learningRate*w_gradient)
        return [new_b,new_w]
    def gradient_descent_runner(points,starting_b,starting_w,
                                learing_rate,num_iterations):
        b=starting_b
        w=starting_w
        for i in range(num_iterations):
            b,m=step_gradient(b,w,np.array(points),learing_rate)
        return [b,m]
    

    2.张量数据类型

    张量是一个多维数组,它是标量、向量、矩阵的高维拓展。

    Variable是torch .autograd中的数据类型

    主要用于封装Tensor,进行自动求导

    • data : 被包装的Tensor

    • grad: data的梯度

    • grad_fn : 创建Tensor的Function,是自动求导的关键

    • requires_ grad : 指示是否需要梯度

    • is_ leaf: 指示是否是叶子结点(张量)

    PyTorch 0.4.0版开始,Variable并入Tensor

    • dtype : 张量的数据类型,如 torch .FloatTensor, torch .cuda.FloatTensor

    • shape : 张量的形状,如( 64 , 3 , 224 , 224 )

    • device : 张量所在设备,GPU/CPU,是加速的关键

    在这里插入图片描述

    直接创建

    #直接创建
    import torch
    torch.tensor(
        data, #数据, 可以是list, numpy
        dtype=None, #数据类型,默认与data的一致
        device=None, #所在设备, cuda/cpu
        requires_grad=False, #是否需要梯度
        pin_memory=False#是否存于锁页内存
    )
    

    numpy引入法

    torch.from_numpy (ndarray)

    功能:从numpy创建tensor

    注意事项:从torch.from_numpy 创建的tensor于原ndarray共享内存,当修改其中一个的数据,另外一个也将会被改动

    import numpy as np
    import torch 
    a=np.array([2,3.3])
    #从numpy导入的float其实是double类型
    torch.from_numpy(a)
    a=np.ones([2,3])
    torch.from_numpy(a)
    

    依据数值创建

    torch.zeros(*size, 
                out=None, #输出的张量,将torch.zeros生成的张量赋值给out
                dtype=None, 
                layout=torch.strided, 
                device=None, 
                requires_grad=False)
    torch.zeros_like(input, #功能:依input形状创建全0张量
                    dtype=None, 
                    layout=None, 
                    device=None, 
                    requires_grad=False)
    torch.ones(*size, 
                out=None, 
                dtype=None, 
                layout=torch.strided, 
                device=None, 
                requires_grad=False
    torch.ones_like(input, 
                    dtype=None, 
                    layout=None, 
                    device=None, 
                    requires_grad=False)
    torch.full( size, #张量的形状, 如(3, 3)
                fill_value, #张量的值
                out=None, 
                dtype=None, 
                layout=torch.strided, 
                device=None, 
                requires_grad=False) 
    torch.arange(#功能:创建等差的1维张量,注意事项:数值区间为[start, end)
                start=0, #起始值
                end, #结束值
                step=1, #步长,数列公差,默认为1
                out=None, 
                dtype=None, 
                layout=torch.strided, 
                device=None, 
                requires_grad=False)    
    torch.linspace(start, #功能:创建均分的1维张量
                    end, #注意事项:数值区间为[start, end]
                    steps=100,#steps: 数列长度 
                    out=None, 
                    dtype=None, 
                    layout=torch.strided, 
                    device=None, 
                    requires_grad=False)
    torch.logspace(start, #功能:创建对数均分的1维张量  
                    end, #注意事项:长度为steps, 底为base
                    steps=100, 
                    base=10.0, 
                    out=None, 
                    dtype=None, 
                    layout=torch.strided, 
                    device=None, 
                    requires_grad=False)
    torch.eye(#功能:创建单位对角矩阵( 2维张量)注意事项:默认为方阵
                n,#矩阵行数
                m=None, #矩阵列数
                out=None, 
                dtype=None, 
                layout=torch.strided, 
                device=None, 
                requires_grad=False)
    

    依据概率分布创建

    torch.normal(mean,#功能:生成正态分布(高斯分布) 
                std, 
                out=None)
    torch.randn(*size, #功能:生成标准正态分布
                out=None, 
                dtype=None, 
                layout=torch.strided, 
                device=None, 
                requires_grad=False)
    torch.rand(*size, #功能:在区间[0, 1)上,生成均匀分布
                out=None, 
                dtype=None, 
                layout=torch.strided, 
                device=None, 
                requires_grad=False)
    torch.randint(  low=0, #功能:区间[low, high)生成整数均匀分布
                    high, 
                    size, 
                    out=None, 
                    dtype=None, 
                    layout=torch.strided, 
                    device=None, 
                    requires_grad=False)
    torch.randperm( n,#功能:生成生成从0到n-1的随机排列,n : 张量的长度
                    out=None,
                    dtype=torch.int64,
                    layout=torch.strided,
                    device=None,
                    requires_grad=False)
    torch.bernoulli(input, #功能:以input为概率,生成伯努力分布(0-1分布,两点分布)
                    *, 
                    generator=None, 
                    out=None)
    

    List引入法

    import numpy as np
    import torch 
    torch.tensor([2.,3.2])
    torch.FloatTensor([2.,3.2])
    torch.tensor([[2.,3.2],[1.,22.3]])
    

    未初始化数据

    Torch.empty()

    Torch.FloatTensor(d1,d2,d3)注意:这里不是torch.FloatTensor([1,2])=torch.tensor([1,2])

    Torch.IntTensor(d1,d2,d3)

    import numpy as np
    import torch 
    torch.empty(1)
    torch.Tensor(2,3)
    torch.IntTensor(2,3)
    torch.FloatTensor(2,3)
    

    一个小技巧:设置对应的Tensor格式:

    #增强学习中一般使用tensor,很少使用double
    torch.tensor([1.2,3]).type()
    torch.set_default_tensor_type(torch.DoubleTensor)
    torch.tensor([1.2,3]).type()
    

    3.索引与切片

    #dim 0 first
    a=torch.rand(4,3,28,28)
    a[0].shape#对第一个维度索引(索引的是第一个维度,相当于把第一张图片全部取出)
    a[0,0].shape#(取第0张图片的第0个通道)
    a[0,0,2,4]#dim0 标量
    

    沿用python中的索引方法:start:end:step

    #对各个维度进行索引切片
    #select first/last N
    a[:2].shape
    a[:2,:1,:,:].shape
    a[:2,1:,:,:].shape
    a[:2,-1:,:,:].shape
    a[:,:,0:28:2,0:28:2].shape#进行隔点采样
    a[:,:,::2,::2].shape
    

    根据特殊的索引来进行采样:

    a.index_select(0,torch.tensor([0,2])).shape
    a.index_select(1,torch.tensor([1,2])).shape
    a.index_select(2,torch.arange(28)).shape
    a.index_select(2,torch.arange(8)).shape
    

    特殊的切片符号...,为了方便与: :的表示法相同

    a.shape
    a[...].shape
    a[0,...].shape
    a[:,1,...].shape
    a[...,:2].shape
    #当有...出现时,右边的索引需要理解为最右边
    

    根据mask来选择:

    x=torch.randn(3,4)
    mask=x.ge(0.5)#生成一个bool矩阵,将>0.5的位置变成1,类似于matlab用法
    torch.masked_select(x,mask)
    torch.masked_select(x,mask).shape
    

    select by flatten index

    src=torch.tensor([[4,3,5],[6,7,8]])
    torch.take(src,torch.tensor([0,2,5]))#先进行打平,然后根据索引取出打平之后的值
    

    4.维度变换

    View/reshape操作

    t=torch.randperm(8)
    t_reshape=torch.reshape(t,(2,4))
    print("t:{}\nt_reshape:\n{}".format(t,t_reshape))
    t[0]=1024
    #使用过程中原张量和改变维度之后的张量的内存共享,改变之后会同时改变
    print("t:{}\nt_reshape:\n{}".format(t,t_reshape))
    print("t.data 内存地址:{}".format(id(t.data)))
    print("t_reshape.data 内存地址:{}".format(id(t_reshape.data)))
    
    a=torch.rand(4,1,28,28)
    #一定要具有物理意义,如果没有物理意义可能导致数据的丢失和混乱
    a.view(4,28*28)
    a.view(4,28*28).shape
    a.view(4*28,28).shape#只关心一行的形式
    a.view(4*1,28,28).shape
    #主要问题
    b=a.view(4,784)
    b.view(4,28,28,1)#逻辑错误,把数据中的维度信息丢失掉了
    

    Squeeze/unsqueeze操作unsqueeze(pos\index)

    注意:[-a.dim()-1,a.dim()+1)

    a.shape
    #添加一个维度,没有增加数据,数据的理解方式变了,只是概念增加了
    a.unsqueeze(0).shape
    a.unsqueeze(-1).shape#这里的-1指的是原来的a的最后一个维度
    a.unsqueeze(4).shape
    a.unsqueeze(-4).shape
    a.unsqueeze(5).shape#Dimension out of range
    

    实际的应用,在图像处理的过程中添加偏置项:

    b=torch.rand(32)
    f=torch.rand(4,32,14,14)
    b=b.unsqueeze(1).unsqueeze(2).unsqueeze(0)
    b.shape
    

    squeeze(pos\index)如果不设置索引,会将所有的维度全部收缩挤压

    b.shape
    b.squeeze().shape
    b.squeeze(0).shape
    b.squeeze(-1).shape
    b.squeeze(1).shape
    

    Expand/repeat

    Expand:broadcasting

    Repeat:memory copied

    a=torch.rand(4,32,14,14)
    b.shape
    b.expand(4,32,14,14).shape
    b.expand(-1,32,-1,-1).shape
    b.expand(-1,32,-1,-4).shape
    
    b.shape
    #表示要拷贝的次数
    b.repeat(4,32,1,1).shape#torch.Size([4, 1024, 1, 1])
    b.repeat(4,1,1,1).shape#torch.Size([4, 32, 1, 1])
    b.repeat(4,1,32,32).shape#torch.Size([4, 32, 32, 32])
    

    转置操作

    #t()只能转置2维数据,如果转置4维数据会报错
    a=torch.randn(3,4)
    a.t()
    a=torch.rand(4,3,32,32)
    #Transpose表示交换特定的维度
    #维度信息已经丢掉了,数据的维度顺序必须和存储顺序一致
    #.contiguous()先将原来的数据变得连续
    #view()会造成数据维度的丢失,一定要注意数据的跟踪
    a2=a.transpose(1,3).contiguous().view(4,3*32*32).view(4,32,32,3)
                                                    .transpose(1,3)
    torch.all(torch.eq(a,a2))#数据完全一致,保持了变换前后的数据不变
    #直接进行维度索引的变换,对应位置放对应的维度
    a.permute(0,2,3,1)
    

    5.Broadcasting自动扩张

    Expand

    ▪ without copying data

    Key idea

    • Insert 1 dim ahead Expand dims with size 1 to same size

    • Feature maps: [4, 32, 14, 14] Bias: [32, 1, 1] => [1, 32, 1, 1] => [4, 32, 14, 14]

    自动扩张:先添加1维,再扩展数据

    ▪ Match from Last dim!

    ▪ If current dim=1, expand to same

    ▪ If either has no dim, insert one dim and expand to same

    ▪ otherwise, NOT broadcasting-able

    小维度指定,大维度随意

    6.拼接与拆分

    Merge or split

    CatStackSplitChunk

    cat: 必须保证除了拼接维度以外的维度都要相等

    a=torch.rand(4,32,8)
    b=torch.rand(5,32,8)
    torch.cat([a,b],dim=0).shape
    #torch.Size([9, 32, 8])
    

    stack:插入一个新的维度create new dim

    a=torch.rand(4,32,8,8)
    b=torch.rand(4,32,8,8)
    torch.stack([a,b],dim=2).shape
    #torch.Size([4, 32, 2, 8, 8])
    

    chunk:将张量按照维度dim进行平均切分,返回值为张量列表

    注意:如果不能整除,最后一份张量小于其他的张量。

    a=torch.ones((2,5))
    list_of_tensors=torch.chunk(a,dim=1,chunks=2)
    for idx,t in enumerate(list_of_tensors):
        print("第{}个张量:{},shape is {}".format(idx+1,t,t.shape))
    #第1个张量:tensor([[1., 1., 1.],[1., 1., 1.]]),shape is torch.Size([2, 3])
    #第2个张量:tensor([[1., 1.],[1., 1.]]),shape is torch.Size([2, 2])
    

    split:将张量按维度dim进行切分,返回值为张量列表

    torch.split(tensor, split_size_or_sections, dim=0)

    split_size_or_sections:为int时,表示每一份的长度,为list时,按list元素切分

    t=torch.ones((2,5))
    list_of_tensors=torch.split(t,2,dim=1)
    for idx,t in enumerate(list_of_tensors):
        print("第{}个张量:{},shape is {}".format(idx+1,t,t.shape))
    t=torch.ones((2,5))
    list_of_tensors=torch.split(t,[2,1,2],dim=1)#列表中各个元素的和一定要等于指定维度的长度
    for idx,t in enumerate(list_of_tensors):
        print("第{}个张量:{},shape is {}".format(idx+1,t,t.shape))
    

    7.数学运算

    torch.add()                      torch.addcdiv()
    torch.addcmul()                  torch.sub()
    torch.div()                      torch.mul()
    torch.log(input, out=None)       torch.log10(input, out=None)
    torch.log2(input, out=None)      torch.exp(input, out=None)
    torch.pow()                      torch.abs(input, out=None)
    torch.acos(input, out=None)      torch.cosh(input, out=None)
    torch.cos(input, out=None)       torch.asin(input, out=None)
    torch.atan(input, out=None)      torch.atan2(input, other, out=None)
    #矩阵乘法
    torch.mm(a,b)#只能应用于2D的矩阵
    torch.matmul(a,b)#可以应用于很多维
    a@b#可以应用于很多维
    

    为了便于深度学习的进行pytorch封装了一些内置的特殊用法:

    torch.add(input,alpha=1,other,out=None)#逐元素计算 input+alpha × other
    

    torch.addcdiv()
    o u t i = i n p u t i + v a l u e ×  tensor  1 i  tensor  2 i out _{i}= input _{i}+ value \times \frac{\text { tensor } 1_{i}}{\text { tensor } 2_{i}} outi=inputi+value× tensor 2i tensor 1i
    torch.addcmul()
    o u t i = i n p u t i + v a l u e × t e n s o r 1 i × t e n s o r 2 i out _{i}= input _{i}+ value \times tensor 1_{i} \times tensor 2_{i} outi=inputi+value×tensor1i×tensor2i

    torch.addcmul(input,value=1,tensor1,tensor2,out=None)
    
    a=torch.full([2,2],3)
    a.pow(2)
    a**2
    aa=a**2
    aa.sqrt()
    aa.rsqrt()
    aa**(0.5)
    

    梯度裁剪:clamp▪ gradient clipping (min) (min, max)

    8.随机梯度

    8.1什么是梯度

    Optimizer Performance

    ▪ initialization status(初始值)

    ▪ learning rate(学习率)

    ▪ momentum(动量,惯性)

    8.2激活函数及其梯度

    Sigmoid / Logistic函数——光滑可导

    d d x σ ( x ) = d d x ( 1 1 + e − x ) = e − x ( 1 + e − x ) 2 = ( 1 + e − x ) − 1 ( 1 + e − x ) 2 = 1 + e − x ( 1 + e − x ) 2 − ( 1 1 + e − x ) 2 = σ ( x ) − σ ( x ) 2 σ ′ = σ ( 1 − σ ) \begin{aligned} \frac{d}{d x} \sigma(x) &=\frac{d}{d x}\left(\frac{1}{1+e^{-x}}\right) \\ &=\frac{e^{-x}}{\left(1+e^{-x}\right)^{2}} \\ &=\frac{\left(1+e^{-x}\right)-1}{\left(1+e^{-x}\right)^{2}} \\ &=\frac{1+e^{-x}}{\left(1+e^{-x}\right)^{2}}-\left(\frac{1}{1+e^{-x}}\right)^{2} \\ &=\sigma(x)-\sigma(x)^{2} \\ \sigma^{\prime} &=\sigma(1-\sigma) \end{aligned} dxdσ(x)σ=dxd(1+ex1)=(1+ex)2ex=(1+ex)2(1+ex)1=(1+ex)21+ex(1+ex1)2=σ(x)σ(x)2=σ(1σ)

    import torch
    a=torch.linspace(-100,100,10)
    torch.sigmoid(a)
    

    Tanh——RNN中用的较多

    d d x tanh ⁡ ( x ) = ( e x + e − x ) ( e x + e − x ) − ( e x − e − x ) ( e x − e − x ) ( e x + e − x ) 2 = 1 − ( e x − e − x ) 2 ( e x + e − x ) 2 = 1 − tanh ⁡ 2 ( x ) \begin{array}{l} \frac{d}{d x} \tanh (x)=\frac{\left(e^{x}+e^{-x}\right)\left(e^{x}+e^{-x}\right)-\left(e^{x}-e^{-x}\right)\left(e^{x}-e^{-x}\right)}{\left(e^{x}+e^{-x}\right)^{2}} \\ =1-\frac{\left(e^{x}-e^{-x}\right)^{2}}{\left(e^{x}+e^{-x}\right)^{2}}=1-\tanh ^{2}(x) \end{array} dxdtanh(x)=(ex+ex)2(ex+ex)(ex+ex)(exex)(exex)=1(ex+ex)2(exex)2=1tanh2(x)

    import torch
    a=torch.linspace(-1,1,10)
    torch.tanh(a)
    

    Rectified Linear Unit——RELU——非线性激活函数

    f ′ ( x ) = { 0  for  x < 0 1  for  x ≥ 0 f^{\prime}(x)=\left\{\begin{array}{ll} 0 & \text { for } x<0 \\ 1 & \text { for } x \geq 0 \end{array}\right. f(x)={01 for x<0 for x0

    from torch.nn import functional as F
    a=torch.linspace(-1,1,10)
    torch.relu(a)
    F.relu(a)
    

    8.3LOSS及其梯度

    Mean Squared Error(MSE)
    loss ⁡ = ∑ [ y − ( x w + b ) ] 2 L 2 − norm ⁡ = ∣ ∣ y − ( x w + b ) ∣ ∣ 2 loss ⁡ = norm ⁡ ( y − ( x w + b ) ) 2 \begin{array}{l} \operatorname{loss} =\sum[y-(x w+b)]^{2} \\ L 2-\operatorname{norm}=|| y-(x w+b)||_{2} \\ \operatorname{loss} =\operatorname{norm}(y-(x w+b))^{2} \end{array} loss=[y(xw+b)]2L2norm=y(xw+b)2loss=norm(y(xw+b))2
    Derivative
    loss ⁡ = ∑ [ y − f θ ( x ) ] 2 ∇ l o s s ∇ θ = 2 ∑ [ y − f θ ( x ) ] × ∇ f θ ( x ) ∇ θ \operatorname{loss} =\sum\left[y-f_{\theta}(x)\right]^{2} \\ \frac{ { \nabla loss }}{\nabla \theta}=2 \sum\left[y-f_{\theta}(x)\right] \times \frac{\nabla f_{\theta}(x)}{\nabla \theta} loss=[yfθ(x)]2θloss=2[yfθ(x)]×θfθ(x)

    • torch.autograd.grad(loss, [w1, w2,…])----->[w1 grad, w2 grad…]

    • loss.backward() —-> w1.grad w2.grad

    x=torch.ones(1)
    w=torch.full([1],2)
    w.requires_grad_()#更新w的信息为可求导的
    mse=F.mse_loss(torch.ones(1),x*w)#重新绘制动态图
    torch.autograd.grad(mse,[w])
    

    softmax

    p i = e a i ∑ k = 1 N e a k p_{i}=\frac{e^{a_{i}}}{\sum_{k=1}^{N} e^{a_{k}}} pi=k=1Neakeai

    w h e n   i = j ∂ e a i ∑ k = 1 N e a k ∂ a j = e a i ∑ k = 1 N e a k − e a j e a i ( ∑ k = 1 N e a k ) 2 = e a i ( ∑ k = 1 N e a k − e a j ) ( ∑ k = 1 N e a k ) 2 = e a j ∑ k = 1 N e a k × ( ∑ k = 1 N e a k − e a j ) ∑ k = 1 N e a k = p i ( 1 − p j ) when \ i=j \begin{aligned} \frac{\partial \frac{e^{a_{i}}}{\sum_{k=1}^{N} e^{a_{k}}}}{\partial a_{j}} &=\frac{e^{a_{i}} \sum_{k=1}^{N} e^{a_{k}}-e^{a_{j}} e^{a_{i}}}{\left(\sum_{k=1}^{N} e^{a_{k}}\right)^{2}} \\ &=\frac{e^{a_{i}}\left(\sum_{k=1}^{N} e^{a_{k}}-e^{a_{j}}\right)}{\left(\sum_{k=1}^{N} e^{a_{k}}\right)^{2}} \\ &=\frac{e^{a_{j}}}{\sum_{k=1}^{N} e^{a_{k}}} \times \frac{\left(\sum_{k=1}^{N} e^{a_{k}}-e^{a_{j}}\right)}{\sum_{k=1}^{N} e^{a_{k}}} \\ &=p_{i}\left(1-p_{j}\right) \end{aligned} when i=jajk=1Neakeai=(k=1Neak)2eaik=1Neakeajeai=(k=1Neak)2eai(k=1Neakeaj)=k=1Neakeaj×k=1Neak(k=1Neakeaj)=pi(1pj)

    w h e n     i ≠ j ∂ e a i ∑ k = 1 N e a k ∂ a j = 0 − e a j e a i ( ∑ k = 1 N e a k ) 2 = − e a j ∑ k = 1 N e a k × e a i ∑ k = 1 N e a k = − p j ⋅ p i when\ \ \ i \neq j \begin{aligned} \frac{\partial \frac{e^{a_{i}}}{\sum_{k=1}^{N} e^{a_{k}}}}{\partial a_{j}} &=\frac{0-e^{a_{j}} e^{a_{i}}}{\left(\sum_{k=1}^{N} e^{a_{k}}\right)^{2}} \\ &=\frac{-e^{a_{j}}}{\sum_{k=1}^{N} e^{a_{k}}} \times \frac{e^{a_{i}}}{\sum_{k=1}^{N} e^{a_{k}}} \\ &=-p_{j} \cdot p_{i} \end{aligned} when   i=jajk=1Neakeai=(k=1Neak)20eajeai=k=1Neakeaj×k=1Neakeai=pjpi

    8.4利用pytorch实现线性回归

    import torch
    import matplotlib.pyplot as plt
    torch.manual_seed(10)
    lr = 0.05  # 学习率    20191015修改
    # 创建训练数据
    x = torch.rand(20, 1) * 10  # x data (tensor), shape=(20, 1)
    y = 2*x + (5 + torch.randn(20, 1))  # y data (tensor), shape=(20, 1)
    # 构建线性回归参数
    w = torch.randn((1), requires_grad=True)
    b = torch.zeros((1), requires_grad=True)#随机初始化可求导
    for iteration in range(1000):
        # 前向传播
        wx = torch.mul(w, x)
        y_pred = torch.add(wx, b)
        # 计算 MSE loss
        loss = (0.5 * (y - y_pred) ** 2).mean()
        # 反向传播
        loss.backward()
        # 更新参数
        b.data.sub_(lr * b.grad)
        w.data.sub_(lr * w.grad)
    

    9.计算图和动态图

    9.1计算图

    计算图是用来描述运算的有向无环图。

    计算图有两个主要元素:结点和边

    结点表示数据,如向量,矩阵,张量。边表示运算,如:加减乘除和卷积等。

    用计算图表示:
    y = ( x + w ) × ( w + 1 ) a = x + w b = w + 1 y = a × b y=(x+w)\times(w+1)\\ a=x+w\\ b=w+1\\ y=a\times b y=(x+w)×(w+1)a=x+wb=w+1y=a×b

    ∂ y ∂ w = ∂ y ∂ a ∂ a ∂ w + ∂ y ∂ b ∂ b ∂ w = b × 1 + a × 1 = b + a = ( w + 1 ) + ( x + w ) = 2 × w + x + 1 = 2 × 1 + 2 + 1 = 5 \begin{aligned} \frac{\partial y}{\partial w} &=\frac{\partial y}{\partial a} \frac{\partial a}{\partial w}+\frac{\partial y}{\partial b} \frac{\partial b}{\partial w} \\ &=b \times 1+a \times 1 \\ &=b+a \\ &=(w+1)+(x+w)\\ &=2 \times w+x+1 \\ &=2 \times 1+2+1=5 \end{aligned} wy=aywa+bywb=b×1+a×1=b+a=(w+1)+(x+w)=2×w+x+1=2×1+2+1=5

    通过分析可以知道,y对w求导就是在计算图中找到所有y到w的路径,把路径上的导数进行求和。

    import torch
    w = torch.tensor([1.], requires_grad=True)  
    #由于需要计算梯度,所以requires_grad设置为True
    x = torch.tensor([2.], requires_grad=True)  
    a = torch.add(w, x)     # a = w + x
    b = torch.add(w, 1)     # b = w + 1
    y = torch.mul(a, b)     # y = a * b
    y.backward()    #对y进行反向传播
    print(w.grad)   #输出w的梯度
    

    叶子节点:用户创建的结点称为叶子结点,如X与W;
    is_leaf:指示张量是否为叶子节点;

    ​ 叶子节点是整个计算图的根基,例如前面求导的计算图,在前向传导中的a、b和y都要依据创建的叶子节点x和w进行计算的。同样,在反向传播过程中,所有梯度的计算都要依赖叶子节点。

    ​ 设置叶子节点主要是为了节省内存,在梯度反向传播结束之后,非叶子节点的梯度都会被释放掉。

    #查看叶子结点,通过运算得来的结点不是叶子结点
    print("is_leaf:\n", w.is_leaf, x.is_leaf, a.is_leaf, b.is_leaf, y.is_leaf)  
    #输出为True True False False False,只有前面两个是叶子节点
    #查看梯度
    print("gradient:\n", w.grad, x.grad, a.grad, b.grad, y.grad)  
    #输出为tensor([5.]) tensor([2.]) None None None,因为非叶子节点都被释放掉了
    

    ​ 如果想使用非叶子结点梯度,可以使用pytorch中的retain_grad()。例如对上面代码中的a执行相关操作a.retain_grad(),则a的梯度会被保留下来,b和y的梯度会被释放掉。

    a.retain_grad()  
    #保存非叶子结点a的梯度,输出为tensor([5.]) tensor([2.]) tensor([2.]) None None
    

    ​ torch.Tensor中还有一个属性为grad_fn,grad_fn的作用是记录创建该张量时所用的方法(函数),该属性在梯度反向传播的时候用到。

    # 查看 grad_fn
    print("grad_fn:\n", w.grad_fn, x.grad_fn, a.grad_fn, b.grad_fn, y.grad_fn)
    #上面代码的输出结果为
    grad_fn:
    None 
    None 
    <AddBackward0 object at 0x000001EEAA829308> 
    <AddBackward0 object at 0x000001EE9C051548> 
    <MulBackward0 object at 0x000001EE9C29F948>
    

    9.2动态图

    动态图:pytorch使用的,运算与搭建同时进行;灵活,易调节。

    静态图:tensorflow使用的,先搭建图,后运算;高效,不灵活。

    根据计算图搭建方式,可将计算图分为动态图和静态图。

    10.自动求导

    10.1torch.autograd

    #第一个常用的函数
    torch.autograd.backward(tensors,
                            grad_tensors=None,#多梯度权重
                            retain_graph=None,#保存计算图
                            create_graph=False)#创建导数计算图,用于高阶求导
    
    w = torch.tensor([1.], requires_grad=True)
    x = torch.tensor([2.], requires_grad=True)
    a = torch.add(w, x)
    b = torch.add(w, 1)
    y = torch.mul(a, b)
    y.backward(retain_graph=True)
    print(w.grad)
    y.backward()
    
    w = torch.tensor([1.], requires_grad=True)
    x = torch.tensor([2.], requires_grad=True)
    a = torch.add(w, x)     # retain_grad()
    b = torch.add(w, 1)
    y0 = torch.mul(a, b)    # y0 = (x+w) * (w+1)
    y1 = torch.add(a, b)    # y1 = (x+w) + (w+1)    dy1/dw = 2
    loss = torch.cat([y0, y1], dim=0)       # [y0, y1]
    grad_tensors = torch.tensor([1., 2.])
    loss.backward(gradient=grad_tensors)    
    # gradient 传入 torch.autograd.backward()中的grad_tensors
    print(w.grad)
    
    torch.autograd.grad(outputs,#用于求导的张量
                        inputs,#需要梯度的张量
                        grad_tensors=None,#多梯度权重
                        retain_graph=None,#保存计算图
                        create_graph=False)#创建导数计算图,用于高阶求导
    
    x = torch.tensor([3.], requires_grad=True)
    y = torch.pow(x, 2)     # y = x**2
    grad_1 = torch.autograd.grad(y, x, create_graph=True)   
    # grad_1 = dy/dx = 2x = 2 * 3 = 6
    print(grad_1)#(tensor([6.], grad_fn=<MulBackward0>),)
    grad_2 = torch.autograd.grad(grad_1[0], x)              
    # grad_2 = d(dy/dx)/dx = d(2x)/dx = 2
    print(grad_2)#(tensor([2.]),)
    
    1. 梯度不自动清零
    2. 依赖于叶子结点的结点,requires_grad默认为True
    3. 叶子结点不可执行in-place
    a = torch.ones((1, ))
    print(id(a), a)#2312853053752 tensor([1.])
    a = a + torch.ones((1, ))#in_place操作
    print(id(a), a)#2312876365208 tensor([2.])
    a += torch.ones((1, ))#place操作
    print(id(a), a)#2312876365208 tensor([3.])
    

    10.2逻辑回归

    利用pytorch生成训练的数据

    import torch
    import torch.nn as nn
    import matplotlib.pyplot sas plt
    import numpy as np
    #step1:数据
    sample_nums=100
    mean_value=1.7
    bias=100
    n_data=torch.ones(sample_nums,2)
    x0=torch.normal(mean_value*n_data,1)+bias     #类别0 数据shape=(100,2)
    y0=torch.zeros(sample_nums)                   #类别0 标签shape=(100,1)
    x1=torch.normal(-mean_value*n_data,1)+bias    #类别1 数据shape=(100,2)
    y0=torch.zeros(sample_nums)                   #类别1 标签shape=(100,1) 
    train_x=torch.cat((x0,x1),0)
    train_y=torch.cat((y0,y1),0)
    

    选择模型:

    #step2:模型
    #定义逻辑回归中的前向传播算法
    class LR(nn.Module):
        def __init__(self):#继承自nn.Module类
            super(LR,self).__init__()
            self.features=nn.Linear(2,1)
            self.sigmoid=nn.Sigmoid()
        def forward(self,x):
            x=self.features(x)
            x=self.sigmoid(x)
            return x
    #实例化逻辑回归模型
    lr_net=LR()
    

    定义损失函数:

    #step3:损失函数
    loss_fn=nn.BCELoss()#交叉熵损失函数
    

    定义优化器:

    #step4:优化器
    lr=0.01
    optimizer=torch.optim.SGD(lr_net.parameters(),lr=lr,momentum=0.9)
    #使用随机梯度下降的优化器
    

    迭代训练模型

    #step5:迭代训练
    for iteration in range(100):
        y_pred=lr_net(train_x)#前向传播
        loss=loss_fn(y_pred.squeeze(),train_y)#计算loss
        loss.backward()#反向传播
        optimizer.step()#更新参数
        optimizer.zero_grad()#清空梯度
        if iteration % 20 == 0:
            mask = y_pred.ge(0.5).float().squeeze()  # 以0.5为阈值进行分类
            correct = (mask == train_y).sum()  # 计算正确预测的样本个数
            acc = correct.item() / train_y.size(0)  # 计算分类准确率
            plt.scatter(x0.data.numpy()[:, 0], x0.data.numpy()[:, 1], 
                        c='r', label='class 0')
            plt.scatter(x1.data.numpy()[:, 0], x1.data.numpy()[:, 1], 
                        c='b', label='class 1')
            w0, w1 = lr_net.features.weight[0]
            w0, w1 = float(w0.item()), float(w1.item())
            plot_b = float(lr_net.features.bias[0].item())
            plot_x = np.arange(-6, 6, 0.1)
            plot_y = (-w0 * plot_x - plot_b) / w1
            plt.xlim(-5, 7)
            plt.ylim(-7, 7)
            plt.plot(plot_x, plot_y)
            plt.text(-5, 5, 'Loss=%.4f' % loss.data.numpy(), 
                     fontdict={'size': 20, 'color': 'red'})
            plt.title("Iteration: {}\nw0:{:.2f} w1:{:.2f} b: {:.2f} accuracy:{:.2%}"
                      .format(iteration, w0, w1, plot_b, acc))
            plt.legend()
            plt.show()
            plt.pause(0.5)
            if acc > 0.99:
                break
    

    11.DataLoader和DataSet

    #构建课迭代的数据装载器
    torch.utils.data.DataLoader( dataset,#Dataset类,决定数据从哪读取以及如何读取
                                batch_size=1,#批大小
                                shuffle=False,#每个epoch是否乱序
                                sampler=None,
                                batch_sampler=None,
                                num_workers=0,#是否多进程读取数据
                                collate_fn=None,
                                pin_memory=False,
                                drop_last=False,
                                #当样本数不能被batchsize整除时,是否舍弃最后一波数据
                                timeout=0,
                                worker_init_fn=None,
                                multiprocessing_context=None)
    
    • Epoch: 所有训练样本都已输入到模型中,称为一个Epoch
    • Iteration:一批样本输入到模型中,称之为一个Iteration
    • Batchsize:批大小,决定一个Epoch有多少个Iteration

    【举例】样本总数:80, Batchsize:8 ,则1 Epoch = 10 Iteration
    样本总数:87, Batchsize:8
    1 Epoch = 10 Iteration when drop_last = True
    1 Epoch = 11 Iteration when drop_last = False

    #torch.utils.data.Dataset
    #功能:Dataset抽象类,所有自定义的Dataset需要继承它,并且复写__getitem__()
    #getitem :接收一个索引,返回一个样本
    class Dataset(object):
    def __getitem__(self, index):
    raise NotImplementedError
    def __add__(self, other):
    return ConcatDataset([self, other])
    

    数据读取:

    • 读哪些数据——Sampler输出的index
    • 从哪里读数据——DataSet中的data_dir
    • 怎么读数据——Dataset中的getitem

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W6ShCD8Y-1621019963119)(image-20200820203241733.png)]

    12.transforms

    12.1概述

    torchvision.transforms : 常用的图像预处理方法
    torchvision.datasets : 常用数据集的dataset实现,MNIST,CIFAR-10,ImageNet等
    torchvision.model : 常用的模型预训练,AlexNet,VGG, ResNet,GoogLeNet等

    torchvision.transforms : 常用的图像预处理方法:数据中心化,数据标准化,缩放,裁剪,旋转,翻转,填充,噪声添加,灰度变换,线性变换,仿射变换,亮度、饱和度及对比度变换

    train_transform = transforms.Compose([
        transforms.Resize((32, 32)),#图像大小变化
        transforms.RandomCrop(32, padding=4),#随机裁剪
        transforms.ToTensor(),#图片转化为张量
        transforms.Normalize(norm_mean, norm_std),#图像归一化
    ])
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YKdmL7K1-1621019963120)(image-20200820223032848.png)]

    #功能:逐channel的对图像进行标准化output = (input - mean) / std
    transforms.Normalize(mean,#各通道的均值
                        std,#各通道的标准差
                        inplace=False)#是否原地操作
    

    12.2数据增强

    数据增强又称为数据增广,数据扩增,它是对训练集进行变换,使训练集更丰富,从而让模型更具泛化能力。

    12.3剪裁

    1.transforms.CenterCrop

    功能:从图像中心裁剪图片 size:所需裁剪图片尺寸

    #功能:从图像中心裁剪图片   size:所需裁剪图片尺寸
    train_transform = transforms.Compose([
        transforms.Resize((224, 224)),
        # CenterCrop
        transforms.CenterCrop(200)#如果超出了范围自动补充黑色区域    
    ])
    

    2.transforms.RandomCrop

    2.transforms.RandomCrop(size,#所需裁剪图片尺寸 
                            padding=None, #设置填充大小
                            #当为a时,上下左右均填充a个像素
                            #当为(a,b)时,上下填充b个像素,左右填充a个像素
                            #当为(a,b,c,d)时,左上右下填充abcd
                            pad_if_needed=False, #若图像小于设定size,则填充
                            fill=0, 
                            padding_mode='constant')
    #padding_mode:填充模式,有4种模式
    #1、constant:像素值由fill设定
    #2、edge:像素值由图像边缘像素决定
    #3、reflect:镜像填充,最后一个像素不镜像,eg:[1,2,3,4] → [3,2,1,2,3,4,3,2]
    #4、symmetric:镜像填充,最后一个像素镜像,eg:[1,2,3,4] → [2,1,1,2,3,4,4,3]
    #fill:constant时,设置填充的像素值,可以设置其他的RGB值
    
    train_transform = transforms.Compose([
        transforms.Resize((224, 224)),
        # 2 RandomCrop
        transforms.RandomCrop(224, padding=16),
        transforms.RandomCrop(224, padding=(16, 64)),
        transforms.RandomCrop(224, padding=16, fill=(255, 0, 0)),
        transforms.RandomCrop(512, pad_if_needed=True),   # pad_if_needed=True
        transforms.RandomCrop(224, padding=64, padding_mode='edge'),
        transforms.RandomCrop(224, padding=64, padding_mode='reflect'),
        transforms.RandomCrop(1024, padding=1024, padding_mode='symmetric')
    ])
    
    1. transforms.RandomResizedCrop()
    RandomResizedCrop(size, #size:所需裁剪图片尺寸
                     scale=(0.08, 1.0), #scale:随机裁剪面积比例, 默认(0.08, 1)
                     ratio=(3/4, 4/3), # ratio:随机长宽比,默认(3/4, 4/3)
                     interpolation)
    #功能:随机大小、长宽比裁剪图片
    #interpolation:插值方法
    #PIL.Image.NEAREST
    #PIL.Image.BILINEAR
    #PIL.Image.BICUBIC
    
    train_transform = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.RandomResizedCrop(size=224, scale=(0.5, 0.5))
    ])
    

    4.transforms.FiveCrop

    transforms.FiveCrop(size)
    transforms.TenCrop(size, vertical_flip=False)
    #功能:在图像的上下左右以及中心裁剪出尺寸为size的5张图片
    #TenCrop对这5张图片进行水平或者垂直镜像获得10张图片
    #size:所需裁剪图片尺寸
    #vertical_flip:是否垂直翻转
    
    train_transform = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.FiveCrop(112),
        transforms.Lambda(lambda crops: 
                          torch.stack([(transforms.ToTensor()(crop)) 
                                       for crop in crops]))
        transforms.TenCrop(112, vertical_flip=False),
        transforms.Lambda(lambda crops: 
                          torch.stack([(transforms.ToTensor()(crop)) 
                                       for crop in crops])),
    ])
    

    12.4翻转

    1.RandomHorizontalFlipRandomVerticalFlip

    RandomHorizontalFlip(p=0.5)
    RandomVerticalFlip(p=0.5)
    #功能:依概率水平(左右)或垂直(上下)翻转图片p:翻转概率
    
    train_transform = transforms.Compose([
        transforms.Resize((224, 224)),
        # 1 Horizontal Flip
        transforms.RandomHorizontalFlip(p=1),
        # 2 Vertical Flip
        transforms.RandomVerticalFlip(p=0.5),
    ])
    

    2.RandomRotation

    RandomRotation(degrees, # degrees:旋转角度
                   #当为a时,在(-a,a)之间选择旋转角度
                   #当为(a, b)时,在(a, b)之间选择旋转角度
                   resample=False, #resample:重采样方法
                   expand=False, #expand:是否扩大图片
                   center=None)#默认为中心旋转,也可以设置左上角旋转
    #功能:随机旋转图片
    
    train_transform = transforms.Compose([
         transforms.Resize((224, 224)),
         transforms.RandomRotation(90),
         transforms.RandomRotation((90), expand=True),
         transforms.RandomRotation(30, center=(0, 0)),
         transforms.RandomRotation(30, center=(0, 0), expand=True),   
        # expand only for center rotation
    ])
    

    12.5图像变换

    1.transforms.Pad()

    transforms.Pad(padding, 
                   fill=0, 
                   padding_mode='constant')
    #1.padding:设置填充大小
    #当为a时,上下左右均填充a个像素
    #当为(a, b)时,上下填充b个像素,左右填充a个像素
    #当为(a, b, c, d)时,左,上,右,下分别填充a, b, c, d
    #2.padding_mode:填充模式,有4种模式,constant、edge、reflect和symmetric
    #3.fill:constant时,设置填充的像素值,(R, G, B) or (Gray)
    
    transforms.Pad(padding=32, fill=(255, 0, 0), padding_mode='constant'),
    transforms.Pad(padding=(8, 64), fill=(255, 0, 0), padding_mode='constant'),
    transforms.Pad(padding=(8, 16, 32, 64), fill=(255, 0, 0), padding_mode='constant'),
    transforms.Pad(padding=(8, 16, 32, 64), fill=(255, 0, 0), padding_mode='symmetric')
    

    2.transforms.ColorJitter

    transforms.ColorJitter(brightness=0, #brightness:亮度调整因子
                           #当为a时,从[max(0, 1-a), 1+a]中随机选择
                           #当为(a, b)时,从[a, b]中随机选择 
                           contrast=0, #contrast:对比度参数,同brightness
                           saturation=0,#saturation:饱和度参数,同brightness 
                           hue=0)
    #功能:调整亮度、对比度、饱和度和色相
    #hue:色相参数,当为a时,从[-a, a]中选择参数,注: 0<= a <= 0.5
    #             当为(a, b)时,从[a, b]中选择参数,注:-0.5 <= a <= b <= 0.5
    
    transforms.ColorJitter(brightness=0.5),
    transforms.ColorJitter(contrast=0.5),
    transforms.ColorJitter(saturation=0.5),
    transforms.ColorJitter(hue=0.3),
    

    3.RandomGrayscale

    RandomGrayscale(num_output_channels,#num_ouput_channels:输出通道数,只能设1或3
                    p=0.1)#p:概率值,图像被转换为灰度图的概率
                    Grayscale(num_output_channels)
    Grayscale(num_output_channels)#p=1的RandomGrayscale
    

    4.RandomAffine

    功能:对图像进行仿射变换,仿射变换是二维的线性变换,由五种基本原子变换构成,分别是旋转、平移、缩放、错切和翻转。

    • degrees:旋转角度设置
    • translate:平移区间设置,如(a, b), a设置宽(width),b设置高(height)
      图像在宽维度平移的区间为KaTeX parse error: Expected '}', got '_' at position 12: \text{-img_̲width} \times a…
    • scale:缩放比例(以面积为单位)
    • fill_color:填充颜色设置
    • shear:错切角度设置,有水平错切和垂直错切
      若为 a a a,则仅在x轴错切,错切角度在 ( − a , a ) (-a, a) (a,a)之间
      若为 ( a , b ) (a,b) (ab),则a设置x轴角度,b设置y的角度
      若为 ( a , b , c , d ) (a, b, c, d) (a,b,c,d),则a, b设置x轴角度,c, d设置y轴角度
    • resample:重采样方式,有NEARESTBILINEARBICUBIC
    RandomAffine(degrees, 
                 translate=None, 
                 scale=None, 
                 shear=None, 
                 resample=False, 
                 fillcolor=0)
    
    transforms.RandomAffine(degrees=30),
    transforms.RandomAffine(degrees=0, translate=(0.2, 0.2), fillcolor=(255, 0, 0)),
    transforms.RandomAffine(degrees=0, scale=(0.7, 0.7)),
    transforms.RandomAffine(degrees=0, shear=(0, 0, 0, 45)),
    transforms.RandomAffine(degrees=0, shear=90, fillcolor=(255, 0, 0)),
    

    5.RandomErasing

    功能:对图像进行随机遮挡

    • p:概率值,执行该操作的概率
    • scale:遮挡区域的面积
    • ratio:遮挡区域长宽比
    • value:设置遮挡区域的像素值,(R, G, B) or (Gray)
    RandomErasing(p=0.5, 
                  scale=(0.02, 0.33), 
                  ratio=(0.3, 3.3), 
                  value=0, 
                  inplace=False)
    
    #随机遮挡是在张量上操作,而不是PIL文件
    #下面的代码中给出的数据是在论文中推荐的数值,可以在不损失图片信息的情况下进行遮挡
    transforms.ToTensor(),
    transforms.RandomErasing(p=1, scale=(0.02, 0.33), 
                             ratio=(0.3, 3.3), value=(254/255, 0, 0)),
    transforms.RandomErasing(p=1, scale=(0.02, 0.33), 
                             ratio=(0.3, 3.3), value='1234'),
    
    1. transforms.Lambda

    功能:用户自定义lambda方法
    • lambd:lambda匿名函数

    transforms.Lambda(lambd)

    lambda [arg1 [,arg2, … , argn]] : expression

    transforms.TenCrop(200, vertical_flip=True),
    transforms.Lambda(lambda crops: torch.stack([transforms.Totensor()(crop) 
                                                 for crop in crops])),
    #把输出的十张PIL图片拼接成tensor文件
    

    12.6transform选择操作

    1. transforms.RandomChoice
      功能:从一系列transforms方法中随机挑选一个
    transforms.RandomChoice([transforms1, transforms2, transforms3])
    
    1. transforms.RandomApply
      功能:依据概率执行一组transforms操作
    transforms.RandomApply([transforms1, transforms2, transforms3], p=0.5)
    
    1. transforms.RandomOrder
      功能:对一组transforms操作打乱顺序
    transforms.RandomOrder([transforms1, transforms2, transforms3])
    
    #1 RandomChoice
    transforms.RandomChoice([transforms.RandomVerticalFlip(p=1), 
                             transforms.RandomHorizontalFlip(p=1)]),
    #2 RandomApply
    transforms.RandomApply([
        transforms.RandomAffine(degrees=0, shear=45, fillcolor=(255, 0, 0)),
        transforms.Grayscale(num_output_channels=3)], 
        p=0.5),
    #3.RandomOrder
    transforms.RandomOrder([transforms.RandomRotation(15),
                                transforms.Pad(padding=32),
                                transforms.RandomAffine(degrees=0, 
                                                        translate=(0.01, 0.1), 
                                                        scale=(0.9, 1.1))])
    

    12.7自定义transforms

    自定义transforms要素:

    1. 仅接收一个参数,返回一个参数
    2. 注意上下游的输出与输入
    class Compose(object):
        def __call__(self, img):
            for t in self.transforms:
            	img = t(img)
        	return img
    

    通过类实现多参数输入:

    class YourTransforms(object):
        def __init__(self, ...):
            ....
        def __call__(self, img):
            ....
            return img
    

    椒盐噪声椒盐噪声又称为脉冲噪声,是一种随机出现的白点或者黑点, 白点称为盐噪声,黑色为椒噪声。

    信噪比(Signal-Noise Rate, SNR)是衡量噪声的比例,图像中为图像像素的占比。

    class AddPepperNoise(object):
        def __init__(self, snr, p):
            self.snr = snr
            self.p = p
        def __call__(self, img):
        	```
            添加椒盐噪声具体实现过程
            ```
        	return img
    
    
    ​```python
    class AddPepperNoise(object):
        """增加椒盐噪声
        Args:
            snr (float): Signal Noise Rate
            p (float): 概率值,依概率执行该操作
        """
        def __init__(self, snr, p=0.9):
            assert isinstance(snr, float) and (isinstance(p, float))    
            self.snr = snr
            self.p = p
        def __call__(self, img):
            """
            Args:img (PIL Image): PIL Image
            Returns:PIL Image: PIL image.
            """
            if random.uniform(0, 1) < self.p:
                img_ = np.array(img).copy()
                h, w, c = img_.shape
                signal_pct = self.snr
                noise_pct = (1 - self.snr)
                mask = np.random.choice((0, 1, 2), 
                                        size=(h, w, 1), 
                                        p=[signal_pct, 
                                        noise_pct/2., 
                                        noise_pct/2.])
                mask = np.repeat(mask, c, axis=2)
                img_[mask == 1] = 255   # 盐噪声
                img_[mask == 2] = 0     # 椒噪声
                return Image.fromarray(img_.astype('uint8')).convert('RGB')
            else:
                return img
    
    train_transform = transforms.Compose([
        transforms.Resize((224, 224)),
        AddPepperNoise(0.9, p=0.5),#接受PIL文件,返回PIL文件
        transforms.ToTensor(),
        transforms.Normalize(norm_mean, norm_std),
    ])
    

    12.8总结

    数据增强原则:让训练集与测试集更接近

    • 空间位置:平移

    • 色彩:灰度图,色彩抖动

    • 形状:仿射变换

    • 上下文场景:遮挡,填充

    13.模型创建

    13.1模型创建

    13.2torch.nn

    • nn.Parameter:张量子类,表示可学习参数,如weight, bias

    • nn.Module:所有网络层基类,管理网络属性

    • nn.functional:函数具体实现,如卷积,池化,激活函数等

    • nn.init:参数初始化方法

    nn.Module中的八个字典:

    parameters : 存储管理nn.Parameter类

    modules : 存储管理nn.Module类

    buffers:存储管理缓冲属性,如BN层中的running_mean

    ***_hooks:存储管理钩子函数

    self._parameters = OrderedDict()
    self._buffers = OrderedDict()
    self._backward_hooks = OrderedDict()
    self._forward_hooks = OrderedDict()
    self._forward_pre_hooks = OrderedDict()
    self._state_dict_hooks = OrderedDict()
    self._load_state_dict_pre_hooks = OrderedDict()
    self._modules = OrderedDict()
    

    nn.Module总结

    • 一个module可以包含多个子module
    • 一个module相当于一个运算,必须实现forward()函数
    • 每个module都有8个字典管理它的属性

    13.3模型容器Containers

    • nn.Sequetial 按顺序包装多个网络层

    • nn.ModuleDict 像python的dict一样包装多个网络层

    • nn.ModuleList 像python的list一样包装多个网络层

    Sequetial容器

    class LeNetSequential(nn.Module):
        def __init__(self, classes):
            super(LeNetSequential, self).__init__()
            self.features = nn.Sequential(
                nn.Conv2d(3, 6, 5),
                nn.ReLU(),
                nn.MaxPool2d(kernel_size=2, stride=2),
                nn.Conv2d(6, 16, 5),
                nn.ReLU(),
                nn.MaxPool2d(kernel_size=2, stride=2),)
            self.classifier = nn.Sequential(
                nn.Linear(16*5*5, 120),
                nn.ReLU(),
                nn.Linear(120, 84),
                nn.ReLU(),
                nn.Linear(84, classes),)
        def forward(self, x):
            x = self.features(x)
            x = x.view(x.size()[0], -1)
            x = self.classifier(x)
            return x
    

    注意这种情况下每一层的输出都是按照序号来索引的,但是如果神经网络的层数很多的时候,这样的数字索引并不方便,所以采用下面的这种字典名称索引的方法:

    class LeNetSequentialOrderDict(nn.Module):
        def __init__(self, classes):
            super(LeNetSequentialOrderDict, self).__init__()
            self.features = nn.Sequential(OrderedDict({
                'conv1': nn.Conv2d(3, 6, 5),
                ....
            }))
            self.classifier = nn.Sequential(OrderedDict({
                'fc1': nn.Linear(16*5*5, 120),
                ....
            }))
        def forward(self, x):
            x = self.features(x)
            x = x.view(x.size()[0], -1)
            x = self.classifier(x)
            return x
    

    nn.Sequential 是 nn.module的容器,用于按顺序包装一组网络层

    • 顺序性:各网络层之间严格按照顺序构建

    • 自带forward():自带的forward里,通过for循环依次执行前向传播运算

    ModuleList容器

    nn.ModuleList是 nn.module的容器,用于包装一组网络层,以迭代方式调用网络层

    主要方法:

    • append():在ModuleList后面添加网络层

    • extend():拼接两个ModuleList

    • insert():指定在ModuleList中位置插入网络层

    class ModuleList(nn.Module):
        def __init__(self):
            super(ModuleList, self).__init__()
            self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(20)])
        def forward(self, x):
            for i, linear in enumerate(self.linears):
                x = linear(x)
            return x
    

    nn.ModuleDict容器

    是 nn.module的容器,用于包装一组网络层,以索引方式调用网络层**

    主要方法:

    • clear():清空ModuleDict

    • items():返回可迭代的键值对(key-value pairs)

    • keys():返回字典的键(key)

    • values():返回字典的值(value)

    • pop():返回一对键值,并从字典中删除

    class ModuleDict(nn.Module):
        def __init__(self):
            super(ModuleDict, self).__init__()
            self.choices = nn.ModuleDict({
                'conv': nn.Conv2d(10, 10, 3),
                'pool': nn.MaxPool2d(3)
            })
            self.activations = nn.ModuleDict({
                'relu': nn.ReLU(),
                'prelu': nn.PReLU()
            })
        def forward(self, x, choice, act):
            x = self.choices[choice](x)
            x = self.activations[act](x)
            return x
    
    
    • nn.Sequential:顺序性,各网络层之间严格按顺序执行,常用于block构建
    • nn.ModuleList:迭代性,常用于大量重复网构建,通过for循环实现重复构建
    • nn.ModuleDict:索引性,常用于可选择的网络层

    13.4AlexNet构建

    import torchvision
    alexnet = torchvision.models.AlexNet()#AlexNet源码如下
    
    class AlexNet(nn.Module):
        def __init__(self, num_classes=1000):
            super(AlexNet, self).__init__()
            self.features = nn.Sequential(
                nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=3, stride=2),
                nn.Conv2d(64, 192, kernel_size=5, padding=2),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=3, stride=2),
                nn.Conv2d(192, 384, kernel_size=3, padding=1),
                nn.ReLU(inplace=True),
                nn.Conv2d(384, 256, kernel_size=3, padding=1),
                nn.ReLU(inplace=True),
                nn.Conv2d(256, 256, kernel_size=3, padding=1),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=3, stride=2),
            )
            self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
            self.classifier = nn.Sequential(
                nn.Dropout(),
                nn.Linear(256 * 6 * 6, 4096),
                nn.ReLU(inplace=True),
                nn.Dropout(),
                nn.Linear(4096, 4096),
                nn.ReLU(inplace=True),
                nn.Linear(4096, num_classes),
            )
        def forward(self, x):
            x = self.features(x)
            x = self.avgpool(x)
            x = torch.flatten(x, 1)
            x = self.classifier(x)
            return x
    

    13.5卷积层

    1d /2d /3d Convolution

    卷积运算:卷积核在输入信号(图像)上滑动,相应位置上进行乘加

    卷积核:又称为滤波器,过滤器,可认为是某种模式,某种特征。

    卷积过程类似于用一个模版去图像上寻找与它相似的区域,与卷积核模式越相似,激活值越高,从而实现特征提取。

    卷积维度:**一般情况下,卷积核在几个维度上滑动,**就是几维卷积。

    nn.Conv2d

    #对多个二维信号进行二维卷积
    nn.Conv2d(in_channels,#输入通道数
            out_channels,#输出通道数,等价于卷积核个数
            kernel_size,#卷积核尺寸
            stride=1,#步长
            padding=0,#填充个数
            dilation=1,#空洞卷积大小
            groups=1,#分组卷积设置
            bias=True,#偏置
            padding_mode='zeros')
    

    尺寸计算:

    • 简化版

    o u t s i z e = I n size − kernelsize stride + 1 o u t_{s i z e}=\frac{I n_{\text {size}}-\text {kernelsize}}{\text {stride}}+1 outsize=strideInsizekernelsize+1

    • 完整版:

    H out = ⌊ H in + 2 ×  padding  [ 0 ] −  dilation  [ 0 ] × (  kernel  size  [ 0 ] − 1 ) − 1  stride  [ 0 ] + 1 ] H_{\text {out}}=\left\lfloor\frac{H_{\text {in}}+2 \times \text { padding }[0]-\text { dilation }[0] \times\left(\right.\text { kernel } \left._{\text {size }}[0]-1\right)-1}{\text { stride }[0]}+1\right] Hout= stride [0]Hin+2× padding [0] dilation [0]×( kernel size [0]1)1+1]

    conv_layer = nn.Conv2d(3, 1, 3)   # input:(i, o, size) weights:(o, i , h, w)
    nn.init.xavier_normal_(conv_layer.weight.data)
    # calculation
    img_conv = conv_layer(img_tensor)
    

    卷积维度:一般情况下,卷积核在几个维度上滑动,就是几维卷积

    转置卷积:

    转置卷积又称为反卷积(Deconvolution)和部分跨越卷积(Fractionally strided Convolution) ,用于对图像进行上采样(UpSample)

    nn.ConvTranspose2d( in_channels,
                        out_channels,
                        kernel_size,
                        stride=1,
                        padding=0,
                        output_padding=0,
                        groups=1,
                        bias=True,
                        dilation=1,
                        padding_mode='zeros')
    

    尺寸计算简化版:
    o u t s i z e = ( i n s i z e − 1 ) × stride + k e r n e l s i z e o u t_{s i z e}=\left(\mathrm{in}_{s i z e}-1\right) \times\text {stride}+\mathrm{kernel}_{s i z e} outsize=(insize1)×stride+kernelsize
    尺寸计算完整版:
    KaTeX parse error: Expected '}', got '_' at position 218: …+\text { output_̲padding }[0]+1

    conv_layer = nn.ConvTranspose2d(3, 1, 3, stride=2)   # input:(i, o, size)
    nn.init.xavier_normal_(conv_layer.weight.data)
    # calculation
    img_conv = conv_layer(img_tensor)
    

    13.6池化层

    池化运算:对信号进行收集”并 “总结”,类似水池收集水资源,因而得名池化层

    “收集”:多变少

    “总结”:最大值/平均值

    #最大值池化
    nn.MaxPool2d(kernel_size,#卷积核尺寸 
                 stride=None, #步长
                 padding=0,#填充个数 
                 dilation=1, #池化核大小
                 return_indices=False,#记录池化像素索引 
                 ceil_mode=False)#尺寸向上取整
    
    maxpool_layer = nn.MaxPool2d((2, 2), stride=(2, 2))   
    # input:(i, o, size) weights:(o, i , h, w)
    img_pool = maxpool_layer(img_tensor)
    

    平均值池化:

    nn.AvgPool2d(kernel_size, 
                stride=None, 
                padding=0, 
                ceil_mode=False, #尺寸向上取整
                count_include_pad=True, #填充值用于计算
                divisor_override=None)#除法因子
    

    反池化:功能:对二维信号(图像)进行最大值池化上采样

    nn.MaxUnpool2d( kernel_size, #池化核尺寸
                    stride=None, #步长
                    padding=0)#填充个数
    forward(self, input, indices, output_size=None)
    
    # pooling
    img_tensor = torch.randint(high=5, size=(1, 1, 4, 4), dtype=torch.float)
    maxpool_layer = nn.MaxPool2d((2, 2), stride=(2, 2), return_indices=True)
    img_pool, indices = maxpool_layer(img_tensor)
    # unpooling
    img_reconstruct = torch.randn_like(img_pool, dtype=torch.float)
    maxunpool_layer = nn.MaxUnpool2d((2, 2), stride=(2, 2))
    img_unpool = maxunpool_layer(img_reconstruct, indices)
    print("raw_img:\n{}\nimg_pool:\n{}".format(img_tensor, img_pool))
    print("img_reconstruct:\n{}\nimg_unpool:\n{}".format(img_reconstruct, 
                                                         img_unpool))
    

    13.7线性层

    线性层又称全连接层,其每个神经元与上一层所有神经元相连实现对前一层的线性组合,线性变换

    nn.Linear(in_features,#输入结点数 
              out_features, #输出结点数
              bias=True)#偏置
    #功能:对一维信号(向量)进行线性组合
    
    inputs = torch.tensor([[1., 2, 3]])
    linear_layer = nn.Linear(3, 4)
    linear_layer.weight.data = torch.tensor([[1., 1., 1.],
                                             [2., 2., 2.],
                                             [3., 3., 3.],
                                             [4., 4., 4.]])
    linear_layer.bias.data.fill_(0.5)
    output = linear_layer(inputs)
    print(inputs, inputs.shape)
    print(linear_layer.weight.data, linear_layer.weight.data.shape)
    print(output, output.shape)
    

    y = x W T + b i a s y=x W^{T}+ bias y=xWT+bias

    13.8激活函数层

    激活函数对特征进行非线性变换,赋予多层神经网络具有深度的意义

    常用的激活函数:

    nn.Sigmoid()
    nn.tanh()
    nn.ReLu()
    nn.LeakyReLU()#negative_slope: 负半轴斜率
    nn.PReLU()#init: 可学习斜率
    nn.RReLU()#lower: 均匀分布下限,upper:均匀分布上限
    

    14.网络层权值初始化

    14.1梯度消亡和梯度爆炸

    $$

    1. \mathrm{E}(\boldsymbol{X} \times \boldsymbol{Y})=\boldsymbol{E}(\boldsymbol{X}) \times \boldsymbol{E}(\boldsymbol{Y})\
    2. \mathrm{D}(\boldsymbol{X})=\boldsymbol{E}\left(\mathrm{X}{2}\right)-[\boldsymbol{E}(\boldsymbol{X})]{2} \
    3. \mathbf{D}(\boldsymbol{X}+\boldsymbol{Y})=\boldsymbol{D}(\boldsymbol{X})+\boldsymbol{D}(\boldsymbol{Y}) \
      $$

    D ( X × Y ) = D ( X ) × D ( Y ) + D ( X ) × [ E ( Y ) ] 2 + D ( Y ) × [ E ( X ) ] 2  若E  ( X ) = 0 , E ( Y ) = 0 D ( X × Y ) = D ( X ) × D ( Y ) \begin{array}{l} \mathrm{D}(\mathrm{X} \times \mathrm{Y})=\mathrm{D}(\mathrm{X}) \times \mathrm{D}(\mathrm{Y})+\mathrm{D}(\mathrm{X}) \times[E(\boldsymbol{Y})]^{2}+\mathrm{D}(\mathrm{Y}) \times[\boldsymbol{E}(\boldsymbol{X})]^{2} \\ \text { 若E }(\mathrm{X})=0, \mathrm{E}(\mathrm{Y})=0 \\ \quad \mathrm{D}(\mathrm{X} \times \mathrm{Y})=\mathrm{D}(\mathrm{X}) \times \mathrm{D}(\mathrm{Y}) \end{array} D(X×Y)=D(X)×D(Y)+D(X)×[E(Y)]2+D(Y)×[E(X)]2 (X)=0,E(Y)=0D(X×Y)=D(X)×D(Y)

    已知 H 1 H_1 H1层输出的结果 H 11 = ∑ i = 0 n X i × W 1 i \mathrm{H}_{11}=\sum_{i=0}^{n} X_{i} \times W_{1 i} H11=i=0nXi×W1i,根据 D ( X × Y ) = D ( X ) × D ( Y ) \quad \mathrm{D}(\mathrm{X} \times \mathrm{Y})=\mathrm{D}(\mathrm{X}) \times \mathrm{D}(\mathrm{Y}) D(X×Y)=D(X)×D(Y),可以得到:
    D ( H 11 ) = ∑ i = 0 n D ( X i ) × D ( W 1 i ) = n × ( 1 × 1 ) = n std ⁡ ( H 11 ) = D ( H 11 ) = n D ( H 1 ) = n × D ( X ) × D ( W ) = 1 D ( W ) = 1 n ⇒ std ⁡ ( W ) = 1 n \begin{array}{c} \mathbf{D}\left(\mathrm{H}_{11}\right)=\sum_{i=0}^{n} D\left(X_{i}\right) \times D\left(W_{1 i}\right) \\ =\mathrm{n} \times(1 \times 1) \\ =\mathrm{n} \\ \operatorname{std}\left(\mathrm{H}_{11}\right)=\sqrt{\mathbf{D}\left(\mathrm{H}_{11}\right)}=\sqrt{n} \\ \mathbf{D}\left(\mathrm{H}_{1}\right)=n \times D(X) \times D(W)=1 \\ \mathbf{D}(W)=\frac{1}{n} \Rightarrow \operatorname{std}(W)=\sqrt{\frac{1}{n}} \end{array} D(H11)=i=0nD(Xi)×D(W1i)=n×(1×1)=nstd(H11)=D(H11) =n D(H1)=n×D(X)×D(W)=1D(W)=n1std(W)=n1
    可以发现对于神经网络,每一层的标准差都变为原来的 n \sqrt{n} n 倍。

    def initialize(self):
       for m in self.modules():
           if isinstance(m, nn.Linear):#采用恰当的权值初始化方法
              nn.init.normal_(m.weight.data, std=np.sqrt(1/self.neural_num))    
            # normal: mean=0, std=1
    

    14.2Xavier初始化

    带有激活函数时如何进行初始化:

    方差一致性**:保持数据尺度维持在恰当范围,通常方差为1**

    激活函数:饱和函数,如Sigmoid,Tanh
    n i × D ( W ) = 1 n i + 1 × D ( W ) = 1 ⇒ D ( W ) = 2 n i + n i + 1 n_{i} \times D(W)=1 \\ n_{i+1} \times D(W)=1 \\ \Rightarrow D(W)=\frac{2}{n_{i}+n_{i+1}} ni×D(W)=1ni+1×D(W)=1D(W)=ni+ni+12

    W ∼ U [ − a , a ] D ( W ) = ( − a − a ) 2 12 = ( 2 a ) 2 12 = a 2 3 2 n i + n i + 1 = a 2 3 ⇒ a = 6 n i + n i + 1 ⇒ W ∼ U [ − 6 n i + n i + 1 , 6 n i + n i + 1 ] W \sim U[-a, a] \\ D(W)=\frac{(-a-a)^{2}}{12}=\frac{(2 a)^{2}}{12}=\frac{a^{2}}{3} \\ \frac{2}{n_{i}+n_{i+1}}=\frac{a^{2}}{3} \Rightarrow a=\frac{\sqrt{6}}{\sqrt{n_{i}+n_{i+1}}} \\ \Rightarrow \quad W \sim U\left[-\frac{\sqrt{6}}{\sqrt{n_{i}+n_{i+1}}}, \frac{\sqrt{6}}{\sqrt{n_{i}+n_{i+1}}}\right] WU[a,a]D(W)=12(aa)2=12(2a)2=3a2ni+ni+12=3a2a=ni+ni+1 6 WU[ni+ni+1 6 ,ni+ni+1 6 ]

    #利用公式进行的初始化方法
    a = np.sqrt(6 / (self.neural_num + self.neural_num))
    tanh_gain = nn.init.calculate_gain('tanh')
    a *= tanh_gain
    nn.init.uniform_(m.weight.data, -a, a)
    
    #nn模块中的Xavier初始化方法
    tanh_gain = nn.init.calculate_gain('tanh')
    nn.init.xavier_uniform_(m.weight.data, gain=tanh_gain)
    

    14.3Kaiming初始化

    方差一致性:保持数据尺度维持在恰当范围,通常方差为1

    激活函数:ReLU及其变种
    D ( W ) = 2 n i D ( W ) = 2 ( 1 + a 2 ) × n i std ⁡ ( W ) = 2 ( 1 + a 2 ) × n i \mathbf{D}(W)=\frac{2}{n_{i}} \\ \mathbf{D}(W)=\frac{2}{\left(1+\mathrm{a}^{2}\right) \times n_{i}} \\ \operatorname{std}(W)=\sqrt{\frac{2}{\left(1+\mathrm{a}^{2}\right) \times n_{i}}} D(W)=ni2D(W)=(1+a2)×ni2std(W)=(1+a2)×ni2

    nn.init.normal_(m.weight.data, std=np.sqrt(2 / self.neural_num))
    
    nn.init.kaiming_normal_(m.weight.data)
    

    计算方差变化尺度:

    nn.init.calculate_gain(nonlinearity, param=None)
    #nonlinearity: 激活函数名称
    #param: 激活函数的参数,如Leaky ReLU的negative_slop
    
    x = torch.randn(10000)
    out = torch.tanh(x)
    gain = x.std() / out.std()
    print('gain:{}'.format(gain))
    tanh_gain = nn.init.calculate_gain('tanh')
    print('tanh_gain in PyTorch:', tanh_gain)#1.666667表示方差每次会减少1.6左右
    

    15.损失函数

    损失函数:衡量模型输出与真实标签的差异

    损失函数(Loss Function):
    Loss ⁡ = f ( y ^ , y ) \operatorname{Loss} =f\left(\hat{y}, y\right) Loss=f(y^,y)
    代价函数(Cost Function):
    cos ⁡ t = 1 N ∑ i N f ( y i ^ , y i ) \cos t=\frac{1}{N} \sum_{i}^{N} f\left(\hat{y_{i}}, y_{i}\right) cost=N1iNf(yi^,yi)
    目标函数(Objective Function):
    O b j = C o s t + R e g u l a r i z a t i o n \boldsymbol{O} \boldsymbol{b} \boldsymbol{j}= Cost + Regularization Obj=Cost+Regularization

    class _Loss(Module):
        def __init__(self, size_average=None, reduce=None, reduction='mean'):
            super(_Loss, self).__init__()
            if size_average is not None or reduce is not None:
                self.reduction = _Reduction.legacy_get_string(
                                size_average, reduce)
            else:
                self.reduction = reduction
    

    1.交叉熵损失函数

    功能: nn.LogSoftmax ()与nn.NLLLoss ()结合,进行交叉熵计算

    主要参数:

    • weight:各类别的loss设置权值**

    • ignore _index**:忽略某个类别**

    • reduction :计算模式,可为none/sum /mean

      • none- 逐个元素计算
      • sum- 所有元素求和,返回标量
      • mean- 加权平均,返回标量

    熵:
    H ( P ) = E x ∼ p [ I ( x ) ] = − ∑ i N P ( x i ) log ⁡ P ( x i ) \mathrm{H}(\mathrm{P})=E_{x \sim p}[I(x)]=-\sum_{i}^{N} P\left(x_{i}\right) \log P\left(x_{i}\right) H(P)=Exp[I(x)]=iNP(xi)logP(xi)
    自信息:
    I ( x ) = − log ⁡ [ p ( x ) ] I(x)=-\log [p(x)] I(x)=log[p(x)]
    相对熵:
    D K L ( P , Q ) = E x ∼ p [ log ⁡ P ( x ) Q ( x ) ] = E x ∼ p [ log ⁡ P ( x ) − log ⁡ Q ( x ) ] = ∑ i = 1 N P ( x i ) [ log ⁡ P ( x i ) − log ⁡ Q ( x i ) ] = ∑ i = 1 N P ( x i ) log ⁡ P ( x i ) − ∑ i = 1 N P ( x i ) log ⁡ Q ( x i ) = H ( P , Q ) − H ( P ) \begin{aligned} D_{K L}(P, Q) &=E_{x \sim p}\left[\log \frac{P(x)}{Q(x)}\right] \\ &=E_{x \sim p}[\log P(x)-\log Q(x)] \\ &=\sum_{i=1}^{N} P\left(x_{i}\right)\left[\log P\left(x_{i}\right)-\log Q\left(x_{i}\right)\right] \\ &=\sum_{i=1}^{N} P\left(x_{i}\right) \log P\left(x_{i}\right)-\sum_{i=1}^{N} P\left(x_{i}\right) \log Q\left(x_{i}\right) \\ &=H(P, Q)-H(\mathrm{P}) \end{aligned} DKL(P,Q)=Exp[logQ(x)P(x)]=Exp[logP(x)logQ(x)]=i=1NP(xi)[logP(xi)logQ(xi)]=i=1NP(xi)logP(xi)i=1NP(xi)logQ(xi)=H(P,Q)H(P)
    交叉熵:
    H ( P , Q ) = D K L ( P , Q ) + H ( P ) \mathrm{H}(\boldsymbol{P}, \boldsymbol{Q})=\boldsymbol{D}_{K L}(\boldsymbol{P}, \boldsymbol{Q})+\mathrm{H}(\boldsymbol{P}) H(P,Q)=DKL(P,Q)+H(P)

    H ( P , Q ) = − ∑ i = 1 N P ( x i ) log ⁡ Q ( x i ) \mathrm{H}(\boldsymbol{P}, \boldsymbol{Q})=-\sum_{i=1}^{N} \boldsymbol{P}\left(\boldsymbol{x}_{i}\right) \log Q\left(\boldsymbol{x}_{i}\right) H(P,Q)=i=1NP(xi)logQ(xi)

    nn.CrossEntropyLoss(weight=None, #各类别的loss设置权值
                        size_average=None, 
                        ignore_index=-100,#忽略某一个类别 
                        reduce=None, 
                        reduction=‘mean’)#计算模式
    #none-逐元素计算
    #sum-所有元素求和返回标量
    #mean-加权平均,返回标量
    

    H ( P , Q ) = − ∑ i = 1 N P ( x i ) log ⁡ Q ( x i ) loss ⁡ ( x , class ) = − log ⁡ ( exp ⁡ ( x [ class ⁡ ] ) ∑ j exp ⁡ ( x [ j ] ) ) = − x [ class ⁡ ] + log ⁡ ( ∑ j exp ⁡ ( x [ j ] ) ) loss ⁡ ( x , class ) =  weight[class]  ( − x [  class  ] + log ⁡ ( ∑ j exp ⁡ ( x [ j ] ) ) ) \mathrm{H}(\boldsymbol{P}, \boldsymbol{Q})=-\sum_{\boldsymbol{i}=1}^{N} \boldsymbol{P}\left(\boldsymbol{x}_{\boldsymbol{i}}\right) \log \boldsymbol{Q}\left(\boldsymbol{x}_{\boldsymbol{i}}\right) \\ \operatorname{loss}(x, \text {class})=-\log \left(\frac{\exp (x[\operatorname{class}])}{\sum_{j} \exp (x[j])}\right)=-x[\operatorname{class}]+\log \left(\sum_{j} \exp (x[j])\right) \\ \operatorname{loss}(x, \text {class})=\text { weight[class] }\left(-x[\text { class }]+\log \left(\sum_{j} \exp (x[j])\right)\right) H(P,Q)=i=1NP(xi)logQ(xi)loss(x,class)=log(jexp(x[j])exp(x[class]))=x[class]+log(jexp(x[j]))loss(x,class)= weight[class] (x[ class ]+log(jexp(x[j])))

    # def loss function
    loss_f_none = nn.CrossEntropyLoss(weight=None, reduction='none')
    # forward
    loss_none = loss_f_none(inputs, target)
    # view
    print("Cross Entropy Loss:\n ", loss_none, loss_sum, loss_mean)
    #实现原理:
        idx = 0
        input_1 = inputs.detach().numpy()[idx]      # [1, 2]
        target_1 = target.numpy()[idx]              # [0]
        # 第一项
        x_class = input_1[target_1]
        # 第二项
        sigma_exp_x = np.sum(list(map(np.exp, input_1)))
        log_sigma_exp_x = np.log(sigma_exp_x)
        # 输出loss
        loss_1 = -x_class + log_sigma_exp_x
        print("第一个样本loss为: ", loss_1)
    #带权值:
    weights = torch.tensor([1, 2], dtype=torch.float)
    loss_f_none_w = nn.CrossEntropyLoss(weight=weights, reduction='none')
    

    2.NLLLoss

    功能:实现负对数似然函数中的负号功能
    ℓ ( x , y ) = L = { l 1 , … , l N } ′ , l n = − w y n x n , y n \ell(x, y)=L=\left\{l_{1}, \ldots, l_{N}\right\}^{\prime}, \quad l_{n}=-w_{y_{n}} x_{n, y_{n}} (x,y)=L={l1,,lN},ln=wynxn,yn

    nn.NLLLoss( weight=None,
                size_average=None, 
                ignore_index=-100, 
                reduce=None, 
                reduction='mean')
    

    3.BCELoss

    功能:二分类交叉熵
    注意事项:输入值取值在[0,1]
    主要参数:

    nn.BCELoss( weight=None, 
                size_average=None, 
                reduce=None, 
                reduction='mean')
    
    inputs = torch.tensor([[1, 2], [2, 2], [3, 4], [4, 5]], dtype=torch.float)
    target = torch.tensor([[1, 0], [1, 0], [0, 1], [0, 1]], dtype=torch.float)
    target_bce = target
    # itarget
    inputs = torch.sigmoid(inputs)
    weights = torch.tensor([1, 1], dtype=torch.float)
    loss_f_none_w = nn.BCELoss(weight=weights, reduction='none')
    # forward
    loss_none_w = loss_f_none_w(inputs, target_bce)
    

    4.BCEWithLogitsLoss

    功能:结合Sigmoid与二分类交叉熵

    注意事项:网络最后不加sigmoid函数
    l n = − w n [ y n ⋅ log ⁡ σ ( x n ) + ( 1 − y n ) ⋅ log ⁡ ( 1 − σ ( x n ) ) ] l_{n}=-w_{n}\left[y_{n} \cdot \log \sigma\left(x_{n}\right)+\left(1-y_{n}\right) \cdot \log \left(1-\sigma\left(x_{n}\right)\right)\right] ln=wn[ynlogσ(xn)+(1yn)log(1σ(xn))]

    nn.BCEWithLogitsLoss(weight=None, 
                        size_average=None, 
                        reduce=None, 
                        reduction='mean', 
                        pos_weight=None)#正样本的权值
    
    inputs = torch.tensor([[1, 2], [2, 2], [3, 4], [4, 5]], dtype=torch.float)
    target = torch.tensor([[1, 0], [1, 0], [0, 1], [0, 1]], dtype=torch.float)
    target_bce = target
    weights = torch.tensor([1], dtype=torch.float)
    pos_w = torch.tensor([3], dtype=torch.float)        # 3
    loss_f_none_w = nn.BCEWithLogitsLoss(weight=weights, reduction='none', 
                                         pos_weight=pos_w)
    loss_none_w = loss_f_none_w(inputs, target_bce)
    

    5、nn.L1Loss

    功能: 计算inputs与target之差的绝对值
    l n = ∣ x n − y n ∣ l_{n}=\left|x_{n}-y_{n}\right| ln=xnyn

    nn.L1Loss(size_average=None, 
              reduce=None, 
              reduction='mean’)
    

    6、nn.MSELoss

    功能: 计算inputs与target之差的平方
    l n = ( x n − y n ) 2 l_{n}=\left(x_{n}-y_{n}\right)^{2} ln=(xnyn)2

    nn.MSELoss(size_average=None, 
               reduce=None, 
               reduction='mean’)
    

    7、SmoothL1Loss

    功能: 平滑的L1Loss

    image-20200821160415861
    nn.SmoothL1Loss(size_average=None, 
                    reduce=None, 
                    reduction='mean’)
    

    8、PoissonNLLLoss

    功能:泊松分布的负对数似然损失函数

    • log_input = True:loss(input, target) = exp(input) - target * input

    • log_input = False:loss(input, target) = input - target * log(input+eps)

    nn.PoissonNLLLoss(log_input=True, #log_input:输入是否为对数形式,决定计算公式
                      full=False, #full:计算所有loss,默认为False
                      size_average=None, 
                      eps=1e-08,#eps:修正项,避免log(input)为nan*
                      reduce=None, 
                      reduction='mean')
    

    9.nn.KLDivLoss

    功能:计算KLD(divergence),KL散度,相对熵

    注意事项:需提前将输入计算 log-probabilities,如通过nn.logsoftmax()
    D K L ( P ∥ Q ) = E x ∼ p [ log ⁡ P ( x ) Q ( x ) ] = E x − p [ log ⁡ P ( x ) − log ⁡ Q ( x ) ] = ∑ i = 1 N P ( x i ) ( log ⁡ P ( x i ) − log ⁡ Q ( x i ) ) l n = y n ⋅ ( log ⁡ y n − x n ) D_{K L}(P \| Q)=E_{x \sim p}\left[\log \frac{P(x)}{Q(x)}\right]=E_{x-p}[\log P(x)-\log Q(x)] \\ =\sum_{i=1}^{N} P\left(x_{i}\right)\left(\log P\left(x_{i}\right)-\log Q\left(x_{i}\right)\right) \\ l_{n}=y_{n} \cdot\left(\log y_{n}-x_{n}\right) DKL(PQ)=Exp[logQ(x)P(x)]=Exp[logP(x)logQ(x)]=i=1NP(xi)(logP(xi)logQ(xi))ln=yn(logynxn)

    nn.KLDivLoss(size_average=None, 
                 reduce=None, 
                 reduction='mean')
    #reduction :none/sum/mean/batchmean
    #batchmean- batchsize维度求平均值
    
    inputs = torch.tensor([[0.5, 0.3, 0.2], [0.2, 0.3, 0.5]])
    inputs_log = torch.log(inputs)
    target = torch.tensor([[0.9, 0.05, 0.05], [0.1, 0.7, 0.2]], 
                          dtype=torch.float)
    loss_f_bs_mean = nn.KLDivLoss(reduction='batchmean')
    loss_bs_mean = loss_f_bs_mean(inputs, target)
    

    10.nn.MarginRankingLoss

    功能:计算两个向量之间的相似度,用于排序任务

    特别说明:该方法计算两组数据之间的差异,返回一个 n × n n\times n n×n的 loss 矩阵
    loss ⁡ ( x , y ) = max ⁡ ( 0 , − y × ( x 1 − x 2 ) + margin ⁡ ) \operatorname{loss}(x, y)=\max (0,-y \times(x_ 1-x _2)+\operatorname{margin}) loss(x,y)=max(0,y×(x1x2)+margin)

    nn.MarginRankingLoss(margin=0.0, #margin :边界值,x1与x2之间的差异值
                         size_average=None, 
                         reduce=None, 
                         reduction='mean')#reduction :计算模式,可为none/sum/mean
    #y = 1时, 希望x1比x2大,当x1>x2时,不产生loss
    #y = -1时,希望x2比x1大,当x2>x1时,不产生loss
    
    x1 = torch.tensor([[1], [2], [3]], dtype=torch.float)
    x2 = torch.tensor([[2], [2], [2]], dtype=torch.float)
    target = torch.tensor([1, 1, -1], dtype=torch.float)
    loss_f_none = nn.MarginRankingLoss(margin=0, reduction='none')
    loss = loss_f_none(x1, x2, target)
    print(loss)
    

    11.nn.MultiLabelMarginLoss

    功能:多标签边界损失函数

    举例:四分类任务,样本x属于0类和3类,标签:[0, 3, -1, -1] , 不是[1, 0, 0, 1]

    image-20200821161750301
    nn.MultiLabelMarginLoss(
        size_average=None, 
        reduce=None, 
        reduction='mean')
    
    x = torch.tensor([[0.1, 0.2, 0.4, 0.8]])
    y = torch.tensor([[0, 3, -1, -1]], dtype=torch.long)
    loss_f = nn.MultiLabelMarginLoss(reduction='none')
    loss = loss_f(x, y)
    #下面是手动计算的代码
    x = x[0]
    item_1 = (1-(x[0] - x[1])) + (1 - (x[0] - x[2]))    # [0]
    item_2 = (1-(x[3] - x[1])) + (1 - (x[3] - x[2]))    # [3]
    loss_h = (item_1 + item_2) / x.shape[0]
    print(loss_h)
    

    12、 nn.SoftMarginLoss

    功能:计算二分类的logistic损失
    loss ⁡ ( x , y ) = ∑ i log ⁡ ( 1 + exp ⁡ ( − y [ i ] × x [ i ] ) )  x.nelement  ( ) \operatorname{loss}(x, y)=\sum_{i} \frac{\log (1+\exp (-y[i] \times x[i]))}{\text { x.nelement }()} loss(x,y)=i x.nelement ()log(1+exp(y[i]×x[i]))

    nn.SoftMarginLoss(size_average=None, 
                      reduce=None, 
                      reduction='mean')
    

    13、nn.MultiLabelSoftMarginLoss

    功能:SoftMarginLoss多标签版本
    los ⁡ s ( x , y ) = − 1 C ∗ ∑ i y [ i ] ∗ log ⁡ ( ( 1 + exp ⁡ ( − x [ i ] ) ) − 1 ) + ( 1 − y [ i ] ) ∗ log ⁡ ( exp ⁡ ( − x [ i ] ) ( 1 + exp ⁡ ( − x [ i ] ) ) ) \operatorname{los} s(x, y)=-\frac{1}{C} * \sum_{i} y[i] * \log \left((1+\exp (-x[i]))^{-1}\right)+(1-y[i]) * \log \left(\frac{\exp (-x[i])}{(1+\exp (-x[i]))}\right) loss(x,y)=C1iy[i]log((1+exp(x[i]))1)+(1y[i])log((1+exp(x[i]))exp(x[i]))

    nn.MultiLabelSoftMarginLoss(weight=None, 
                                size_average=None, 
                                reduce=None, 
                                reduction='mean')
    

    14、nn.MultiMarginLoss

    功能:计算多分类的折页损失
    loss ⁡ ( x , y ) = ∑ i max ⁡ ( 0 , margin ⁡ − x [ y ] + x [ i ] ) ) p x . size ⁡ ( 0 ) \operatorname{loss}(x, y)=\frac{\left.\sum_{i} \max (0, \operatorname{margin}-x[y]+x[i])\right)^{p}}{x . \operatorname{size}(0)} loss(x,y)=x.size(0)imax(0,marginx[y]+x[i]))p
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D0AKeob5-1621019963125)(image-20200821162352276.png)]

    nn.MultiMarginLoss(p=1, #可选参数1或2
                       margin=1.0, #边界值
                       weight=None, #各类别的loss设置权值
                       size_average=None, 
                       reduce=None, 
                       reduction='mean')
    

    15、nn.TripletMarginLoss

    功能:计算三元组损失,人脸验证中常用
    L ( a , p , n ) = max ⁡ { d ( a i , p i ) − d ( a i , n i ) + margin ⁡ , 0 } d ( x i , y i ) = ∥ x i − y i ∥ p \begin{array}{c} L(a, p, n)=\max \left\{d\left(a_{i}, p_{i}\right)-d\left(a_{i}, n_{i}\right)+\operatorname{margin}, 0\right\} \\ \qquad d\left(x_{i}, y_{i}\right)=\left\|\mathbf{x}_{i}-\mathbf{y}_{i}\right\|_{p} \end{array} L(a,p,n)=max{d(ai,pi)d(ai,ni)+margin,0}d(xi,yi)=xiyip
    image-20200821162803647

    nn.TripletMarginLoss(margin=1.0, 
                         p=2.0, 
                         eps=1e-06, 
                         swap=False, 
                         size_average=None, 
                         reduce=None, 
                         reduction='mean')
    

    16、nn.HingeEmbeddingLoss

    功能:计算两个输入的相似性,常用于非线性embedding和半监督学习

    特别注意:输入x应为两个输入之差的绝对值。
    l n = { x n ,  if  y n = 1 max ⁡ { 0 , Δ − x n } ,  if  y n = − 1 l_{n}=\left\{\begin{array}{ll} x_{n}, & \text { if } y_{n}=1 \\ \max \left\{0, \Delta-x_{n}\right\}, & \text { if } y_{n}=-1 \end{array}\right. ln={xn,max{0,Δxn}, if yn=1 if yn=1

    nn.HingeEmbeddingLoss(margin=1.0, 
                          size_average=None, 
                          reduce=None, 
                          reduction='mean’)
    
    inputs = torch.tensor([[1., 0.8, 0.5]])
    target = torch.tensor([[1, 1, -1]])
    loss_f = nn.HingeEmbeddingLoss(margin=1, reduction='none')
    loss = loss_f(inputs, target)
    print("Hinge Embedding Loss", loss)
    

    17、nn.CosineEmbeddingLoss

    功能:采用余弦相似度计算两个输入的相似性
    loss ⁡ ( x , y ) = { 1 − cos ⁡ ( x 1 , x 2 ) ,  if  y = 1 max ⁡ ( 0 , cos ⁡ ( x 1 , x 2 ) − margin ⁡ ) ,  if  y = − 1 cos ⁡ ( θ ) = A ⋅ B ∥ A ∥ ∥ B ∥ = ∑ i = 1 n A i × B i ∑ i = 1 n ( A i ) 2 × ∑ i = 1 n ( B i ) 2 \begin{array}{l} \operatorname{loss}(x, y)=\left\{\begin{array}{ll} 1-\cos \left(x_{1}, x_{2}\right), & \text { if } y=1 \\ \max \left(0, \cos \left(x_{1}, x_{2}\right)-\operatorname{margin}\right), & \text { if } y=-1 \end{array}\right. \\ \cos (\theta)=\frac{A \cdot B}{\|A\|\|B\|}=\frac{\sum_{i=1}^{n} A_{i} \times B_{i}}{\sqrt{\sum_{i=1}^{n}\left(A_{i}\right)^{2}} \times \sqrt{\sum_{i=1}^{n}\left(B_{i}\right)^{2}}} \end{array} loss(x,y)={1cos(x1,x2),max(0,cos(x1,x2)margin), if y=1 if y=1cos(θ)=ABAB=i=1n(Ai)2 ×i=1n(Bi)2 i=1nAi×Bi

    nn.CosineEmbeddingLoss(margin=0.0, #可取值[-1, 1] , 推荐为[0, 0.5]
                           size_average=None, 
                           reduce=None, 
                           reduction='mean')
    

    18、nn.CTCLoss

    功能: 计算CTC损失,解决时序类数据的分类

    Connectionist Temporal Classification

    torch.nn.CTCLoss(blank=0, 
                     reduction='mean', 
                     zero_infinity=False)
    
    image-20200821163417976

    16.优化器

    pytorch的优化器:管理并更新模型中可学习参数的值,使得模型输出更接近真实标签

    导数:函数在指定坐标轴上的变化率

    方向导数:指定方向上的变化率

    梯度:一个向量,方向为方向导数取得最大值的方向

    image-20200821163927532
    class Optimizer(object):
        def __init__(self, params, defaults):
            self.defaults = defaults
            self.state = defaultdict(dict)
            self.param_groups = []
            param_groups = [{'params': param_groups}]
    

    基本属性

    • defaults:优化器超参数
    • state:参数的缓存,如momentum的缓存
    • params_groups:管理的参数组
    • _step_count:记录更新次数,学习率调整中使用

    基本方法

    zero_grad():清空所管理参数的梯度

    pytorch特性:张量梯度不自动清零

    class Optimizer(object):
        def zero_grad(self):
            for group in self.param_groups:
                for p in group['params']:
                    if p.grad is not None:
                        p.grad.detach_()
                        p.grad.zero_()
    

    step():执行一步更新

    class Optimizer(object):
        def __init__(self, params, defaults):
            self.defaults = defaults
            self.state = defaultdict(dict)
            self.param_groups = []
    

    add_param_group():添加参数组

    class Optimizer(object):
        def add_param_group(self, param_group):
            for group in self.param_groups:
                param_set.update(set(group['params’]))
                self.param_groups.append(param_group)
    

    state_dict():获取优化器当前状态信息字典

    load_state_dict() :加载状态信息字典

    class Optimizer(object):
        def state_dict(self):
            return {'state': packed_state, 'param_groups': param_groups, }
        def load_state_dict(self, state_dict):
    
    optimizer = optim.SGD([weight], lr=0.1, momentum=0.9)
    opt_state_dict = optimizer.state_dict()
    print("state_dict before step:\n", opt_state_dict)
    for i in range(10):
        optimizer.step()
    print("state_dict after step:\n", optimizer.state_dict())
    torch.save(optimizer.state_dict(), os.path.join(BASE_DIR, 
                                                    "optimizer_state_dict.pkl"))
    

    学习率:
    w i + 1 = w i − g ( w i ) w i + 1 = w i − LR g ( w i ) w_{i+1}=w_{i}-g\left(w_{i}\right)\\ w_{i+1}=w_{i}-\text{LR}g\left(w_{i}\right) wi+1=wig(wi)wi+1=wiLRg(wi)
    学习率(learning rate)控制更新的步伐。

    Momentum(动量,冲量):结合当前梯度与上一次更新信息,用于当前更新。

    iter_rec, loss_rec, x_rec = list(), list(), list()
    lr = 0.01    # /1. /.5 /.2 /.1 /.125
    max_iteration = 20   # /1. 4     /.5 4   /.2 20 200
    for i in range(max_iteration):
       y = func(x)
       y.backward()
       print("Iter:{}, X:{:8}, X.grad:{:8}, loss:{:10}".format(
               i, x.detach().numpy()[0], x.grad.detach().numpy()[0], y.item()))
       x_rec.append(x.item())
       x.data.sub_(lr * x.grad)    # x -= x.grad  数学表达式意义:  x = x - x.grad  
       x.grad.zero_()
       iter_rec.append(i)
       loss_rec.append(y)
    

    指数加权平均:
    v t = β × v t − 1 + ( 1 − β ) × θ t = ∑ i N ( 1 − β ) × β i × θ N − i v_t=\beta\times v_{t-1}+(1-\beta)\times \theta_t\\ =\sum_{i}^{N}(1-\boldsymbol{\beta}) \times \boldsymbol{\beta}^{i} \times \boldsymbol{\theta}_{N-i} vt=β×vt1+(1β)×θt=iN(1β)×βi×θNi

    def exp_w_func(beta, time_list):
        return [(1 - beta) * np.power(beta, exp) for exp in time_list]
     beta_list = [0.98, 0.95, 0.9, 0.8]
     w_list = [exp_w_func(beta, time_list) for beta in beta_list]
    

    pytorch中参数更新的公式:
    v i = m × v i − 1 + g ( w i ) w i + 1 = w i − L R × v i v_i=m\times v_{i-1}+g(w_i)\\ w_{i+1}=w_i-LR\times v_i vi=m×vi1+g(wi)wi+1=wiLR×vi

    • w i + 1 w_{i+1} wi+1是第i+1次更新的参数
    • l r lr lr:学习率
    • v i v_i vi:更新量
    • m m m:momentum参数
    • g ( w i ) g(w_i) g(wi) w i w_i wi的梯度

    optim.SGD

    optim.SGD(params, #管理的参数组
              lr=<object object>,#初始学习率 
              momentum=0, #动量系数
              dampening=0, 
              weight_decay=0,#L2正则化系数 
              nesterov=False)#是否采用NAG
    
    #其他的优化器
    1. optim.SGD:随机梯度下降法
    2. optim.Adagrad:自适应学习率梯度下降法
    3. optim.RMSprop: Adagrad的改进
    4. optim.Adadelta: Adagrad的改进
    5. optim.Adam:RMSprop结合Momentum
    6. optim.Adamax:Adam增加学习率上限
    7. optim.SparseAdam:稀疏版的Adam
    8. optim.ASGD:随机平均梯度下降
    9. optim.Rprop:弹性反向传播
    10. optim.LBFGS:BFGS的改进
    

    17.学习率

    先使用大学习率后使用小学习率。

    基类:class _LRScheduler

    • optimizer:关联的优化器
    • last_epoch:记录epoch数
    • base_lrs:记录初始学习率

    主要方法:

    • step():更新下一个epoch的学习率
    • get_lr():虚函数,计算下一个epoch的学习率
    class _LRScheduler(object):
        def __init__(self, optimizer, last_epoch=-1):
        def get_lr(self):
            raise NotImplementedError
    

    六种学习率调整策略:

    1.lr_scheduler.StepLR
    lr = lr × gamma \text{lr}=\text{lr}\times \text{gamma} lr=lr×gamma

    #等间隔调整学习率
    lr_scheduler.StepLR(optimizer, 
                        step_size, #调整间隔数
                        gamma=0.1, #调整系数
                        last_epoch=-1)
    

    2.lr_scheduler.MultiStepLR

    功能:按给定间隔调整学习率
    lr = lr × gamma \text{lr}=\text{lr}\times \text{gamma} lr=lr×gamma

    lr_scheduler.MultiStepLR(optimizer, 
                             milestones,#设定调整时刻数 
                             gamma=0.1, 
                             last_epoch=-1)
    

    3.lr_scheduler.ExponentialLR

    功能:按照指数衰减调整学习率
    lr = lr × gamma epoches \text{lr}=\text{lr}\times \text{gamma}^{\text{epoches}} lr=lr×gammaepoches

    lr_scheduler.ExponentialLR(optimizer, 
                               gamma, #指数的底
                               last_epoch=-1)
    

    4.lr_scheduler.CosineAnnealingLR

    功能:余弦周期调整学习率
    η t = η min ⁡ + 1 2 ( η max ⁡ − η min ⁡ ) ( 1 + cos ⁡ ( T c u r T max ⁡ π ) ) \eta_{t}=\eta_{\min }+\frac{1}{2}\left(\eta_{\max }-\eta_{\min }\right)\left(1+\cos \left(\frac{T_{c u r}}{T_{\max }} \pi\right)\right) ηt=ηmin+21(ηmaxηmin)(1+cos(TmaxTcurπ))

    lr_scheduler.CosineAnnealingLR(optimizer, 
                                   T_max, #下降周期
                                   eta_min=0, #学习率下限
                                   last_epoch=-1)
    

    5.lr_scheduler.ReduceLROnPlateau

    功能:监控指标,当指标不再变化则调整

    lr_scheduler.ReduceLROnPlateau(optimizer, 
                                   mode='min',#模式 min/max模式 
                                   factor=0.1, #调整系数
                                   patience=10, #“耐心”,接受几次没变化
                                   verbose=False, #是否打印日志
                                   threshold=0.0001, 
                                   threshold_mode='rel', 
                                   cooldown=0, #冷却时间
                                   min_lr=0, #设置学习率下限
                                   eps=1e-08)#学习率衰减最小值
    

    6.lr_scheduler.LambdaLR

    功能:自定义调整策略

    lr_scheduler.LambdaLR(optimizer, 
                          lr_lambda,#lr_lambda:function or list 
                          last_epoch=-1)
    
    lr_init = 0.1
    weights_1 = torch.randn((6, 3, 5, 5))
    weights_2 = torch.ones((5, 5))
    optimizer = optim.SGD([
            {'params': [weights_1]},
            {'params': [weights_2]}], lr=lr_init)
    lambda1 = lambda epoch: 0.1 ** (epoch // 20)
    lambda2 = lambda epoch: 0.95 ** epoch
    scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, 
                                                  lr_lambda=[lambda1, lambda2])
    lr_list, epoch_list = list(), list()
    for epoch in range(max_epoch):
          for i in range(iteration):
                # train(...)
                optimizer.step()
                optimizer.zero_grad()
           scheduler.step()
           lr_list.append(scheduler.get_lr())
           epoch_list.append(epoch)
           print('epoch:{:5d}, lr:{}'.format(epoch, scheduler.get_lr()))
    

    学习率调整小结

    有序调整:Step、MultiStep、Exponential 和 CosineAnnealing

    自适应调整:ReduceLROnPleateau

    自定义调整:Lambda

    1, #调整系数
    last_epoch=-1)

    
    **==2.lr_scheduler.MultiStepLR==**
    
    功能:按给定间隔调整学习率
    $$
    \text{lr}=\text{lr}\times \text{gamma}
    $$
    
    ```python
    lr_scheduler.MultiStepLR(optimizer, 
                             milestones,#设定调整时刻数 
                             gamma=0.1, 
                             last_epoch=-1)
    

    3.lr_scheduler.ExponentialLR

    功能:按照指数衰减调整学习率
    lr = lr × gamma epoches \text{lr}=\text{lr}\times \text{gamma}^{\text{epoches}} lr=lr×gammaepoches

    lr_scheduler.ExponentialLR(optimizer, 
                               gamma, #指数的底
                               last_epoch=-1)
    

    4.lr_scheduler.CosineAnnealingLR

    功能:余弦周期调整学习率
    η t = η min ⁡ + 1 2 ( η max ⁡ − η min ⁡ ) ( 1 + cos ⁡ ( T c u r T max ⁡ π ) ) \eta_{t}=\eta_{\min }+\frac{1}{2}\left(\eta_{\max }-\eta_{\min }\right)\left(1+\cos \left(\frac{T_{c u r}}{T_{\max }} \pi\right)\right) ηt=ηmin+21(ηmaxηmin)(1+cos(TmaxTcurπ))

    lr_scheduler.CosineAnnealingLR(optimizer, 
                                   T_max, #下降周期
                                   eta_min=0, #学习率下限
                                   last_epoch=-1)
    

    5.lr_scheduler.ReduceLROnPlateau

    功能:监控指标,当指标不再变化则调整

    lr_scheduler.ReduceLROnPlateau(optimizer, 
                                   mode='min',#模式 min/max模式 
                                   factor=0.1, #调整系数
                                   patience=10, #“耐心”,接受几次没变化
                                   verbose=False, #是否打印日志
                                   threshold=0.0001, 
                                   threshold_mode='rel', 
                                   cooldown=0, #冷却时间
                                   min_lr=0, #设置学习率下限
                                   eps=1e-08)#学习率衰减最小值
    

    6.lr_scheduler.LambdaLR

    功能:自定义调整策略

    lr_scheduler.LambdaLR(optimizer, 
                          lr_lambda,#lr_lambda:function or list 
                          last_epoch=-1)
    
    lr_init = 0.1
    weights_1 = torch.randn((6, 3, 5, 5))
    weights_2 = torch.ones((5, 5))
    optimizer = optim.SGD([
            {'params': [weights_1]},
            {'params': [weights_2]}], lr=lr_init)
    lambda1 = lambda epoch: 0.1 ** (epoch // 20)
    lambda2 = lambda epoch: 0.95 ** epoch
    scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, 
                                                  lr_lambda=[lambda1, lambda2])
    lr_list, epoch_list = list(), list()
    for epoch in range(max_epoch):
          for i in range(iteration):
                # train(...)
                optimizer.step()
                optimizer.zero_grad()
           scheduler.step()
           lr_list.append(scheduler.get_lr())
           epoch_list.append(epoch)
           print('epoch:{:5d}, lr:{}'.format(epoch, scheduler.get_lr()))
    

    学习率调整小结

    有序调整:Step、MultiStep、Exponential 和 CosineAnnealing

    自适应调整:ReduceLROnPleateau

    自定义调整:Lambda

    展开全文
  • pytorch [::-1] 镜像翻转

    千次阅读 2020-10-13 18:36:08
    Tensor的镜像翻转 在使用numpy时我们可以对数组进行镜像翻转操作,如以下例子 import numpy as np array = np.array(range(10)) print(array) print(array[::-1]) [0 1 2 3 4 5 6 7 8 9] [9 8 7 6 5 4 3 2 1 0] ...

    本文转载于https://heroinlin.github.io/2018/03/12/Pytorch/Pytorch_tensor_flip/

    Tensor的镜像翻转

    在使用numpy时我们可以对数组进行镜像翻转操作,如以下例子

    import numpy as np
    array = np.array(range(10))
    print(array)
    print(array[::-1])
    
    [0 1 2 3 4 5 6 7 8 9]
    [9 8 7 6 5 4 3 2 1 0]
    

    但是在pytorch中并不能通过tensor[::-1]进行镜像的翻转,此处给出了tensor的镜像翻转方法

    # https://github.com/pytorch/pytorch/issues/229
    import torch
    from torch.autograd import Variable
    def flip(x, dim):
        xsize = x.size()
        dim = x.dim() + dim if dim < 0 else dim
        x = x.view(-1, *xsize[dim:])
        x = x.view(x.size(0), x.size(1), -1)[:, getattr(torch.arange(x.size(1)-1, 
                          -1, -1), ('cpu','cuda')[x.is_cuda])().long(), :]
        return x.view(xsize)
    
    
    # Code to test it with cpu Variable
    a = Variable(torch.Tensor([range(1, 25)]).view(1, 2, 3, 4))
    print(a)
    print(flip(a, 0)) # Or -4
    print(flip(a, 1)) # Or -3
    print(flip(a, 2)) # Or -2
    print(flip(a, 3)) # Or -1
    
    tensor([[[[ 1.,  2.,  3.,  4.],
              [ 5.,  6.,  7.,  8.],
              [ 9., 10., 11., 12.]],
    
             [[13., 14., 15., 16.],
              [17., 18., 19., 20.],
              [21., 22., 23., 24.]]]])
    tensor([[[[ 1.,  2.,  3.,  4.],
              [ 5.,  6.,  7.,  8.],
              [ 9., 10., 11., 12.]],
    
             [[13., 14., 15., 16.],
              [17., 18., 19., 20.],
              [21., 22., 23., 24.]]]])
    tensor([[[[13., 14., 15., 16.],
              [17., 18., 19., 20.],
              [21., 22., 23., 24.]],
    
             [[ 1.,  2.,  3.,  4.],
              [ 5.,  6.,  7.,  8.],
              [ 9., 10., 11., 12.]]]])
    tensor([[[[ 9., 10., 11., 12.],
              [ 5.,  6.,  7.,  8.],
              [ 1.,  2.,  3.,  4.]],
    
             [[21., 22., 23., 24.],
              [17., 18., 19., 20.],
              [13., 14., 15., 16.]]]])
    tensor([[[[ 4.,  3.,  2.,  1.],
              [ 8.,  7.,  6.,  5.],
              [12., 11., 10.,  9.]],
    
             [[16., 15., 14., 13.],
              [20., 19., 18., 17.],
              [24., 23., 22., 21.]]]])
    

    以下是pytorch>=0.4.0的代码

    # https://github.com/pytorch/pytorch/issues/229
    import torch
    def flip(x, dim):
        indices = [slice(None)] * x.dim()
        indices[dim] = torch.arange(x.size(dim) - 1, -1, -1,
                                    dtype=torch.long, device=x.device)
        return x[tuple(indices)]
    
    展开全文
  • 医学影像数据增强——水平翻转 from PIL import Image import numpy as np import os import os.path import cv2 ### 水平翻转 === rootdir = r'1' # 指明被遍历的文件夹,取文件名1下面的全部图片 for parent, ...
    from PIL import Image
    import numpy as np
    import os
    import os.path
    import cv2
    ### 水平翻转 ===
    rootdir = r'1'  # 指明被遍历的文件夹,取文件名1下面的全部图片
    for parent, dirnames, filenames in os.walk(rootdir):
    	for filename in filenames:
    		print('parent is :' + parent)
    		print('filename is :' + filename)
    		currentPath = os.path.join(parent, filename)
    		print('the fulll name of the file is :' + currentPath)
    		im = Image.open(currentPath)
    		out = im.transpose(Image.FLIP_LEFT_RIGHT)
    		newname = r"1pre" + '/' + filename + "_sym.jpg" #保存为"1pre/原文件名__sym.jpg" 
    		out.save(newname)
    
    展开全文
  • PyTorch

    2019-09-23 12:23:19
    随机水平翻转 random_horizontal_flip = tfs.RandomHorizontalFlip() pic2 = random_horizontal_flip(pic) plt.subplot( 1,2,1 ) plt.imshow(pic1) plt.xlabel( ' vertical ' ) plt.legend() plt.subplot( 1,2,...

    1.Torch.nn

    class torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)

    卷积

    //1是输入图像的channel
    //6是输出图像的channel
    //5是卷积核大小
    nn.Conv2d(1, 6, 5)

    class torch.nn.Linear(in_features, out_features, bias=True)

    对输入数据做线性变换:y=Ax+b,全连接

    2.torch.nn.functional

    torch.nn.functional.max_pool2d(input, kernel_size, stride=None, padding=0, dilation=1, ceil_mode=False, return_indices=False)

    import torch.nn.functional as F
    //使用2*2的核进行maxpooling
    x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))

    3.torch.optim

    import torch.optim as optim
    # create your optimizer optimizer = optim.SGD(net.parameters(), lr = 0.01) # in your training loop: optimizer.zero_grad() # zero the gradient buffers output = net(input) loss = criterion(output, target) loss.backward() optimizer.step() # Does the update

    4.torch.cat(inputs, dimension=0) → Tensor

     

    torch.cat(inputs, dimension=0) → Tensor通道合并

    x = torch.randn(2, 3)
    
    0.5983 -0.0341 2.4918
    
    1.5981 -0.5265 -0.8735
    
    torch.cat((x, x,), 0)
    0.5983 -0.0341 2.4918
    
    1.5981 -0.5265 -0.8735
    0.5983 -0.0341 2.4918
    
    1.5981 -0.5265 -0.8735

    将多维展开到1维 

    x = x.view(x.size(0),-1)

    5.torch.autograd

    class torch.autograd.Variable

    2个重要变量

    • data – 包含的Tensor

    • grad – 保存着Variable的梯度。

    6.torch.nn.Conv2d

    class torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)

    7.torch.nn.BatchNorm2d

    class torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True)

    8.torch.nn.MaxPool2d

    class torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)

    PyTorch数据增强方法

    1.对图片进行一定比例缩放

    torchvision.transforms.Resize()

      第一个参数是一个 tuple,图片会直接把宽和高缩放到这个大小;第二个参数表示放缩图片使用的方法,比如最邻近法,或者双线性差值等,一般双线性差值能够保留图片更多的信息,所以 pytorch 默认使用的是双线性差值

    import matplotlib.pyplot as plt
    from torchvision import transforms as tfs
    from PIL import Image
    #pic.shape(640, 1024, 3)
    pic = Image.open('bridge.jpg')
    resize = tfs.Resize((300,300))
    pic1 = resize(pic)
    
    plt.subplot(1,2,1)
    plt.imshow(pic)
    plt.axis('off') # 不显示坐标轴
    
    plt.subplot(1,2,2)
    plt.imshow(pic1)
    plt.axis('off') # 不显示坐标轴
    
    plt.show()

    2.对图片进行随机位置的截取

      随机位置截取能够提取出图片中局部的信息,使得网络接受的输入具有多尺度的特征,所以能够有较好的效果。在 torchvision 中主要有下面两种方式,一个是 torchvision.transforms.RandomCrop(),传入的参数就是截取出的图片的长和宽,对图片在随机位置进行截取;第二个是 torchvision.transforms.CenterCrop()

    传入的参数就是截取出的图片的长和宽,会在图片的中心进行截取

    import matplotlib.pyplot as plt
    from torchvision import transforms as tfs
    from PIL import Image
    #pic.shape(640, 1024, 3)
    pic = Image.open('bridge.jpg')
    #随机位置截取,多截几次结果不一样
    random_crop = tfs.RandomCrop((400,400))
    pic1 = random_crop(pic)
    
    #中心位置截取,多截几次结果一样
    center_crop = tfs.CenterCrop((400,400))
    pic2 = center_crop(pic)
    plt.subplot(1,2,1)
    plt.imshow(pic1)
    plt.xlabel('RandomCrop')
    plt.legend()
    
    plt.subplot(1,2,2)
    plt.imshow(pic2)
    plt.xlabel('CenterCrop')
    plt.legend()
    
    plt.show()

    3.对图片进行随机的水平和竖直翻转

    随机翻转使用的是 torchvision.transforms.RandomHorizontalFlip() 和 torchvision.transforms.RandomVerticalFlip(),翻转概率是0.5,有可能不翻转

    import matplotlib.pyplot as plt
    from torchvision import transforms as tfs
    from PIL import Image
    #pic.shape(640, 1024, 3)
    pic = Image.open('bridge.jpg')
    #随机垂直翻转
    random_vertical_flip = tfs.RandomVerticalFlip()
    pic1 = random_vertical_flip(pic)
    
    #随机水平翻转
    random_horizontal_flip = tfs.RandomHorizontalFlip()
    pic2 = random_horizontal_flip(pic)
    plt.subplot(1,2,1)
    plt.imshow(pic1)
    plt.xlabel('vertical')
    plt.legend()
    
    plt.subplot(1,2,2)
    plt.imshow(pic2)
    plt.xlabel('horizontal')
    plt.legend()
    
    plt.show()

    4.对图片进行随机角度的旋转

      在 torchvision 中,使用 torchvision.transforms.RandomRotation() 来实现,其中第一个参数就是随机旋转的角度,比如填入 10,那么每次图片就会在 -10 ~ 10 度之间随机旋转

    import matplotlib.pyplot as plt
    from torchvision import transforms as tfs
    from PIL import Image
    #pic.shape(640, 1024, 3)
    pic = Image.open('bridge.jpg')
    #随机旋转10度
    random_rotation = tfs.RandomRotation(10)
    pic1 = random_rotation(pic)
    
    plt.subplot(1,2,1)
    plt.imshow(pic)
    plt.xlabel('original')
    plt.legend()
    
    plt.subplot(1,2,2)
    plt.imshow(pic1)
    plt.xlabel('random_rotation')
    plt.legend()
    
    plt.show()

    5.对图片进行亮度、对比度和颜色的随机变化

      在 torchvision 中主要使用 torchvision.transforms.ColorJitter() 来实现的,第一个参数就是亮度的比例,第二个是对比度,第三个是饱和度,第四个是颜色

    参数:

    brightness(亮度,float类型)——调整亮度的程度,从 [max(0,1-brightness), 1+brightness] 中均匀选取。

    contrast(对比度,float类型)——调整对比度的程度,从 [max(0,1-contrast),1+contrast] 中均匀选取。
    saturation(饱和度,float类型)——调整饱和度的程度, [max(0,1-saturation),1+saturation] 中均匀选取。
    hue(色相,float类型) —— 调整色相的程度,从 [-hue,hue] 等均匀选择, 其中hue的大小为 [0, 0.5]。

     

    pic = Image.open('bridge.jpg')
    #亮度
    brightness = tfs.ColorJitter(brightness=0.5)
    pic1 = brightness(pic)
    #对比度
    contrast = tfs.ColorJitter(contrast=0.4)
    pic2 = contrast(pic)
    #饱和度
    saturation = tfs.ColorJitter(saturation=0.3)
    pic3 = saturation(pic)
    #饱和度
    hue = tfs.ColorJitter(hue=0.4)
    pic4 = hue(pic)

    亮度

    对比度

    饱和度

    色相

    多个数据增强方法组合使用 

    import matplotlib.pyplot as plt
    from torchvision import transforms as tfs
    from PIL import Image
    #pic.shape(640, 1024, 3)
    pic = Image.open('bridge.jpg')
    
    aug = tfs.Compose([
        #旋转5度
        tfs.RandomRotation(5),
        #垂直翻转
        tfs.RandomVerticalFlip(),
        #随机裁剪到400,,400
        tfs.RandomCrop((400,400))
    ])
    
    pic1 = aug(pic)
    plt.subplot(1,2,1)
    plt.imshow(pic)
    plt.xlabel('original')
    plt.legend()
    
    plt.subplot(1,2,2)
    plt.imshow(pic1)
    plt.xlabel('aug')
    plt.legend()
    
    plt.show()

     将增强后的PIL.Image转换为PyTorch的Tensor供神经网络处理,使用 torchvision.transforms.ToTensor(),使用MNIST数据集并对数据集增强

    #数据增强
    def data_augmentation(x):
        transform = torchvision.transforms.Compose([
            torchvision.transforms.Resize(40),
            torchvision.transforms.RandomHorizontalFlip(),
            torchvision.transforms.RandomHorizontalFlip(),
            torchvision.transforms.RandomCrop(28),
            torchvision.transforms.ColorJitter(brightness=0.5, contrast=0.5, hue=0.5),
            torchvision.transforms.ToTensor()
        ])
        x = transform(x)
        return x
    
    train_data = torchvision.datasets.MNIST(
        './mnist', train=True, transform=data_augmentation, download=True
    )

    torchvision.transforms.Normalize

    class torchvision.transforms.Normalize(mean, std),这个数据增强方法会把数据的通道数变为3,在MNIST上本来单通道的结果变成3通道了

     

    转载于:https://www.cnblogs.com/vshen999/p/11189130.html

    展开全文
  • PyTorch框架

    2021-03-11 09:10:31
    整理 深度之眼 pytorch训练营 笔记
  • Pytorch 学习

    2021-07-14 10:45:53
    PyTorch 是一个基于 Torch 的 Python 开源机器学习库(Python+Torch(深度学习框架)),由Facebook的人工智能研究小组开发。 Pytorch类似于Numpy,可以使用GPU,运行在CUDA上;内置动态图,可以定义深度学习模型,...
  • Pytorch CookBook

    2021-02-02 20:12:16
    检查 PyTorch 版本: torch.__version__ # PyTorch version torch.version.cuda # Corresponding CUDA version torch.backends.cudnn.version() # Corresponding cuDNN version torch.cuda.get_device_name(0) # GPU...
  • pytorch transform

    2019-09-22 09:39:21
    本文截取自《PyTorch 模型训练实用教程》,获取全文pdf请点击:https://github.com/tensor-yu/PyTorch_Tutorial 文章目录 一、 裁剪——Crop 1.随机裁剪:transforms.RandomCrop 2.中心裁剪:tra...
  • 核心代码 train_transform = transforms.Compose([ ... transforms.RandomHorizontalFlip(),#随机左右翻转 transforms.ToTensor()#必不可少的数据转换 ]) 效果图 填充、随机裁剪效果 Cutout() 的遮挡效
  • rows = 2, cols = 4, scale = 1.5): Y = [aug(img) for _ in range(0,rows*cols)] show_img(Y, rows, cols, scale) 水平翻转 # 采用默认的行列 apply(img, torchvision.transforms.RandomHorizontalFlip()) 垂直翻转...
  • Pytorch 图片处理与数据增广 本方法总结自《动手学深度学习》(Pytorch版)github项目 Pytorch 数据增广主要依赖于 torchvision....aug1 = torchvision.transforms.RandomHorizontalFlip(p=0.5) # 随机水平翻转,
  • 实现的功能是,1tensor翻转 1.tensor翻转 x=torch.tensor([i for i in range(10)]) #首先要把shape是(n,)的tensor换成(n,1)的 y=x.reshape(x.shape[0],1) #然后使用torch.flip翻转 z=torch.flip(y,[0,1]) ...
  • Pytorch学习笔记

    万次阅读 2021-05-15 00:29:08
    class torchvision.transforms.RandomCrop(size, padding=...随机水平翻转给定的PIL.Image,概率为0.5。即:一半的概率翻转,一半的概率不翻转。 .转为tensor:transforms.ToTensor class torchvision.transforms.ToTen.
  • Pytorch 使用技巧

    2021-03-10 14:29:40
    不少读者问我,Pytorch深度学习框架怎么学,答曰:Github大法好啊! 这个是我当初入门Pytorch的教程,简洁易懂: https://github.com/yunjey/pytorch-tutorial 其实,学习Pytorch最好的资料是官方文档,不过需要...
  • Pytorch手册

    2019-08-28 17:39:31
    PyTorch 主要提供以下两大特色: 支持强力GPU加速的Tensor计算能力 基于tape的具有自动微分求导能力的深度神经网络框架 PyTorch 主要包含以下组成要素: 组成要素 描述说明 torch 一个类似于numpy的tensor...
  • RandomHorizontalFlip() class torchvision....功能:依据概率p对PIL图片进行水平翻转,p默认0.5 RandomVerticalFlip() class torchvision.transforms.RandomVerticalFlip() **功能:**按照概率p对PIL图片...
  • pytorch 猫狗大战

    千次阅读 2019-09-29 14:53:32
    pytorch 迁移学习 猫狗大战 kaggle