【机器学习入门】使用YOLO模型进行物体检测

系列文章目录

第1章 专家系统
第2章 决策树
第3章 神经元和感知机
识别手写数字——感知机
第4章 线性回归
第5章 逻辑斯蒂回归和分类
第5章 支持向量机
第6章 人工神经网络(一)
第6章 人工神经网络(二) 卷积和池化
第6章 使用pytorch进行手写数字识别


文章目录

  • 系列文章目录
  • 前言
  • 一、物体检测技术
  • 二、YOLO模型
    • 模型设计思路
    • YOLO模型的损失函数
    • 缩微YOLO模型的网络结构
  • 三、实现缩微YOLO模型
    • 定义模型类TinyYoloNetwork
    • 定义模型的前向计算过程
    • 定义YOLO 输出 (yolo) 方法
    • 加载模型权值数据
      • `load_weight_to` 函数
      • `load_weights` 函数
    • 处理真实图像
  • 总结


前言

在此之前,我们都是用模型解决简单的二分类、多分类问题或者是回归问题。这一篇开始解决稍微复杂的问题:物体检测。
不同于图片分类时的输入图片仅包含一个物体,并且位于图片中央,占据图片的大部分未知;物体检测的任务恰与之相反,任务目标主要是识别图像中的物体位置,用矩形框标注出物体的位置,同时给出物体类别。
在这里插入图片描述
物体检测技术,已经广泛应用于人脸检测 \行人检测系统 \辅助驾驶 车辆检测等等中.


一、物体检测技术

在传统方法中,物体识别可以拆分为两个步骤。第1步是从图像中识别局部特征,物体由局部特征组合构成。第2步是找到能够组合成物体的局部特征,判断它们所属的物体类别,进而确定物体的位置和大小。
图像的局部特征通常是将局部图像颜色和梯度分布描述为向量,相似的纹理或者形状通常具有类似的分布。将物体描述为局部特征组合的方法大致可以分为两类,一类方法类似于自然语言处理中的"词袋模型"。词袋模型将句子和文章描述为单词出现的频率,忽略了单词之间的位置关系。我们也可以忽略局部特征之间的位置关系,将物体视为局部特征的无序组合。另一类方法则把位置关系作为约束条件,那么寻找能够构成物体的特征组合就变成了有约束的优化问题。
人们在使用神经网络解决物体检测问题的时候,最初也采取了分步的策略。由于神经网络已经解决图像分类问题,于是可以将图片的局部拿来进行分类。只要用分类器扫描整幅图像的各个位置,就可以找到物体并将它的类别识别出来。暴力扫描的方式显然是效率低下的,于是人们提出了各种算法来筛选可能存在物体的候选框,减少候选框的数量来提高算法的性能。另一种策略是单步的方法,也叫作端到端的方法,即将整幅图像直接作为输入,同时输出物体框和类别,没有中间步骤。两种方法各有千秋。分步方法通常具有更高的准确率,可以处理大量小物体,但是提取候选框的过程中无法利用物体类别信息,进行物体分类时无法利用图像其他位置的背景信息。端到端的单步方法实现起来更为直接,运行速度通常更快,在识别物体时能够利用整幅图像的背景信息,但是有时会漏掉一些数量较多的小物体。

物体检测算法的发展经历了从传统方法到深度学习方法的转变。以下是一些重要的算法和它们的特点:

  1. 传统物体检测算法
    Haar特征+Adaboost:2001年Viola和Jones提出了基于Haar特征和Adaboost的快速人脸检测方法。这种方法通过集成多个弱分类器来构建一个强分类器,能够实现实时检测。
    HOG(Histogram of Oriented Gradients):HOG是一种描述图像局部特征的方法,通过统计图像局部区域的梯度方向直方图来构建特征,广泛应用于物体检测和行人检测。
    SVM(Support Vector Machine):支持向量机是一种监督学习模型,用于分类和回归分析。在物体检测中,SVM可以作为分类器来识别图像中的目标。
  2. 深度学习物体检测算法
    R-CNN(Regions with CNN features):R-CNN首先使用选择性搜索(Selective Search)提取候选区域,然后使用CNN提取特征,最后通过SVM进行分类。R-CNN开启了深度学习在物体检测领域的应用。
    Fast R-CNN:Fast R-CNN改进了R-CNN的效率问题,通过RoI(Region of Interest)Pooling层来提取固定大小的特征,并且实现了网络的端到端训练。
    Faster R-CNN:Faster R-CNN引入了RPN(Region Proposal Network)来自动生成高质量的候选区域,进一步提高了检测速度。
    YOLO(You Only Look Once):YOLO将物体检测问题视为一个回归问题,通过单个神经网络直接从图像像素到边界框坐标和类别概率的映射,实现了实时检测。
    SSD(Single Shot MultiBox Detector):SSD在不同尺度的特征图上进行检测,能够同时处理不同大小的目标,也适用于实时检测场景。

常用的物体检测数据集

  • PASCAL VOC:PASCAL VOC挑战赛是物体检测领域的一个重要基准,提供了丰富的图像和标注,用于评估和训练物体检测算法。
  • COCO(Common Objects in Context):COCO数据集包含了大量图像,每张图像中都包含多个目标,提供了更加复杂和多样的场景,是目前最流行的物体检测数据集之一。
  • ImageNet:虽然ImageNet主要以分类任务著称,但它也提供了物体检测的挑战,即ImageNet Large Scale Visual Recognition Challenge(ILSVRC)中的物体检测任务。
  • Objects365:由旷视科技发布的Objects365数据集是目前最大的物体检测数据集之一,包含63万张图像,覆盖365个类别,提供了更加丰富和多样的数据用于训练和测试物体检测算法。

这些算法和数据集共同推动了物体检测技术的发展和进步,使得计算机视觉系统能够更好地理解和解释图像内容。随着技术的不断演进,未来可能会出现更多高效、准确的物体检测算法和更加丰富多样的数据集。

二、YOLO模型

YOLO模型的全称是You Only Look Once,也就是说,神经网络模型只“看”一次,就输出物体检测的结果,是一种端到端的方法。
前反馈神经网络的输出长度一般是固定的(带有反馈的循环神经网络确实可以产生不固定长度的输出,也可以用于物体检测任务),然而一张图像中物体的数量是不确定的,如何将数量不确定的物体用固定长度的输出向量表示出来,这就是YOLO的关键。

模型设计思路

模型的思路是:将图像划分为大小相等的网格,每个网格负责输出中心点落在其中的物体框。假设物体类别数量为 K K K,那么,每个物体框可以用一个长度为 5 + K 5+K 5+K的向量表示,即 ( t x , t y , t w , t h , c , p 1 , p 2 , . . . , p K ) (t_x,t_y,t_w,t_h,c,p_1,p_2,...,p_K) (tx,ty,tw,th,c,p1,p2,...,pK)。前4个元素分别用来计算物体框的中心坐标和物体框的尺寸,第5个元素用于表示物体框中是否识别出物体的置信度(confidence);剩余 K K K个元素表示物体属于各个类别的概率。如果将图片切割为 S × S S \times S S×S个网格,那么神经网络的输出维度为 S × S × ( 5 + K ) S \times S \times (5+K) S×S×(5+K)
实际物体框的形状并不是完全随机的,如果对图片数据集中的标记进行统计,可以发现,物体框总是接近一些“常见”的尺寸。通过对训练数据集中的物体框尺寸进行聚类,可以得到若干个最常见的物体框尺寸,其他物体框可以看作这些常见物体框上进行“微调”的结果。这些聚类得出的常见物体框被称作“先验物体框”(prior)或“锚定物体框”(anchor)。
假设取A个先验物体框,那么神经网络的输出维度应该为 S × S × A × ( 5 + K ) S \times S \times A \times (5+K) S×S×A×(5+K),即每个网格输出A个物体框,分别基于先验物体框进行微调。

物体框的位置计算方式
设每个网格的长宽为单位1,每个网格输出的前两维 ( t x , t y ) (t_x,t_y) (tx,ty)经过Sigmoid函数之后,变成 ( 0 , 1 ) (0,1) (0,1)之间的数值,用来表示物体中心距离网格左上角的偏移量, 加上网格左上角的坐标 ( c x , c y ) (c_x,c_y) (cx,cy),就得到了物体框中心的坐标 ( b x , b y ) = ( c x + σ ( t x ) , c y + σ ( t y ) ) (b_x,b_y)=(c_x+\sigma(t_x),c_y+\sigma(t_y)) (bx,by)=(cx+σ(tx),cy+σ(ty))。对于物体框的尺寸,网格输出的第3、4维 ( t w , t h ) (t_w,t_h) (tw,th)经过指数函数,得到正实数值 ( e t w , e t h ) (e^{t_w},e^{t_h}) (etw,eth),再乘以先验物体框尺寸 ( p w , p h ) (p_w,p_h) (pw,ph),就得到了预测物体框的尺寸 ( b w , b h ) = ( p w e t w , p h e t h ) (b_w,b_h)=(p_we^{t_w},p_he^{t_h}) (bw,bh)=(pwetw,pheth)
在这里插入图片描述

YOLO模型的损失函数

YOLO的损失函数分为3个部分:物体框位置误差 识别物体的置信度误差 物体类别的误差.
物体框位置误差使用朴素的均方误差 , 只在有物体的框中计算位置误差. 我们用标记变量 1 i o b j \mathbb{1}_i^{obj} 1iobj表示编号为i的物体框中是否有物体,当有物体时该变量为1,反之为0. 物体框位置的预测值用 ( x i ^ , y i ^ , w i ^ , h i ^ ) (\hat{x_i},\hat{y_i},\hat{w_i},\hat{h_i}) (xi^,yi^,wi^,hi^)表示, 真实物体框尺寸表示为 ( x i , y i , w i , h i ) ({x_i},{y_i},{w_i},{h_i}) (xi,yi,wi,hi).
于是:
L coord  = λ coord  ∑ i 1 i obj  [ ( x i − x ^ i ) 2 + ( y i − y ^ i ) 2 ] + λ coord  ∑ i 1 i obj  [ ( w i − w ^ i ) 2 + ( h i − h ^ i ) 2 ] \begin{aligned} L_{\text {coord }}= & \lambda_{\text {coord }} \sum_{i} \mathbb{1}_{i}^{\text {obj }}\left[\left(x_{i}-\hat{x}_{i}\right)^{2}+\left(y_{i}-\hat{y}_{i}\right)^{2}\right] \\ & +\lambda_{\text {coord }} \sum_{i} \mathbb{1}_{i}^{\text {obj }}\left[\left(\sqrt{w_{i}}-\sqrt{\hat{w}_{i}}\right)^{2}+\left(\sqrt{h_{i}}-\sqrt{\hat{h}_{i}}\right)^{2}\right] \end{aligned} Lcoord =λcoord i1iobj [(xix^i)2+(yiy^i)2]+λcoord i1iobj [(wi w^i )2+(hi h^i )2]
其中, λ c o o r d \lambda_{coord} λcoord是用来调整位置误差所占比例的系数.

误差的第2部分是检测到物体的置信度误差. 在实际图片中, 有标记物体的网格但愿是户量要远小于没有物体的网格单元数量, 这实际上是一个不平衡的二分类问题. 为了改进平衡性, 防止误差函数引导网络输出的置信度向0靠近, 我们需要减弱没有物体的网格单元的误差惩罚. 这是通过引入系数 λ n o o b j \lambda _{noobj} λnoobj实现的,这个系数小于1. 下面是置信度误差, 其中 c i ^ \hat{c_i} ci^ 表示物体框 i i i 有物体的预测值, 1 i n o o b j = 1 − 1 i o b j \mathbb{1}_i^{noobj}=1-\mathbb{1}_i^{obj} 1inoobj=11iobj表示物体框 i i i中没有物体的标记变量.
L c o n f i d e n c e = − ∑ i 1 i o b j ln ⁡ c i ^ − ∑ i 1 i n o o b j ln ⁡ ( 1 − c i ^ ) L_{confidence}=-\sum_i \mathbb{1}_i ^{obj} \ln{\hat{c_i}}-\sum_i \mathbb{1}_i^{noobj} \ln{(1-\hat{c_i})} Lconfidence=i1iobjlnci^i1inoobjln(1ci^)

第3部分是物体分类误差,其中, p i , k p_{i,k} pi,k是第 i i i个物体属于类别 k k k的概率, 为神经网络的Soft Max输出值, k i k_i ki 为物体框里面的物体的真实类标签. 物体分类误差只在有物体的框中计算.
L c l a s s = ∑ i 1 o b j ln ⁡ p i , k i L_{class}=\sum_i \mathbb{1}_obj \ln{p_{i,k_i}} Lclass=i1objlnpi,ki

YOLO模型最终的损失函数为上面3部分的和 .
L c o o r d + L c o n f i d e n c e + L c l a s s L_{coord}+L_{confidence}+L_{class} Lcoord+Lconfidence+Lclass

缩微YOLO模型的网络结构

YOLO模型的神经网络结构是一个以卷积为主的多层前馈神经网络。这里我们介绍一个缩微版本模型,这个模型叫作YOLO v2tiny (模型配置文件下载地址),它是使用Pascal VOC数据集(数据集主页)训练得到的,可以识别20个类别的不同物体。
采用缩微版本,我们可以在CPU机器上快速实现物体识别,甚至可以使用CPU机器实现模型训练。Pascal VOC数据集包含 10000多幅图片,图片中包含了 20000多个20个不同类别的物体。在该数据集上完整训练一轮,CPU机器大约耗时十几个到几十个小时不等。而采用GPU 则能够极大地加速训练过程,以几十倍的比例压缩训练时间。
这个网络有9个卷积层,除了最后一个卷积层,每个卷积层后都增加了批归一化处理,用来加速训练过程,防止过拟合和梯度爆炸;激活函数采用了带泄露的修正线性单元(LeakyReLU),当卷积之后有池化层时,激活函数安排在池化层之后,对于没有池化层的卷积,激活函数直接作用于卷积层输出。
网络的前8个卷积层采用了大小为3×3的卷积核,较小的卷积核级联叠加,可以实现与较大的卷积核相似的效果,但是参数量较少,降低了模型的复杂度。比如,5×5的卷积核, 每个卷积核需要25个权值; 如果采用两个 3×3 的卷积核级联处理, 同样可以实现每个输出单元覆盖 5×5 范围内的输入, 但是, 卷积核的权值数量减少到 2×3×3=18 个. 压缩权值数量可以有效避免模型参数规模过度增长, 从而加速训练过程, 避免过拟合.

在这里插入图片描述
为了保证卷积输出结果尺寸的确定性, 卷积层都在输入的行列方向各补齐一行(或者一列)数据, 经过数据补齐, 3x3 的卷积输出尺寸就不会发生变化, 否则, 没经过一次3x3 卷积, 行列数都会各减少2, 控制网络输出尺寸会变得更加复杂. 因此, 数据补齐是一个简化网络设计的技巧.
卷积输出的尺寸不发生变化,只有通道数量不断增加。这是因为随着卷积和池化的不断级联叠加处理,每个神经元所感知的图像区域不断扩大,描述图像局部特征需要的容量(特征向量的长度)也相应增长。卷积结果从“通道”这个维度看,每个位置就是一个局部特征向量,向量长度就是通道的数量(即卷积核的数量)。因此,卷积层的输出通道数逐层加倍,从第1层到第7层,卷积层从16个输出通道增加到1024个输出通道。

数据的空间压缩过程由池化层完成。前5层卷积之后的最大池化层,采用2×9大小的核,以2为步长进行扫描,每经过一次这样的池化层,可以将数据长宽尺寸各缩小一半。这样5层过后,数据的尺寸缩小到原来的 1 / 32 1/32 1/32。后面的卷积层和池化层不再继续缩小数据尺寸,采取了步长为1,配合补齐数据,保持了输出数据的尺寸。其中,第6层卷积之后的池化层只在行列方向单侧补齐一行或者一列数据,这样,对于步长为1、尺寸为2×2的池化层,就可以保持输/入输出的尺寸相同。

网络最终输出的数据尺寸是原始输入的1/32。举例来说,如果输入图像大小为320×320像素,那么,输出网格的数量则为10×10。在实际训练过程中,训练图片被调整为 416×416像素作为输入,产生13×13个网格。模型有A=5个先验物体框,所以,每个网格单元产生5个物体框,最终将产生13×13×5个物体框。

值得注意的是最后一个卷积层,这一层卷积核大小为1×1。这是一种特殊的卷积核尺寸,它不进行空间上的数据整合,而是在每个局部的“点”(也就是网格单元)的输入通道维度上进行数据整合。它可以看作输入通道维度上的“全连接层”,用于产生每个网格单元的最终输出。当先验物体框数量为A,物体类别数量为K时,每个网格单元的输出值数量为Ax(5+K)。由于 Pascal VOC数据集中有20类物体,同时,模型聚类产生了5个先验物体框,因此,每个网格单元的输出长度为5×(5+20)=125。网络的最后一个卷积层的输出通道数就是125。

三、实现缩微YOLO模型

接下来使用pytorch实现缩微版本的YOLO模型.

定义模型类TinyYoloNetwork

首先,定义模型类TinyYolo-Network, 在构造函数中定义先验物体框 物体类别数量和网络的各层单元模块.

import torch
import torch.nn as nn# 缩微YOLO网络模型
class TinyYoloNetwork(nn.Module):def __init__(self):super(TinyYoloNetwork, self).__init__()# 从训练数据集中聚类得到的 先验物体框尺寸# 这些是物体框 最有可能的各种尺寸anchors = ((1.08,1.19),(3.42,4.41),(6.63,11.38),(9.42,5.11),(16.62,10.52))self.register_buffer("anchors",torch.tensor(anchors))# 物体类别数self.num_classes = 20# LeakyReLU 作为激活函数, 输入小于0时, 斜率为0.1self.relu = torch.nn.LeakyReLU(0.1, inplace=True)# 最大值池化层self.pool = torch.nn.MaxPool2d(2, stride=2)self.lastpool = torch.nn.MaxPool2d(2, 1)# 最后一个 池化层之前的数据补齐self.pad = torch.nn.ReflectionPad2d((0,1,0,1))# 批归一化层和卷积层self.norm1 = torch.nn.BatchNorm2d(16)self.conv1 = torch.nn.Conv2d(3, 16, 3, stride=1, padding=1, bias=False)self.norm2 = torch.nn.BatchNorm2d(32)self.conv2 = torch.nn.Conv2d(16, 32, 3, stride=1, padding=1, bias=False)self.norm3 = torch.nn.BatchNorm2d(64)self.conv3 = torch.nn.Conv2d(32, 64, 3, stride=1, padding=1, bias=False)self.norm4 = torch.nn.BatchNorm2d(128)self.conv4 = torch.nn.Conv2d(64, 128, 3, stride=1, padding=1, bias=False)self.norm5 = torch.nn.BatchNorm2d(256)self.conv5 = torch.nn.Conv2d(128, 256, 3, stride=1, padding=1, bias=False)self.norm6 = torch.nn.BatchNorm2d(512)self.conv6 = torch.nn.Conv2d(256, 512, 3, stride=1, padding=1, bias=False)self.norm7 = torch.nn.BatchNorm2d(1024)self.conv7 = torch.nn.Conv2d(512, 1024, 3, stride=1, padding=1, bias=False)self.norm8 = torch.nn.BatchNorm2d(1024)self.conv8 = torch.nn.Conv2d(1024, 1024, 3, stride=1, padding=1, bias=False)# 最后一个卷积层self.conv9 = torch.nn.Conv2d(1024, len(anchors)*(5+self.num_classes), 1, stride=1, padding=0)

关于上面代码的几点解释:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 批归一化层BatchNorm2d是作用于卷积输出的,在每个通道上独立进行归一化,它的参数是通道数量。
  • 最后一个池化层使用特殊的数据补齐ReflectionPad2d,在行列方向的单侧各补齐一行或者一列数据。

定义模型的前向计算过程

定义模型的前向计算过程,将各个模块连接在一起。

    def forward(self, x, yolo=True):# 将各个模块组织 为 神经网络x = self.relu(self.pool(self.norm1(self.conv1(x))))x = self.relu(self.pool(self.norm2(self.conv2(x))))x = self.relu(self.pool(self.norm3(self.conv3(x))))x = self.relu(self.pool(self.norm4(self.conv4(x))))x = self.relu(self.pool(self.norm5(self.conv5(x))))x = self.relu(self.lastpool(self.pad(self.conv6(x))))x = self.relu(self.norm7(self.conv7(x)))x = self.relu(self.norm8(self.conv8(x)))x = self.conv9(x)# 从神经网络的输出计算物体框位置和物体类别return self.yolo(x)

定义YOLO 输出 (yolo) 方法

下面定义yolo方法,将网络输出重新组织,使其包含每个锚框的位置、宽高、置信度和类别概率(第一部分提到过的形式),使用 torch.cat 将这些信息合并为一个张量,并返回。

# 将卷积层的输出结果转换为物体框的位置、检测到物体的置信度和分类结果def yolo(self, x):# 神经网络输出的结果形状如下:# 各维度依次是批次样本索引,  输出通道, 输出高度, 输出宽度n_batch, n_channel, height, width = x.shape# 将输出通道拆分为 若干先验物体框x = x.view(n_batch, self.anchors.shape[0], -1, height, width)# 重新调整 各个维度的顺序如下# 样本索引, 先验物体框编号, 网格纵向和横向序号, 物体框维度# 其中, 位置及分类输出的维度尺寸是 (5+类别数)x = x.permute(0, 1, 3, 4, 2)# 准备用于计算物体框位置的 辅助张量# 首先是先验物体框的尺寸anchors = self.anchors.to(dtype=x.dtype, device=x.device)anchor_width, anchor_height = anchors[:, 0], anchors[:, 1]# 然后是网格的偏移量grid_y, grid_x = torch.meshgrid(torch.arange(height, dtype=x.dtype, device=x.device),torch.arange(width, dtype=x.dtype, device=x.device),)# 计算物体框位置 和 物体分类输出, 最后一维各列分别是:# 中心位置坐标, 物体框宽高, 检测到物体的置信度, 物体类别概率return torch.cat([(x[:, :, :, :, 0:1].sigmoid()+grid_x[None, None, :, :, None])/width,(x[:, :, :, :, 1:2].sigmoid()+grid_y[None, None, :, :, None])/height,(x[:, :, :, :, 2:3].exp()*anchor_width[None, :, None, None, None])/width,(x[:, :, :, :, 3:4].exp()*anchor_height[None, :, None, None, None])/height,x[:, :, :, :, 4:5].sigmoid(),x[:, :, :, :, 5:].softmax(-1),], -1)

在这里插入图片描述
关于softmax(-1)的理解:查到一个不错的解答,点此跳转
简单说,softmax(-1) 表示 softmax 函数将在张量的最后一个维度上操作。在 PyTorch 中,-1 被用作维度的占位符,它告诉函数在输入张量的最后一个维度上应用 softmax 函数。

现在测试一下网络模型是否工作正常,

# 先测试一下网络模型工作是否正常, 填入一个320x320像素的随机图像作为输入, 输出的网格数量为10x10
# 每个网格单元的物体框数量为5, 每个物体框表示为长度为25的向量
x = torch.rand((1, 3, 320, 320))
net = TinyYoloNetwork()
y = net(x)
print(y.shape)

使用Debugger工具追踪,可以得到以下输出,在上面的测试代码中,我们随机生成了一个形状为(1,3,320,320)的张量, 其中第一个维度 1 表示批次大小(batch size),在这里是 1,意味着这个张量代表一个单独的图像样本。第二个维度 3 表示图像的通道数(channels),在这里是 3,对应于 RGB 彩色图像的三个颜色通道:红色(R)、绿色(G)和蓝色(B)。第三个维度 320 表示图像的高度(height),即图像有 320 像素高。第四个维度 320 表示图像的宽度(width),即图像有 320 像素宽。
在这里插入图片描述
在这里插入图片描述

加载模型权值数据

这里我们直接加载YOLO模型的作者提供的预训练好的模型权值文件,直接省去了训练模型的过程。但如果我们需要处理自己的数据集,还是需要在这个的基础上进行增量训练。与从头开始训练相比,这种方法可以节省训练时间。YOLO模型权值文件下载地址:https://pjreddie.com/media/files/yolov2-tiny-voc.weights

# 读取权值的辅助函数
# 从weights中offset位置读取权值到target
def load_weight_to(weights, offset, target):n = target.numel()target.data[:] = torch.from_numpy(weights[ offset : offset+n]).view_as(target.data)return offset+nimport numpy# 从文件加载训练好的网络权值
def load_weights(network, filename="G:\\BrowserDownload\\2024-03\\yolov2-tiny-voc.weights"):with open(filename, "rb") as file:# 读取版本号 和 训练状态记录header = numpy.fromfile(file, count=4, dtype=numpy.int32)# 其余所有值都是网络权值weights = numpy.fromfile(file, dtype=numpy.float32)idx = 0for layer in network.children():# 读取卷积层权值if isinstance(layer, torch.nn.Conv2d):if layer.bias is not None:idx = load_weight_to(weights, idx, layer.bias)idx = load_weight_to(weights, idx, layer.weight)# 读取批归一化层权值if isinstance(layer, torch.nn.BatchNorm2d):idx = load_weight_to(weights, idx, layer.bias)idx = load_weight_to(weights, idx, layer.weight)idx = load_weight_to(weights, idx, layer.running_mean)idx = load_weight_to(weights, idx, layer.running_var)# 调用函数加载权值
load_weights(net)

这段代码提供了两个函数,用于将训练好的 YOLOv2-tiny 模型权重从 Darknet 格式加载到 PyTorch 模型中。

load_weight_to 函数

这是一个辅助函数,用于从权重数组中加载权重到 PyTorch 模型的特定参数中。它接受三个参数:

  • weights:一个 NumPy 数组,包含了从权重文件中读取的所有权重。
  • offset:一个整数,表示在 weights 数组中当前要读取的起始位置。
  • target:一个 PyTorch 参数(例如,nn.Conv2d 的权重或偏置),它是要更新的目标参数。

函数执行以下步骤:

  1. 使用 target.numel() 获取目标参数中的元素数量 n
  2. 使用 torch.from_numpy 将权重数组的一部分转换为 PyTorch 张量,并使用 view_as 方法调整张量的形状以匹配目标参数的形状。
  3. 将转换后的张量赋值给目标参数的数据。
  4. 返回更新后的偏移量 offset+n,用于后续加载其他层的权重。

load_weights 函数

这个函数用于遍历 PyTorch 模型的所有层,并加载预训练的权重。它接受两个参数:

  • network:要加载权重的 PyTorch 网络模型。
  • filename:包含预训练权重的文件路径。

函数执行以下步骤:

  1. 打开权重文件进行二进制读取。
  2. 读取文件头部信息,通常包括版本号和训练状态记录,但在此代码中未使用。
  3. 读取剩余的文件内容作为权重值。
  4. 遍历网络的每一层,对于每种类型的层(卷积层和批归一化层),使用 load_weight_to 函数加载权重和偏置。
  5. 对于批归一化层,除了权重和偏置外,还需要加载 running_meanrunning_var

这个权重加载过程假设 Darknet 格式的权重文件与 PyTorch 模型的结构是匹配的。如果结构不匹配,需要对加载过程进行相应的调整。这个过程是将预训练模型的权重迁移到 PyTorch 框架中,以便可以在 PyTorch 环境中使用这些模型进行推理或继续训练。

处理真实图像

真实图像的尺寸各不相同,网络模型通常需要特定输入的尺寸。YOLO模型需要图像的长度和宽度都是32的整倍数,每32x32个像素对应一个网格。
在YOLO模型训练阶段,输入图像都为426x416, 所以在使用模型时, 也要尽量输入与训练图片尺寸接近或一致的图片.
下面的代码用来加载真实图像, 并且对图像进行缩放和补齐, 使得图像与我们期望的输入尺寸一致, 是以哦那个Pillow包进行图像的读取; 再使用PyTorch软件包中的torchvision视觉模块进行图像缩放,补齐操作, 将图像转化为网络模型可以接受的张量.

import torchvision
from PIL import Imagedef load_image(filename, width, height):img = Image.open(filename)scale = min(width / img.width, height / img.height)new_width, new_height = int(img.width * scale), int(img.height * scale)diff_width, diff_height = width-new_width, height-new_heightpadding = (diff_width // 2, diff_height // 2,diff_width // 2 + diff_width % 2,diff_height // 2 + diff_height % 2)transform = torchvision.transforms.Compose([torchvision.transforms.Resize(size=(new_height, new_width)),torchvision.transforms.Pad(padding=padding),torchvision.transforms.ToTensor()])# 用unsqueeze 方法增加一维样本编号# 网络模型是成批接受输入的# 即 一批只有一个样本return transform(img).unsqueeze(0)

下面准备绘制物体框,要在输出绘制的物体框中对物体类别进行标注, 我们需要提前准备VOC数据集中的20个物体类别的名字.

# 准备工作: 将网络模型的输出可视化
class_labels = ("aeroplane", "bicycle", "bird", "boat", "bottle","bus", "car", "cat", "chair",  "cow","diningtable", "dog", "horse", "motorbike", "person","pottedplant", "sheep", "sofa", "train", "tvmonitor",
)

网络模型会产生大量物体框, 以320x320像素的输入图像为例, 网络会产生100个网格, 每个网格产生5个物体框. 显然, 我们不能把所有500个物体框都绘制出来, 只需要通过一个threshold过滤掉置信度不高的物体框, 同时, 我们希望物体类别的softmax输出也比较大, 因此, 过滤时比较的是置信度与softmax输出的乘积,将这个乘积显示出来,用来观察网络模型认为某处存在某种物体的概率大小.

# 下面是绘制物体框的函数def show_images_with_boxes(input_tensor, output_tensor, class_labels, threshold):# 区分不同物体框的颜色表colors = ['r', 'g', 'b', 'y', 'c', 'm', 'k']to_img = torchvision.transforms.ToPILImage()img = to_img(input_tensor[0])# 显示图片plt.imshow(img)axis = plt.gca()# 将网络输出提取为numpy数组output = output_tensor[0].cpu().detach().numpy().reshape((-1, 5+len(class_labels)))classes = numpy.argmax(output[:, 5:], axis=-1)confidences = output[:, 4] * numpy.max(output[:, 5:], axis=-1)# 将物体框调整到输入图片的尺寸boxes = output[:, 0:4]boxes[:, 0::2] *= img.widthboxes[:, 1::2] *= img.heightboxes[:, 0:2] -= boxes[:, 2:4] / 2# 逐个显示物体框for box, confidence, class_id in zip(boxes, confidences, classes):# 忽略置信度较低的物体框if confidence < threshold: continue# 绘制物体框color = colors[class_id % len(colors)]rect = patches.Rectangle(box[0:2], box[2], box[3],linewidth=1, edgecolor=color, facecolor='none')axis.add_patch(rect)label = class_labels[class_id]label = '{0}{1:.2f}'.format(label, confidence)plt.text(box[0], box[1], label, color='w', backgroundcolor=color)plt.show()

在这里插入图片描述
接下来,输入真实的图像,测试一下啊

net = TinyYoloNetwork()
load_weights(net)
imgs = load_image('D:\\For_Study\\SelfLearn\\ForPythonArcgis\\demo1\\data\\dogs.jpg', 320, 320)
output_tensor = net(imgs)
show_images_with_boxes(imgs, output_tensor, class_labels, 0.3)

在这里插入图片描述
从输出结果中,我们可以看到有若干个位置相似的物体框套在同一个物体上,它们可能来自相邻的网格单元, 或者来自同一网格单元内不同的先验物体框。更加完善的处理是,计算这些重叠物体框的重叠比例,当重叠比例超过一定阈值时,认为它们描述的时同一个物体,选择其中置信度最高的物体框,忽略其他物体框。这个过程叫作最大值抑制,可以有效地过滤重复的物体框。


总结

今天学习的是使用预训练好的YOLO模型对真实图片进行物体检测的代码.

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

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

相关文章

八股面试速成—计算机网络部分

暑期实习面试在即&#xff0c;这几天八股和算法轮扁我>_ 八股部分打算先找学习视屏跟着画下思维导图&#xff0c;然后看详细的面试知识点&#xff0c;最后刷题 其中导图包含的是常考的题&#xff0c;按照思维导图形式整理&#xff0c;会在复盘后更新 细节研究侧重补全&a…

基于单片机冬季供暖室温调节控制系统

**单片机设计介绍&#xff0c;基于单片机冬季供暖室温调节控制系统 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的冬季供暖室温调节控制系统是一种集温度检测、控制和显示功能于一体的智能化系统。该系统以单片机为…

C++ 指针与结构

三种存取结构成员的方式&#xff1a; ① 通过结构变量名&#xff1b; ②通过指向结构的指针和间接运算符(*)&#xff1b; ③通过指向结构的指针和指向成员运算符(->);

【已解决】ZIP压缩文件如何设置密码?

ZIP是常用的压缩格式之一&#xff0c;对于重要的ZIP文件&#xff0c;我们还可设置密码保护&#xff0c;那ZIP压缩文件怎么设置密码呢&#xff1f;不清楚的小伙伴一起来看看吧&#xff01; 给ZIP文件设置密码&#xff0c;我们需要用到支持ZIP格式的解压缩软件&#xff0c;比如7…

2024HW-->Wireshark攻击流量分析

在HW中&#xff0c;最离不开的&#xff0c;肯定是看监控了&#xff0c;那么就要去了解一些wireshark的基础用法以及攻击的流量&#xff01;&#xff01;&#xff01;&#xff01; 1.Wireshark的基本用法 比如人家面试官给你一段流量包&#xff0c;你要会用 1.分组详情 对于我…

UE4_如果快速做出毛玻璃效果_假景深

UE4_如果快速做出毛玻璃效果_假景深 2022-08-20 15:02 一个SpiralBlur-SceneTexture材质节点完成效果&#xff0c;启用半透明材质通过修改BlurAmount数值大小调整效果spiralBlur-SceneTexture custom节点&#xff0c;HLSL语言float3 CurColor 0;float2 BaseUV MaterialFloa…

pytest的时候输出一个F后面跟很多绿色的点解读

使用pytest来测试pyramid和kotti项目&#xff0c;在kotti项目测试的时候&#xff0c;输出一个F后面跟很多绿色的点&#xff0c;是什么意思呢&#xff1f; 原来在使用pytest进行测试时&#xff0c;输出中的“F”代表一个失败的测试&#xff08;Failed&#xff09;&#xff0c;而…

隧道风速风向检测器的工作原理

TH-SQX1隧道风速风向检测器是一种专门用于隧道内部风速和风向监测的设备。它基于超声波技术进行测量&#xff0c;通过发射和接收超声波信号&#xff0c;利用信号传输时间差来精确测量风速和风向。这种检测器具有测量准确、响应速度快、稳定性好等优点&#xff0c;适用于隧道内部…

技术再度取得优势,人工智能兴起推动需求,美芯涨价收割市场,收割中国制造?...

独家首发 ------------- 分析机构指出一季度全球存储芯片涨价了15%左右&#xff0c;而近期三星半导体预测全球存储芯片的价格还将继续上涨&#xff0c;预计二季度至少上涨两成&#xff0c;显示出美系芯片在忍受了一年多的亏损之后再度联手涨价。 2022年中国存储芯片取得了重大进…

08 Python进阶:XML 解析

什么是 XML&#xff1f; XML&#xff08;可扩展标记语言&#xff0c;Extensible Markup Language&#xff09;是一种用于表示和传输数据的标记语言。它被设计用来以一种结构化的形式描述文档的内容&#xff0c;并且具有良好的跨平台和跨语言的特性。XML使用标签来定义数据的结构…

免费https详细教程

简单叙述一下https的定义和实现https的一些基本作用&#xff0c;然后会给到申请SSL证书的方式以及安装部署流程&#xff0c;最终实现网站的https访问。 随着互联网的快速发展&#xff0c;网络安全问题日益凸显。在互联网上传输敏感信息、进行在线交易和共享个人数据时&#xf…

Spring boot微服务分布式框架Rouyi Cloud权限认证

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

Go 程序的启动流程【1/2】

Go 程序的启动流程 本文将以一个简单的 HelloWorld 程序为例&#xff0c;探究 Go 程序的启动流程 package mainfunc main() {_ "Hello World" }入口 我们先通过 go build . 将代码编译成可执行文件&#xff0c;众所周知&#xff0c;我们在一个 shell 中执行可执行…

CLoVe:在对比视觉语言模型中编码组合语言

CLoVe:在对比视觉语言模型中编码组合语言 摘要引言相关工作CLoVe: A Framework to Increase Compositionality in Contrastive VLMsSynthetic CaptionsHard NegativesModel Patching CLoVe: Encoding Compositional Language inContrastive Vision-Language Models 摘要 近年来…

记一次安服薅洞实战

记一次为数不多但还算有点收获的一次实战&#xff08;平时摸鱼来着...&#xff09;&#xff0c;大致任务是对某某市某*院进行次漏洞收集和外网资产梳理且是有授权的&#xff08;其实是甲方不大清楚自己外网有多少资产&#xff09;&#xff0c;漏洞质量要求还挺高。emmm本来是打…

大米自动化生产线设备:现代粮食加工的核心力量

随着科技的不断进步和粮食加工行业的快速发展&#xff0c;大米自动化生产线设备在现代粮食加工中的地位愈发重要。这些设备不仅大大提高了生产效率&#xff0c;还保证了产品的质量和安全&#xff0c;成为了现代粮食加工行业不可或缺的核心力量。 一、自动化生产线设备助力效率提…

【面试经典150 | 动态规划】交错字符串

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;动态规划 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的数据结构等内容进行…

一些Java面试题

1、 Java语言有哪些特点 1、简单易学、有丰富的类库 2、面向对象&#xff08;Java最重要的特性&#xff0c;让程序耦合度更低&#xff0c;内聚性更高&#xff09; 3、与平台无关性&#xff08;JVM是Java跨平台使用的根本&#xff09; 4、可靠安全 5、支持多线程 2、面向对象和…

1.8.3 卷积神经网络近年来在结构设计上的主要发展和变迁——GoogleNet/inception-v1

1.8.3 卷积神经网络近年来在结构设计上的主要发展和变迁——GoogleNet/ inception-v1 前情回顾&#xff1a; 1.8.1 卷积神经网络近年来在结构设计上的主要发展和变迁——AlexNet 1.8.2 卷积神经网络近年来在结构设计上的主要发展和变迁——VGGNet GoogleNet问题 在VGGNet简单堆…

(2024)Ubuntu源码安装多个版本的opencv并切换使用

本人工作会用到x86_64的opencv和aarch64的opencv&#xff0c;所以写下来备忘自用 一、源码编译安装 依赖库安装&#xff1a; sudo apt-get install build-essential libgtk2.0-dev libgtk-3-dev libavcodec-dev libavformat-dev libjpeg-dev libswscale-dev libtiff5-dev o…