谈谈Darknet53为啥这么难训练

 在我使用Imagenet2012对Darknet53进行预训练的时候,往往训练到一半,就会出现过拟合,导致无法继续向下训练,尝试了很多方法,最后发现问题出现在下图红框的部分。

得出这个结论是因为当我使用Resnet中,包含有下采样作用的 BTNK1结构代替了上面红框里的卷积层后,模型变得可以训练了。

表现在代码里:

原代码:

class ResidualBlock(Module):def __init__(self, in_channels):super(ResidualBlock, self).__init__()self.conv1 = Conv2d(in_channels=in_channels, out_channels=in_channels // 2, kernel_size=1, stride=1, padding=0)self.bn1 = BatchNorm2d(in_channels // 2)self.conv2 = Conv2d(in_channels // 2, in_channels, 3, 1, 1)self.bn2 = BatchNorm2d(in_channels)def forward(self, x):y = self.conv1(x)y = self.bn1(y)y = leaky_relu(y)y = self.conv2(y)y = self.bn2(y)y = leaky_relu(y) + xreturn y

修改后的代码:

class ResidualBlock(Module):def __init__(self, in_channels, hidden_channels, shortcut=False, stride=1):super(ResidualBlock, self).__init__()self.conv1 = Conv2dBnLeakyReLU(in_channels, hidden_channels, 1, 1, 0)self.conv2 = Conv2dBnLeakyReLU(hidden_channels, hidden_channels * 4, 3, stride, 1)# self.conv3 = Conv2dBnLeakyReLU(hidden_channels, hidden_channels * 4, 1, 1, 0)self.shortcut = Conv2dBnLeakyReLU(in_channels, hidden_channels * 4, 1, stride, 0) if shortcut else Nonedef forward(self, x):if self.shortcut is None:return x + self.conv2(self.conv1(x))else:return self.shortcut(x) + self.conv2(self.conv1(x))

 原模型代码:

class Darknet53(Module):def __init__(self, init_weights=True, num_classes=1000):super(Darknet53, self).__init__()self.conv1 = Conv2dBnLeakyReLU(3, 32, 3, 1, 1)self.conv2 = Conv2dBnLeakyReLU(32, 64, 3, 2, 1)self.residual_block1 = ResidualBlock(64)self.conv3 = Conv2dBnLeakyReLU(64, 128, 3, 2, 1)self.residual_block2 = Sequential(*([ResidualBlock(128)] * 2))self.conv4 = Conv2dBnLeakyReLU(128, 256, 3, 2, 1)self.residual_block3 = Sequential(*([ResidualBlock(256)] * 8))self.conv5 = Conv2dBnLeakyReLU(256, 512, 3, 2, 1)self.residual_block4 = Sequential(*([ResidualBlock(512)] * 8))self.conv6 = Conv2dBnLeakyReLU(512, 1024, 3, 2, 1)self.residual_block5 = Sequential(*([ResidualBlock(1024)] * 4))self.avg_pool = AdaptiveAvgPool2d(1)self.fn = Linear(1024, num_classes)if init_weights:self._initialize_weights()

修改后的模型代码:

class Darknet53(Module):def __init__(self, init_weights=True, num_classes=1000):super(Darknet53, self).__init__()self.conv1 = Conv2dBnLeakyReLU(3, 32, 3, 1, 1)# self.conv2 = Conv2dBnLeakyReLU(32, 64, 3, 2, 1)self.conv2 = ResidualBlock(32, 16, shortcut=True, stride=2)self.residual_block1 = ResidualBlock(64, 16)# self.conv3 = Conv2dBnLeakyReLU(64, 128, 3, 2, 1)self.conv3 = ResidualBlock(64, 32, shortcut=True, stride=2)self.residual_block2 = Sequential(*([ResidualBlock(128, 32)] * 2))# self.conv4 = Conv2dBnLeakyReLU(128, 256, 3, 2, 1)self.conv4 = ResidualBlock(128, 64, shortcut=True, stride=2)self.residual_block3 = Sequential(*([ResidualBlock(256, 64)] * 8))# self.conv5 = Conv2dBnLeakyReLU(256, 512, 3, 2, 1)self.conv5 = ResidualBlock(256, 128, shortcut=True, stride=2)self.residual_block4 = Sequential(*([ResidualBlock(512, 128)] * 8))# self.conv6 = Conv2dBnLeakyReLU(512, 1024, 3, 2, 1)self.conv6 = ResidualBlock(512, 256, shortcut=True, stride=2)self.residual_block5 = Sequential(*([ResidualBlock(1024, 256)] * 4))self.avg_pool = AdaptiveAvgPool2d(1)self.fn = Linear(1024, num_classes)if init_weights:self._initialize_weights()

于是有了大胆的猜测,Darknet相比Resnet,因为中间的下采样卷积层没有残差结构连接,所以模型实际上是被分割了,小loss很难向后传播到模型头部,所以会逐渐倾向于用尾部的几层网络来拟合数据,刚好Darknet的尾部很重,所以模型会逐渐走向过拟合,


以下内容就是发牢骚,但我真的好气呀,明明这么信任它。



熟悉YOLO系列的同学应该都知道,Darknet53是YOLOv3的主干网络,本人乘着暑假也在学习YOLO,从1学到了3,基本上都会用代码复现一遍,和作者所说的效果过基本吻合以后再开始新的篇章,直到遇上了Darknet53,按照作者的话说,Darknet53在ImageNet2012上的效果堪比Resnet152,而速度几乎是Resnet的2两倍。才看到这句话,我是不信的,人家Resnet152比你怎么说也多了近100层,你说这效果差不多,那Resnet家族不是很尴尬,这家人以后还怎么混是吧。但一想,YOLOv3也是久经考验的老同志了,要是数据造假,那不是早就被拖出来游街了,奈何网上又找不到相关的资料,那就自己上手训练吧。

Darknet53的网络并不复杂,用pytorch不用半个小时就搭建好了,上数据,开炼。

本人只有一张游戏显卡,为了速度上了混合精度,懒得调参,优化器用了Adam,100个epoch差不多也跑了一整天。

一看效果,在train上Top1 acc到了80,而在val上有65。这可比论文里的77.2差远了,可能是开了混合精度造成的损失?关了试一试吧,好,又是两天过去了,这次再val上的Top1 acc到了68,步子迈进了一步,可还是离77.2差了一截,难道是Adam的锅,那换SGDM再来!没想到,噩梦开始了。

自从换上SGDM,val的指标没有再突破过50,val在上升一定数值后就会逐步下降,毫无疑问,过拟合了。

batchsize小了?上梯度累计,不行!

weight_decay大了?换!,1e-4,1e-3,1e-2,1e-1,1...不行!

学习率设置大了?上指数衰减,上余弦衰减,上重启机制,全都失败。

找github看别人的代码,一行行对比,没什么区别。

怀疑过激活函数,怀疑过batchnorm,统统没用!!!

impossible!‘质子’干扰了实验。

啥都怀疑过,就是没怀疑过是网络本身的问题,毕竟有人复现过,当然都没说100%,github的同学跑出的数据是75,那也比咱强。

直到前前后后快半个月,所有能想到的方案我都试过后,一切的矛头都指向了Darknet53本身,它怎么就那么爱背答案。

砍,把后面的残差块砍掉一半。

可它死性不改,就是背。

明明Resnet50和它的层数差不多,但人家就没那么爱过拟合,到底是哪里出问题了。

抱着这个疑问,那就一点点测。

把Resnet的三层残差结构给他,不行!

把Resnet顶部的7X7卷积给他,不行!

直到!直到!直到!把Resnet用来代替池化层的BTNK1给它。

动了,它动了!问题找到了,是连接残差块的卷积层,它阻塞了loss向深度回传,在加上Darknet53很重的脚,所以只能走向过拟合。

这里面还出现了一个有意思的现象,在大学习率阶段,这种过拟合并不会太明显,但当学习率开始衰减,那么网络就会开始走向灭亡,这可能是由于在学习率较大时,loss还能有部分穿过残差块之间的间隙,而学习率逐步衰减,网络就只能靠底部的神经元进行学习了。

最后就只剩一个疑问,github上那些跑到75+的代码究竟用了什么魔法喂!。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/275402.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ypay源支付6.9无授权聚合免签系统可运营源码

YPay是一款专为个人站长设计的聚合免签系统,YPay基于高性能的ThinkPHP 6.1.2 Layui PearAdmin架构,提供了实时监控和管理的功能,让您随时随地掌握系统运营情况。 说明 Ypay源支付6.9无授权聚合免签系统可运营源码 已搭建测试无加密版本…

【快速上手ProtoBuf】proto 3 语法详解

1 🍑字段规则🍑 消息的字段可以⽤下⾯⼏种规则来修饰: singular :消息中可以包含该字段零次或⼀次(不超过⼀次)。 proto3 语法中,字段默认使⽤该规则。repeated :消息中可以包含该…

Python中的类【详谈】

零.前言: 本文适合对Python有浅浅了解的读者,并不能作为Python入门使用。 一.Python中类的属性、方法 在Python中有变量,有函数,例如下方: def IAmAFunction():print("I am a function")IAmVariable 25…

iOS 判断触摸位置是否在图片的透明区域

装扮功能系列: Swift 使用UIScrollerView 实现装扮功能(基础)Swift 使用UIScrollerView 实现装扮功能(拓展)iOS 判断触摸位置是否在图片的透明区域 背景 在装扮功能中,一般都是长按使道具进入编辑状态&…

【Java设计模式】十九、中介者模式

文章目录 1、中介者模式2、案例3、总结 1、中介者模式 如图: 同事类之间关联较多时,整体出现网状结构,耦合度极高。一个对象一变动,好多对象都得改。若变为右边的星形结构,则一个类的变动,只影响自身与中介…

记录一下在Pycharm中虚拟环境的创建

如果在Pycharm中要新建一个虚拟环境,那你可以在Terminal中选择Command Prompt,在这里面执行相关命令 一、安装了Anaconda,创建虚拟环境 当你使用解释器是Anaconda提供的时,你可以使用conda命令执行,见以下操作&#x…

05-ESP32-S3-IDF USART

ESP32-S3 IDF USART详解 USART简介 USART是一种串行通信协议,广泛应用于微控制器和计算机之间的通信。USART支持异步和同步模式,因此它可以在没有时钟信号的情况下(异步模式)或有时钟信号的情况下(同步模式&#xff…

SQLiteC/C++接口详细介绍之sqlite3类(五)

快速跳转文章列表:SQLite—系列文章目录 上一篇:SQLiteC/C接口详细介绍之sqlite3类(四) 下一篇:SQLiteC/C接口详细介绍之sqlite3类(六)(未发表) 14.sqlite3_busy_handle…

STM32输入捕获频率和占空比proteus仿真失败

这次用了两天的时间来验证这个功能,虽然实验没有成功,但是也要记录一下,后面能解决了,回来再写上解决的办法: 这个程序最后的实验结果是读取到的CCR1和CCR2的值都是0,所以没有办法算出来频率和占空比。 还…

STM32平替GD32有多方便

众所周知, GD32一直模仿STM32,从未被超越。 我最近公司使用的GD32E230C6T6 这款芯片有48个引脚。 属于小容量的芯片。 我有一个用STM32写的代码,之前是用的 STM32F103CB 这款芯片是中容量的。 不过在keil中,只需要这两步,就能使用原来的逻辑,几乎不用修改代码。 1. …

关于分布式微服务数据源加密配置以及取巧方案(含自定义加密配置)

文章目录 前言Spring Cloud 第一代1、创建config server项目并加入加解密key2、启动项目,进行数据加密3、实际项目中的测试server Spring Cloud Alibaba低版本架构不支持,取巧实现无加密配置,联调环境问题加密数据源配置原理探究自定义加密解…

Java学习笔记(11)

面向对象进阶 Static 静态变量 所有对象一起共享,就用static修饰 不属于对象,属于类的 可以用 类名.静态变量 “”;赋值 但是 对象.静态变量也可以访问到内容 Static内存图 Student这个类的字节码文件加载到方法区,并在内…

【C语言】Windows下的C语言线程编程详解

文章目录 1. 头文件1.1 windows.h1.2 process.h 2. 创建线程3. 线程同步3.1 线程同步方式3.1 互斥量(Mutex)3.2 事件(Event) 4. 线程的结束与资源管理5.线程池(简要) 在Windows平台下,C语言提供…

如何“使用Docker快速安装Jenkins,在CentOS7”?

1、运行 docker run -d --namejenkins -p 8080:8080 jenkins/jenkins 2、查看日志 ,使用 "docker logs -f jenkins",可以持续刷新日志 docker logs jenkins 3、通过命令查看密码 docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminP…

VUE内盘期货配资软件源码国际外盘二合一

开发一个Vue内盘期货配资软件源码,同时兼容国际外盘二合一的功能,是一个复杂且专业的任务,涉及前端Vue.js框架的使用、后端服务器处理、数据库管理、实时交易接口对接等多个方面。下面是一些关于开发此类软件的基本指导和考虑因素&#xff1a…

FFmpeg工作流程及视频文件分析

FFmpeg工作流程: 解封装(Demuxing)--->解码(Decoding)--->编码(Encoding)--->封装(Muxing) FFmpeg转码工作流程: 读取输入流--->音视频解封装--->解码音视频帧--->编码音视频帧--->音视频封装--->输出目标流 可简单理解为如下流程: 读文件-->解…

程序员的三重境界:码农,高级码农、程序员!

见字如面,我是军哥! 掐指一算,我在 IT 行业摸爬滚打 19 年了,见过的程序员至少大好几千,然后真正能称上程序员不到 10% ,绝大部分都是高级码农而已。 今天和你聊聊程序员的三个境界的差异,文章不…

【BFS二叉树】113路径总和II

113路径总和 II 给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 思路: 题目最终输出的是路径,因此用BFS遍历的时候,需要记录走到每个节点的路径&#xff1…

排序算法之快速排序算法介绍

目录 快速排序介绍 时间复杂度和稳定性 代码实现 C语言实现 c实现 java实现 快速排序介绍 快速排序(Quick Sort)使用分治法策略。 它的基本思想是:选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分;其中一部分的所有数据…

报错:Nginx 部署后刷新页面 404 问题

文章目录 问题分析解决 问题 在部署完项目后 刷新页面,页面进入了404 分析 加载单页应用后路由改变均由浏览器处理,而刷新时将会请求当前的链接,而Nginx无法找到对应的页面 关键代码try_files,剩下俩如果其他地方配置了则可以省略。 在这…