一、什么是负样本
负样本是指不包含任务所要识别的目标的图像,也叫负图像(Negtive Image)。
二、为什么要训练负样本
训练负样本的目的是为了降低误检测率、误识别率,提高网络模型的泛化能力。通俗地讲就是告诉检测器,这些“不是你要检测的目标”。
三、Faster R-CNN、SSD、YOLO等神经网络模型中的负样本
例如在Faster R-CNN中,在RPN阶段,会根据backbone生成的特征图上的每一点,按照不同尺寸、不同长宽比构建很多的候选锚框。这些锚框按照与Ground Truth box的交并比,选择特定阈值进行分类
比如IOU>0.7的锚框,被视为正锚框,也就是正样本,IOU<0.3的被视为负锚框,也就是负样本。
通常负样本的数量会远远超过正样本,因此为了平衡类别比例,提高模型精度,常通过Focal Loss方法或hard example mining算法来恰当地利用正、负样本计算Clasification Loss与regresion Loss,并进行反向传播。因此网络模型中的负样本与负图像是不一样的,不能完全同等看待。
四、如何收集负样本
可以通过下面两种方式收集负样本:
1.采用本任务场景的不包含目标物体的背景图像,作者的目标是识别空间场景下的蚊子,那么所有场景内不包含蚊子的图片都视作负样本。不要使用不属于本任务场景的负图像,因为其对检测器的性能影响不大。
2.测试图像中被识别错误的目标所在区域。(通常对原图像进行裁剪,使得裁剪下来的图像只包含误识别的物体,而不包含目标)
五、负样本的标签文件
用于目标检测任务的标签通常是xml文件,在xml文件中没有目标位置相关的节点,只有文件名,文件路径、图片宽度、高度、通道数等信息,这就是负样本的标签文件。关于负样本的标签文件的生成方式,可以参考博客:
https://blog.csdn.net/weixin_44836143/article/details/105952819
深度学习中经常需要训练无标记的负样本,用于降低目标检测中的误检率、误识别率。生成负样本xml文件的一个方法是用labelImg在图像上随便画一个框,生成xml文件,然后手动去除相关的object节点,但是负样本太多的话,这种方法太耗时间,所以我写了一个脚本可以批量生成空的xml文件。
import os
import xml.dom.minidom
img_path = '/home/dulingwen/Pictures/img/'
xml_path = '/home/dulingwen/Pictures/xml/'
for img_file in os.listdir(img_path):
img_name = os.path.splitext(img_file)[0]
#create an empty dom document object
doc = xml.dom.minidom.Document()
#creat a root node which name is annotation
annotation = doc.createElement('annotation')
#add the root node to the dom document object
doc.appendChild(annotation)
#add the folder subnode
folder = doc.createElement('folder')
folder_text = doc.createTextNode('VOC2012')
folder.appendChild(folder_text)
annotation.appendChild(folder)
#add the filename subnode
filename = doc.createElement('filename')
filename_text = doc.createTextNode(img_file)
filename.appendChild(filename_text)
annotation.appendChild(filename)
# add the path subnode
path = doc.createElement('path')
path_text = doc.createTextNode(img_path + img_file)
path.appendChild(path_text)
annotation.appendChild(path)
#add the source subnode
source = doc.createElement('source')
database = doc.createElement('database')
database_text = doc.createTextNode('Unknown')
source.appendChild(database)
database.appendChild(database_text)
annotation.appendChild(source)
#add the size subnode
size = doc.createElement('size')
width = doc.createElement('width')
width_text = doc.createTextNode('1920')
height = doc.createElement('height')
height_text = doc.createTextNode('1080')
depth = doc.createElement('depth')
depth_text = doc.createTextNode('3')
size.appendChild(width)
width.appendChild(width_text)
size.appendChild(height)
height.appendChild(height_text)
size.appendChild(depth)
depth.appendChild(depth_text)
annotation.appendChild(size)
#add the segmented subnode
segmented = doc.createElement('segmented')
segmented_text = doc.createTextNode('0')
segmented.appendChild(segmented_text)
annotation.appendChild(segmented)
#write into the xml text file
os.mknod(xml_path+'%s.xml'%img_name)#Linux下的操作
fp = open(xml_path+'%s.xml'%img_name, 'w+')#windows下的操作
doc.writexml(fp, indent='\t', addindent='\t', newl='\n', encoding='utf-8')
fp.close()
在重命名jpg和xml文件后,利用voc_label.py
脚本生成正、负样本的txt文件,删除负样本的空txt文件,然后再生成归一化后的“class, x, y, w, h”参数,均为0
(注意小数点的位数与正样本中一致)
生成负样本的txt文件的代码如下:
import os
import shutil
save_path = 'C:\\Users\\luchenxun\\Desktop\\新建文件夹\\'
if not os.path.exists(save_path):
os.mkdir(save_path)
else:
shutil.rmtree(save_path)
os.mkdir(save_path)
global IDctrl #用于批量生成txt
IDctrl = 3298 #txt文件起始名称即:3298.txt
for i in range(4903): #输出的最后一个txt文件的名称为8200.txt
filename = 'C:\\Users\\luchenxun\\Desktop\\新建文件夹\\' + str(IDctrl) + '.txt' #批量生成的文件名
#for j in range(3):
b = '0 0.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000'
f = open(filename,'a')
f.write(str(b))
f.close()
IDctrl = IDctrl +1
六、如何训练负样本
将正负样本集及其标签作为训练集送入模型训练即可。
注意:
1|正负样本必须放在一起训练,不能单独训练负样本,否则经过训练,网络会把所有的图像都识别为背景。
2|正负样本的比例最好为1:1到1:2左右,数量差距不能太悬殊,特别是正样本数量本来就不太多的情况下。
尝试方法一:
直接对误检的图像生成一个空的xml文件。(文件中没有任何对象)
训练结果:由于xml文件中没有任何正样本对象,所以网络无法学习到背景信息,使用训练后的模型测试误检的图像,依然会产生误检。(这里网上有人说即使没有正样本,SSD网络在训练时也会产生负样本,本人测试,当整幅图像没有任何正样本时,网络学习不到任何信息,训练时loss=0)
尝试方法二:
误检的图像中含有正样本对象,但不是误检的类别。(例如:该图像中人物类别出现误检,但对该图像进行训练时只标注了一个汽车类别,而没有增加人物的正样本类别)
训练结果:这种情况下,对网络进行训练时,会产生loss,并且训练后的模型,不会再把误检图像中的背景误检为人物。
七、怎样消除误检——加强检测器
1|把使用正样本训练好的模型拿来进行测试,此时会得到一些被错误识别的图片。
2|把这些图片收集起来作为负样本加入到正样本集(如果图片中同时包含误识别物体和目标,可以将图像裁剪,裁剪后的图像包含误识别物体而不包含目标并尽量覆盖原图大部分区域,然后再将其分辨率resize回原图大小)
3|组成新的训练集,送入模型进行训练。
如果负样本的来源只有误识别的图片,那么由于误识别的图片往往占少数,可以利用图像增强(如高斯滤波、对比度增强、锐化、平滑等)的方法扩充负图像数量至和正样本数量相同,并组合在一起。将这样得到的训练集送入模型进行训练,经过若干个epoch,当Loss收敛到稳定值时,再次测试原来的出现误识别的图像你会发现误识别现象基本消失了,并且类似原来误识别的场景将会被正确识别。
参考博客:
https://blog.csdn.net/weixin_44836143/article/details/105952819