字幕组双语原文:如何使用RetinaNet模型构建面罩检测器
英语原文:How to build a Face Mask Detector using RetinaNet Model!
翻译:雷锋字幕组(李珺毅)
目标检测是计算机视觉中一个非常重要的领域,对于自动驾驶、视频监控、医疗应用和许多其他领域都是十分必要的。
我们正在努力应对一场规模空前的大流行。全球各地的研究人员都在疯狂地尝试开发COVID-19的疫苗或治疗方法,而医生们正努力防止这一流行病席卷整个世界。另一方面,许多国家发现了社交距离,戴口罩和手套是稍微抑制这种情况的一种方法。
最近我有了一个想法,用我的深度学习知识来帮助一下现在的情况。在本文中,我将向您介绍RetinaNet的实现,而不需要什么背景知识和相关工作。
最重要的是什么?我们将使用RetinaNet建立一个口罩探测器来帮助我们应对这一持续的流行病。您可以推断相同的想法,为你的智能家居构建一个启用ai的解决方案。这个人工智能解决方案将只对戴着口罩和手套的开启大楼的大门。
随着无人机的成本随着时间的推移而降低,我们看到了航拍数据的激增。因此,您可以使用这个RetinaNet模型在航空图像甚至卫星图像中检测不同的对象,如汽车(自行车、汽车等)或行人,以解决不同的业务问题。
所以,你可以看到对象检测模型的应用是无穷无尽的。
1.RetinaNet是什么
2.RetinaNet的需求
3.RetinaNet网络结构
-1.主干网络
-2.目标分类自网络
-3.目标回归自网络
4.Focal loss 损失函数
5.使用RetianNet模型建立口罩检测器
-1.获取数据
-2.建立数据集
-3.模型训练
-4.模型测试
6.备注
RetinaNet是最好的单阶段目标检测模型之一,它已被证明可以很好地处理密集和小规模的物体。因此,它已成为我们在航空和卫星图像中使用的流行目标检测模型。
RetinaNet是由Facebook AI研究公司引入来解决密集检测问题的。在处理极端的前景-背景类时,需要用它来弥补YOLO和SSD等单镜头对象检测器的不平衡和不一致性。
从本质上讲,RetinaNet是由以下基本分组成的:
1.主干网络(自下而上的边路和具有侧连接的自上而下的路径)
2.目标分类子网络
3.目标回归子网络
为了更好地理解,让我们分别理解架构的每个组件
1.自底向上路径:自底向上路径(例如,ResNet)用于特征提取。因此,它计算不同比例的特征地图,而不考虑输入图像的大小。
2.具有横向连接的自顶向下路径:自顶向下路径从更高的金字塔层次上采 样空间上较粗糙的特征图,横向连接将具有相同空间大小的自顶向下和自底向上的层合并在一起。较高层次的特征图往往具有较小的分辨率,但语义上更强。因此,更适合于检测较大的物体; 相反,来自较低级特征图的网格单元具有高分辨率,因此更擅长检测较小的对象(参见图64)。 因此,通过自上而下的路径及其与路径的自下而上的横向连接的组合,这些操作不需要太多额外的计算,因此生成的特征图的每个级别在语义和空间上都可以很强。 因此,该体系结构是规模不变的,并且可以在速度和准确性方面提供更好的性能。
每个FPN层都附加一个全卷积网络(FCN)进行对象分类。如图所示,该子网包含3*3个卷积层,256个滤波器,然后是3*3个卷积层,K*A滤波器,因此输出的feature map大小为W*H*KA,其中W和H与输入特征图的宽度和高度成比例,K和A分别为对象类和锚盒的数量。
最后采用Sigmoid层(不是softmax层)进行对象分类。
而最后一个卷积层之所以有KA滤波器是因为,如果从最后一个卷积层得到的feature map中的每个位置都有很多锚盒候选区域,那么每个锚盒都有可能被分类为K个类。所以输出的特征图大小将是KA通道或过滤器。
回归子网络与分类子网络并行附着在FPN的每个特征图上。回归子网络的设计与分类子网络相同,只是最后一个卷积层大小为3*3,有4个filter,输出的feature map大小为W*H*4A。
最后一个卷积层有4个过滤器的原因是,为了定位类对象,回归子网络为每个锚定盒产生4个数字,这些数字预测锚定盒和真实框锚盒之间的相对偏移量(根据中心坐标、宽度和高度)。因此,回归子网的输出特征图具有4A滤波器或通道。
Focal Loss(FL)是Cross-Entropy Loss(CE)的改进版本,它通过为困难的或容易错误分类的示例(即具有嘈杂纹理或部分对象或我们感兴趣的对象的背景)分配更多权重来尝试处理类不平衡问题 ,并简化简单的示例(即背景对象)。
因此,“Focal Loss”减少了简单示例带来的损失贡献,并提高了纠正错误分类的示例的重要性。 焦点损失只是交叉熵损失函数的扩展,它将降低简单示例的权重,并将训练重点放在困难样本上。
所以为了实现这些研究人员提出了-
1- pt代表交叉熵损失,可调聚焦参数≥0。 RetinaNet物体检测方法使用焦距损失的α平衡变体,其中α= 0.25,γ= 2效果最佳。
所以focal loss 的定义是:
对于γ∈[0,5]的几个值,可以看到Focal Loss,请参见图1。我们将注意到聚焦损耗的以下特性:
1.当示例分类错误且pt小时,调制因子接近1并且不影响损失。
2.当pt→1时,因子变为0,并且可以很好地权衡分类良好的示例的损失。
3.Focal loss γ平滑地调整了简单示例的权重。 随着γ的增加,调制因子的 作用也同样增加。 (经过大量实验和试验,研究人员发现γ= 2最有效)
注意:-当FL = 0时,FL等于CE。如图蓝色曲线所示
直观地看,调制因子减小了简单例的损耗贡献,扩展了例的低损耗范围。
你可以在这篇文章中读到关于Focal loss的详细信息(链接到我的focal loss文章),在这篇文章中我讨论了交叉熵演变成focal loss,focal loss的需要,focal loss和交叉熵的比较
最重要的是,我用了几个例子来解释为什么focal loss比交叉熵好。
现在让我们看看用Python实现RetinaNet来构建口罩检测器。
任何深度学习模型都需要大量的训练数据才能在测试数据上得到好的结果。在这篇文章中(链接到我的Web文章),我谈到了为您的深度学习项目收集大量图像的Web方法。
创建数据集
我们首先使用LabelImg工具为训练和验证数据集创建注释。这个优秀的注释工具允许您快速注释对象的边框,以训练机器学习模型。
您可以在anaconda命令提示符中使用下面的命令来安装它
pip install labelImg |
您可以使用如下所示的labelmg工具对每个JPEG文件进行注释,它将生成带有每个边框坐标的XML文件。我们将使用这些xml文件来训练我们的模型。
第一步:克隆安装keras-retinaNet代码仓库
import osprint(os.getcwd()) git clone https://github.com/fizyr/keras-retinanet.git%cd keras-retinanet/ !pip install . !python setup.py build_ext --inplace |
import numpy as np import shutil import pandas as pd import os, sys, random import xml.etree.ElementTree as ET import pandas as pd from os import listdir from os.path import isfile, join import matplotlib.pyplot as plt from PIL import Image import requests import urllib from keras_retinanet.utils.visualization import draw_box, draw_caption , label_color from keras_retinanet.utils.image import preprocess_image, resize_image |
pngPath='C:/Users/PraveenKumar/RetinaNet//maskDetectorJPEGImages/' annotPath='C:/Users/PraveenKumar/RetinaNet//maskDetectorXMLfiles/' data=pd.DataFrame(columns=['fileName','xmin','ymin','xmax','ymax','class']) os.getcwd()#read All filesallfiles = [f for f in listdir(annotPath) if isfile(join(annotPath, f)) |
#Read all pdf files in images and then in text and store that in temp folder #Read all pdf files in images and then in text and store that in temp folder for file in allfiles: #print(file) if (file.split(".")[1]=='xml'):
fileName='C:/Users/PraveenKumar/RetinaNet/maskDetectorJPEGImages/'+file.replace(".xml",'.jpg') tree = ET.parse(annotPath+file) root = tree.getroot() for obj in root.iter('object'): cls_name = obj.find('name').text xml_box = obj.find('bndbox') xmin = xml_box.find('xmin').text ymin = xml_box.find('ymin').text xmax = xml_box.find('xmax').text ymax = xml_box.find('ymax').text # Append rows in Empty Dataframe by adding dictionaries data = data.append({'fileName': fileName, 'xmin': xmin, 'ymin':ymin,'xmax':xmax,'ymax':ymax,'class':cls_name}, ignore_index=True) |
# pick a random image filepath = df.sample()['fileName'].values[0]
# get all rows for this image df2 = df[df['fileName'] == filepath] im = np.array(Image.open(filepath))
# if there's a PNG it will have alpha channel im = im[:,:,:3]
for idx, row in df2.iterrows(): box = [ row['xmin'], row['ymin'], row['xmax'], row['ymax'], ] print(box) draw_box(im, box, color=(255, 0, 0))
plt.axis('off') plt.imshow(im) plt.show()
show_image_with_boxes(data) |
#Check few records of datadata.head( |
#Define labels & write them in a fileclasses = ['mask','noMask']with open('../maskDetectorClasses.csv', 'w') as f: for i, class_name in enumerate(classes): f.write(f'{class_name},{i}\n')
if not os.path.exists('snapshots'): os.mkdir('snapshots') |
注意:最好从一个预先训练好的模型开始,而不是从头开始训练一个模型。我们将使用已经在Coco数据集上预先训练好的ResNet50模型。
URL_MODEL = 'https://github.com/fizyr/keras-retinanet/releases/download/0.5.1/resnet50_coco_best_v2.1.0.h5' urllib.request.urlretrieve(URL_MODEL, PRETRAINED_MODEL) |
注意:如果您正在使用谷歌Colab,您可以使用下面的代码片段来训练您的模型。
#Put your training data path & file that has labels for your training data!keras_retinanet/bin/train.py --freeze-backbone \ --random-transform \ --weights {PRETRAINED_MODEL} \ --batch-size 8 \ --steps 500 \ --epochs 15 \ csv maskDetectorData.csv maskDetectorClasses.csv |
但如果你重新训练你的本地Jupyter Notebook或不同的IDE训练,然后你可以从命令提示符下面的命令
python keras_retinanet/bin/train.py --freeze-backbone --random-transform \ --weights {PRETRAINED_MODEL} --batch-size 8 --steps 500 --epochs 15 csv maskDetectorData.csv maskDetectorClasses.csv |
让我们解释一下传递给脚本train.py的每个参数。
1.freeze-backbone:冻结主干层,当我们使用小数据集时特别有用,以避免过拟合
2.random-transform:随机变换数据集以获得数据增强
3.weights:使用一个预先训练好的模型(您自己的模型或者Fizyr发布的模型)初始化模型
4.batch-size:训练批量大小,值越高,学习曲线越平滑
5.step:迭代的步数
6.epochs:迭代的次数
7.csv:上面的脚本生成的注释文件
from glob import glob model_paths = glob('snapshots/resnet50_csv_0*.h5') latest_path = sorted(model_paths)[-1] print("path:", latest_path)from keras_retinanet import models
model = models.load_model(latest_path, backbone_name='resnet50') model = models.convert_model(model)
label_map = {}for line in open('../maskDetectorClasses.csv'): row = line.rstrip().split(',') label_map[int(row[1])] = row[0] |
#Write a function to choose one image randomly from your dataset and predict using Trained model.def show_image_with_predictions(df, threshold=0.6): # choose a random image row = df.sample() filepath = row['fileName'].values[0] print("filepath:", filepath) # get all rows for this image df2 = df[df['fileName'] == filepath] im = np.array(Image.open(filepath)) print("im.shape:", im.shape)
# if there's a PNG it will have alpha channel im = im[:,:,:3]
# plot true boxes for idx, row in df2.iterrows(): box = [ row['xmin'], row['ymin'], row['xmax'], row['ymax'], ] print(box) draw_box(im, box, color=(255, 0, 0))
### plot predictions ### # get predictions imp = preprocess_image(im) imp, scale = resize_image(im)
boxes, scores, labels = model.predict_on_batch( np.expand_dims(imp, axis=0) )
# standardize box coordinates boxes /= scale
# loop through each prediction for the input image for box, score, label in zip(boxes[0], scores[0], labels[0]): # scores are sorted so we can quit as soon # as we see a score below threshold if score < threshold: break box = box.astype(np.int32) color = label_color(label) draw_box(im, box, color=color)
class_name = label_map caption = f"{class_name} {score:.3f}" draw_caption(im, box, caption) score, label=score, label plt.axis('off') plt.imshow(im) plt.show() return score, label plt.rcParams['figure.figsize'] = [20, 10] |
#Feel free to change threshold as per your business requirement score, label=show_image_with_predictions(data, threshold=0.6) |
http://arxiv.org/abs/1605.06409.
https://arxiv.org/pdf/1708.02002.pdf
https://developers.arcgis.com/python/guide/how-retinanet-works/
https://analyticsindiamag.com/what-is-retinanet-ssd-focal-loss/
https://github.com/fizyr/keras-retinanet
https://www.freecodecamp.org/news/object-detection-in-colab-with-fizyr-retinanet-efed36ac4af3/
https://deeplearningcourses.com/
https://blog.zenggyu.com/en/post/2018-12-05/retinanet-explained-and-demystified/
总而言之,我们完成了使用RetinaNet制作面罩检测器的整个过程。 我们创建了一个数据集,训练了一个模型并进行了测试(这是我的笔记本和数据集的Github存储库)。
Retina Net是一个功能强大的模型,使用Feature Pyramid Networks&ResNet作为其骨干。 我能够通过非常有限的数据集和极少的迭代(每个迭代有500个步长,共6次迭代)获得口罩检测器的良好结果。当然你也可以更改阈值。
1.确保你训练你的模型至少20次迭代,以获得好的结果。
2.一个好的想法是提交使用RetinaNet模型构建口罩检测器的方法。 人们总是可以根据业务需求调整模型,数据和方法。
一般来说,RetinaNet是开始目标检测项目的一个很好的选择,特别是如果您需要快速获得良好的结果。
雷锋字幕组是一个由 AI 爱好者组成的翻译团队,汇聚五百多位志愿者的力量,分享最新的海外AI资讯,交流关于人工智能技术领域的行业变革与技术创新的见解。
团队成员有大数据专家、算法工程师、图像处理工程师、产品经理、产品运营、IT咨询人、在校师生;志愿者们来自IBM、AVL、Adobe、阿里、百度等知名企业,北大、清华、港大、中科院、南卡罗莱纳大学、早稻田大学等海内外高校研究所。
如果,你也是位热爱分享的AI爱好者。欢迎与雷锋字幕组一起,学习新知,分享成长。
雷锋网雷锋网