深度学习Faster-RCNN网络

news2024/11/20 10:27:29

目录

  • 1 网络工作流程
    • 1.1 数据加载
    • 1.2 模型加载
    • 1.3 模型预测过程
      • 1.3.1 RPN获取候选区域
      • 1.3.2 FastRCNN进行目标检测
  • 2 模型结构详解
    • 2.1 backbone
    • 2.2 RPN网络
      • 2.2.1 anchors
      • 2.2.2 RPN分类
      • 2.2.3 RPN回归
      • 2.2.4 Proposal层
    • 2.4 ROIPooling
    • 2.5 目标分类与回归
  • 3 FasterRCNN的训练
    • 3.1 RPN网络的训练
      • 3.1.1 正负样本标记
      • 3.1.2 RPN网络的损失函数
      • 3.1.3 训练过程
      • 3.1.4 实现
        • 3.1.4.1 正负样本设置
        • 3.1.4.2 损失函数
    • 3.2 FastRCNN网络的训练
      • 3.2.1 正负样本标记
      • 3.2.2 FastRCNN的损失函数
      • 3.2.3 训练过程
      • 3.2.4 实现
        • 3.2.4.1 正负样本设置
        • 3.2.4.2 损失函数
    • 3.3 共享卷积训练
  • 4 端到端训练
    • 4.1 数据加载
    • 4.2 模型实例化
    • 4.3 模型训练
  • 5 总结


1 网络工作流程

在R-CNN和Fast RCNN的基础上,在2016年提出了Faster RCNN网络模型,在结构上,Faster RCNN已经将候选区域的生成,特征提取,目标分类及目标框的回归都整合在了一个网络中,综合性能有较大提高,在检测速度方面尤为明显。接下来我们给大家详细介绍fasterRCNN网络模型。网络基本结构如下图所示:

在这里插入图片描述

Faster RCNN可以看成是区域生成网络(RPN)与Fast RCNN的组合,其中区域生成网络(RPN)替代选择性搜索来生成候选区域,Fast RCNN用来进行目标检测。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1UdoCSjs-1646211351944)(笔记图片/image-20200914143645050-16461195891481.png)]

FasterRCNN的工作流程是:

在这里插入图片描述

1、特征提取:将整个图像缩放至固定的大小输入到CNN网络中进行特征提取,得到特征图。

2、候选区域提取:输入特征图,使用区域生成网络RPN,产生一些列的候选区域

3、ROIPooling: 与Fast RCNN网络中一样,使用最大池化固定候选区域的尺寸,送入后续网络中进行处理

4、目标分类和回归:与Fast RCNN网络中一样,使用两个同级层:K+1个类别的SoftMax分类层和边框的回归层,来完成目标的分类和回归。

Faster R-CNN的流程与Fast R-CNN的区别不是很大,重要的改进是使用RPN网络来替代选择性搜索获取候选区域,所以我们可以将Faster R-CNN网络看做RPN和Fast R-CNN网络的结合。

接下来我们来看下该网络预训练模型的使用过程,模型源码位置:fasterRCNN中,如下图所示:

在这里插入图片描述

detection文件夹中是模型,数据的实现,weights中包含网络的预训练模型。接下来我们按照以下步骤进行目标检测:

1、获取数据和加载预训练网络

2、获取RPN网络生成的候选区域

3、获取网络的目标检测结果

首先导入相应的工具包:

# 获取VOC数据使用
from detection.datasets import pascal_voc
# 绘图
import matplotlib.pyplot as plt
import numpy as np
# 模型构建
from detection.models.detectors import faster_rcnn
import tensorflow as tf
# 图像展示
import visualize
12345678910

遇到的问题1:ModuleNotFoundError: No module named ‘cv2’

解决方法:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-python

遇到问题2:ModuleNotFoundError: No module named ‘skimage’

解决办法:pip install scikit-image

另外每次pip install 都会特别慢,我找到一些办法可以参考:

https://blog.csdn.net/yang5915/article/details/83175804

https://waple.blog.csdn.net/article/details/104574277

确实下载快了许多!!!

1.1 数据加载

加载voc数据集中的一张图片进行网络预测:

# 实例化voc数据集的类,获取送入网络中的一张图片
pascal = pascal_voc.pascal_voc("train")
# image:送入网络中的数据,imagemeta:图像的yuan'x
image,imagemeta,bbox,label = pascal[218]
1234

在将图像送入网络之前,我们对其进行了尺度的调整,标准化等处理,获取可展示的图像:

# 图像的均值和标准差
img_mean = (122.7717, 115.9465, 102.9801)
img_std = (1., 1., 1.)
# RGB图像
rgd_image= np.round(image+img_mean).astype(np.uint8)

获取原始图像,进行比较:

# 获取原始图像
from detection.datasets.utils import get_original_image
ori_img = get_original_image(image[0],imagemeta[0],img_mean)

将图像进行对比显示:

# 展示原图像和送入网络中图像
rgd_image= np.round(image+img_mean).astype(np.uint8)
fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
axes[0].imshow(ori_img.astype('uint8'))
axes[0].set_title("原图像")
axes[1].imshow(rgd_image[0])
axes[1].set_title("送入网络中的图像")
plt.show()

在这里插入图片描述

将原图像的长边缩放为1216,短边按相应比例进行调整后,并按照均值进行填充

# 原图像的大小
ori_img.shape
(375, 500, 3)
# 送入网络中图像的大小
image.shape
(1, 1216, 1216, 3)

imagemeta中的信息是:原图像大小,图像缩放后的大小,送入网络中图像的大小,图像缩放比例,图像是否翻转(未使用)。

# 原始图像和送入网络中图像的信息imagemeta
array([[ 375.   ,  500.   ,    3.   ,  912.   , 1216.   ,    3.   ,        1216.   , 1216.   ,    3.   ,    2.432,    0.   ]], dtype=float32)

1.2 模型加载

加载使用coco数据集预训练的模型,对图像进行预测。

# coco数据集的class,共80个类别:人,自行车,火车,。。。
classes = ['bg', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard',
           'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush']

实例化faster-RCNN模型:

# 实例化模型
model = faster_rcnn.FasterRCNN(num_classes=len(classes))

加载预训练模型,由于fasterRCNN不是按照model的子类构建,所以无法通过h5文件直接加载模型结构,我们将结构实例化后,在加载权重获取整个预训练模型。

model((image,imagemeta,bbox,label),training=True)
# 加载训练好的weights
model.load_weights("weights/faster_rcnn.h5")

通过model.summary()查看网络架构,如下:

在这里插入图片描述

1.3 模型预测过程

模型的预测分为两部分:RPN生成候选区域和Fast RCNN进行目标的分类与回归

1.3.1 RPN获取候选区域

# RPN获取候选区域:输入图像和对应的元信息,输出是候选的位置信息proposals = model.simple_test_rpn(image[0],imagemeta[0])

候选区域的结果如下所示:对于上述图像共产生1533个候选区域,每个候选区域使用相对于输入网络中图像归一化后的左上角坐标和右下角坐标。

<tf.Tensor: shape=(1533, 4), dtype=float32, numpy=array([[0.20729761, 0.00852748, 0.748096  , 0.46975034],       [0.42213044, 0.5887971 , 0.7810232 , 0.9806169 ],       [0.40125194, 0.4384725 , 0.48458642, 0.47913405],       ...,       [0.25977597, 0.435113  , 0.27290097, 0.4483906 ],       [0.38884488, 0.41798416, 0.41393432, 0.4339822 ],       [0.5885266 , 0.65331775, 0.62330776, 0.6913476 ]], dtype=float32)>

我们将这些候选区域绘制在图像上,需要获取绝对位置:

# 绘制在图像上(将proposal绘制在图像上)visualize.draw_boxes(rgd_image[0],boxes=proposals[:,:4]*1216)plt.show()

如下图所示:

在这里插入图片描述

1.3.2 FastRCNN进行目标检测

我们将获取的候选区域送入到Fast RCNN网络中进行检测:

# rcnn进行预测,得到的是原图像的检测结果:
# 输入:要检测的送入网络中的图像,图像的元信息,RPN产生的候选区域
# 输出:目标检测结果:检测框(相对于原图像),类别,置信度
res = model.simple_test_bboxes(image[0],imagemeta[0],proposals)

res是一个字典,其结果如下所示:rois是目标框,class_ids是所属的类别,scores是置信度。

{'rois': array([[ 95.65208   ,   8.963474  , 370.8639    , 224.11072   ],
        [ 57.620296  , 226.60101   , 159.39307   , 310.5221    ],
        [ 86.15405   , 323.98065   , 369.12762   , 497.70337   ],
        [ 83.67178   , 170.96815   , 135.69716   , 221.05861   ],
        [ 74.37474   , 327.855     , 210.86298   , 422.48798   ],
        [ 73.24604   ,   0.97371644, 206.86272   ,  47.992523  ],
        [ 63.968616  , 256.5716    , 192.52466   , 365.3871    ],
        [ 67.055145  ,  88.534515  , 137.3221    , 130.74608   ],
        [227.8164    , 291.93015   , 370.9528    , 434.4086    ],
        [147.73048   , 218.15501   , 177.35306   , 260.56738   ],
        [ 82.44483   ,  40.140255  , 133.15623   , 107.72627   ],
        [122.62652   , 239.552     , 141.19394   , 272.06354   ],
        [154.03288   , 115.91441   , 372.5167    , 426.57187   ],
        [218.90562   , 364.88345   , 247.20554   , 419.03842   ],
        [248.15126   , 407.61325   , 373.63068   , 479.57568   ],
        [139.69551   , 248.66753   , 154.51906   , 264.16055   ],
        [212.88734   , 195.23204   , 238.25243   , 209.22202   ]],
       dtype=float32),
 'class_ids': array([ 1,  1,  1,  1,  1,  1,  1,  1,  1, 46,  1, 46, 61, 46, 57, 45, 40],
       dtype=int32),
 'scores': array([0.99917287, 0.992269  , 0.99193186, 0.98929125, 0.986894  ,
        0.98671734, 0.98594207, 0.97716457, 0.97271395, 0.97136974,
        0.9637522 , 0.9585419 , 0.9218482 , 0.8920589 , 0.85597926,
        0.81343234, 0.78660023], dtype=float32)}

将检测结果展示在图像上:

# 将检测结果绘制在图像上visualize.display_instances(ori_img,res['rois'],res['class_ids'],classes,res['scores'])plt.show()

在这里插入图片描述

上述我们介绍了Faster RCNN的工作流程并且给大家展示了网络的检测结果。那接下来我们解决以下几个问题:

1、网络中的每一部分是怎么构建,怎么完成相应的功能的?

2、怎么训练fastrcnn网络去完成我们自己的任务?

那接下来我们就解决上述问题。

2 模型结构详解

Faster RCNN的网络结构如下图所示:

在这里插入图片描述

我们依然将网络分为四部分:

  • Backbone:Backbone由CNN卷积神经网络构成,常用的是VGG和resnet, 用来提取图像中的特征,获取图像的特征图。该特征图被共享用于后续RPN层生成候选区域和ROIPooling层中。
  • RPN网络:RPN网络用于生成候选区域,用于后续的目标检测。
  • Roi Pooling: 该部分收集图像的特征图和RPN网络提取的候选区域位置,综合信息后获取固定尺寸的特征,送入后续全连接层判定目标类别和确定目标位置。
  • 目标分类与回归: 该部分利用ROIpooling输出特征向量计算候选区域的类别,并通过回归获得检测框最终的精确位置。

接下来我们就从这四个方面来详细分析fasterRCNN网络的构成,并结合源码理解每一部分实现的功能。

2.1 backbone

backbone一般为VGG,ResNet等网络构成,主要进行特征提取,将最后的全连接层舍弃,得到特征图送入后续网络中进行处理。

在这里插入图片描述

在源码中使用ResNet + FPN 结构来提取特征。与普通的 FasterRCNN 只需要将一个特征图输入到后续网络中不同,由于加入 FPN结构,需要将多个特征图逐个送入到后续网络中,如下图所示:

在这里插入图片描述

Resnet进行特征提取,FPN结构作用是当前层的特征图会融合未来层的特征进行上采样,并加以利用。因为有了这样一个结构,当前的特征图就可以获取未来层的信息,也就将低阶特征与高阶特征就有机融合起来了,提升检测精度。如下图所示:

在这里插入图片描述

在这里ResNet和FPN的完整结构如下图所示:Resnet进行特征提取,FPN网络进行特征融合获取多个特征图后,输入到RPN网络中的特征图是[p2,p3,p4,p5,p6] ,而作为后续目标检测网络FastRCNN的输入则是 [p2,p3,p4,p5] 。

在这里插入图片描述

我们看下源码实现的内容:

1、resnet特征提取的结果

# 使用backbone获取特征图C2,C3,C4,C5 = model.backbone(image,training=False)

C2,C3,C4,C5是resnet进行特征提取的结果,送入网络中图像大小为(1216,1216,3),经过特征提取后特征图的大小为:

# C2.shape:1216/4 TensorShape([1, 304, 304, 256])# C3.shape:1216/8TensorShape([1, 152, 152, 512])# C4.shape:1216/16TensorShape([1, 76, 76, 1024])# C5.shape:1216/32TensorShape([1, 38, 38, 2048])
1

2、FPN特征融合的结果

# FPN网络融合:C2,C3,C4,C5是resnet提取的特征结果P2,P3,P4,P5,P6 = model.neck([C2,C3,C4,C5],training=False)

P2,P3,P4,P5,P6是特征融合之后的结果,送入后续网络中,其特征图的大小:

# P2.shape:1216/4 
TensorShape([1, 304, 304, 256])
# P3.shape:1216/8
TensorShape([1, 152, 152, 512])
# P4.shape:1216/16
TensorShape([1, 76, 76, 1024])
# P5.shape:1216/32
TensorShape([1, 38, 38, 2048])
# P6.shape:1216/64
TensorShape([1, 19, 19, 256])

那网络的整体架构表示成:

在这里插入图片描述

2.2 RPN网络

经典的检测方法生成检测框都非常耗时,如overfeat中使用滑动窗口生成检测框;或如R-CNN使用选择性搜索方法生成检测框。而Faster RCNN则抛弃了传统的滑动窗口和选择性搜索的方法,直接使用RPN生成候选区域,能极大提升检测速度。

在这里插入图片描述

RPN网络的主要流程是:

1、生成一系列的固定参考框anchors,覆盖图像的任意位置,然后送入后续网络中进行分类和回归

2、分类分支:通过softmax分类判断anchor中是否包含目标

3、回归分支:计算目标框对于anchors的偏移量,以获得精确的候选区域

4、最后的Proposal层则负责综合含有目标的anchors和对应bbox回归偏移量获取候选区域,同时剔除太小和超出边界的候选区域。

2.2.1 anchors

nchor在目标检测中表示 固定的参考框 ,首先预设一组不同尺度不同长宽比的固定参考框,覆盖几乎所有位置, 每个参考框负责检测与其交并比大于阈值 (训练预设值,常用0.5或0.7) 的目标 ,anchor技术将候选区域生成问题转换为 “这个固定参考框中有没有目标,目标框偏离参考框多远” ,不再需要多尺度遍历滑窗,真正实现了又好又快。

在FastRCNN中框出多尺度、多种长宽比的anchors,如下图所示:下图中分别是尺度为32,64,128,长宽比为1:1,1:2,2:1的一组anchors,我们利用这组anchor在特征图上进行滑动,并对应到原图上即可获取一系列的固定参考框。

在这里插入图片描述

由于有 FPN 网络,所以会在多个不同尺度特征图中生成anchor,假设某一个特征图大小为hxw,首先会计算这个特征相对于输入图像的下采样倍数 stride:

在这里插入图片描述

如下图所示:

在这里插入图片描述

每一个尺度特征图上生成不同比列的anchor:

在这里插入图片描述

得到一系列的anchors后就可送入后续网络中进行分类和回归。

在源码中我们可生成一幅图像对应的anchors:

# 产生anchor:输入图像元信息即可,输出anchor对应于原图的坐标值anchors,valid_flags = model.rpn_head.generator.generate_pyramid_anchors(imagemeta)

对于1216x1216的图像生成的anchor的数量为:

# anchors.shape:#304*304*3+152*152*3+76*76*3+38*38*3+19*19*3=369303TensorShape([369303, 4])

anchor的取值为:

<tf.Tensor: shape=(369303, 4), dtype=float32, numpy=array([[ -22.627417,  -11.313708,   22.627417,   11.313708],       [ -16.      ,  -16.      ,   16.      ,   16.      ],       [ -11.313708,  -22.627417,   11.313708,   22.627417],       ...,       [ 789.9613  ,  970.98065 , 1514.0387  , 1333.0193  ],       [ 896.      ,  896.      , 1408.      , 1408.      ],       [ 970.98065 ,  789.9613  , 1333.0193  , 1514.0387  ]],      dtype=float32)>

我们将前10000个anchor绘制在图像上:

# 绘制在图像上(将anchor绘制在图像上)visualize.draw_boxes(rgd_image[0],boxes=anchors[:10000,:4])plt.show()

在这里插入图片描述

2.2.2 RPN分类

一副MxN大小的矩阵送入Faster RCNN网络后,经过backbone特征提取到RPN网络变为HxW大小的特征图。如下图所示,是RPN进行分类的网络结构:(k=9)

在这里插入图片描述

先做一个1x1的卷积,得到[batchsize,H,W,18]的特征图,然后进行变形,将特征图转换为[batchsize,9xH,W,2]的特征图后,送入softmax中进行分类,得到分类结果后,再进行reshape最终得到[batchsize,H,W,18]大小的结果,18表示k=9个anchor是否包含目标的概率值。

在这里插入图片描述

2.2.3 RPN回归

RPN回归的结构如下图所示:(k=9)

在这里插入图片描述

经过该卷积输出特征图为为[1, H, W,4x9],这里相当于feature maps每个点都有9个anchors,每个anchors又都有4个用于回归的:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UMs4iNmm-1646211351955)(笔记图片/image-20200914173617857.png)]

变换量。

该变换量预测的是anchor与真实值之间的平移量和尺度因子:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IW1JXeCe-1646211351956)(笔记图片/image-20200914174033826.png)]

利用源码我们可以获得对anchors的分类和回归结果:

# RPN网络的输入:FPN网络获取的特征图rpn_feature_maps = [P2,P3,P4,P5,P6]# RPN网络预测,返回:logits送入softmax之前的分数,包含目标的概率,对框的修正结果rpn_class_logits,rpn_probs,rpn_deltas = model.rpn_head(rpn_feature_maps,training = False)

结果分析:

# rpn_class_logits.shape,每一个anchor都进行了分类分析TensorShape([1, 369303, 2])# rpn_probs.shape:softmax输出的概率值TensorShape([1, 369303, 2])# rpn_deltas.shape :回归结果TensorShape([1, 369303, 4])

其中 rpn_probs的取值为:

<tf.Tensor: shape=(1, 369303, 2), dtype=float32, numpy=array([[[9.94552910e-01, 5.44707105e-03],        [9.97310877e-01, 2.68914248e-03],        [9.95540321e-01, 4.45961533e-03],        ...,        [9.99888301e-01, 1.11637215e-04],        [9.99961257e-01, 3.87872169e-05],        [9.99820888e-01, 1.79159630e-04]]], dtype=float32)>

我们获取一些分类置信度较高的结果,将这些anchor绘制在图像上:

# 获取分类结果中包含目标的概率值rpn_probs_tmp = rpn_probs[0,:,1]# 获取前100个较高的anchorlimit = 100ix = tf.nn.top_k(rpn_probs_tmp,k=limit).indices[::-1]# 获取对应的anchor绘制图像上,那这些anchor就有很大概率生成候选区域visualize.draw_boxes(rgd_image[0],tf.gather(anchors,ix).numpy())

在这里插入图片描述

2.2.4 Proposal层

Proposal层负责综合RPN网络对anchors分类和回归的结果,利用回归的结果对包含目标的anchors进行修正,计算出候选区域,送入后续RoI Pooling层中。

Proposal层处理流程如下:

  1. 利用RPN网络回归的结果[ d x ( A ) , d y ( A ) , d w ( A ) , d h ( A ) ] \left[d_{x}(A), d_{y}(A), d_{w}(A), d_{h}(A)\right][dx(A),dy(A),dw(A),dh(A)]对所有的anchors进行修正,得到修正后的检测框
  2. 根据RPN网络分类的softmax输出的概率值由大到小对检测框进行排序,提取前6000个结果,即提取修正位置后的检测框
  3. 限定超出图像边界的检测框为图像边界,防止后续roi pooling时候选区域超出图像边界。

在这里插入图片描述

  1. 对剩余的检测框进行非极大值抑制NMS
  2. Proposal层的输出是对应输入网络图像尺度的归一化后的坐标值[x1, y1, x2, y2]。

到此RPN网络的工作就结束了。

Proposal层有3个输入:RPN分类和回归结果,以及图像的元信息。

# 获取候选区域
proposals_list = model.rpn_head.get_proposals(rpn_probs,rpn_deltas,imagemeta)

结果为:

[<tf.Tensor: shape=(1533, 4), dtype=float32, numpy=
 array([[0.20729761, 0.00852748, 0.748096  , 0.46975034],
        [0.42213044, 0.5887971 , 0.7810232 , 0.9806169 ],
        [0.40125194, 0.4384725 , 0.48458642, 0.47913405],
        ...,
        [0.25977597, 0.435113  , 0.27290097, 0.4483906 ],
        [0.38884488, 0.41798416, 0.41393432, 0.4339822 ],
        [0.5885266 , 0.65331775, 0.62330776, 0.6913476 ]], dtype=float32)>]

将其绘制在图像上

# 绘制在图像上(将proposal绘制在图像上)
visualize.draw_boxes(rgd_image[0],boxes=proposals_list[0].numpy()[:,:4]*1216)
plt.show()

在这里插入图片描述

2.4 ROIPooling

RoI Pooling层则负责收集RPN网络生成的候选区域,并将其映射到特征图中并固定维度,送入后续网络中进行分类和回归。

在这里插入图片描述

RoI Pooling 的作用过程,如下图所示:

在这里插入图片描述

RoIpooling使用最大池化将任何有效的RoI区域内的特征转换成具有pool_H×pool_W的固定空间范围的小的特征图,其中pool_H和pool_W是超参数,比如设置为7x7, 它们独立于任何特定的RoI,如下图所示:

在这里插入图片描述

在实现过程中,FPN网络产生了多个尺度特征图,那候选区域要映射到哪个特征图中呢?

在这里插入图片描述

在这里,不同尺度的ROI使用不同特征层作为ROI pooling层的输入,大尺度ROI就用后面一些的金字塔层,比如P5;小尺度ROI就用前面一点的特征层,比如P3,我们使用下面的公式确定ROI所在的特征层:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K3rGLrTT-1646211351959)(笔记图片/image-20201021102107570.png)]

其中,224是ImageNet的标准输入,k0是基准值,设置为4,w和h是ROI区域的长和宽,假设ROI是112x112的大小,那么k = k0-1 = 4-1 = 3,意味着该ROI应该使用P3的特征层。k值会做取整处理,防止结果不是整数,而且为了保证k值在2-5之间,还会做截断处理。

# ROI Pooling层实现:输入是候选区域,特征图,图像的元信息
pool_region_list = model.roi_align((proposals_list,rcnn_feature_maps,imagemeta),training = False)

输出结果为:每一个候选区域都被固定为7x7大小

[<tf.Tensor: shape=(1533, 7, 7, 256), dtype=float32, numpy=
 array([[[[-6.26428795e+00,  3.55317879e+00,  3.37260556e+00, ...,
            6.22574663e+00,  3.75851846e+00, -2.49103808e+00],
          [-9.01443863e+00,  7.67611027e-01,  7.18744850e+00, ...,
            6.20492172e+00,  4.09835625e+00,  6.05924249e-01],
          [-7.43907213e+00, -3.76329374e+00,  5.01457691e+00, ...,
            6.22656918e+00,  1.19414163e+00,  3.06410480e+00],
          ...,
          [ 1.39127302e+00, -1.71078873e+00,  4.01916075e+00, ...,
            5.94641972e+00,  3.63194764e-01,  2.91014194e+00],
          [-5.21681070e+00,  2.39917469e+00,  2.49682212e+00, ...,
            5.92232943e+00,  3.01222801e+00,  1.63518691e+00],
          [-1.26697767e+00, -6.90211892e-01,  4.50919747e-01, ...,
            1.97156405e+00, -1.07467103e+00,  4.54943466e+00]]

2.5 目标分类与回归

该部分利用获得的候选区域的特征图,通过全连接层与softmax计算每个候选区域具体属于的类别(如人,车,电视等),输出概率值;同时再次利用回归方法获得每个候选区域的位置偏移量,用于回归更加精确的目标检测框。该部分网络结构如下所示:

在这里插入图片描述

从RoI Pooling层获取到固定大小的特征图后,送入后续网络,可以看到做了如下2件事:

  1. 通过全连接和softmax对候选区域进行分类
  2. 再次对候选区域进行回归修正,获取更高精度的检测框

实现流程如下:

首先获取网络分类和回归的结果:

# RCNN网络的预测:输入是ROIPooling层的特征,输出:类别的score,类别的概率值,回归结果rcnn_class_logits,rcnn_class_probs,rcnn_deltas_list = model.bbox_head(pool_region_list,training=False)

利用结果对候选区域进行修正:

# 获取预测结果:输入:rcnn返回的分类和回归结果,候选区域,图像元信息,输出:目标检测结果detection_list = model.bbox_head.get_bboxes(rcnn_class_probs,rcnn_deltas_list,proposals_list,imagemeta)

结果为:一共检测出17个目标,每个目标右目标位置,目标类别id,目标类别置信度6个值构成。

[<tf.Tensor: shape=(17, 6), dtype=float32, numpy=
 array([[2.3262584e+02, 2.1799168e+01, 9.0194098e+02, 5.4503723e+02,
         1.0000000e+00, 9.9917287e-01],
        [1.4013255e+02, 5.5109363e+02, 3.8764392e+02, 7.5518970e+02,
         1.0000000e+00, 9.9226898e-01],
        [2.0952664e+02, 7.8792090e+02, 8.9771838e+02, 1.2104146e+03,
         1.0000000e+00, 9.9193186e-01],
        [2.0348978e+02, 4.1579453e+02, 3.3001547e+02, 5.3761450e+02,
         1.0000000e+00, 9.8929125e-01],
        [1.8087936e+02, 7.9734338e+02, 5.1281873e+02, 1.0274907e+03,
         1.0000000e+00, 9.8689401e-01],
        [1.7813437e+02, 2.3680782e+00, 5.0309012e+02, 1.1671781e+02,
         1.0000000e+00, 9.8671734e-01],
        [1.5557167e+02, 6.2398212e+02, 4.6821997e+02, 8.8862134e+02,
         1.0000000e+00, 9.8594207e-01],
        [1.6307811e+02, 2.1531593e+02, 3.3396735e+02, 3.1797446e+02,
         1.0000000e+00, 9.7716457e-01],
        [5.5404950e+02, 7.0997412e+02, 9.0215717e+02, 1.0564817e+03,
         1.0000000e+00, 9.7271395e-01],
        [3.5928052e+02, 5.3055298e+02, 4.3132263e+02, 6.3369983e+02,
         4.6000000e+01, 9.7136974e-01],
        [2.0050583e+02, 9.7621101e+01, 3.2383597e+02, 2.6199030e+02,
         1.0000000e+00, 9.6375221e-01],
        [2.9822769e+02, 5.8259045e+02, 3.4338364e+02, 6.6165851e+02,
         4.6000000e+01, 9.5854193e-01],
        [3.7460797e+02, 2.8190384e+02, 9.0596057e+02, 1.0374227e+03,
         6.1000000e+01, 9.2184818e-01],
        [5.3237848e+02, 8.8739655e+02, 6.0120386e+02, 1.0191014e+03,
         4.6000000e+01, 8.9205891e-01],
        [6.0350385e+02, 9.9131537e+02, 9.0866974e+02, 1.1663280e+03,
         5.7000000e+01, 8.5597926e-01],
        [3.3973947e+02, 6.0475940e+02, 3.7579034e+02, 6.4243842e+02,
         4.5000000e+01, 8.1343234e-01],
        [5.1774200e+02, 4.7480432e+02, 5.7942987e+02, 5.0882794e+02,
         4.0000000e+01, 7.8660023e-01]], dtype=float32)>]

可以将其绘制在图像上:

# 绘制在图像上
visualize.draw_boxes(rgd_image[0],boxes=detection_list[0][:,:4])
plt.show(

在这里插入图片描述

到这我们就完成了整个网络的介绍。

3 FasterRCNN的训练

Faster R-CNN的训练分为两部分,即RPN网络和检测网络fastRCNN的训练:

在这里插入图片描述

整个训练过程分为四步:

  • 第一步:RPN网络的训练,使用ImageNet预训练的模型初始化,并端到端微调用于区域建议任务。
  • 第二步:利用第一步的RPN生成的建议框,由Fast R-CNN训练一个单独的检测网络,这个检测网络同样是由ImageNet预训练的模型初始化的,这时候两个网络还没有共享卷积层。
  • 第三步:用检测网络初始化RPN训练,但是固定共享的卷积层,并且只微调RPN独有的层,现在两个网络共享卷积层了。
  • 第四步:保持共享的卷积层固定,微调Fast R-CNN的fc层。这样,两个网络共享相同的卷积层,构成一个统一的网络。

在这里插入图片描述

接下来我们分别介绍各个训练步骤。

3.1 RPN网络的训练

RPN网络的作用从众多的anchors中提取包含目标的,并且经过回归调整的候选区域。为了训练RPN,给每个anchor分配是否包含目标的标签,也就是正负样本的标记,然后进行训练。

3.1.1 正负样本标记

  • 与真实框ground truth(GT)交并比IOU大于0.7的anchor是正样本,即anchor中包含目标,目标值设为1
  • 与真实框ground truth(GT)交并比IOU小于0.3的anchor是负样本,即anchor中不包含目标,目标值设为-1
  • 其他的anchor舍弃,不参与网络的训练,目标值设为0

3.1.2 RPN网络的损失函数

RPN网络的损失函数是:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gKMensMt-1646211351962)(笔记图片/image-20200915095837059.png)]

其中

  • i表示anchor的索引
  • pi是第i个anchor 预测为目标的可能性,pi*为ground-truth标签。如果这个anchor是positive的,则ground-truth标签为1,否则为0。(即当第i个anchor与GT间IoU>0.7,认为是该anchor是positive,标签为1;反之IoU<0.3时,认为是该anchor是negative,标签为0)
  • ti表示表示正样本anchor到预测区域bounding box的4个参数化预测结果,ti*是这个positive anchor对应的ground-truth box的偏移,如下所示:

预测值:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N9PKkRRI-1646211351962)(笔记图片/image-20200915101257709.png)]

真实值:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vWMEZ1Gm-1646211351963)(笔记图片/image-20200915101314285.png)]

其中,x,y,w,h表示窗口中心坐标和窗口的宽度和高度,变量x, xa和x* 分别表示预测窗口、anchor窗口和Ground Truth的坐标(y,w,h同理)

整个Loss分为两部分:分类和回归的损失

  • Lcls分类的损失(classification loss),是一个二分类器的softmax loss。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jTFvo6m7-1646211351964)(笔记图片/image-20200915100858122.png)]

  • Lreg是回归损失,为smooth(x)损失,并且只有正样本才参与回归损失计算

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TlHJQK6y-1646211351964)(笔记图片/image-20200915100839788.png)]

  • Ncls和Nreg分别用来标准化分类损失项Lcls和回归损失项Lreg,默认用batch size设置Ncls,用anchor位置数目~2000初始化Nreg
  • Ncls和Nreg相差过大,用参数λ来平衡两者,一般取值为Nreg和Ncls的比值10即可。

3.1.3 训练过程

在训练时每次迭代的正负样本是由一幅图像的正负样本组成的:

  • 随机采样256个anchor,计算损失函数,其中采样的正负anchor的比例是1:1。
  • 通过从零均值标准差为0.01的高斯分布中获取的权重来随机初始化所有新层(最后一个卷积层其后的层),所有其他层(即共享的卷积层)是通过对ImageNet分类预训练的模型来初始化的
  • 采用带动量的随机梯度下降算法对网络进行训练

3.1.4 实现

3.1.4.1 正负样本设置

将产生的369303个anchor与目标真实值的计算交并比设置正负样本:

# 获取对应的目标值:输入:要设置正负样本的anchors,anchor在有效区域的标识,样本标记的bbox及类别label;输出:rpn的分类目标值,RPN的回归目标值
rpn_target_matchs,rpn_target_deltas = model.rpn_head.anchor_target.build_targets(anchors,valid_flags,bbox,label)

所有的anchor都设置了分类的目标值,回归的目标值只有正负样本设置了目标值,一共有369303个Anchor,参与训练的有256个anchor。

# rpn_target_matchs.shape
TensorShape([1, 369303])
# rpn_target_deltas.shape
TensorShape([1, 256, 4])

获取正样本:正样本是包含目标的anchor,其目标值设为1,正样本的个数是29个

# 属于正样本的anchors,与GT交并比较大的anchor,目标值设为1
positive_anchors = tf.gather(anchors,tf.where(tf.equal(rpn_target_matchs,1))[:,1])
# 正样本的个数:一共使用29个属于正样本的anchor
TensorShape([29, 4])

我们将这些正样本绘制在图像上:可以看出这些anchor与目标还是非常接近的

在这里插入图片描述

接下来,我们看下负样本的结果,负样本的目标值是-1,负样本的个数是227,与29个正样本一共是256个anchor参与网络训练,其余的不参与网络训练。

# 负样本
negtivate_anchors = tf.gather(anchors,tf.where(tf.equal(rpn_target_matchs,-1))[:,1])
# negtivate_anchors.shape
TensorShape([227, 4])

同样我们也将负样本展示在图像上,从图像可以看出这些负样本的anchor与目标差距还是很大的。

在这里插入图片描述

3.1.4.2 损失函数

损失函数计算是将网络预测结果和真实值进行比较,获取两者之间的差别。损失函数由两部分组成:分类和回归

# RPN网络的损失函数# 输入:rpn的分类结果rpn_class_logits,rpn的回归结果,bbox标注框,label是目标累呗,imagemera图像元信息# 输出:分类损失和回归损失rpn_class_loss, rpn_bbox_loss = model.rpn_head.loss(                rpn_class_logits, rpn_deltas, bbox, label, imagemeta)# 分类损失:rpn_bbox_loss<tf.Tensor: shape=(), dtype=float32, numpy=0.20614956># 回归损失:rpn_class_loss<tf.Tensor: shape=(), dtype=float32, numpy=0.034301624>

接下来我们使用梯度下降算法对网络进行训练就可以了

3.2 FastRCNN网络的训练

使用RPN网络收集到的候选区域和imageNet预训练的卷积网络提取的特征对检测的FastRCNN网络进行训练。

3.2.1 正负样本标记

在FastRCNN网络训练时:

  • 首先将与真实框ground truth(GT)交并比IOU大于0.5的候选区域设为正样本,类别的目标值是GT的类别
  • 将与真实框ground truth(GT)交并比IOU小于0.5的候选区域设为负样本,类别的目标值是0

3.2.2 FastRCNN的损失函数

FastRCNN的输出由两部分组成:一部分是softmax层进行分类,输出类别有K个类别加上”背景”类,另一部分是回归bounding box regressor。也就是:

  • 一部分输出在K+1个类别上的离散概率分布(每个候选区域),p=(p0,p1,…,pk)。通常,通过全连接层的K+1个输出上的Softmax来计算p。
  • 另一部分输出对于由K个类别中的每一个检测框回归偏移,t k = ( t x k , t y k , t w k , t h k ) t{k}=\left(t_{x}{k}, t_{y}^{k}, t_{w}^{k}, t_{h}^{k}\right)tk=(txk,tyk,twk,thk)。其中t k t^{k}tk指定相对于候选框的尺度不变转换和对数空间高度/宽度移位,与在RPN网络中是一样的。

每个训练的候选区域用 分类目标值u和检测框回归目标值v标记 。背景样本用u=0来表示,对每个标记的候选区域使用多任务损失L以联合训练分类和检测框回归:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QZe8AlLU-1646211351965)(笔记图片/image-20200915111032596.png)]

其中Lcls(p,u)=−logpu,表示交叉熵损失,第二个损失Lloc,是定义目标值和预测检测框的四元组之间的损失使用smoothL1损失计算,同样是只有正样本(非背景)的候选区域才计算回归损失,参数λ设为1。

3.2.3 训练过程

FastRCNN的训练获取每张图片中的正负样本:

  • 对所有正样本根据IOU值进行排序,每张图片取前256个区域,将这些区域的坐标保存下来,作为该图片的训练样本
  • 用于Softmax分类和检测框回归的全连接层的权重分别使用具有方差0.01和0.001的零均值高斯分布初始化,偏置初始化为0,特征提取网络使用ImageNet的预训练网络
  • 使用梯度下降算法进行优化

3.2.4 实现

3.2.4.1 正负样本设置

将proposal层产生的候选区域与目标真实值的计算交并比设置正负样本:

# fastRCNN的正负样本设置# 输入:RPN网络生成的候选区域,bbox是标记框,label是目标类别# 输出:参与训练的候选区域rois_list,候选区域分类的目标值rcnn_target_matchs_list,回归的目标值rcnn_target_deltas_listrois_list, rcnn_target_matchs_list, rcnn_target_deltas_list = \                model.bbox_target.build_targets(                    proposals_list,bbox, label, imagemeta)

获取正样本:正样本是负责目标检测的候选区域,其目标值不是0,正样本的个数是64个

# 获取正样本:positive_proposal = tf.gather(rois_list[0], tf.where(    tf.not_equal(rcnn_target_matchs_list, 0))[:, 1])# positive_proposal.shapeTensorShape([64, 4])

将其展示在图像上:可以这些框跟真实值是非常接近的

# 显示visualize.draw_boxes(rgd_image[0],positive_proposal.numpy()*1216)plt.show()

在这里插入图片描述

同样我们也可以获取负样本(背景),并绘制在图像上:

# 负样本
negtivate_proposal = tf.gather(rois_list[0], tf.where(
    tf.equal(rcnn_target_matchs_list, 0))[:, 1])
# negtivate_proposal.shape
TensorShape([192, 4])
# 显示
visualize.draw_boxes(rgd_image[0],negtivate_proposal.numpy()*1216)
plt.show()

在这里插入图片描述

3.2.4.2 损失函数

损失函数计算是将网络预测结果和真实值进行比较,获取两者之间的差别。在这里我们需要将参与网络训练的候选区域进行ROIPooling后送入网络中训练。损失函数由两部分组成:分类和回归:

# 将参与网络训练的候选区域rois_list送入到ROIpooling层中进行维度固定pooled_regions_list = model.roi_align(            (rois_list, rcnn_feature_maps, imagemeta), training=True)# 送入网络中进行预测,得到预测结果rcnn_class_logits_list, rcnn_probs_list, rcnn_deltas_list = \            model.bbox_head(pooled_regions_list, training=True)# 计算损失函数:分类和回归# 输入:网络的预测结果和目标值rcnn_class_loss, rcnn_bbox_loss = model.bbox_head.loss(                rcnn_class_logits_list, rcnn_deltas_list,                 rcnn_target_matchs_list, rcnn_target_deltas_list)  # 分类损失rcnn_class_loss<tf.Tensor: shape=(), dtype=float32, numpy=0.56958425># 回归损失rcnn_bbox_loss<tf.Tensor: shape=(), dtype=float32, numpy=0.28708345>

接下来使用梯度下降算法进行预测即可

3.3 共享卷积训练

用fastRCNN检测网络初始化RPN训练,但是固定共享的卷积层,并且只微调RPN独有的层,现在两个网络共享卷积层了,接下来保持共享的卷积层固定,微调Fast R-CNN的fc层。这样,RPN网络和Fast R-CNN网络共享相同的卷积层,构成一个统一的网络。

Faster R-CNN还有一种端到端的训练方式,可以一次完成训练,将RPN loss与Fast RCNN loss相加,然后进行梯度下降优化,更新参数。

4 端到端训练

前面我们已经介绍了网络模型架构和预测结果,在网络预测前我们需要对网络进行训练,接下来使用端到端的方式进行模型训练,基本步骤是:

1、加载数据集:我们在这里使用VOC数据集,所以需要加载VOC数据集

2、模型实例化:加载faster RCNN模型

3、模型训练:计算损失函数,使用反向传播算法对模型进行训练

完成网络的训练。首先导入相关的工具包:

# 数据集加载
from detection.datasets import pascal_voc
# 深度学习框架
import tensorflow as tf
import numpy as np
# 绘图
from matplotlib import pyplot as plt
# 要训练的模型
from detection.models.detectors import faster_rcnn

4.1 数据加载

# 加载数据集
train_dataset = pascal_voc.pascal_voc('train')
# 数据的类别: train_dataset.classes
['background',
 'person',
 'aeroplane',
 'bicycle',
 'bird',
 'boat',
 'bottle',
 'bus',
 'car',
 'cat',
 'chair',
 'cow',
 'diningtable',
 'dog',
 'horse',
 'motorbike',
 'pottedplant',
 'sheep',
 'sofa',
 'train',
 'tvmonitor']
# 数据类别数量:21
num_classes = len(train_dataset.classes)

4.2 模型实例化

# 指定数据集中类别个数
model = faster_rcnn.FasterRCNN(num_classes=num_classes)

4.3 模型训练

模型训练也就是要使用损失函数,进行反向传播,利用优化器进行参数更新,训练的流程是:

1、指定优化器:在这里我们使用加动量的SGD方法

2、设置epoch,进行遍历获取batch数据送入网络中进行预测

3、计算损失函数,使用反向传播更新参数,我们使用tf.GradientTape实现:

  • 定义上下文环境:tf.GradientTape
  • 计算损失函数loss
  • 使用 tape.gradient(loss,model.trainable_variables) 自动计算梯度,loss是损失结果,trainable_variables为所有需要训练的变量。
  • 使用 optimizer.apply_gradients(zip(grads,model.trainable_variables)) 自动更新模型参数,zip(grads, trainable_variables)将梯度和参数关联起来,然后apply_gradients会自动的利用梯度对参数进行更新。

接下来我们按照这个流程完成模型训练。

# 1.定义优化器
optimizer = tf.keras.optimizers.SGD(1e-3, momentum=0.9, nesterov=True)
# 模型优化
loss_his = []
# 2.设置epoch,进行遍历获取batch数据送入网络中进行预测
for epoch in range(7):
    # 获取索引
    indices = np.arange(train_dataset.num_gtlabels)
    # 打乱
    np.random.shuffle(indices)
    # 迭代次数
    iter = np.round(train_dataset.num_gtlabels/train_dataset.batch_size).astype(np.uint8)
    for idx in range(iter):
        # 获取batch数据索引
        idx = indices[idx]
        # 获取batch_size
        batch_image,batch_metas,batch_bboxes,batch_label = train_dataset[idx]
        # 3.模型训练,计算损失函数,使用反向传播更新参数
        # 3.1 定义作用域
        with tf.GradientTape() as tape:
            # 3.2 计算损失函数
            rpn_class_loss,rpn_bbox_loss,rcnn_class_loss,rcnn_bbox_loss = model((batch_image,batch_metas,batch_bboxes,batch_label),training=True)
            # 总损失
            loss = rpn_class_loss+rpn_bbox_loss+rcnn_class_loss+rcnn_bbox_loss
            # 3.3 计算梯度
            grads = tape.gradient(loss,model.trainable_variables)
            # 3.4 更新参数值
            optimizer.apply_gradients(zip(grads,model.trainable_variables))
            print("epoch:%d,batch:%d,loss:%f"%(epoch+1,idx,loss))
            loss_his.append(loss)

结果为:

epoch:1, loss:147.117371
epoch:2, loss:72.580498
epoch:3, loss:79.347351
epoch:4, loss:41.220577
epoch:5, loss:5.238140
epoch:6, loss:2.924250
epoch:7, loss:5.287500

损失函数的变换如下图所示:

# 绘制损失函数变化的曲线
plt.plot(range(len(loss_his)),[loss.numpy() for loss in loss_his])
plt.grid()

在这里插入图片描述

当我们训练好模型后,就可以使用训练好的模型进行预测了。

5 总结

  • 熟悉FasterRCNN目标检测的思想

利用CNN网络进行特征提取,利用RPN生成候选区域,最后进行分类和回归

  • 知道anchor的思想

anchor技术将检测问题转换为**“这个固定参考框中有没有目标,目标框偏离参考框多远”**,不再需要多尺度遍历滑窗

  • 掌握RPN网络是如何进行候选区域的生成的

通过softmax判断anchors属于positive或者negative,再利用bounding box regression修正anchors获得精确的proposals

  • 掌握ROIPooling的使用方法

RoIpooling使用最大池化将任何有效的RoI区域内的特征转换成具有H×W的固定空间范围的小feature map

  • 知道fasterRCNN的训练方法

分步训练:RPN网络,fastrcnn训练,共享网络训练,端到端的网络训练

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

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

相关文章

Yolov5移植树莓派实现目标检测

Hallo&#xff0c;大家好啊&#xff01;之前写了几篇Yolov5相关项目的博客&#xff0c;然后学习了树莓派之后&#xff0c;更新了几篇树莓派的博客&#xff0c;我的最终目的是将Yolov5移植到树莓派&#xff0c;通过树莓派上面的摄像头实现目标检测。你想啊&#xff0c;在工厂里面…

【word文档使用方法记录】论文格式修改

word文档使用方法记录**去掉文档都以也封面的页眉&#xff1a;****WPS公式居中&#xff0c;公式号右对齐**为论文每个章节设置不同的页眉去掉文档都以也封面的页眉&#xff1a; 将光标定位在第一页页尾&#xff0c;进入“页面布局”选项卡&#xff0c;点击“分隔符”&#xff…

【MySQL数据库入门】:数据库基础和基本操作

文章目录1. 数据库基础1.1 什么是数据库1.2 主流数据库1.3 如何配置环境1.4 链接数据库1.5服务器&#xff0c;数据库&#xff0c;表关系2.基本的数据库操作3.MySQL架构4.SQL分类5.存储引擎5.1查看存储引擎5.2 存储引擎对比6.总结1. 数据库基础 1.1 什么是数据库 存储数据用文…

安装包UI美化之路-在线安装包

在一些安装场景中&#xff0c;由于完整的安装包很大&#xff0c;下载时间长&#xff0c;且下载后需要人工干预来进行安装&#xff0c;这样会一定程度的降低用户使用体验&#xff1b;nsNiuniuSkin安装包制作解决方案提供了一种在线版本的安装包&#xff0c;支持将实际要安装的内…

C++中delete 和 delete []的真正区别

1.我们通常从教科书上看到这样的说明&#xff1a; delete 释放new分配的单个对象指针指向的内存 delete[] 释放new分配的对象数组指针指向的内存 那么&#xff0c;按照教科书的理解&#xff0c;我们看下下面的代码&#xff1a; int *a new int[10]; delete a; //方…

Java | 详解 Java连接MySQL、编写JdbcUtils工具类、使用数据库连接池、使用JavaTemplate

一、连接mysql数据库 步骤&#xff1a; 1、启动 MySQL &#xff1a;以管理员身份打开 cmd 命令行&#xff0c;输入 net start mysql 2、在 MySQL 创建一张表&#xff0c;用于后面的操作。我这里创建了一张 user 表&#xff0c;有id、name、password三个字段。 3、导入jar包 (1)…

【总结】最系统化的CV内容,有三AI所有免费与付费的计算机视觉课程汇总(2022年12月)...

欢迎大家关注有三AI的计算机视觉课程系列&#xff0c;我们的课程系列共分为5层境界&#xff0c;内容和学习路线图如下&#xff1a;第1层&#xff1a;掌握学习算法必要的预备知识&#xff0c;包括Python编程&#xff0c;深度学习基础&#xff0c;数据使用&#xff0c;框架使用等…

基于xml的自动装配之byType

基于xml的自动装配之byType 使用bean标签的autowire属性设置自动装配效果 自动装配方式&#xff1a;byType byType&#xff1a;根据要赋值的属性的类型&#xff0c;在IOC容器中匹配某个兼容类型的bean&#xff0c;为属性自动赋值 若在IOC中&#xff0c;没有任何一个兼容类型的b…

net/http 库的客户端实现(下)

前言 上一篇文章我们讲了 net/http 库客户端 request 的构建&#xff0c;接下来继续讲构建HTTP请求之后的处理操作 net/http 库的客户端实现(上) 启动事务 构建 HTTP 请求后&#xff0c;接着需要开启HTTP事务进行请求并且等待远程响应&#xff0c;以net/http.Client.Do()方法…

stm32f407VET6 系统学习 day01 GPIO 配置

1. GPIO 的5个配置 GPIO,即通用I/O(输入/输出)端口&#xff0c;是STM32可控制的引脚。STM32芯片的GPIO引脚与外部设备连接起来&#xff0c;可实现与外部通讯、控制外部硬件或者采集外部硬件数据的功能。 STM32F407有7组IO。分别为GPIOA~GPIOG&#xff0c;每组IO有16个IO口&…

首创证券上交所上市:募资19亿市值306亿 上半年净利降14%

雷递网 雷建平 12月23日首创证券股份有限公司&#xff08;简称&#xff1a;“首创证券”&#xff0c;股票代码为&#xff1a;“601136”&#xff09;昨日在上交所上市。首创证券此次发行27,333.38万股&#xff0c;发行价为7.07元&#xff0c;募资总额为19亿元。首创证券首日收盘…

小学生C++编程基础 课程10

938.最小公倍数的简单方法 &#xff08;课程A&#xff09; 难度&#xff1a;1 登录 939.最大公约数的简单方法 ( 课程A&#xff09; 难度&#xff1a;1 登录 940.韩信点兵 &#xff08;课程A&#xff09; 难度&#xff1a;1 登录 941.求123…N的和 &#xff08;课程A&#x…

Bloom filter-based AQM 和 BBR 公平性

设 B 为 Delivery rate&#xff0c;D 为 Delay&#xff0c;将 E B/D 作为衡量效能&#xff0c;所有流量的收敛状态是一个 Nash 均衡&#xff0c;没有任何流量有动机增加或者减少 inflight。参见&#xff1a;更合理的 BBR。 并不是都知道这道理&#xff0c;增加 inflight 能挤…

Java环境配置——Linux 安装JDK

注意这是用普通用户登录后&#xff0c;单独设置用户的java环境变量&#xff0c;非root用户 root用户的编辑命令是 vi /etc/profile 下载安装包 创建java目录 mkdir java 进入目录 cd java 上传安装包 将jdk-8u161-linux-x64.tar.gz上传到java目录 配置环境变量 解压安…

并查集详解

1.并查集原理 某公司今年校招全国总共招生10人&#xff0c;西安招4人&#xff0c;成都招3人&#xff0c;武汉招3人&#xff0c;10个人来自不同的学校&#xff0c;起先互不相识&#xff0c;每个学生都是一个独立的小团体&#xff0c;现给这些学生进行编号&#xff1a;{0, 1, 2,…

156. 如何在 SAP UI5 应用里显示 PDF 文件

SAP 不少标准应用都可以在业务流程进行到某个阶段,根据系统里的业务数据和 SAP 事先开发好的表单模板,生成最终的 PDF 文件并显示在应用页面上。 本文介绍一种在 SAP UI5 页面里嵌入显示 PDF 文件内容的方式,效果如下。 点击屏幕右上角的下载图标,可以将这个显示的 PDF 下…

ASP.NET 企业人力资源管理系统源码 HR源码 前端bootstrap框架开发

中小型企业HR人力资源管理系统源码带使用手册和操作说明 了解更多&#xff0c;可私信我&#xff01; 【程序语言】&#xff1a;.NET 【数据库】&#xff1a;SQL SERVER 2008 【运行环境】&#xff1a;WINDOWSIIS 【其他】&#xff1a;前端bootstrap框架 运行环境&#xff1…

学习记录-mybatis+vue+elementUi实现分页查询(前端部分)

前端这一块最方便的莫过于是element已经提供好了 接口&#xff0c;三个最关键的接口这里首先解决第一个&#xff0c;总数。 //总记录数totalCount:100,我直接在data中将其先初始化为100&#xff0c;之后直接在响应中设置&#xff0c;这是从后端查询到的值&#xff0c;不需要任何…

华为被迫开源,从认知到落地SpringBoot企业级实战手册(完整版)

前言 本手册重在引导读者进入真实的项目开发体验&#xff0c;围绕Spring Boot技术栈全面展开&#xff0c;兼顾相关技术的知识拓展&#xff0c;由浅入深&#xff0c;步步为营&#xff0c;使读者既能学习基础知识&#xff0c;又能掌握. 一定的开发技巧。本书的目标是让读者拥有一…

图文详解 (Kubernetes)K8S 和 容器中的退出状态码含义和原因及解决方法

图文详解 (Kubernetes)K8S 和 容器中的退出状态码含义和原因及解决方法。 什么是容器退出码 当容器终止时,容器引擎使用退出码来报告容器终止的原因。如果您是 Kubernetes 用户,容器故障是 pod 异常最常见的原因之一,了解容器退出码可以帮助您在排查时找到 pod 故障的根本…