news 2026/5/1 9:41:32

cudnn实现残差网络(憋出大招)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
cudnn实现残差网络(憋出大招)

我用pytorch写了一个极简单版本的残差网络(默认训练cifar10),架构如下::

classResidualBlock( torch.nn.Module ):
def __init__(self, channels):
super( ResidualBlock, self).__init__()
self.Conv1 = torch.nn.Conv2d( channels, channels, kernel_size=(3, 3), padding=1)
self.Conv2 = torch.nn.Conv2d( channels, channels, kernel_size=(3, 3), padding=1)

self.relu=torch.nn.LeakyReLU()

def forward(self, x):
y = self.relu((self.Conv1(x) ))
y = (self.Conv2(y))

return self.relu( y+x )

classModel( torch.nn.Module ):
def __init__(self):
super( Model, self).__init__()
self.conv1 = torch.nn.Conv2d( 3, 32, kernel_size=(3,3) , padding=1)
self.conv2 = torch.nn.Conv2d( 32, 64,kernel_size=(3,3) , padding=1 )
self.relu=torch.nn.LeakyReLU()
self.resblk1 = ResidualBlock(32)
self.resblk2 = ResidualBlock(64)

self.pool = torch.nn.MaxPool2d(2)
self.out = torch.nn.Linear(4096,10)


def forward(self, x):

x = self.relu( (self.conv1(x) ) )
x = self.resblk1(x)
x = self.pool( self.relu( (self.conv2(x) ) ))
x = self.resblk2(x)
x = self.pool(( ((x) ) ))
x=x.view(x.size(0),-1)
x=self.out(x)
return x

我想,能不能用cudnn写一个呢?难度太大了!!!!!!!!!!!!!!

昨天是吉祥日,竟然尝试成功,虽然还比不上pytorch,但训练上了61分,运行也有些慢!但没关系,比c++cpu版本(61分)残差网络快多了!

lenet改vgg成功后,我们再改为最简单的resnet-CSDN博客

成功运行已经是天大的幸运了!仿照上面的架构,在cudnn上摸索残差!再接再厉cudnn vgg!

最难是要自己写残差的反向传播,因为pytorch反向传播是自动的!这也是为什么大家喜欢用的原因!

使用类是真的好!cudnn残差架构如下:

class LeNet :public Layer {
public:
LeNet(cublasHandle_t &cublas_, cudnnHandle_t &cudnn_, int batch_) :cublas(cublas_), cudnn(cudnn_), batch(batch_) {

//使用pading方式,vgg
layers.emplace_back(std::make_shared<Conv2D>(cudnn, batch, 3, 32, 32, 32, 3,1,1));//输入->>>c1,5*5,1*28*28-》6*24*24
layers.emplace_back(std::make_shared<ReLU>(cudnn, batch, 32, 32, 32));

////尝试残差,此处要记住输入X
//layers.emplace_back(std::make_shared<Conv2D>(cudnn, batch, 32, 32, 32, 32, 3,1,1));//c3,6*12*12->>16*8*8
//layers.emplace_back(std::make_shared<ReLU>(cudnn, batch, 32, 32, 32)); //c3,6*12*12->>16*8*8
//layers.emplace_back(std::make_shared<Conv2D>(cudnn, batch, 32, 32, 32, 32, 3, 1, 1));
////尝试残差,此处输出Y,然后操作Y+X,完成后,做relu
////即要定义一个Y+X,而且反向时如何改?//参考自己c++cpu版本

// LeNet LeNet_net(cublas, cudnn, batch_size);
/*layers.emplace_back(std::make_shared<residual>(cublas, cudnn, batch, 32, 32, 32));*/
layers.emplace_back(std::make_shared<residual>(cublas, cudnn, batch));

layers.emplace_back(std::make_shared<ReLU>(cudnn, batch, 32, 32, 32));
//第二次做残差,residualExt
layers.emplace_back(std::make_shared<Conv2D>(cudnn, batch, 32, 64, 32, 32, 3, 1, 1));
layers.emplace_back(std::make_shared<ReLU>(cudnn, batch, 64, 32, 32));
layers.emplace_back(std::make_shared<MaxPool2D>(cudnn, batch, 64, 32, 32, 2, 2, 0, 2));

layers.emplace_back(std::make_shared<residualExt>( cudnn, batch, 64, 16, 16));
layers.emplace_back(std::make_shared<ReLU>(cudnn, batch, 64, 16, 16));

layers.emplace_back(std::make_shared<MaxPool2D>(cudnn, batch, 64, 16, 16, 2, 2, 0, 2));

layers.emplace_back(std::make_shared<Linear>(cublas, batch, 64 * 8 * 8, 120));//c5,16*4*4->>>120
layers.emplace_back(std::make_shared<ReLU>(cudnn, batch, 120, 1, 1)); //c5,16*4*4->>>120
//layers.emplace_back(std::make_shared<Linear>(cublas, batch, 120, 84));//120->84
//layers.emplace_back(std::make_shared<ReLU>(cudnn, batch, 84, 1, 1)); //120->84
layers.emplace_back(std::make_shared<Linear>(cublas, batch, 120, 10));//84->10


cudaMalloc(&output, batch * 10 * sizeof(float));
cudaMalloc(&grad_input, batch * 3 * 32*32 * sizeof(float));
}

......

};

上面最大的成功是仿照lenet类,写residualExt类,即就是他们都是类的集合,lenet大一些,residualExt类小一些!

而且可以在lenet类中调用,真的是妙不可言!

让我想起机器视觉康耐视visionpro工具的封装,真是一模一样!自己仿照版本机器视觉,其实也可以仿照这种类架构,很妙!也就是工具组中嵌套工具组!这样你不用管理,因为在递归中,他自己就自动化处理了,大神c++原著中有句话:递归是神,迭代是人。就是这个意思了!

残差类的详细如下(residual是尝试成功版本,residualExt是改进版):

classresidualExt:public Layer {
public:
residualExt( cudnnHandle_t &cudnn_, int batch_ ,int c, int h, int w) :cudnn(cudnn_), batch(batch_)
, _c(c), _h(h), _w(w) {


//使用了数字常量,这个残差只能用一次!!!!!!!!!!!!!!202602091332

//尝试残差,此处要记住输入X
layers.emplace_back(std::make_shared<Conv2D>(cudnn, batch, _c, _c, _h, _w, 3, 1, 1));//c3,6*12*12->>16*8*8
layers.emplace_back(std::make_shared<ReLU>(cudnn, batch, _c, _h, _w)); //c3,6*12*12->>16*8*8
layers.emplace_back(std::make_shared<Conv2D>(cudnn, batch, _c, _c, _h, _w, 3, 1, 1));
//尝试残差,此处输出Y,然后操作Y+X,完成后,做relu
//即要定义一个Y+X,而且反向时改如何?

cudaMalloc(&output, batch * _c * _h * _w * sizeof(float));//输出32*32*32-----------------------显然输入也是32*32*32
cudaMalloc(&input2, batch * _c * _h * _w * sizeof(float));
cudaMalloc(&d_residual, batch * _c * _h * _w * sizeof(float));
// cudaMalloc(&output, batch * 10 * sizeof(float));//这里的10代表10个类,所以不能用
cudaMalloc(&grad_input, batch * _c * _h * _w * sizeof(float));//反向和梯度计算不管!!!!!!!!!!!!!!
}
void forward(float *input_)override {
input = input_;
input2 = input_;
for (const auto &l : layers) {
l->forward(input);
input = l->get_output();
}
/*int NN = _n * _c * _h * _w;*/
/*int NN = batch * 32 * 32 * 32;*/
int NN = batch * _c * _h * _w;
residual_forward_kernel << <(NN + 255) / 256, 256 >> >(output, input, input2, NN);
error_handling(cudaGetLastError());
//cudaMemcpy(input2, inputTemp, sizeof(float)*batch * 10, cudaMemcpyDeviceToDevice);
}
void backward(float *grad_output)override {//梯度来自残差块后的relu,当前只有一个残差块!!!!!!!!!!!
float* grad = grad_output;//要记住这个梯度,即备份一个
float* grad备用 = grad_output;
for (int i = layers.size() - 1; i >= 0; i--) {
layers[i]->backward(grad);
grad = layers[i]->get_grad_input();
}

//float* d_residual = grad备用*X输入数据;//input2 = input_;
// float* d_residual = grad备用*input2;//input2 = input_;
int NN = batch * _c * _h * _w;
/*for (int i = 0; i <NN; i++)
{
d_residual[i] = grad备用[i]*input2[i];
}*/

int threads = 256;
int blocks = (NN + threads - 1) / threads;
mul << <blocks, threads >> >(grad备用, input2, d_residual, NN);//c为输出=d_residual
error_handling(cudaGetLastError());

residual_backprop_kernel << <blocks, threads >> >(grad_input, grad, d_residual, NN);
error_handling(cudaGetLastError());
// cudaMemcpy(grad_input, grad, sizeof(float)*batch * 32 * 32 * 32, cudaMemcpyDeviceToDevice);
}
float* get_output() override { return output; }
float* get_grad_input() override { return grad_input; }
void update(float lr) {
for (const auto &l : layers) {
l->update(lr);

}
}


~residualExt() {
cudaFree(output);
cudaFree(grad_input);
}


private:
// cublasHandle_t &cublas;
int _c, _h, _w;
cudnnHandle_t &cudnn;
int batch;
float *input, *output, *grad_input;
float *input2;
float* d_residual;
std::vector<std::shared_ptr<Layer>> layers;
};

所以,上面pytorch残差网络用cudnn c++重写的话(复现),就是如此!

程序的其他部分参考:

我的第一个cudnn(cuda)人工智能程序(lenet)-CSDN博客

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 6:28:26

5分钟学会Z-Image-Turbo:孙珍妮风格图片生成技巧

5分钟学会Z-Image-Turbo&#xff1a;孙珍妮风格图片生成技巧 1. 这个模型到底能做什么 你有没有想过&#xff0c;只用一句话描述&#xff0c;就能生成一张带着孙珍妮气质的高清人像&#xff1f;不是简单贴图&#xff0c;不是粗糙换脸&#xff0c;而是从构图、光影、神态到氛围…

作者头像 李华
网站建设 2026/5/1 6:28:52

BGE-Large-Zh小白指南:从安装到中文语义检索全流程

BGE-Large-Zh小白指南&#xff1a;从安装到中文语义检索全流程 想快速搭建一个能理解中文、能精准检索信息的AI工具吗&#xff1f;今天要介绍的BGE-Large-Zh镜像&#xff0c;就是一个让你在10分钟内就能上手的“中文语义搜索引擎”。它能把一段段文字变成机器能懂的“数字指纹…

作者头像 李华
网站建设 2026/5/1 9:12:22

手把手教学:AgentCPM本地研报生成工具部署教程

手把手教学&#xff1a;AgentCPM本地研报生成工具部署教程 你是否曾为撰写一份专业、详实、逻辑严密的行业研究报告而反复查阅资料、熬夜整理框架、反复修改措辞&#xff1f;是否担心数据上传到云端带来的隐私风险&#xff1f;又是否厌倦了在线服务的响应延迟、配额限制和网络…

作者头像 李华
网站建设 2026/5/1 6:16:05

RMBG-2.0新手指南:从安装到出图的完整教程

RMBG-2.0新手指南&#xff1a;从安装到出图的完整教程 还在为抠图烦恼吗&#xff1f;无论是想给商品换个背景&#xff0c;还是制作证件照&#xff0c;或者处理复杂的发丝边缘&#xff0c;传统工具要么操作繁琐&#xff0c;要么效果生硬。今天&#xff0c;我要带你体验一款全新…

作者头像 李华
网站建设 2026/4/18 13:40:05

零基础玩转BGE Reranker:手把手教你搭建文本相关性排序工具

零基础玩转BGE Reranker&#xff1a;手把手教你搭建文本相关性排序工具 你是否遇到过这样的问题&#xff1a;在做知识库问答、文档检索或内容推荐时&#xff0c;系统返回的前几条结果明明和你的问题“字面上很像”&#xff0c;读起来却完全不相关&#xff1f;比如搜索“Python…

作者头像 李华
网站建设 2026/5/1 7:14:01

Ollama框架加持:PasteMD本地化AI文本处理方案

Ollama框架加持&#xff1a;PasteMD本地化AI文本处理方案 你有没有过这样的时刻&#xff1a;刚开完一场信息密度爆炸的线上会议&#xff0c;会议纪要散落在聊天窗口、语音转文字记录和手写笔记里&#xff1b;或者深夜整理技术文档&#xff0c;面对一堆从不同网页复制粘贴过来的…

作者头像 李华