【机器学习入门】使用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.rhkb.cn/news/299912.html

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

相关文章

静态路由协议实验综合实验

需求&#xff1a; 1、除R5的换回地址已固定外&#xff0c;整个其他所有的网段基于192.168.1.0/24进行合理的IP地址划分。 2、R1-R4每台路由器存在两个环回接口&#xff0c;用于模拟连接PC的网段&#xff1b;地址也在192.168.1.0/24这个网络范围内。 3、R1-R4上不能直接编写到…

Android14应用启动流程(源码+Trace)

1.简介 应用启动过程快的都不需要一秒钟&#xff0c;但这整个过程的执行是比较复杂的&#xff0c;无论是对手机厂商、应用开发来说启动速度也是核心用户体验指标之一&#xff0c;本文采用Android14源码与perfetto工具进行解析。 源码参考地址&#xff1a;Search trace分析工…

Redis的配置文件详解

单位&#xff1a;Redis配置对大小写不敏感&#xff01; 注意这里&#xff1a;任何写法都可&#xff0c;不区分大小写。 units are case insensitive so 1GB 1Gb 1gB are all the same.包含&#xff1a;搭建Redis集群时&#xff0c;可以使用includes包含其他配置文件网络&…

在c# 7.3中不可用,请使用9.0或更高的语言版本

参考连接&#xff1a;在c# 7.3中不可用,请使用8.0或更高的语言版本_功能“可为 null 的引用类型”在 c# 7.3 中不可用。请使用 8.0 或更高的语言版本-CSDN博客https://blog.csdn.net/liangyely/article/details/106163660 [踩坑记录] 某功能在C#7.3中不可用,请使用 8.0 或更高的…

RGB三通道和灰度值的理解

本文都是来自于chatGPT的回答!!! 目录 Q1:像素具有什么属性?Q2:图像的色彩是怎么实现的?Q3:灰度值和颜色值是一个概念吗?Q4:是不是像素具有灰度值&#xff0c;也有三个颜色分量RGB&#xff1f;Q5:灰度图像是没有色彩的吗&#xff1f;Q6: 彩色图像是既具有灰度值也具有RGB三…

【JavaWeb】Day30.SpringBootWeb请求响应——响应

响应 HTTL协议的交互方式&#xff1a;请求响应模式&#xff08;有请求就有响应&#xff09;那么Controller程序&#xff0c;除了接收请求外&#xff0c;还可以进行响应。 1.ResponseBody 在我们前面所编写的controller方法中&#xff0c;都已经设置了响应数据。 controller方…

利用甘特图实现精细化项目管控

在项目管理中,通过精细化管控,项目经理能够有效规划、监督和协调各项任务,从而最大限度控制风险,优化资源配置,并确保按时、按质、按量完成项目目标。而在众多项目管理工具中,甘特图无疑是实现精细化项目管控的利器。zz-plan 是一个非常好用的在线甘特图制作工具&#xff0c;一…

【力扣】94. 二叉树的中序遍历、144. 二叉树的前序遍历、145. 二叉树的后序遍历

先序遍历&#xff1a;根-左-右中序遍历&#xff1a;左-根-右后序遍历&#xff1a;左-右-根 94. 二叉树的中序遍历 题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3…

【三十五】【算法分析与设计】综合练习(2),22。 括号生成,77。 组合,494。 目标和,模拟树递归,临时变量自动维护树定义,递归回溯,非树结构模拟树

22. 括号生成 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;["&#xff08;&#xff08;&#xff08;&#xff09;&#xff09;&#xff0…

C 回调函数的两种使用方法

对回调&#xff08;callback&#xff09;函数的一点粗陋理解&#xff0c;在我小时候&#xff0c;隔壁村有家月饼小作坊&#xff08;只在中秋那段时间手工制作一些月饼出售&#xff0c;后来好像不做了&#xff09;&#xff0c;做出的月饼是那种很传统很经典的款式&#xff0c;里…

金融中的数学模型

平稳时间序列 时间序列的基本统计特性&#xff0c;如均值、方差和自相关等&#xff0c;在时间上不随时间的推移而发生显著的变化。 平稳时间序列通常具有以下特征&#xff1a; 均值不随时间变化&#xff1a;序列的均值在时间上保持恒定。方差不随时间变化&#xff1a;序列的…

人工智能上手 Pytorch

人工智能上手 Pytorch 1、人工智能框架历史走向 2015年&#xff0c; caffe&#xff0c;优势配置简单&#xff0c;缺点安装麻烦&#xff0c;且不更新维护 2016年&#xff0c;tensorflow 1.x&#xff0c;定义太严格&#xff0c;很复杂。开发成本高。简单的任务&#xff0c;也很…

DVWA -File Upload-通关教程-完结

DVWA -File Upload-通关教程-完结 文章目录 DVWA -File Upload-通关教程-完结页面功能LowMediumHighImpossible 页面功能 此页面的功能为选择某个图片文件点击Upload按钮上传&#xff0c;上传成功后得知文件上传路径为DVWA\hackable\uploads。 Low 源码审计 这段 PHP 代码…

Unknown redis exception; event execu tor terminated;解决

最近查看服务器日记是不是报发现有台服务器报错&#xff1a; rocessing failed; nested exception is org.springframework.data.redis.RedisSystemException: Unknown redis exception; nested exception is java.util.concurrent.RejectedExecutionException: event execu …

58商铺全新UI试客试用平台网站php源码

探索未来商铺新纪元&#xff0c;58商铺全新UI试客试用平台网站PHP源码完整版震撼来袭&#xff01; 在这个数字化飞速发展的时代&#xff0c;58商铺一直致力于为商家和消费者打造更加便捷、高效的交易平台。今天&#xff0c;我们荣幸地推出全新UI试客试用平台网站PHP源码完整版…

免注册,ChatGPT可即时访问了!

AI又有啥进展&#xff1f;一起看看吧 Apple进军个人家用机器人 Apple在放弃自动驾驶汽车项目并推出混合现实头显后&#xff0c;正在进军个人机器人领域&#xff0c;处于开发家用环境机器人的早期阶段 报告中提到了两种可能的机器人设计。一种是移动机器人&#xff0c;可以跟…

MES实施之工控机和电脑的选择

在MES项目实施过程中,经常会碰到工控机和电脑的选型问题,那么他们的区别是什么? 1、控机和普通个人电脑(PC)相比,具有以下几个区别: 1.运行环境不同:工控机通常需要在各种恶劣的工业环境中运行,如高温、高湿、强电磁干扰等,因此需要具有防尘、防水、抗干扰等特点。而…

物联网可视化平台

随着数字化转型的深入&#xff0c;物联网技术正在成为企业实现智能化、高效化运营的重要工具。物联网可视化平台&#xff0c;作为连接物理世界与数字世界的桥梁&#xff0c;为企业提供了直观、实时的数据展示和监控能力&#xff0c;从而在数字化转型中扮演着关键角色。 一、物…

分享three.js实现乐高小汽车

前言 Web脚本语言JavaScript入门容易&#xff0c;但是想要熟练掌握却需要几年的学习与实践&#xff0c;还要在弱类型开发语言中习惯于使用模块来构建你的代码&#xff0c;就像小时候玩的乐高积木一样。 应用程序的模块化理念&#xff0c;通过将实现隐藏在一个简单的接口后面&a…

JavaWeb--JavaScript Part 01

1. JavaScript概述 JavaScript&#xff08;简称JS&#xff09;是一种轻量级的、解释执行的客户端脚本语言&#xff0c;主要用于增强网页的交互性和动态性。它起源于Netscape的LiveScript&#xff0c;并在1995年发布时更名为JavaScript。尽管名称中包含"Java"&#xf…