[Intensive Reading]目标检测(object detection)系列(四) Faster R-CNN:有RPN的Fast R-CNN

本文深入解析Faster R-CNN的目标检测原理及实现过程,包括RPN网络结构、损失函数、训练步骤等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

目标检测系列:
目标检测(object detection)系列(一) R-CNN:CNN目标检测的开山之作
目标检测(object detection)系列(二) SPP-Net:让卷积计算可以共享
目标检测(object detection)系列(三) Fast R-CNN:end-to-end的愉快训练
目标检测(object detection)系列(四) Faster R-CNN:有RPN的Fast R-CNN
目标检测(object detection)系列(五) YOLO:目标检测的另一种打开方式
目标检测(object detection)系列(六) SSD:兼顾效率和准确性
目标检测(object detection)系列(七) R-FCN:位置敏感的Faster R-CNN
目标检测(object detection)系列(八) YOLOv2:更好,更快,更强
目标检测(object detection)系列(九) YOLOv3:取百家所长成一家之言
目标检测(object detection)系列(十) FPN:用特征金字塔引入多尺度
目标检测(object detection)系列(十一) RetinaNet:one-stage检测器巅峰之作
目标检测(object detection)系列(十二) CornerNet:anchor free的开端
目标检测(object detection)系列(十三) CenterNet:no Anchor,no NMS
目标检测(object detection)系列(十四)FCOS:用图像分割处理目标检测

目标检测扩展系列:
目标检测(object detection)扩展系列(一) Selective Search:选择性搜索算法
目标检测(object detection)扩展系列(二) OHEM:在线难例挖掘
目标检测(object detection)扩展系列(三) Faster R-CNN,YOLO,SSD,YOLOv2,YOLOv3在损失函数上的区别

简介:有RPN的Fast R-CNN

RBG团队在2015年,与Fast R-CNN同年推出了Faster R-CNN,我们先从头回顾下Object Detection任务中各个网络的发展,首先R-CNN用分类+bounding box解决了目标检测问题,SPP-Net解决了卷积共享计算问题,Fast R-CNN解决了end-to-end训练的问题,那么最后还能下一个ss算法,依旧独立于网络,是一个单独的部分,然而这个算法需要大概2秒的时间,这个点是R-CNN系列的性能瓶颈,所有Fast R-CNN是没有什么实时性的。那么Faster R-CNN的出现就是为了解决这个瓶颈问题。
在Faster R-CNN中提出了RPN网络,Region Proposal Network(区域建议网络)以代替原来的ss算法,可以简单的理解为:

Faster R-CNN =Fast R-CNN+RPN-ss算法

所以,可以说除了RPN,Faster R-CNN剩下的地方与Fast R-CNN是一样的, 那么理解Faster R-CNN的关键其实理解RPN。Faster R-CNN的论文是《Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks》

Faster R-CNN实现

RPN网络结构

这里写图片描述
首先,上面这张图说明了RPN在Faster R-CNN中的位置,它在CNN卷积后的特征图上做区域建议(大约300个),并根据RPN生成的区域建议对feature maps做提取,并对提取后的特征做RoI pooling。在RoI pooling之后的东西就和Fast R-CNN一样了。

所以RPN的输入是卷积后的特征图,输出是多个打过分的建议框,所谓打分是对框中是否是物体打分,建议框是四个值(x,y,w,h)。

RPN是一种全卷积网络,它的前几层卷积层和Faster R-CNN的前五层是一样的,所以RPN是在进一步的共享卷积层的计算,以降低区域建议的时间消耗。
也是因为共享卷积的原因,所以我们一般认为RPN只有两层。而RPN前面到底有几层,决定于Faster R-CNN选择哪种初始模型,如果是AlexNet的话,那就是5层,如果是ZFNet的话,也是5层,如果是VGG16的话,就是13层,等等。
那么我们还是用AlexNet举例好了,此时的conv5特征图的尺寸为 13 × 13 × 256 13\times13\times256 13×13×256,也就是这一层的特征别送入到RPN中,RPN在这个特征图上用 3 × 3 × 256 3\times3\times256 3×3×256的卷积核,一共用了256个。那么卷积核每滑动一次输出的特征就是 1 × 1 × 256 1\times1\times256 1×1×256,也就是下图中的256-d,之后该特征出两个分支:
第一个分支(reg layer)用4k个 1 × 1 × 256 1\times1\times256 1×1×256的卷积核对256-d进行卷积,最后输出4k个数,这里的4是一个建议框的参数,即(x,y,w,h);
第二个分支(cls layer)用2k个 1 × 1 × 256 1\times1\times256 1×1×256的卷积核卷积,最后输出2k个数,这里的2是该区域到底有没有物体,即(object,non-object)的二分类问题。

这里写图片描述
那么,k是什么呢?
k是Anchor box(参考框)的类型数,在Faster R-CNN中k=9,分别是3个尺度scale和3个比例ratio,其中:
scale为(128,256,512)
ratio为 1:1,1:2,2:1
这里写图片描述 这里写图片描述
参考框的中心就是卷积核的中心。
所以,在conv5层上,用 3 × 3 3\times3 3×3卷积核每卷积一次,都会生成k个参考框,那么参考框的总数就应该是 W × H × K W\times H\times K W×H×K,如上所说,conv5的尺寸为 13 × 13 13\times13 13×13的话,那么生成的Anchor box的总数就是1521个。

然后我们就会发现通过上面的解释,RPN有一些地方是说不通的,下面我们一一解释下这些坑:
1.上面提到Anchor box的总数是1521个,那为什么说RPN生成300个左右的区域建议呢?
每一个参考框都会有一个是不是物体的打分,在检测过程中RPN计算所有的参考框后会选择其中300个得分最高的区域,也就是二分类confidence更高的。

2.参考框中的尺寸为(128,256,512),但是conv5的尺寸只有 13 × 13 13\times13 13×13,在哪里生成这些参考框呢?
这些参考框不是在特征图上生成,而是在原图上,所以anchor size的选择一定是要考虑缩放前的原图的尺寸,因为最后anchor超过的图像大小,并没有意义。所以RPN在做的是将每个点产生的9个参考框来映射原始图像,也就是通过4k个位置偏移输出和k个参考框,得到参考框在原始图像中的位置。就像Fast R-CNN中ss算法,其实也是在原图上生成的,最后只是经过了坐标变化才能在conv5上提取。

3.在卷积核卷积到一个点的时候,输出了9个参考框,但是这9个建议框的特征是相同的,都是256个 3 × 3 × 256 3\times3\times256 3×3×256卷积核,卷积得到的 1 × 1 × 256 1\times1\times256 1×1×256的特征,那么这9个参考框在哪里引导的RPN关注这些区域呢?
特征确实是相同的,但是得到的特征最终是要向原图做映射的,以得到最终的区域建议,而相同的特征对应了9种不同的参考映射方式,于是相同的特征,映射给不同的参考框时,loss是不同的。那么哪种方式是做好的呢,当然是loss最小的那个。所以不同的9个参考框,它们的区别并不体现在特征上,而是在loss上,我们下面就看下RPN的损失函数。

RPN损失函数

首先给出函数的公式:
这里写图片描述
这个公式和Fast R-CNN的多任务损失其实很像,同样是一个在做分类,一个在做回归,然后把两个函数加在一起。i是一个batch中anchor box的索引。

分类损失:

这里写图片描述

这依然是一个负的log值, P i Pi Pi为第i个参考框是物体的预测概率, P i ∗ Pi^* Pi是一个指示函数,如果anchor是物体的话, P i ∗ Pi^* Pi 就是1;如果anchor是背景,那么 P i ∗ Pi^* Pi 就是0。
那么如果某一个区域是物体的话,如果 P i = 1 Pi=1 Pi=1 P i ∗ = 1 Pi^*=1 Pi=1,此时的损失函数为0;同理 P i = 0 Pi=0 Pi=0的话,损失函数为正无穷。

回归损失:
这里写图片描述
其中R还是smooth L1平滑方程:

这里写图片描述

同样的背景没有边界框,所以需要Pi* Lreg。

t i t_{i} ti t i ∗ t_{i}^* ti分布对应四个值,分别是 x , y , w , h x,y,w,h xywh的坐标偏差,其中:
x , y , w , h x,y,w,h xywh是预测框(就是reg layer的输出);
x a , y a , w a , h a x_{a},y_{a},w_{a},h_{a} xayawaha是anchor参考框;
x ∗ , y ∗ , w ∗ , h ∗ x^{*},y^{*},w^*,h^* xywh是ground truth框;
这里写图片描述
t i t_{i} ti是预测框与anchor之间的偏差, t i ∗ t_{i}^* ti是ground truth与anchor之间的偏差,那么我们考虑一种情况,那就是 t i t_{i} ti t i ∗ t_{i}^* ti与相同了,此时损失函数就是0,那么这意味着:
预测值与anchor之间的偏差 = ground truth与anchor之间的偏差
也就是说预测值完全等于ground truth。这就是上面提到的注意机制引导RPN关注anchor的过程,当anchor不同的时候,loss函数是不同的。所以这是一个反向的过程,我们选择出来了某一个点上 3 × 3 3\times3 3×3范围内的特征,那么这个特征是物体还是背景呢,还有就是它对应原图中哪个区域的时候,效果是最好的呢?这就是RPN要解决的问题。

在这里顺便说一下个人的一个想法,会更方便理解,RPN在conv5上用 3 × 3 3\times3 3×3的卷积核卷积,那么如果原图上某一个区域在conv5上的大小恰好就是 3 × 3 3\times3 3×3呢?那么这个卷积就相当于一个全尺寸卷积了,显然它是可以学习到这个区域内的所有特征的,然后我们再看下这些尺寸,这方便我们理解为什么RPN选择了 3 × 3 3\times3 3×3卷积。
conv5特征图输出的尺寸为 13 × 13 13\times13 13×13
卷积核尺寸为 3 × 3 3\times3 3×3
原图大小如果是1024;
那么anchor选择为256的时候,它们的比例非常接近:
13 3 ≈ 1024 256 \frac{13}{3}\approx \frac{1024}{256} 3132561024
但是原图的尺寸不一定都是 1024 × 1024 1024\times1024 1024×1024,所以为了考虑形变与缩放,anchor有9个选择。

Faster R-CNN训练

Faster R-CNN的训练时分步的,但是不是分阶段的,因为end-to-end的问题在fast R-CNN就已经解决了。前面说了Faster R-CNN =Fast R-CNN +RPN,所以训练的过程需要分步来完成,但是每一步都是end-to-end。

Step 1:训练RPN网络;用的是ImageNet上的初始模型,因为RPN是由自己的损失函数的,所以在这里可以先把RPN训练起来,但是在组合mini-batch做梯度回传的时候为了避免负样本(背景)偏多的情况,会人为的我们随机地在一个图像中选择256个anchor,其中采样的正负anchor的比例是1:1。如果一个图像中的正样本数小于128,我们就用负样本填补这个mini-batch。

Step 2:训练Fast R-CNN;训练好RPN之后,单独训练Fast R-CNN,此时Fast R-CNN是不与RPN共享卷积层的,也就是初始模型还是ImageNet上得到的,用的区域建议是RPN生成的,训练的过程在之前的文章就就介绍了。

Step 3:调优RPN,在这一步中将再次训练RPN,这不过这次的前五层卷积核与Fast R-CNN共享,用Step2中的结果初始化RPN,并固定卷积层,finetune剩下的层。

Step 4:调优Fast R-CNN,此时用的区域建议是Step3中调优后的RPN生成的,同样是固定了卷积层,finetune剩下的层。

Faster R-CNN性能评价

这里写图片描述
上面这张图说明了Faster R-CNN的单图测试时间与mAP,可以看到,Fast R-CNN与R-CNN的时间与Object Detection系列(三) Fast R-CNN的说法不一样了,这是因为后者加上了ss算法的时间,大概2s左右的样子。
单图测试时间的大幅缩减,让Fast R-CNN能够真正意义上实现实时检测任务。但是吧,Faster R-CNN的性能评价是在K40 GPU上做出来的。
多么痛的领悟~~

<think>我们面对的问题是:Ubuntu系统中因内存不足导致Python进程被终止,日志显示"Out of memory: Killed process 1413 (python) total-vm:15738976kB"。我们需要理解并解决这个问题。 ### 问题分析 从引用[1][2][3]中,我们可以了解到: - 当系统内存不足(尤其是low memory耗尽)时,内核的OOM killer会杀死进程以保持系统运行。 - 即使有剩余内存,如果low memory不足,也会触发OOM killer。 - 日志中会记录被杀死进程的详细信息,如:`Out of memory: Killed process 1413 (python) total-vm:15738976kB`。 ### 解决步骤 #### 1. **确认内存使用情况** 使用命令查看内存和交换空间使用情况: ```bash free -h ``` 重点关注`available`内存和`swap`使用情况。如果可用内存很少且交换空间已满,则可能触发OOM killer。 #### 2. **检查系统日志** 查看详细日志,确认OOM killer的活动: ```bash grep -i 'out of memory' /var/log/syslog # 或 grep -i 'killed process' /var/log/syslog ``` #### 3. **分析进程内存使用** 使用`top`或`htop`命令查看哪些进程占用了大量内存: ```bash top ``` 按内存排序(在top中按`Shift+M`)。 #### 4. **临时解决方案:增加交换空间(Swap)** 交换空间可以作为内存不足时的缓冲。增加交换空间: ```bash # 创建交换文件(例如4GB) sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile # 永久生效:将以下行加入/etc/fstab echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab ``` #### 5. **调整OOM Killer行为(谨慎使用)** 可以通过调整进程的`oom_score_adj`来影响OOM killer的选择(值越低越不容易被杀死)[^5]: ```bash # 例如,保护重要的python进程(将PID替换为实际进程ID) echo -1000 | sudo tee /proc/PID/oom_score_adj ``` **注意**:不推荐全局调整,因为可能导致系统不稳定。 #### 6. **优化应用程序(Python进程)** - **检查内存泄漏**:使用内存分析工具(如`valgrind`、`tracemalloc`)分析Python程序。 - **减少内存使用**:优化代码,避免加载大量数据到内存,使用流式处理或分块处理。 - **使用更高效的数据结构**:例如使用生成器(generators)代替列表(lists)。 #### 7. **系统级优化** - **调整内存分配策略**:修改`/proc/sys/vm/overcommit_memory`(可选值:0,1,2)[^2]: ```bash # 允许内存超量分配(可能风险) echo 1 | sudo tee /proc/sys/vm/overcommit_memory ``` 或者设置为2,并指定超量分配比例(`/proc/sys/vm/overcommit_ratio`)。 - **清理缓存**:内核会自动管理缓存,但可以手动释放(仅临时): ```bash echo 3 | sudo tee /proc/sys/vm/drop_caches ``` #### 8. **升级硬件或调整系统配置** - 增加物理内存(RAM)。 - 如果运行在虚拟机中,调整虚拟机内存分配。 ### 预防措施 - **监控**:使用`cron`定期检查内存使用,或使用监控工具(如`monit`、`prometheus`)。 - **限制内存**:使用`cgroups`或`systemd`限制进程组的内存使用[^4]: ```bash # 例如,使用systemd服务限制内存 [Service] MemoryLimit=2G ``` - **代码优化**:在编写Python程序时,注意内存管理,及时释放不再使用的对象。 ### 总结 解决OOM问题需要综合应用优化: 1. 增加交换空间(临时缓解) 2. 优化应用程序(根本解决) 3. 调整系统参数(谨慎使用) 4. 监控和预警 > 💡 **提示**:优先优化应用程序,因为增加交换空间只能缓解问题,而优化代码才能从根本上减少内存占用。 --- ### 相关问题 1. 如何监控Linux系统的内存使用情况? 2. 如何在Python程序中检测和避免内存泄漏? 3. 如何配置cgroups来限制进程组的内存使用? 4. 调整`overcommit_memory`参数有哪些风险和好处?</think>### 解决 Ubuntu 中 Python 进程因内存不足被终止的问题 当系统日志出现 `Out of memory: Killed process 1413 (python) total-vm:15738976kB` 时,表明内核的 OOM killer 已终止你的 Python 进程以保护系统稳定性。以下是系统性的解决方案: --- ### 一、诊断内存问题根源 1. **查看完整内存状态**: ```bash free -h && grep -i oom /var/log/syslog ``` - 检查 `available` 列(真实可用内存) - 确认交换空间(Swap)是否耗尽 2. **分析 Python 进程内存**: ```bash ps aux | grep python | awk '{print $2,$4,$11}' | sort -k2rn | head ``` 输出示例: ``` 1413 25.9 /usr/bin/python3 ``` 显示 PID 1413 的 Python 进程占用了 25.9% 内存 3. **检查内核 low memory 状态**: ```bash grep -E 'LowTotal|LowFree' /proc/meminfo ``` - 当 `LowFree` 接近 0 时,即使总内存充足也会触发 OOM[^1] --- ### 二、立即缓解措施 #### 1. 增加交换空间(推荐) ```bash # 创建 4GB 交换文件 sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile # 永久生效 echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab ``` #### 2. 调整 OOM killer 优先级 ```bash # 降低 Python 进程被选中的概率 (-1000 到 1000) echo -15 | sudo tee /proc/1413/oom_score_adj ``` #### 3. 手动释放缓存 ```bash echo 3 | sudo tee /proc/sys/vm/drop_caches # 释放页缓存/目录项/inode ``` --- ### 三、长期解决方案 #### 1. 优化 Python 程序内存 - **使用内存分析工具**: ```bash pip install memory_profiler ``` 在代码中添加: ```python @profile def memory_intensive_function(): # 你的代码 ``` - **关键优化点**: - 用生成器替代列表 `(x for x in range)` - 使用 `numpy.array` 替代原生列表 - 及时释放大对象:`del big_object; gc.collect()` - 使用分块处理大数据集 #### 2. 系统级配置 ```bash # 增加 overcommit 比例 (默认50%) echo 80 | sudo tee /proc/sys/vm/overcommit_ratio # 允许内存超量分配 echo 1 | sudo tee /proc/sys/vm/overcommit_memory ``` #### 3. 使用 cgroups 限制内存(推荐) ```bash # 创建内存控制组 sudo cgcreate -g memory:/python_group sudo cgset -r memory.max=2G /python_group sudo cgset -r memory.swap.max=4G /python_group # 运行 Python 程序 cgexec -g memory:python_group python your_script.py ``` --- ### 、预防监控方案 1. **设置内存警戒脚本** (`/usr/local/bin/memalert.sh`): ```bash #!/bin/bash threshold=90 current=$(free | awk '/Mem/{printf("%.0f"), $3/$2*100}') [ $current -ge $threshold ] && \ echo "警报: 内存使用 ${current}%" | mail -s "内存告警" admin@example.com ``` 添加到 cron: ```bash * * * * * /usr/local/bin/memalert.sh ``` 2. **使用专业监控工具**: ```bash sudo apt install prometheus-node-exporter # 实时内存监控 ``` --- ### 关键原理总结 | 组件 | 作用 | 调优建议 | |------|------|----------| | **Low Memory** | 内核数据结构存储 | 监控 `/proc/meminfo` 的 LowFree[^1] | | **OOM Killer** | 进程终止机制 | 调整 `oom_score_adj`[^5] | | **Overcommit** | 内存分配策略 | `vm.overcommit_memory=1`[^2] | | **cgroups** | 资源隔离 | 限制进程组内存[^4] | > 💡 **最佳实践**:对于长期运行的 Python 服务,建议结合 cgroups 内存限制 + swap 扩展 + 代码分块处理,可降低 90% OOM 概率[^3]。 --- ### 相关问题 1. 如何诊断 Python 程序中的内存泄漏? 2. 在 Docker 容器中如何避免 OOM Killer 终止进程? 3. 除了 OOM Killer,还有哪些方法可以预防系统内存耗尽? 4. 如何配置 Linux 内核参数以优化大内存应用?
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值