做网站难度大吗,查询建设工程施工规范网站,公共资源交易中心总结,紫鸟超级浏览器手机版系列文章目录
第1章 专家系统 第2章 决策树 第3章 神经元和感知机 识别手写数字——感知机 第4章 线性回归 第5章 逻辑斯蒂回归和分类 第5章 支持向量机 第6章 人工神经网络(一) 第6章 人工神经网络(二) 卷积和池化 第6章 使用pytorch进行手写数字识别 文章目录 系列文章目录前…系列文章目录
第1章 专家系统 第2章 决策树 第3章 神经元和感知机 识别手写数字——感知机 第4章 线性回归 第5章 逻辑斯蒂回归和分类 第5章 支持向量机 第6章 人工神经网络(一) 第6章 人工神经网络(二) 卷积和池化 第6章 使用pytorch进行手写数字识别 文章目录 系列文章目录前言一、物体检测技术二、YOLO模型模型设计思路YOLO模型的损失函数缩微YOLO模型的网络结构 三、实现缩微YOLO模型定义模型类TinyYoloNetwork定义模型的前向计算过程定义YOLO 输出 (yolo) 方法加载模型权值数据load_weight_to 函数load_weights 函数 处理真实图像 总结 前言
在此之前我们都是用模型解决简单的二分类、多分类问题或者是回归问题。这一篇开始解决稍微复杂的问题物体检测。 不同于图片分类时的输入图片仅包含一个物体并且位于图片中央占据图片的大部分未知物体检测的任务恰与之相反任务目标主要是识别图像中的物体位置用矩形框标注出物体的位置同时给出物体类别。 物体检测技术,已经广泛应用于人脸检测 \行人检测系统 \辅助驾驶 车辆检测等等中. 一、物体检测技术 在传统方法中物体识别可以拆分为两个步骤。第1步是从图像中识别局部特征物体由局部特征组合构成。第2步是找到能够组合成物体的局部特征判断它们所属的物体类别进而确定物体的位置和大小。 图像的局部特征通常是将局部图像颜色和梯度分布描述为向量相似的纹理或者形状通常具有类似的分布。将物体描述为局部特征组合的方法大致可以分为两类一类方法类似于自然语言处理中的词袋模型。词袋模型将句子和文章描述为单词出现的频率忽略了单词之间的位置关系。我们也可以忽略局部特征之间的位置关系将物体视为局部特征的无序组合。另一类方法则把位置关系作为约束条件那么寻找能够构成物体的特征组合就变成了有约束的优化问题。 人们在使用神经网络解决物体检测问题的时候最初也采取了分步的策略。由于神经网络已经解决图像分类问题于是可以将图片的局部拿来进行分类。只要用分类器扫描整幅图像的各个位置就可以找到物体并将它的类别识别出来。暴力扫描的方式显然是效率低下的于是人们提出了各种算法来筛选可能存在物体的候选框减少候选框的数量来提高算法的性能。另一种策略是单步的方法也叫作端到端的方法即将整幅图像直接作为输入同时输出物体框和类别没有中间步骤。两种方法各有千秋。分步方法通常具有更高的准确率可以处理大量小物体但是提取候选框的过程中无法利用物体类别信息进行物体分类时无法利用图像其他位置的背景信息。端到端的单步方法实现起来更为直接运行速度通常更快在识别物体时能够利用整幅图像的背景信息但是有时会漏掉一些数量较多的小物体。 物体检测算法的发展经历了从传统方法到深度学习方法的转变。以下是一些重要的算法和它们的特点
传统物体检测算法 Haar特征Adaboost2001年Viola和Jones提出了基于Haar特征和Adaboost的快速人脸检测方法。这种方法通过集成多个弱分类器来构建一个强分类器能够实现实时检测。 HOGHistogram of Oriented GradientsHOG是一种描述图像局部特征的方法通过统计图像局部区域的梯度方向直方图来构建特征广泛应用于物体检测和行人检测。 SVMSupport Vector Machine支持向量机是一种监督学习模型用于分类和回归分析。在物体检测中SVM可以作为分类器来识别图像中的目标。深度学习物体检测算法 R-CNNRegions with CNN featuresR-CNN首先使用选择性搜索Selective Search提取候选区域然后使用CNN提取特征最后通过SVM进行分类。R-CNN开启了深度学习在物体检测领域的应用。 Fast R-CNNFast R-CNN改进了R-CNN的效率问题通过RoIRegion of InterestPooling层来提取固定大小的特征并且实现了网络的端到端训练。 Faster R-CNNFaster R-CNN引入了RPNRegion Proposal Network来自动生成高质量的候选区域进一步提高了检测速度。 YOLOYou Only Look OnceYOLO将物体检测问题视为一个回归问题通过单个神经网络直接从图像像素到边界框坐标和类别概率的映射实现了实时检测。 SSDSingle Shot MultiBox DetectorSSD在不同尺度的特征图上进行检测能够同时处理不同大小的目标也适用于实时检测场景。
常用的物体检测数据集
PASCAL VOCPASCAL VOC挑战赛是物体检测领域的一个重要基准提供了丰富的图像和标注用于评估和训练物体检测算法。COCOCommon Objects in ContextCOCO数据集包含了大量图像每张图像中都包含多个目标提供了更加复杂和多样的场景是目前最流行的物体检测数据集之一。ImageNet虽然ImageNet主要以分类任务著称但它也提供了物体检测的挑战即ImageNet Large Scale Visual Recognition ChallengeILSVRC中的物体检测任务。Objects365由旷视科技发布的Objects365数据集是目前最大的物体检测数据集之一包含63万张图像覆盖365个类别提供了更加丰富和多样的数据用于训练和测试物体检测算法。
这些算法和数据集共同推动了物体检测技术的发展和进步使得计算机视觉系统能够更好地理解和解释图像内容。随着技术的不断演进未来可能会出现更多高效、准确的物体检测算法和更加丰富多样的数据集。
二、YOLO模型
YOLO模型的全称是You Only Look Once也就是说神经网络模型只“看”一次就输出物体检测的结果是一种端到端的方法。 前反馈神经网络的输出长度一般是固定的带有反馈的循环神经网络确实可以产生不固定长度的输出也可以用于物体检测任务然而一张图像中物体的数量是不确定的如何将数量不确定的物体用固定长度的输出向量表示出来这就是YOLO的关键。
模型设计思路
模型的思路是将图像划分为大小相等的网格每个网格负责输出中心点落在其中的物体框。假设物体类别数量为 K K K那么每个物体框可以用一个长度为 5 K 5K 5K的向量表示即 ( 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 (5K) S×S×(5K)。 实际物体框的形状并不是完全随机的如果对图片数据集中的标记进行统计可以发现物体框总是接近一些“常见”的尺寸。通过对训练数据集中的物体框尺寸进行聚类可以得到若干个最常见的物体框尺寸其他物体框可以看作这些常见物体框上进行“微调”的结果。这些聚类得出的常见物体框被称作“先验物体框”(prior)或“锚定物体框”(anchor)。 假设取A个先验物体框那么神经网络的输出维度应该为 S × S × A × ( 5 K ) S \times S \times A \times (5K) S×S×A×(5K)即每个网格输出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 i∑1iobj [(xi−x^i)2(yi−y^i)2]λcoord i∑1iobj [(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} 1inoobj1−1iobj表示物体框 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−i∑1iobjlnci^−i∑1inoobjln(1−ci^)
第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}} Lclassi∑1objlnpi,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} LcoordLconfidenceLclass
缩微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×318 个. 压缩权值数量可以有效避免模型参数规模过度增长, 从而加速训练过程, 避免过拟合. 为了保证卷积输出结果尺寸的确定性, 卷积层都在输入的行列方向各补齐一行(或者一列)数据, 经过数据补齐, 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个网格。模型有A5个先验物体框所以每个网格单元产生5个物体框最终将产生13×13×5个物体框。
值得注意的是最后一个卷积层这一层卷积核大小为1×1。这是一种特殊的卷积核尺寸它不进行空间上的数据整合而是在每个局部的“点”(也就是网格单元)的输入通道维度上进行数据整合。它可以看作输入通道维度上的“全连接层”用于产生每个网格单元的最终输出。当先验物体框数量为A物体类别数量为K时每个网格单元的输出值数量为Ax(5K)。由于 Pascal VOC数据集中有20类物体同时模型聚类产生了5个先验物体框因此每个网格单元的输出长度为5×(520)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, inplaceTrue)# 最大值池化层self.pool torch.nn.MaxPool2d(2, stride2)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, stride1, padding1, biasFalse)self.norm2 torch.nn.BatchNorm2d(32)self.conv2 torch.nn.Conv2d(16, 32, 3, stride1, padding1, biasFalse)self.norm3 torch.nn.BatchNorm2d(64)self.conv3 torch.nn.Conv2d(32, 64, 3, stride1, padding1, biasFalse)self.norm4 torch.nn.BatchNorm2d(128)self.conv4 torch.nn.Conv2d(64, 128, 3, stride1, padding1, biasFalse)self.norm5 torch.nn.BatchNorm2d(256)self.conv5 torch.nn.Conv2d(128, 256, 3, stride1, padding1, biasFalse)self.norm6 torch.nn.BatchNorm2d(512)self.conv6 torch.nn.Conv2d(256, 512, 3, stride1, padding1, biasFalse)self.norm7 torch.nn.BatchNorm2d(1024)self.conv7 torch.nn.Conv2d(512, 1024, 3, stride1, padding1, biasFalse)self.norm8 torch.nn.BatchNorm2d(1024)self.conv8 torch.nn.Conv2d(1024, 1024, 3, stride1, padding1, biasFalse)# 最后一个卷积层self.conv9 torch.nn.Conv2d(1024, len(anchors)*(5self.num_classes), 1, stride1, padding0)
关于上面代码的几点解释 批归一化层BatchNorm2d是作用于卷积输出的在每个通道上独立进行归一化它的参数是通道数量。最后一个池化层使用特殊的数据补齐ReflectionPad2d在行列方向的单侧各补齐一行或者一列数据。
定义模型的前向计算过程
定义模型的前向计算过程将各个模块连接在一起。 def forward(self, x, yoloTrue):# 将各个模块组织 为 神经网络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(dtypex.dtype, devicex.device)anchor_width, anchor_height anchors[:, 0], anchors[:, 1]# 然后是网格的偏移量grid_y, grid_x torch.meshgrid(torch.arange(height, dtypex.dtype, devicex.device),torch.arange(width, dtypex.dtype, devicex.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 : offsetn]).view_as(target.data)return offsetnimport numpy# 从文件加载训练好的网络权值
def load_weights(network, filenameG:\\BrowserDownload\\2024-03\\yolov2-tiny-voc.weights):with open(filename, rb) as file:# 读取版本号 和 训练状态记录header numpy.fromfile(file, count4, dtypenumpy.int32)# 其余所有值都是网络权值weights numpy.fromfile(file, dtypenumpy.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 的权重或偏置它是要更新的目标参数。
函数执行以下步骤
使用 target.numel() 获取目标参数中的元素数量 n。使用 torch.from_numpy 将权重数组的一部分转换为 PyTorch 张量并使用 view_as 方法调整张量的形状以匹配目标参数的形状。将转换后的张量赋值给目标参数的数据。返回更新后的偏移量 offsetn用于后续加载其他层的权重。
load_weights 函数
这个函数用于遍历 PyTorch 模型的所有层并加载预训练的权重。它接受两个参数
network要加载权重的 PyTorch 网络模型。filename包含预训练权重的文件路径。
函数执行以下步骤
打开权重文件进行二进制读取。读取文件头部信息通常包括版本号和训练状态记录但在此代码中未使用。读取剩余的文件内容作为权重值。遍历网络的每一层对于每种类型的层卷积层和批归一化层使用 load_weight_to 函数加载权重和偏置。对于批归一化层除了权重和偏置外还需要加载 running_mean 和 running_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(paddingpadding),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, 5len(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],linewidth1, edgecolorcolor, facecolornone)axis.add_patch(rect)label class_labels[class_id]label {0}{1:.2f}.format(label, confidence)plt.text(box[0], box[1], label, colorw, backgroundcolorcolor)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模型对真实图片进行物体检测的代码.