精华内容
下载资源
问答
  • godweiyang:PyTorch自定义CUDA算子教程与运行时间分析godweiyang:详解PyTorch编译并调用自定义CUDA算子的三种方式本文我们将讲解如何用自定义cuda算子搭建一个简单的神经网络,并实现反向传播,进行模型训练。...

    在前面两篇教程中,我们详细讲解了如何编写cuda算子,并用PyTorch进行调用,并且详细讲述了三种编译cuda算子的方式,具体可以看前面两篇:

    godweiyang:PyTorch自定义CUDA算子教程与运行时间分析godweiyang:详解PyTorch编译并调用自定义CUDA算子的三种方式

    本文我们将讲解如何用自定义cuda算子搭建一个简单的神经网络,并实现反向传播,进行模型训练。

    完整的代码还是放在了github仓库,欢迎大家star并fork:

    godweiyang/torch-cuda-example

    本文主要涉及到的是train.py这个代码,功能是搭建了一个PyTorch模型,并且调用了自定义的cuda算子,实现了自定义的反向传播函数,最终完成训练。

    模型描述

    之前我们实现了一个

    a + b的tensor求和cuda算子,于是我们可以利用它来实现
    \mathcal{L} = a^2 + b^2

    最终训练收敛后

    a
    b都会趋近于0,模型没有输入,只有两个可训练的参数
    a
    b

    搭建模型

    首先我们还是像正常写PyTorch模型那样搭建一个模型,代码如下:

    class AddModel(nn.Module):
        def __init__(self, n):
            super(AddModel, self).__init__()
            # tensor长度
            self.n = n
            # 定义可训练参数a和b
            self.a = nn.Parameter(torch.Tensor(self.n))
            self.b = nn.Parameter(torch.Tensor(self.n))
            # 正态分布初始化参数a和b
            self.a.data.normal_(mean=0.0, std=1.0)
            self.b.data.normal_(mean=0.0, std=1.0)
    
        def forward(self):
            # 求a^2与b^2
            a2 = torch.square(self.a)
            b2 = torch.square(self.b)
            # 调用自定义cuda算子对两个平方数求和
            c = AddModelFunction.apply(a2, b2, self.n)
            return c

    重点就在调用自定义cuda算子那一行AddModelFunction.apply(),你也可以写成c = a2 + b2。不过这里我们为了演示如何使用自定义cuda算子,所以不这么干了。

    实现自定义cuda算子前向和反向传播

    下面就是如何实现AddModelFunction.apply()函数了,我们先来看一下具体代码:

    class AddModelFunction(Function):
        @staticmethod
        def forward(ctx, a, b, n):
            c = torch.empty(n).to(device="cuda:0")
    
            if args.compiler == 'jit':
                cuda_module.torch_launch_add2(c, a, b, n)
            elif args.compiler == 'setup':
                add2.torch_launch_add2(c, a, b, n)
            elif args.compiler == 'cmake':
                torch.ops.add2.torch_launch_add2(c, a, b, n)
            else:
                raise Exception("Type of cuda compiler must be one of jit/setup/cmake.")
    
            return c
    
        @staticmethod
        def backward(ctx, grad_output):
            return (grad_output, grad_output, None)

    这个类继承的是torch.autograd.Function类,我们可以用它来实现一下无法自动求导的操作,比如arxmax这种不可导的函数。

    我们需要实现两个函数,forwardbackward,分别用来前向和反向传播,注意都得声明成静态函数。

    前向传播接收多个参数,第一个固定为ctx,用来存储反向传播中可能会用到的一些上下文,比如input和一些前向过程中的中间变量等等,其他参数随你定。然后我们根据上一教程中调用cuda算子的方法计算得到求和结果,进行返回。

    反向传播接收两个参数,第一个同样是ctx,里面存着前向过程中保存的一些上下文变量信息。第二个是grad_output,也就是最终的损失函数对前向传播的返回值求导的结果。在我们这里的模型中,令

    a2 = a^2, b2 = b^2, s = a2 + b2, \mathcal{L} = s \\

    那么自定义cuda算子实现的就是

    s = a2 + b2这一步,而
    grad_output就是
    \frac{\partial \mathcal{L}}{\partial s}。我们自定义的cuda算子反向传播的导数就是
    \frac{\partial s}{\partial a2}
    \frac{\partial s}{\partial b2},然后根据链式求导法则就可以得到损失函数对每个参数的导数了。

    反向传播返回值表示损失函数对前向传播每一个参数的梯度,所以个数必须等于前向传播除了ctx以外的其他参数个数,并且顺序也要一一对应。因为

    \frac{\partial s}{\partial a2} = \frac{\partial s}{\partial b2} = 1,所以返回值就是
    grad_outputgrad_outputNone,因为对常数
    n不需要求导,所以直接返回空即可。

    训练流程

    最终训练流程和平常一样:

    # 定义模型
    model = AddModel(n)
    # 将模型中所有参数拷贝到GPU端
    model.to(device="cuda:0")
    # 定义优化器
    opt = torch.optim.SGD(model.parameters(), lr=0.01)
    for epoch in range(500):
        # 清空优化器缓存
        opt.zero_grad()
        # 前向传播
        output = model()
        # 求loss
        loss = output.sum()
        # 反向传播
        loss.backward()
        # 更新参数
        opt.step()
        if epoch % 25 == 0:
            print("epoch {:>3d}: loss = {:>8.3f}".format(epoch, loss))

    最终损失函数降到了0,log信息如下:

    Loading extension module add2...
    Initializing model...
    Initializing optimizer...
    Begin training...
    epoch   0: loss = 1996.658
    epoch  25: loss =  727.122
    epoch  50: loss =  264.796
    epoch  75: loss =   96.431
    epoch 100: loss =   35.117
    epoch 125: loss =   12.789
    epoch 150: loss =    4.657
    epoch 175: loss =    1.696
    epoch 200: loss =    0.618
    epoch 225: loss =    0.225
    epoch 250: loss =    0.082
    epoch 275: loss =    0.030
    epoch 300: loss =    0.011
    epoch 325: loss =    0.004
    epoch 350: loss =    0.001
    epoch 375: loss =    0.001
    epoch 400: loss =    0.000
    epoch 425: loss =    0.000
    epoch 450: loss =    0.000
    epoch 475: loss =    0.000

    小结

    这三个教程暂时告一段落了,通过这些简单的例子,应该大致能学会如何自己写cuda算子,并且用PyTorch调用,完成模型训练了。

    更复杂的模型其实基本的原理都是类似的,我不喜欢上来就讲解很复杂的大项目源码,我喜欢抽象出一个最简的example,这样更容易理解底层的原理,而不会被很多冗余的代码干扰。

    展开全文
  • pytorch自定义反向传播,求导

    千次阅读 2019-02-01 11:34:48
    pytorch自定义backward()函数。在图像处理过程中,我们有时候会使用自己定义的算法处理图像,这些算法多是基于numpy或者scipy等包。那么如何将自定义算法的梯度加入到pytorch的计算图中,能使用Loss.backward()...

    pytorch中自定义backward()函数。在图像处理过程中,我们有时候会使用自己定义的算法处理图像,这些算法多是基于numpy或者scipy等包。那么如何将自定义算法的梯度加入到pytorch的计算图中,能使用Loss.backward()操作自动求导并优化呢。下面的代码展示了这个功能`

    import torch
    import numpy as np
    from PIL import Image
    from torch.autograd import gradcheck
    class Bicubic(torch.autograd.Function):
    def basis_function(self, x, a=-1):
        x_abs = np.abs(x)
        if x_abs < 1 and x_abs >= 0:
            y = (a + 2) * np.power(x_abs, 3) - (a + 3) * np.power(x_abs, 2) + 1
        elif x_abs > 1 and x_abs < 2:
            y = a * np.power(x_abs, 3) - 5 * a * np.power(x_abs, 2) + 8 * a * x_abs - 4 * a
        else:
            y = 0
        return y
    def bicubic_interpolate(self,data_in, scale=1 / 4, mode='edge'):
        # data_in = data_in.detach().numpy()
        self.grad = np.zeros(data_in.shape,dtype=np.float32)
        obj_shape = (int(data_in.shape[0] * scale), int(data_in.shape[1] * scale), data_in.shape[2])
        data_tmp = data_in.copy()
        data_obj = np.zeros(shape=obj_shape, dtype=np.float32)
        data_in = np.pad(data_in, pad_width=((2, 2), (2, 2), (0, 0)), mode=mode)
        print(data_tmp.shape)
        for axis0 in range(obj_shape[0]):
            f_0 = float(axis0) / scale - np.floor(axis0 / scale)
            int_0 = int(axis0 / scale) + 2
            axis0_weight = np.array(
                [[self.basis_function(1 + f_0), self.basis_function(f_0), self.basis_function(1 - f_0), self.basis_function(2 - f_0)]])
            for axis1 in range(obj_shape[1]):
                f_1 = float(axis1) / scale - np.floor(axis1 / scale)
                int_1 = int(axis1 / scale) + 2
                axis1_weight = np.array(
                    [[self.basis_function(1 + f_1), self.basis_function(f_1), self.basis_function(1 - f_1), self.basis_function(2 - f_1)]])
                nbr_pixel = np.zeros(shape=(obj_shape[2], 4, 4), dtype=np.float32)
                grad_point = np.matmul(np.transpose(axis0_weight, (1, 0)), axis1_weight)
                for i in range(4):
                    for j in range(4):
                        nbr_pixel[:, i, j] = data_in[int_0 + i - 1, int_1 + j - 1, :]
                        for ii in range(data_in.shape[2]):
                            self.grad[int_0 - 2 + i - 1, int_1 - 2 + j - 1, ii] = grad_point[i,j]
                tmp = np.matmul(axis0_weight, nbr_pixel)
                data_obj[axis0, axis1, :] = np.matmul(tmp, np.transpose(axis1_weight, (1, 0)))[:, 0, 0]
                # img = np.transpose(img[0, :, :, :], [1, 2, 0])
        return data_obj
    
    def forward(self,input):
        print(type(input))
        input_ = input.detach().numpy()
        output = self.bicubic_interpolate(input_)
        # return input.new(output)
        return torch.Tensor(output)
    
    def backward(self,grad_output):
       print(self.grad.shape,grad_output.shape)
       grad_output.detach().numpy()
       grad_output_tmp = np.zeros(self.grad.shape,dtype=np.float32)
       for i in range(self.grad.shape[0]):
           for j in range(self.grad.shape[1]):
               grad_output_tmp[i,j,:] = grad_output[int(i/4),int(j/4),:]
       grad_input = grad_output_tmp*self.grad
       print(type(grad_input))
       # return grad_output.new(grad_input)
       return torch.Tensor(grad_input)
    
    def bicubic(input):
    return Bicubic()(input)
    
    def main():
    	hr = Image.open('./baboon/baboon_hr.png').convert('L')
    	hr = torch.Tensor(np.expand_dims(np.array(hr), axis=2))
    	hr.requires_grad = True
    	lr = bicubic(hr)
    	print(lr.is_leaf)
    	loss=torch.mean(lr)
    	loss.backward()
    if __name__ =='__main__':
    	main()
    

    要想实现自动求导,必须同时实现forward(),backward()两个函数。
    1.从代码中可以看出来,forward()函数是针对numpy数据操作,返回值再重新指定为torch.Tensor类型。因此就有这个问题出现了:forward输入input被转换为numpy类型,输出转换为tensor类型,那么输出output的grad_fn参数是如何指定的呢。调试发现,当main()中hr的requires_grad被指定为True,即hr被指定为需要求导的叶子节点。只要Bicubic类继承自torch.autograd.Function,那么output也就是代码中的lr的grad_fn就会被指定为<main.Bicubic object at 0x000001DD5A280D68>,即Bicubic这个类。
    2.backward()为求导的函数,gard_output是链式求导法则的上一级的梯度,grad_input即为我们想要得到的梯度。只需要在输入指定grad_output,在调用loss.backward()过程中的某一步会执行到Bicubic的backwward()函数

    展开全文
  • 今天小编就为大家分享一篇pytorch中的自定义反向传播,求导实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 小白提问:pytorch自定义损失函数的反向传播也是需要自己写吗?</p>
  • 需要Numpy实现,需要自定义反向传播的公式 使用Pytorch内部的函数实现的 Custom loss function in PyTorch numpy_extensions_tutorial 使用Numpy的函数实现的 下面模型来自网络模型入门 import torch import ...

    一般来说,Pytorch提供自定义loss的方法,常用的有两种:

    1. 使用pytorch内部函数直接实现,该方法较为简单,不用人工计算梯度
    2. 需要Numpy实现,需要自定义反向传播的公式

    使用Pytorch内部的函数实现的

    1. Custom loss function in PyTorch
    2. numpy_extensions_tutorial
    3. A-Collection-of-important-tasks-in-pytorch/

    使用Numpy的函数实现的

    下面模型来自网络模型入门

    import torch
    import torch.nn as nn
    # https://blog.csdn.net/oBrightLamp/article/details/85137756?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5.control
    from torch.autograd import Function
    import torch.nn.functional as F
    from torch.autograd import Variable
    
    class Net(nn.Module):
    
        def __init__(self):
            super(Net, self).__init__()
            # 输入图像channel:1;输出channel:6;5x5卷积核
            self.conv1 = nn.Conv2d(1, 6, 5)
            self.conv2 = nn.Conv2d(6, 16, 5)
            # an affine operation: y = Wx + b
            self.fc1 = nn.Linear(16 * 5 * 5, 120)
            self.fc2 = nn.Linear(120, 84)
            self.fc3 = nn.Linear(84, 10)
    
        def forward(self, x):
            # 2x2 Max pooling
            x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
            # 如果是方阵,则可以只使用一个数字进行定义
            x = F.max_pool2d(F.relu(self.conv2(x)), 2)
            x = x.view(-1, self.num_flat_features(x))
            x = F.relu(self.fc1(x))
            x = F.relu(self.fc2(x))
            x = self.fc3(x)
            return x
    
        def num_flat_features(self, x):
            size = x.size()[1:]  # 除去批处理维度的其他所有维度
            num_features = 1
            for s in size:
                num_features *= s
            return num_features
    
    
    
    
    class MyLossFcuntion(Function):
    
        @staticmethod
        def forward(ctx, true_out, fake_out):
            # print(x.shape)
            # print(y.shape)
            input_ = true_out.detach().numpy()
            output_ = fake_out.detach().numpy()
            ret = (input_ - output_) ** 2
            totloss = ret.mean()
            # print("numpy loss: {0}, torch loss: {1}".format(totloss, F.mse_loss(x, y, reduction="sum")))
    
            ctx.save_for_backward(true_out, fake_out)
    
            return torch.as_tensor(totloss)
            # return F.mse_loss(true_out, fake_out, reduction="sum")
    
        @staticmethod
        def backward(ctx, grad_output):
            true_out, fake_out = ctx.saved_tensors
            input_ = true_out.detach().numpy()
            output_ = fake_out.detach().numpy()
            ret = 2 * (input_ - output_) / input_.size
            grad_true_out = torch.from_numpy(ret)
            grad_fake_out = torch.from_numpy(-ret)
            return grad_true_out, grad_fake_out
    
    
    class MyLoss(nn.Module):
        def __init__(self):
            super(MyLoss, self).__init__()
    
        def forward(self, true_out, fake_out):
            return MyLossFcuntion.apply(true_out, fake_out)
    
    
    class TorchLoss(nn.Module):
        def __init__(self):
            super(TorchLoss, self).__init__()
    
        def forward(self, true_out, fake_out):
            return F.mse_loss(true_out, fake_out, reduction="mean")
    
    
    def test():
        torch.manual_seed(2)
    
        net = Net()
        input = torch.randn(1, 1, 32, 32)
        output = net(input)
        target = torch.randn(10)  # 本例子中使用模拟数据
        target = target.view(1, -1)  # 使目标值与数据值尺寸一致
        # criterion = TorchLoss()
        criterion = MyLoss()
        # criterion = nn.MSELoss()
    
        loss = criterion(output, target)
        print("loss")
        print(loss)
    
        net.zero_grad()  # 清零所有参数(parameter)的梯度缓存
    
        print('conv1.bias.grad before backward')
        print(net.conv1.bias.grad)
    
        loss.backward()
    
        print('conv1.bias.grad after backward')
        print(net.conv1.bias.grad)
    
    
    
    
    if __name__ == '__main__':
        test()
    
    

    参考链接

    1. Pytorch如何自定义损失函数(Loss Function)?
    2. 均方差损失函数MSELoss详解及反向传播中的梯度求导
    3. pytorch中的其他一些问题
    4. numpy extending
    展开全文
  • pytorch自定义loss函数

    2018-10-12 00:51:35
    h,w),现在我自己按照我下面的方式想实现上述的目的,但是在pytorh中的loss函数,想要能够反向传播就必须所有的值都是Variable,现在发现的问题就在pytorch的tensor中的flatten函数会有问题,想问问大家有没有什么...
  • nn.Module中定义参数:不需要加cuda,可以求导,反向传播 class BiFPN(nn.Module): def __init__(self, fpn_sizes): self.w1 = nn.Parameter(torch.rand(1)) print(no-----------------------------------------...
  • 参考文章:知乎-Pytorch笔记04-自定义torch.autograd.Function import torch from torch.autograd import Variable class MyReLU(torch.autograd.Function): def forward(self, input_): # 在forward中,需要...

    参考文章:知乎-Pytorch笔记04-自定义torch.autograd.Function

    其他参考:csdn-pytorch的自定义拓展之(一)——torch.nn.Module和torch.autograd.Function
    csdn-这个注释做得好

    import torch
    from torch.autograd import Variable
    
    class MyReLU(torch.autograd.Function):
    
        def forward(self, input_):
            # 在forward中,需要定义MyReLU这个运算的forward计算过程
            # 同时可以保存任何在后向传播中需要使用的变量值
            self.save_for_backward(input_)         # 将输入保存起来,在backward时使用
            output = input_.clamp(min=0)               # relu就是截断负数,让所有负数等于0
            return output
    
        def backward(self, grad_output):
            # 根据BP算法的推导(链式法则),dloss / dx = (dloss / doutput) * (doutput / dx)
            # dloss / doutput就是输入的参数grad_output、
            # 因此只需求relu的导数,在乘以grad_outpu    
            input_, = self.saved_tensors # 把上面保存的input_输出
            if ctx.needs_input_grad[0]:# 判断self.saved_tensors中的Variable是否需要进行反向求导计算梯度
                print('input_ need grad')
            grad_input = grad_output.clone()
            grad_input[input < 0] = 0                # 上诉计算的结果就是左式。即ReLU在反向传播中可以看做一个通道选择函数,所有未达到阈值(激活值<0)的单元的梯度都为0
            return grad_input
    

    我给上面代码加了一个形象的图:(图中蓝底应该是forward)
    在这里插入图片描述
    vsd文件备份

    展开全文
  • pytorch 自定义参数不更新

    千次阅读 2019-12-23 20:19:07
    nn.Module中定义参数:不需要加cuda,可以求导,反向传播 class BiFPN(nn.Module): def __init__(self, fpn_sizes): self.w1 = nn.Parameter(torch.rand(1)) print("no--------------------------------------...
  • loss.backward() # 反向传播 optimizer.step() # 用SGD更新参数 # 每2000批数据打印一次平均loss值 running_loss += loss.item() # loss本身为Variable类型,所以要使用data获取其Tensor,因为其为标量,所以...
  • 我们介绍了带变分推理的贝叶斯卷积神经网络,这是卷积神经网络(CNN)的一种变体,其中权重的难处理的后验概率分布是由Backprop的Bayes推断的。 我们证明我们提出的变分推断... 要创建自定义贝叶斯网络,请继承layers.m
  • 对于浅层的网络,我们可以手动的书写前向传播和反向传播过程。但是当网络变得很大时,特别是在做深度学习时,网络结构变得复杂。前向传播和反向传播也随之变得复杂,手动书写这两个过程就会存在很大的困难。幸运地是...
  • pytorch自定义扩展

    2019-12-02 12:46:33
    反向传播: https://blog.csdn.net/Hungryof/article/details/78346304 https://blog.csdn.net/tsq292978891/article/details/79364140
  • 在最近的项目中用到了自定义loss函数,代码一切都准备就绪后,在训练时遇到了梯度爆炸的问题,每次训练几个iterations后,梯度和loss都会变为nan。一般情况下,梯度中间部分值存在0情况,梯度就会产生nan,导致该层...
  • [pytorch] 自定义激活函数swish(三)

    千次阅读 2018-04-02 23:50:43
    在神经网络模型中,激活函数多种多样。大体都是,小于0的部分,进行抑制(即,激活函数输出为非常小的数),大于0的部分,进行放大(即,激活函数输出为较大的数)。...可求导的,在反向传播中,可以方便使用...
  • 参考链接: torch.Tensor.register_hook()的使用举例 代码实验展示: import torch print(torch.__version__) # 1.2.0+cu92 torch.manual_seed(seed=20200910) ... print("\n为x0执行自定义的钩子函数...
  • 将梯度反向传播回网络的参数; 更新网络的参数,主要使用如下简单的更新原则:weight = weight - learning_rate * gradient ''' #定义网络 import torch import torch.nn as nn import torch.nn.functional as F ''...
  • 笔者在最近的项目中用到了自定义loss函数,代码一切都准备就绪后,在训练时遇到了梯度爆炸的问题,每次训练几个step后,梯度/loss都会变为nan。一般情况下,梯度变为nan都是出现了log(0), x/0等情况,导致结果变为+...
  • pytorch:定义一个新的Autograd函数 在pytorch的内部,每个Autograd操作符实际上包括对张量的两种操作: forward函数:从输入向量计算输出向量 ...接着可以通过构造一个实例,送入输入数据的张量,来使用自定义的Au
  • 实验代码展示 ... '''特殊的ReLU,区别在于反向传播时候只考虑大于零的输入和大于零的梯度''' """ @staticmethod def forward(ctx, input_img): # torch.Size([1, 64, 112, 112]) positive_mas
  • 在stackoverflow上看到一个名叫Ismail_Elezi的老铁问了个自定义Loss Function的问题,它的问题在于:1)反向传播报错 2)矩阵算法使用不行 3)算法数值稳定性不行。 我决定在这个例子的基础上(它应该不是torch 0.4.0...
  • 在继承了nn.Module的情况下,并且函数内数学运算均为torch内置函数(运算过程不能够破坏pytorch计算图,否则梯度无法正确反向传播),依旧无法调用: 初始化有问题 可以尝试在类构造函数末尾加入return; 调用方式...
  • 学习Pytorch将要写的内容

    千次阅读 2017-09-12 20:03:17
    秉承一贯风格:先挖坑: 优先级: ...3. 自定义反向传播的写法,以及注意点。包括扩展torch.autograd.Function以及torch.nn.Module 4. Variable的一些注意事项 5. 自定义loss函数 6. 将论坛中的一些常
  • pytorch基础操作

    2020-07-31 12:26:02
    反向传播3. 加载numpy数据4. 数据流:dataset,dataloder5. 自定义dataset6. 加载预训练参数7. 保存和加载模型引用 1. 梯度计算 # Create tensors. x = torch.tensor(1., requires_grad=True) w = torch.tensor(2.,...
  • pytorch官网教程——Learning PyTorch with Examples教程概览: 1、使用numpy实现前向、反向传播;2、使用tensor实现...;3、使用自动求导机制;4、自定义自动求导函数;5、静态图、动态图;6、使用nn报包搭建网络;...
  • 在前一部分博文中,实现了通过torch的相关方法完成反向传播和参数更新,在pytorch中预设了一些更加灵活简单的对象,让我们来构造模型、定义损失,优化损失等。那么接下来,我们一起来了解一下其中常用的API。 1.1 ...
  • Pytorch深度学习编程框架1 数据流数据输入模型前需要部署到cuda上2 模型定义模型初始化模型打印模型权值初始化(可选)自定义初始化加载预训练部署到cuda执行模型--前向传播模型设置为训练模式模型设置为测试模式存储...
  • 文章目录Autograd: 自动微分VariableVariable 和 Tensor自动求导从反向传播中排除子图注册钩子自定义Function 这个是0.3的版本,之后修改。 Autograd: 自动微分 autograd包是PyTorch中神经网络的核心, 它可以为基于...

空空如也

空空如也

1 2 3
收藏数 42
精华内容 16
关键字:

pytorch自定义反向传播