Skip to content

cuijunjie18/YOLO_V1

Repository files navigation

YOLO-V1论文复现

背景

在进行多模态大模型进行目标检测前,先用经典的目标检测框架提高自己对目标检测流程、数据预处理、不同架构的优缺点及目标检测评估中各种概念的了解,夯实目标检测基础,加油吧!

目录结构说明

  • datasets: 数据集存放位置,label为真实文件,原始图像为symbol link
  • raw_paper: yolov1的原始论文
  • utils: 存放常用功能脚本
  • modules: 模型的组件
  • train.py: 训练脚本
  • predict.py: 预测脚本
  • visual.py: 训练结果可视化脚本
  • main.ipynb: 整体流程的构思过程
  • data_preprocess.ipynb: 数据预处理构思流程
  • demo.ipynb: loss的调试
  • debug.ipynb: 过拟合测试

模型结构说明

模型整体架构为YOLO-V1论文里的架构的改编版,原模型如下

  • backbone为VGG架构,Detect_head为两个全连接层.
  • 激活函数将论文中的LeakyReLU改为了全Relu,因为图像的归一化操作及后续的loss计算都要求元素的范围是大于0的.
  • 论文的关键点:每个grid的预选框仅一个有效,从loss计算到后处理均是.

模型效果

目前最优的训练过程如下:

alt text

部分效果展示:

1 2

框架使用——Get start

环境配置

  • 下载本体

    git clone https://github.com/cuijunjie18/YOLO_V1.git
    cd YOLO_V1
  • 创建虚拟环境

    conda create --name "Yolov1" python=3.10
    conda activate Yolov1
  • 安装依赖

    pip install -r requirements.txt

数据集处理

labels格式参考如下:

img_path xmin1 ymin1 xmax1 ymax1 class1 xmin2 ymin2 xmax2 ymax2 class2

如果原始labels格式为xml,如VOC2012数据集,可以运行下面脚本,自动转换数据格式,并进行数据训练集、测试集划分

python3 utils/xml2yolo.py \
        --xml_folder="xxxxx" \
        --save_folder="xxxxx"

训练

直接执行下面的脚本

python3 train.py \
        --data_dir="./datasets" \
        --batch_size=32 \
        --num_epochs=20 \
        --lr=5e-4 \
        --devices_idx 0 1 2 3 \
        --save_dir="results/exp" \

可视化训练的整体结果,运行下面的脚本

python3 visual.py # 注意visual.py里的路径要自己修改

预测

直接执行下面的脚本

python3 predict.py \
        --model_path="results/exp4/best.pt" \
        --img_path="./datasets/JPEGImages/2012_002895.jpg" \
        --output_dir="results/predict"

后续规划

  • 完善命令行给参数,实现训练、预测
  • 模型效果很差,检查数据集处理、loss设计、推理算法
  • 支持多种目标检测模型架构
  • 实现web效果展示

个人理解 + 收获(读者忽略)

一、pytorch中mask的灵活运用

参考loss.py中的实现,发现许多操作都是mask提取实现的,如从一些连续的tensor中提取出某个部分,我们可以先生成对应的mask然后reshape即可.

注意下面两种方式

# 注意要符合数据的要求,归一化后数值0~1
pre = torch.randn(1,30,7,7)  # 随机预测张量 [batch, S, S, B*5+C]
tar = torch.randn(1,30,7,7)  # 随机目标张量
mask1 = pre > 0
mask2 = tar > 0
pre[~mask1] = 0
tar[~mask2] = 0
print(pre)
# pre保持原形状
# 设置有目标的mask
coord_mask = target[:,:,:,4] > 0
coord_mask = coord_mask.unsqueeze(-1).expand_as(target)
# 提取有目标的target
coord_target = target[coord_mask].reshape(-1, 5 * B + C)

二、激活函数Relu的作用

搞了这么久终于理解了为什么要用Relu了,呜呜呜,原来目标检测任务后续的很多操作,如归一化,开根号等操作均要求值大于0,否则NAN!

三、torch.cat与torch.stack的区别

torch.cat主要用于拼接,而torch.stack用于将列表维度转化为tensor维度,一个简单的例子

x1 = torch.rand((7,7,30))
x2 = torch.rand((7,7,30))
x = [x1,x2]
y1 = torch.stack(x,dim = 0)
y2 = torch.cat(x,dim = 0)
print(y1.shape)
print(y2.shape)
# 输出
# torch.Size([2, 7, 7, 30])
# torch.Size([14, 7, 30])

四、非极大值抑制(NMS)在目标检测框的应用

非极大值抑制的流程如下:

  • 根据置信度得分进行排序
  • 选择置信度最高的比边界框添加到最终输出列表中,将其从边界框列表中删除
  • 计算所有边界框的面积
  • 计算置信度最高的边界框与其它候选框的IoU。
  • 删除IoU大于阈值的边界框
  • 重复上述过程,直至边界框列表为空。

参考:https://zhuanlan.zhihu.com/p/37489043

五、sigmoid激活函数的应用

原本的全连接层后用Relu函数作为输出层的激活函数,loss收敛后效果仍旧很差,recall率几乎为0,可能认为不检测的loss还比检测到的小,于是模型错误地往这个方向去收敛了,故我们把模型的最后一层激活函数换成了sigmoid激活函数,让输出层的结果均在[0,1]范围,符合检测框的物理意义,最后对单个图片过拟合还是可以的.

参考文献

About

使用Pytorch复现YOLOV1的论文,搭建可一键部署的YOLO_V1框架

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published