提交 4c3da33a 编写于 作者: C Channingss

Merge remote-tracking branch 'paddle/develop' into cpp_trt

# 模型部署
本目录为PaddleX模型部署代码。
...@@ -16,7 +16,7 @@ paddlex.datasets.ImageNet(data_dir, file_list, label_list, transforms=None, num_ ...@@ -16,7 +16,7 @@ paddlex.datasets.ImageNet(data_dir, file_list, label_list, transforms=None, num_
> * **transforms** (paddlex.cls.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.cls.transforms](./transforms/cls_transforms.md)。 > * **transforms** (paddlex.cls.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.cls.transforms](./transforms/cls_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。 > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。 > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。 > * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。 > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
## VOCDetection类 ## VOCDetection类
...@@ -37,7 +37,7 @@ paddlex.datasets.VOCDetection(data_dir, file_list, label_list, transforms=None, ...@@ -37,7 +37,7 @@ paddlex.datasets.VOCDetection(data_dir, file_list, label_list, transforms=None,
> * **transforms** (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.det.transforms](./transforms/det_transforms.md)。 > * **transforms** (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.det.transforms](./transforms/det_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。 > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。 > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。 > * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。 > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
## CocoDetection类 ## CocoDetection类
...@@ -57,7 +57,7 @@ paddlex.datasets.CocoDetection(data_dir, ann_file, transforms=None, num_workers= ...@@ -57,7 +57,7 @@ paddlex.datasets.CocoDetection(data_dir, ann_file, transforms=None, num_workers=
> * **transforms** (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.det.transforms](./transforms/det_transforms.md)。 > * **transforms** (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.det.transforms](./transforms/det_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。 > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。 > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。 > * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。 > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
## SegDataset类 ## SegDataset类
...@@ -78,5 +78,64 @@ paddlex.datasets.SegDataset(data_dir, file_list, label_list, transforms=None, nu ...@@ -78,5 +78,64 @@ paddlex.datasets.SegDataset(data_dir, file_list, label_list, transforms=None, nu
> * **transforms** (paddlex.seg.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.seg.transforms](./transforms/seg_transforms.md)。 > * **transforms** (paddlex.seg.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.seg.transforms](./transforms/seg_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。 > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。 > * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。 > * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
## EasyDataCls类
```
paddlex.datasets.SegDataset(data_dir, file_list, label_list, transforms=None, num_workers='auto', buffer_size=100, parallel_method='thread', shuffle=False)
```
读取EasyData图像分类数据集,并对样本进行相应的处理。EasyData图像分类任务数据集格式的介绍可查看文档:[数据集格式说明](../datasets.md)
### 参数
> * **data_dir** (str): 数据集所在的目录路径。
> * **file_list** (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对`data_dir`的相对路径)。
> * **label_list** (str): 描述数据集包含的类别信息文件路径。
> * **transforms** (paddlex.seg.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.cls.transforms](./transforms/cls_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
## EasyDataDet类
```
paddlex.datasets.EasyDataDet(data_dir, file_list, label_list, transforms=None, num_workers=‘auto’, buffer_size=100, parallel_method='thread', shuffle=False)
```
读取EasyData目标检测格式数据集,并对样本进行相应的处理,该格式的数据集同样可以应用到实例分割模型的训练中。EasyData目标检测或实例分割任务数据集格式的介绍可查看文档:[数据集格式说明](../datasets.md)
### 参数
> * **data_dir** (str): 数据集所在的目录路径。
> * **file_list** (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对`data_dir`的相对路径)。
> * **label_list** (str): 描述数据集包含的类别信息文件路径。
> * **transforms** (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.det.transforms](./transforms/det_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
## EasyDataSeg类
```
paddlex.datasets.EasyDataSeg(data_dir, file_list, label_list, transforms=None, num_workers='auto', buffer_size=100, parallel_method='thread', shuffle=False)
```
读取EasyData语分分割任务数据集,并对样本进行相应的处理。EasyData语义分割任务数据集格式的介绍可查看文档:[数据集格式说明](../datasets.md)
### 参数
> * **data_dir** (str): 数据集所在的目录路径。
> * **file_list** (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对`data_dir`的相对路径)。
> * **label_list** (str): 描述数据集包含的类别信息文件路径。
> * **transforms** (paddlex.seg.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.seg.transforms](./transforms/seg_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。 > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
\ No newline at end of file
...@@ -34,7 +34,7 @@ pred_result = model.predict('./xiaoduxiong_ins_det/JPEGImages/WechatIMG114.jpeg' ...@@ -34,7 +34,7 @@ pred_result = model.predict('./xiaoduxiong_ins_det/JPEGImages/WechatIMG114.jpeg'
# 在验证集上进行评估 # 在验证集上进行评估
eval_reader = pdx.cv.datasets.CocoDetection(data_dir=data_dir, eval_reader = pdx.cv.datasets.CocoDetection(data_dir=data_dir,
ann_file=ann_file ann_file=ann_file,
transforms=model.eval_transforms) transforms=model.eval_transforms)
eval_result = model.evaluate(eval_reader, batch_size=1) eval_result = model.evaluate(eval_reader, batch_size=1)
``` ```
...@@ -17,7 +17,7 @@ paddlex.cls.ResNet50(num_classes=1000) ...@@ -17,7 +17,7 @@ paddlex.cls.ResNet50(num_classes=1000)
#### 分类器训练函数接口 #### 分类器训练函数接口
> ```python > ```python
> train(self, num_epochs, train_dataset, train_batch_size=64, eval_dataset=None, save_interval_epochs=1, log_interval_steps=2, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=0.025, lr_decay_epochs=[30, 60, 90], lr_decay_gamma=0.1, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05) > train(self, num_epochs, train_dataset, train_batch_size=64, eval_dataset=None, save_interval_epochs=1, log_interval_steps=2, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=0.025, lr_decay_epochs=[30, 60, 90], lr_decay_gamma=0.1, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05, early_stop=False, early_stop_patience=5)
> ``` > ```
> >
> **参数:** > **参数:**
...@@ -37,6 +37,8 @@ paddlex.cls.ResNet50(num_classes=1000) ...@@ -37,6 +37,8 @@ paddlex.cls.ResNet50(num_classes=1000)
> > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认值为False。 > > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认值为False。
> > - **sensitivities_file** (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。 > > - **sensitivities_file** (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
> > - **eval_metric_loss** (float): 可容忍的精度损失。默认为0.05。 > > - **eval_metric_loss** (float): 可容忍的精度损失。默认为0.05。
> > - **early_stop** (float): 是否使用提前终止训练策略。默认值为False。
> > - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认值为5。
#### 分类器评估函数接口 #### 分类器评估函数接口
...@@ -75,7 +77,7 @@ paddlex.cls.ResNet50(num_classes=1000) ...@@ -75,7 +77,7 @@ paddlex.cls.ResNet50(num_classes=1000)
### 其它分类器类 ### 其它分类器类
`ResNet50`外,`paddlex.cls`下还提供了`ResNet18``ResNet34``ResNet101``ResNet50_vd``ResNet101_vd``DarkNet53``MobileNetV1``MobileNetV2``MobileNetV3_small``MobileNetV3_large``Xception41``Xception65``Xception71``ShuffleNetV2`, 使用方式(包括函数接口和参数)均与`ResNet50`一致,各模型效果可参考[模型库](../model_zoo.md)中列表。 `ResNet50`外,`paddlex.cls`下还提供了`ResNet18``ResNet34``ResNet101``ResNet50_vd``ResNet101_vd``ResNet50_vd_ssld``ResNet101_vd_ssld``DarkNet53``MobileNetV1``MobileNetV2``MobileNetV3_small``MobileNetV3_large``MobileNetV3_small_ssld``MobileNetV3_large_ssld``Xception41``Xception65``Xception71``ShuffleNetV2`, 使用方式(包括函数接口和参数)均与`ResNet50`一致,各模型效果可参考[模型库](../model_zoo.md)中列表。
...@@ -109,7 +111,7 @@ paddlex.det.YOLOv3(num_classes=80, backbone='MobileNetV1', anchors=None, anchor_ ...@@ -109,7 +111,7 @@ paddlex.det.YOLOv3(num_classes=80, backbone='MobileNetV1', anchors=None, anchor_
#### YOLOv3训练函数接口 #### YOLOv3训练函数接口
> ```python > ```python
> train(self, num_epochs, train_dataset, train_batch_size=8, eval_dataset=None, save_interval_epochs=20, log_interval_steps=2, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=1.0/8000, warmup_steps=1000, warmup_start_lr=0.0, lr_decay_epochs=[213, 240], lr_decay_gamma=0.1, metric=None, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05) > train(self, num_epochs, train_dataset, train_batch_size=8, eval_dataset=None, save_interval_epochs=20, log_interval_steps=2, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=1.0/8000, warmup_steps=1000, warmup_start_lr=0.0, lr_decay_epochs=[213, 240], lr_decay_gamma=0.1, metric=None, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05, early_stop=False, early_stop_patience=5)
> ``` > ```
> >
> **参数:** > **参数:**
...@@ -132,6 +134,8 @@ paddlex.det.YOLOv3(num_classes=80, backbone='MobileNetV1', anchors=None, anchor_ ...@@ -132,6 +134,8 @@ paddlex.det.YOLOv3(num_classes=80, backbone='MobileNetV1', anchors=None, anchor_
> > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认值为False。 > > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认值为False。
> > - **sensitivities_file** (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',则自动下载在PascalVOC数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。 > > - **sensitivities_file** (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',则自动下载在PascalVOC数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
> > - **eval_metric_loss** (float): 可容忍的精度损失。默认为0.05。 > > - **eval_metric_loss** (float): 可容忍的精度损失。默认为0.05。
> > - **early_stop** (float): 是否使用提前终止训练策略。默认值为False。
> > - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认值为5。
#### YOLOv3评估函数接口 #### YOLOv3评估函数接口
...@@ -186,7 +190,7 @@ paddlex.det.FasterRCNN(num_classes=81, backbone='ResNet50', with_fpn=True, aspec ...@@ -186,7 +190,7 @@ paddlex.det.FasterRCNN(num_classes=81, backbone='ResNet50', with_fpn=True, aspec
#### FasterRCNN训练函数接口 #### FasterRCNN训练函数接口
> ```python > ```python
> train(self, num_epochs, train_dataset, train_batch_size=2, eval_dataset=None, save_interval_epochs=1, log_interval_steps=2,save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=0.0025, warmup_steps=500, warmup_start_lr=1.0/1200, lr_decay_epochs=[8, 11], lr_decay_gamma=0.1, metric=None, use_vdl=False) > train(self, num_epochs, train_dataset, train_batch_size=2, eval_dataset=None, save_interval_epochs=1, log_interval_steps=2,save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=0.0025, warmup_steps=500, warmup_start_lr=1.0/1200, lr_decay_epochs=[8, 11], lr_decay_gamma=0.1, metric=None, use_vdl=False, early_stop=False, early_stop_patience=5)
> >
> ``` > ```
> >
...@@ -208,6 +212,8 @@ paddlex.det.FasterRCNN(num_classes=81, backbone='ResNet50', with_fpn=True, aspec ...@@ -208,6 +212,8 @@ paddlex.det.FasterRCNN(num_classes=81, backbone='ResNet50', with_fpn=True, aspec
> > - **lr_decay_gamma** (float): 默认优化器的学习率衰减率。默认为0.1。 > > - **lr_decay_gamma** (float): 默认优化器的学习率衰减率。默认为0.1。
> > - **metric** (bool): 训练过程中评估的方式,取值范围为['COCO', 'VOC']。默认值为None。 > > - **metric** (bool): 训练过程中评估的方式,取值范围为['COCO', 'VOC']。默认值为None。
> > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认值为False。 > > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认值为False。
> > - **early_stop** (float): 是否使用提前终止训练策略。默认值为False。
> > - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认值为5。
#### FasterRCNN评估函数接口 #### FasterRCNN评估函数接口
...@@ -264,7 +270,7 @@ paddlex.det.MaskRCNN(num_classes=81, backbone='ResNet50', with_fpn=True, aspect_ ...@@ -264,7 +270,7 @@ paddlex.det.MaskRCNN(num_classes=81, backbone='ResNet50', with_fpn=True, aspect_
#### MaskRCNN训练函数接口 #### MaskRCNN训练函数接口
> ```python > ```python
> train(self, num_epochs, train_dataset, train_batch_size=1, eval_dataset=None, save_interval_epochs=1, log_interval_steps=20, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=1.0/800, warmup_steps=500, warmup_start_lr=1.0 / 2400, lr_decay_epochs=[8, 11], lr_decay_gamma=0.1, metric=None, use_vdl=False) > train(self, num_epochs, train_dataset, train_batch_size=1, eval_dataset=None, save_interval_epochs=1, log_interval_steps=20, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=1.0/800, warmup_steps=500, warmup_start_lr=1.0 / 2400, lr_decay_epochs=[8, 11], lr_decay_gamma=0.1, metric=None, use_vdl=False, early_stop=False, early_stop_patience=5)
> >
> ``` > ```
> >
...@@ -286,6 +292,8 @@ paddlex.det.MaskRCNN(num_classes=81, backbone='ResNet50', with_fpn=True, aspect_ ...@@ -286,6 +292,8 @@ paddlex.det.MaskRCNN(num_classes=81, backbone='ResNet50', with_fpn=True, aspect_
> > - **lr_decay_gamma** (float): 默认优化器的学习率衰减率。默认为0.1。 > > - **lr_decay_gamma** (float): 默认优化器的学习率衰减率。默认为0.1。
> > - **metric** (bool): 训练过程中评估的方式,取值范围为['COCO', 'VOC']。默认值为None。 > > - **metric** (bool): 训练过程中评估的方式,取值范围为['COCO', 'VOC']。默认值为None。
> > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认值为False。 > > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认值为False。
> > - **early_stop** (float): 是否使用提前终止训练策略。默认值为False。
> > - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认值为5。
#### MaskRCNN评估函数接口 #### MaskRCNN评估函数接口
...@@ -350,7 +358,7 @@ paddlex.seg.DeepLabv3p(num_classes=2, backbone='MobileNetV2_x1.0', output_stride ...@@ -350,7 +358,7 @@ paddlex.seg.DeepLabv3p(num_classes=2, backbone='MobileNetV2_x1.0', output_stride
#### DeepLabv3训练函数接口 #### DeepLabv3训练函数接口
> ```python > ```python
> train(self, num_epochs, train_dataset, train_batch_size=2, eval_dataset=None, eval_batch_size=1, save_interval_epochs=1, log_interval_steps=2, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=0.01, lr_decay_power=0.9, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05): > train(self, num_epochs, train_dataset, train_batch_size=2, eval_dataset=None, eval_batch_size=1, save_interval_epochs=1, log_interval_steps=2, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=0.01, lr_decay_power=0.9, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05, early_stop=False, early_stop_patience=5):
> >
> ``` > ```
> >
...@@ -370,6 +378,8 @@ paddlex.seg.DeepLabv3p(num_classes=2, backbone='MobileNetV2_x1.0', output_stride ...@@ -370,6 +378,8 @@ paddlex.seg.DeepLabv3p(num_classes=2, backbone='MobileNetV2_x1.0', output_stride
> > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认False。 > > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认False。
> > - **sensitivities_file** (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。 > > - **sensitivities_file** (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
> > - **eval_metric_loss** (float): 可容忍的精度损失。默认为0.05。 > > - **eval_metric_loss** (float): 可容忍的精度损失。默认为0.05。
> > - **early_stop** (float): 是否使用提前终止训练策略。默认值为False。
> > - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认值为5。
#### DeepLabv3评估函数接口 #### DeepLabv3评估函数接口
...@@ -427,7 +437,7 @@ paddlex.seg.UNet(num_classes=2, upsample_mode='bilinear', use_bce_loss=False, us ...@@ -427,7 +437,7 @@ paddlex.seg.UNet(num_classes=2, upsample_mode='bilinear', use_bce_loss=False, us
#### Unet训练函数接口 #### Unet训练函数接口
> ```python > ```python
> train(self, num_epochs, train_dataset, train_batch_size=2, eval_dataset=None, eval_batch_size=1, save_interval_epochs=1, log_interval_steps=2, save_dir='output', pretrain_weights='COCO', optimizer=None, learning_rate=0.01, lr_decay_power=0.9, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05): > train(self, num_epochs, train_dataset, train_batch_size=2, eval_dataset=None, eval_batch_size=1, save_interval_epochs=1, log_interval_steps=2, save_dir='output', pretrain_weights='COCO', optimizer=None, learning_rate=0.01, lr_decay_power=0.9, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05, early_stop=False, early_stop_patience=5):
> ``` > ```
> >
> **参数:** > **参数:**
...@@ -446,6 +456,8 @@ paddlex.seg.UNet(num_classes=2, upsample_mode='bilinear', use_bce_loss=False, us ...@@ -446,6 +456,8 @@ paddlex.seg.UNet(num_classes=2, upsample_mode='bilinear', use_bce_loss=False, us
> > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认False。 > > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认False。
> > - **sensitivities_file** (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。 > > - **sensitivities_file** (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
> > - **eval_metric_loss** (float): 可容忍的精度损失。默认为0.05。 > > - **eval_metric_loss** (float): 可容忍的精度损失。默认为0.05。
> > - **early_stop** (float): 是否使用提前终止训练策略。默认值为False。
> > - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认值为5。
#### Unet评估函数接口 #### Unet评估函数接口
......
...@@ -111,6 +111,8 @@ paddlex.cls.transforms.RandomDistort(brightness_range=0.9, brightness_prob=0.5, ...@@ -111,6 +111,8 @@ paddlex.cls.transforms.RandomDistort(brightness_range=0.9, brightness_prob=0.5,
1. 对变换的操作顺序进行随机化操作。 1. 对变换的操作顺序进行随机化操作。
2. 按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。 2. 按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。
【注意】该数据增强必须在数据增强Normalize之前使用。
### 参数 ### 参数
* **brightness_range** (float): 明亮度因子的范围。默认为0.9。 * **brightness_range** (float): 明亮度因子的范围。默认为0.9。
* **brightness_prob** (float): 随机调整明亮度的概率。默认为0.5。 * **brightness_prob** (float): 随机调整明亮度的概率。默认为0.5。
......
...@@ -87,6 +87,8 @@ paddlex.det.transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5, ...@@ -87,6 +87,8 @@ paddlex.det.transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5,
1. 对变换的操作顺序进行随机化操作。 1. 对变换的操作顺序进行随机化操作。
2. 按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。 2. 按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。
【注意】该数据增强必须在数据增强Normalize之前使用。
### 参数 ### 参数
* **brightness_range** (float): 明亮度因子的范围。默认为0.5。 * **brightness_range** (float): 明亮度因子的范围。默认为0.5。
* **brightness_prob** (float): 随机调整明亮度的概率。默认为0.5。 * **brightness_prob** (float): 随机调整明亮度的概率。默认为0.5。
...@@ -137,6 +139,8 @@ paddlex.det.transforms.RandomExpand(ratio=4., prob=0.5, fill_value=[123.675, 116 ...@@ -137,6 +139,8 @@ paddlex.det.transforms.RandomExpand(ratio=4., prob=0.5, fill_value=[123.675, 116
* **prob** (float): 随机扩张的概率。默认为0.5。 * **prob** (float): 随机扩张的概率。默认为0.5。
* **fill_value** (list): 扩张图像的初始填充值(0-255)。默认为[123.675, 116.28, 103.53]。 * **fill_value** (list): 扩张图像的初始填充值(0-255)。默认为[123.675, 116.28, 103.53]。
【注意】该数据增强必须在数据增强Resize、ResizeByShort之前使用。
## RandomCrop类 ## RandomCrop类
```python ```python
paddlex.det.transforms.RandomCrop(aspect_ratio=[.5, 2.], thresholds=[.0, .1, .3, .5, .7, .9], scaling=[.3, 1.], num_attempts=50, allow_no_crop=True, cover_all_box=False) paddlex.det.transforms.RandomCrop(aspect_ratio=[.5, 2.], thresholds=[.0, .1, .3, .5, .7, .9], scaling=[.3, 1.], num_attempts=50, allow_no_crop=True, cover_all_box=False)
...@@ -154,6 +158,8 @@ paddlex.det.transforms.RandomCrop(aspect_ratio=[.5, 2.], thresholds=[.0, .1, .3, ...@@ -154,6 +158,8 @@ paddlex.det.transforms.RandomCrop(aspect_ratio=[.5, 2.], thresholds=[.0, .1, .3,
4. 换算有效真值标注框相对候选裁剪区域的位置坐标。 4. 换算有效真值标注框相对候选裁剪区域的位置坐标。
5. 换算有效分割区域相对候选裁剪区域的位置坐标。 5. 换算有效分割区域相对候选裁剪区域的位置坐标。
【注意】该数据增强必须在数据增强Resize、ResizeByShort之前使用。
### 参数 ### 参数
* **aspect_ratio** (list): 裁剪后短边缩放比例的取值范围,以[min, max]形式表示。默认值为[.5, 2.]。 * **aspect_ratio** (list): 裁剪后短边缩放比例的取值范围,以[min, max]形式表示。默认值为[.5, 2.]。
* **thresholds** (list): 判断裁剪候选区域是否有效所需的IoU阈值取值列表。默认值为[.0, .1, .3, .5, .7, .9]。 * **thresholds** (list): 判断裁剪候选区域是否有效所需的IoU阈值取值列表。默认值为[.0, .1, .3, .5, .7, .9]。
......
...@@ -155,6 +155,8 @@ paddlex.seg.transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5, ...@@ -155,6 +155,8 @@ paddlex.seg.transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5,
1.对变换的操作顺序进行随机化操作。 1.对变换的操作顺序进行随机化操作。
2.按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。 2.按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。
【注意】该数据增强必须在数据增强Normalize之前使用。
### 参数 ### 参数
* **brightness_range** (float): 明亮度因子的范围。默认为0.5。 * **brightness_range** (float): 明亮度因子的范围。默认为0.5。
* **brightness_prob** (float): 随机调整明亮度的概率。默认为0.5。 * **brightness_prob** (float): 随机调整明亮度的概率。默认为0.5。
......
...@@ -40,8 +40,16 @@ paddlex.det.draw_pr_curve(eval_details_file=None, gt=None, pred_bbox=None, pred_ ...@@ -40,8 +40,16 @@ paddlex.det.draw_pr_curve(eval_details_file=None, gt=None, pred_bbox=None, pred_
**注意:**`eval_details_file`的优先级更高,只要`eval_details_file`不为None,就会从`eval_details_file`提取真值信息和预测结果做分析。当`eval_details_file`为None时,则用`gt``pred_mask``pred_mask`做分析。 **注意:**`eval_details_file`的优先级更高,只要`eval_details_file`不为None,就会从`eval_details_file`提取真值信息和预测结果做分析。当`eval_details_file`为None时,则用`gt``pred_mask``pred_mask`做分析。
### 使用示例 ### 使用示例
> 示例一: 点击下载如下示例中的[模型](https://bj.bcebos.com/paddlex/models/insect_epoch_270.zip)[数据集](https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz)
点击下载如下示例中的[模型](https://bj.bcebos.com/paddlex/models/xiaoduxiong_epoch_12.tar.gz)[数据集](https://bj.bcebos.com/paddlex/datasets/xiaoduxiong_ins_det.tar.gz)
> 方式一:分析训练过程中保存的模型文件夹中的评估结果文件`eval_details.json`,例如[模型](https://bj.bcebos.com/paddlex/models/insect_epoch_270.zip)中的`eval_details.json`。
```
import paddlex as pdx
eval_details_file = 'insect_epoch_270/eval_details.json'
pdx.det.draw_pr_curve(eval_details_file, save_dir='./insect')
```
> 方式二:分析模型评估函数返回的评估结果。
``` ```
import os import os
# 选择使用0号卡 # 选择使用0号卡
...@@ -50,40 +58,18 @@ os.environ['CUDA_VISIBLE_DEVICES'] = '0' ...@@ -50,40 +58,18 @@ os.environ['CUDA_VISIBLE_DEVICES'] = '0'
from paddlex.det import transforms from paddlex.det import transforms
import paddlex as pdx import paddlex as pdx
eval_transforms = transforms.Compose([ model = pdx.load_model('insect_epoch_270')
transforms.Normalize(), eval_dataset = pdx.datasets.VOCDetection(
transforms.ResizeByShort(short_size=800, max_size=1333), data_dir='insect_det',
transforms.Padding(coarsest_stride=32) file_list='insect_det/val_list.txt',
]) label_list='insect_det/labels.txt',
transforms=model.eval_transforms)
eval_dataset = pdx.datasets.CocoDetection( metrics, evaluate_details = model.evaluate(eval_dataset, batch_size=8, return_details=True)
data_dir='xiaoduxiong_ins_det/JPEGImages',
ann_file='xiaoduxiong_ins_det/val.json',
transforms=eval_transforms)
model = pdx.load_model('xiaoduxiong_epoch_12')
metrics, evaluate_details = model.evaluate(eval_dataset, batch_size=1, return_details=True)
gt = evaluate_details['gt'] gt = evaluate_details['gt']
bbox = evaluate_details['bbox'] bbox = evaluate_details['bbox']
mask = evaluate_details['mask'] pdx.det.draw_pr_curve(gt=gt, pred_bbox=bbox, save_dir='./insect')
# 分别可视化bboxmask的准召曲线
pdx.det.draw_pr_curve(gt=gt, pred_bbox=bbox, pred_mask=mask, save_dir='./xiaoduxiong')
``` ```
预测框的各个类别的准确率和召回率的对应关系、召回率和置信度阈值的对应关系可视化如下:
![](./images/xiaoduxiong_bbox_pr_curve(iou-0.5).png)
预测mask的各个类别的准确率和召回率的对应关系、召回率和置信度阈值的对应关系可视化如下:
![](./images/xiaoduxiong_segm_pr_curve(iou-0.5).png)
> 示例二:
使用[yolov3_darknet53.py示例代码](https://github.com/PaddlePaddle/PaddleX/blob/develop/tutorials/train/detection/yolov3_darknet53.py)训练完成后,加载模型评估结果文件进行分析:
```
import paddlex as pdx
eval_details_file = 'output/yolov3_darknet53/best_model/eval_details.json'
pdx.det.draw_pr_curve(eval_details_file, save_dir='./insect')
```
预测框的各个类别的准确率和召回率的对应关系、召回率和置信度阈值的对应关系可视化如下: 预测框的各个类别的准确率和召回率的对应关系、召回率和置信度阈值的对应关系可视化如下:
![](./images/insect_bbox_pr_curve(iou-0.5).png) ![](./images/insect_bbox_pr_curve(iou-0.5).png)
......
...@@ -41,8 +41,8 @@ labelA ...@@ -41,8 +41,8 @@ labelA
labelB labelB
... ...
``` ```
[点击这里](https://bj.bcebos.com/paddlex/datasets/vegetables_cls.tar.gz),下载蔬菜分类分类数据集 [点击这里](https://bj.bcebos.com/paddlex/datasets/vegetables_cls.tar.gz),下载蔬菜分类分类数据集
在PaddleX中,使用`paddlex.cv.datasets.ImageNet`([API说明](./apis/datasets.html#imagenet))加载分类数据集 在PaddleX中,使用`paddlex.cv.datasets.ImageNet`([API说明](./apis/datasets.html#imagenet))加载分类数据集
## 目标检测VOC ## 目标检测VOC
目标检测VOC数据集包含图像文件夹、标注信息文件夹、标签文件及图像列表文件。 目标检测VOC数据集包含图像文件夹、标注信息文件夹、标签文件及图像列表文件。
...@@ -81,8 +81,8 @@ labelA ...@@ -81,8 +81,8 @@ labelA
labelB labelB
... ...
``` ```
[点击这里](https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz),下载昆虫检测数据集 [点击这里](https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz),下载昆虫检测数据集
在PaddleX中,使用`paddlex.cv.datasets.VOCDetection`([API说明](./apis/datasets.html#vocdetection))加载目标检测VOC数据集 在PaddleX中,使用`paddlex.cv.datasets.VOCDetection`([API说明](./apis/datasets.html#vocdetection))加载目标检测VOC数据集
## 目标检测和实例分割COCO ## 目标检测和实例分割COCO
目标检测和实例分割COCO数据集包含图像文件夹及图像标注信息文件。 目标检测和实例分割COCO数据集包含图像文件夹及图像标注信息文件。
...@@ -135,7 +135,7 @@ labelB ...@@ -135,7 +135,7 @@ labelB
] ]
} }
``` ```
每个字段的含义如下所示: 其中,每个字段的含义如下所示:
| 域名 | 字段名 | 含义 | 数据类型 | 备注 | | 域名 | 字段名 | 含义 | 数据类型 | 备注 |
|:-----|:--------|:------------|------|:-----| |:-----|:--------|:------------|------|:-----|
...@@ -155,8 +155,8 @@ labelB ...@@ -155,8 +155,8 @@ labelB
| categories | supercategory | 类别父类的标签名 | str | | | categories | supercategory | 类别父类的标签名 | str | |
[点击这里](https://bj.bcebos.com/paddlex/datasets/garbage_ins_det.tar.gz),下载垃圾实例分割数据集 [点击这里](https://bj.bcebos.com/paddlex/datasets/garbage_ins_det.tar.gz),下载垃圾实例分割数据集
在PaddleX中,使用`paddlex.cv.datasets.COCODetection`([API说明](./apis/datasets.html#cocodetection))加载COCO格式数据集 在PaddleX中,使用`paddlex.cv.datasets.COCODetection`([API说明](./apis/datasets.html#cocodetection))加载COCO格式数据集
## 语义分割数据 ## 语义分割数据
语义分割数据集包含原图、标注图及相应的文件列表文件。 语义分割数据集包含原图、标注图及相应的文件列表文件。
...@@ -191,13 +191,176 @@ images/xxx2.png annotations/xxx2.png ...@@ -191,13 +191,176 @@ images/xxx2.png annotations/xxx2.png
`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示: `labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
``` ```
background
labelA labelA
labelB labelB
... ...
``` ```
标注图像为单通道图像,像素值即为对应的类别,像素标注类别需要从0开始递增, 标注图像为单通道图像,像素值即为对应的类别,像素标注类别需要从0开始递增(一般第一个类别为`background`
例如0,1,2,3表示有4种类别,标注类别最多为256类。其中可以指定特定的像素值用于表示该值的像素不参与训练和评估(默认为255)。 例如0,1,2,3表示有4种类别,标注类别最多为256类。其中可以指定特定的像素值用于表示该值的像素不参与训练和评估(默认为255)。
[点击这里](https://bj.bcebos.com/paddlex/datasets/optic_disc_seg.tar.gz),下载视盘语义分割数据集 [点击这里](https://bj.bcebos.com/paddlex/datasets/optic_disc_seg.tar.gz),下载视盘语义分割数据集。
在PaddleX中,使用`paddlex.cv.datasets.SegReader`([API说明](./apis/datasets.html#segreader))加载语义分割数据集 在PaddleX中,使用`paddlex.cv.datasets.SegReader`([API说明](./apis/datasets.html#segreader))加载语义分割数据集。
## 图像分类EasyDataCls
图像分类EasyDataCls数据集包含存放图像和json文件的文件夹、标签文件及图像列表文件。
参考数据文件结构如下:
```
./dataset/ # 数据集根目录
|--easydata # 存放图像和json文件的文件夹
| |--0001.jpg
| |--0001.json
| |--0002.jpg
| |--0002.json
| └--...
|
|--train_list.txt # 训练文件列表文件
|
|--val_list.txt # 验证文件列表文件
|
└--labels.txt # 标签列表文件
```
其中,图像文件名应与json文件名一一对应。
每个json文件存储于`labels`相关的信息。如下所示:
```
{"labels": [{"name": "labelA"}]}
```
其中,`name`字段代表对应图像的类别。
`train_list.txt``val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为json文件相对于dataset的相对路径。如下所示:
```
easydata/0001.jpg easydata/0001.json
easydata/0002.jpg easydata/0002.json
...
```
`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
```
labelA
labelB
...
```
[点击这里](https://ai.baidu.com/easydata/),可以标注图像分类EasyDataCls数据集。
在PaddleX中,使用`paddlex.cv.datasets.EasyDataCls`([API说明](./apis/datasets.html#easydatacls))加载分类数据集。
## 目标检测和实例分割EasyDataDet
目标检测和实例分割EasyDataDet数据集包含存放图像和json文件的文件夹、标签文件及图像列表文件。
参考数据文件结构如下:
```
./dataset/ # 数据集根目录ß
|--easydata # 存放图像和json文件的文件夹
| |--0001.jpg
| |--0001.json
| |--0002.jpg
| |--0002.json
| └--...
|
|--train_list.txt # 训练文件列表文件
|
|--val_list.txt # 验证文件列表文件
|
└--labels.txt # 标签列表文件
```
其中,图像文件名应与json文件名一一对应。
每个json文件存储于`labels`相关的信息。如下所示:
```
"labels": [{"y1": 18, "x2": 883, "x1": 371, "y2": 404, "name": "labelA",
"mask": "kVfc0`0Zg0<F7J7I5L5K4L4L4L3N3L3N3L3N2N3M2N2N2N2N2N2N1O2N2O1N2N1O2O1N101N1O2O1N101N10001N101N10001N10001O0O10001O000O100000001O0000000000000000000000O1000001O00000O101O000O101O0O101O0O2O0O101O0O2O0O2N2O0O2O0O2N2O1N1O2N2N2O1N2N2N2N2N2N2M3N3M2M4M2M4M3L4L4L4K6K5J7H9E\\iY1"},
{"y1": 314, "x2": 666, "x1": 227, "y2": 676, "name": "labelB",
"mask": "mdQ8g0Tg0:G8I6K5J5L4L4L4L4M2M4M2M4M2N2N2N3L3N2N2N2N2O1N1O2N2N2O1N1O2N2O0O2O1N1O2O0O2O0O2O001N100O2O000O2O000O2O00000O2O000000001N100000000000000000000000000000000001O0O100000001O0O10001N10001O0O101N10001N101N101N101N101N2O0O2N2O0O2N2N2O0O2N2N2N2N2N2N2N2N2N3L3N2N3L3N3L4M2M4L4L5J5L5J7H8H;BUcd<"},
...]}
```
其中,list中的每个元素代表一个标注信息,标注信息中字段的含义如下所示:
| 字段名 | 含义 | 数据类型 | 备注 |
|:--------|:------------|------|:-----|
| x1 | 标注框左下角横坐标 | int | |
| y1 | 标注框左下角纵坐标 | int | |
| x2 | 标注框右上角横坐标 | int | |
| y2 | 标注框右上角纵坐标 | int | |
| name | 标注框中物体类标 | str | |
| mask | 分割区域布尔型numpy编码后的字符串 | str | 该字段可以不存在,当不存在时只能进行目标检测 |
`train_list.txt``val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为json文件相对于dataset的相对路径。如下所示:
```
easydata/0001.jpg easydata/0001.json
easydata/0002.jpg easydata/0002.json
...
```
`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
```
labelA
labelB
...
```
[点击这里](https://ai.baidu.com/easydata/),可以标注图像分类EasyDataDet数据集。
在PaddleX中,使用`paddlex.cv.datasets.EasyDataDet`([API说明](./apis/datasets.html#easydatadet))加载分类数据集。
## 语义分割EasyDataSeg
语义分割EasyDataSeg数据集包含存放图像和json文件的文件夹、标签文件及图像列表文件。
参考数据文件结构如下:
```
./dataset/ # 数据集根目录ß
|--easydata # 存放图像和json文件的文件夹
| |--0001.jpg
| |--0001.json
| |--0002.jpg
| |--0002.json
| └--...
|
|--train_list.txt # 训练文件列表文件
|
|--val_list.txt # 验证文件列表文件
|
└--labels.txt # 标签列表文件
```
其中,图像文件名应与json文件名一一对应。
每个json文件存储于`labels`相关的信息。如下所示:
```
"labels": [{"y1": 18, "x2": 883, "x1": 371, "y2": 404, "name": "labelA",
"mask": "kVfc0`0Zg0<F7J7I5L5K4L4L4L3N3L3N3L3N2N3M2N2N2N2N2N2N1O2N2O1N2N1O2O1N101N1O2O1N101N10001N101N10001N10001O0O10001O000O100000001O0000000000000000000000O1000001O00000O101O000O101O0O101O0O2O0O101O0O2O0O2N2O0O2O0O2N2O1N1O2N2N2O1N2N2N2N2N2N2M3N3M2M4M2M4M3L4L4L4K6K5J7H9E\\iY1"},
{"y1": 314, "x2": 666, "x1": 227, "y2": 676, "name": "labelB",
"mask": "mdQ8g0Tg0:G8I6K5J5L4L4L4L4M2M4M2M4M2N2N2N3L3N2N2N2N2O1N1O2N2N2O1N1O2N2O0O2O1N1O2O0O2O0O2O001N100O2O000O2O000O2O00000O2O000000001N100000000000000000000000000000000001O0O100000001O0O10001N10001O0O101N10001N101N101N101N101N2O0O2N2O0O2N2N2O0O2N2N2N2N2N2N2N2N2N3L3N2N3L3N3L4M2M4L4L5J5L5J7H8H;BUcd<"},
...]}
```
其中,list中的每个元素代表一个标注信息,标注信息中字段的含义如下所示:
| 字段名 | 含义 | 数据类型 | 备注 |
|:--------|:------------|------|:-----|
| x1 | 标注框左下角横坐标 | int | |
| y1 | 标注框左下角纵坐标 | int | |
| x2 | 标注框右上角横坐标 | int | |
| y2 | 标注框右上角纵坐标 | int | |
| name | 标注框中物体类标 | str | |
| mask | 分割区域布尔型numpy编码后的字符串 | str | 该字段必须存在 |
`train_list.txt``val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为json文件相对于dataset的相对路径。如下所示:
```
easydata/0001.jpg easydata/0001.json
easydata/0002.jpg easydata/0002.json
...
```
`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
```
labelA
labelB
...
```
[点击这里](https://ai.baidu.com/easydata/),可以标注图像分类EasyDataSeg数据集。
在PaddleX中,使用`paddlex.cv.datasets.EasyDataSeg`([API说明](./apis/datasets.html#easydataseg))加载分类数据集。
\ No newline at end of file
...@@ -4,35 +4,38 @@ ...@@ -4,35 +4,38 @@
表中相关模型也可下载好作为相应模型的预训练模型,通过`pretrain_weights`指定目录加载使用。 表中相关模型也可下载好作为相应模型的预训练模型,通过`pretrain_weights`指定目录加载使用。
## 图像分类模型 ## 图像分类模型
> 表中模型相关指标均为在ImageNet数据集上使用PaddlePaddle Python预测接口测试得到(测试GPU型号为Nvidia Tesla P4),预测速度为每张图片预测用时(不包括预处理和后处理),表中符号`-`表示相关指标暂未测试。 > 表中模型相关指标均为在ImageNet数据集上使用PaddlePaddle Python预测接口测试得到(测试GPU型号为Nvidia Tesla P40),预测速度为每张图片预测用时(不包括预处理和后处理),表中符号`-`表示相关指标暂未测试。
| 模型 | 模型大小 | 预测速度(毫秒) | Top1准确率 | Top5准确率 | | 模型 | 模型大小 | 预测速度(毫秒) | Top1准确率(%) | Top5准确率(%) |
| :----| :------- | :----------- | :--------- | :--------- | | :----| :------- | :----------- | :--------- | :--------- |
| ResNet18| 46.9MB | 3.456 | 70.98% | 89.92% | | ResNet18| 46.9MB | 1.499 | 71.0 | 89.9 |
| ResNet34| 87.5MB | 5.668 | 74.57% | 92.14% | | ResNet34| 87.5MB | 2.272 | 74.6 | 92.1 |
| ResNet50| 102.7MB | 8.787 | 76.50% | 93.00% | | ResNet50| 102.7MB | 2.939 | 76.5 | 93.0 |
| ResNet101 |179.1MB | 15.447 | 77.56% | 93.64% | | ResNet101 |179.1MB | 5.314 | 77.6 | 93.6 |
| ResNet50_vd |102.8MB | 9.058 | 79.12% | 94.44% | | ResNet50_vd |102.8MB | 3.165 | 79.1 | 94.4 |
| ResNet101_vd| 179.2MB | 15.685 | 80.17% | 94.97% | | ResNet101_vd| 179.2MB | 5.252 | 80.2 | 95.0 |
| DarkNet53|166.9MB | 11.969 | 78.04% | 94.05% | | ResNet50_vd_ssld |102.8MB | 3.165 | 82.4 | 96.1 |
| MobileNetV1 | 16.4MB | 2.609 | 70.99% | 89.68% | | ResNet101_vd_ssld| 179.2MB | 5.252 | 83.7 | 96.7 |
| MobileNetV2 | 14.4MB | 4.546 | 72.15% | 90.65% | | DarkNet53|166.9MB | 3.139 | 78.0 | 94.1 |
| MobileNetV3_large| 22.8MB | - | 75.3% | 75.3% | | MobileNetV1 | 16.0MB | 32.523 | 71.0 | 89.7 |
| MobileNetV3_small | 12.5MB | 6.809 | 67.46% | 87.12% | | MobileNetV2 | 14.0MB | 23.318 | 72.2 | 90.7 |
| Xception41 |92.4MB | 13.757 | 79.30% | 94.53% | | MobileNetV3_large| 21.0MB | 19.308 | 75.3 | 93.2 |
| Xception65 | 144.6MB | 19.216 | 81.00% | 95.49% | | MobileNetV3_small | 12.0MB | 6.546 | 68.2 | 88.1 |
| Xception71| 151.9MB | 23.291 | 81.11% | 95.45% | | MobileNetV3_large_ssld| 21.0MB | 19.308 | 79.0 | 94.5 |
| DenseNet121 | 32.8MB | 12.437 | 75.66% | 92.58% | | MobileNetV3_small_ssld | 12.0MB | 6.546 | 71.3 | 90.1 |
| DenseNet161|116.3MB | 27.717 | 78.57% | 94.14% | | Xception41 |92.4MB | 4.408 | 79.6 | 94.4 |
| DenseNet201| 84.6MB | 26.583 | 77.63% | 93.66% | | Xception65 | 144.6MB | 6.464 | 80.3 | 94.5 |
| ShuffleNetV2 | 10.2MB | 6.101 | 68.8% | 88.5% | | DenseNet121 | 32.8MB | 4.371 | 75.7 | 92.6 |
| DenseNet161|116.3MB | 8.863 | 78.6 | 94.1 |
| DenseNet201| 84.6MB | 8.173 | 77.6 | 93.7 |
| ShuffleNetV2 | 9.0MB | 10.941 | 68.8 | 88.5 |
## 目标检测模型 ## 目标检测模型
> 表中模型相关指标均为在MSCOCO数据集上使用PaddlePaddle Python预测接口测试得到(测试GPU型号为Nvidia Tesla V100测试得到,表中符号`-`表示相关指标暂未测试。 > 表中模型相关指标均为在MSCOCO数据集上使用PaddlePaddle Python预测接口测试得到(测试GPU型号为Nvidia Tesla V100测试得到,表中符号`-`表示相关指标暂未测试。
| 模型 | 模型大小 | 预测时间(毫秒) | BoxAP | | 模型 | 模型大小 | 预测时间(毫秒) | BoxAP(%) |
|:-------|:-----------|:-------------|:----------| |:-------|:-----------|:-------------|:----------|
|FasterRCNN-ResNet50|135.6MB| 78.450 | 35.2 | |FasterRCNN-ResNet50|135.6MB| 78.450 | 35.2 |
|FasterRCNN-ResNet50_vd| 135.7MB | 79.523 | 36.4 | |FasterRCNN-ResNet50_vd| 135.7MB | 79.523 | 36.4 |
...@@ -50,7 +53,7 @@ ...@@ -50,7 +53,7 @@
> 表中模型相关指标均为在MSCOCO数据集上测试得到。 > 表中模型相关指标均为在MSCOCO数据集上测试得到。
| 模型 |模型大小 | 预测时间(毫秒) | BoxAP | SegAP | | 模型 |模型大小 | 预测时间(毫秒) | BoxAP | SegAP(%) |
|:---------|:---------|:----------|:---------|:--------| |:---------|:---------|:----------|:---------|:--------|
|MaskRCNN-ResNet50|51.2MB| 86.096 | 36.5 |32.2| |MaskRCNN-ResNet50|51.2MB| 86.096 | 36.5 |32.2|
|MaskRCNN-ResNet50-FPN|184.6MB | 65.859 | 37.9 |34.2| |MaskRCNN-ResNet50-FPN|184.6MB | 65.859 | 37.9 |34.2|
......
...@@ -20,6 +20,17 @@ from . import seg ...@@ -20,6 +20,17 @@ from . import seg
from . import cls from . import cls
from . import slim from . import slim
try:
import pycocotools
except:
print("[WARNING] pycocotools is not installed, detection model is not available now.")
print("[WARNING] pycocotools install: https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/install.md")
import paddlehub as hub
if hub.version.hub_version < '1.6.2':
raise Exception("[ERROR] paddlehub >= 1.6.2 is required")
env_info = get_environ_info() env_info = get_environ_info()
load_model = cv.models.load_model load_model = cv.models.load_model
datasets = cv.datasets datasets = cv.datasets
......
...@@ -16,3 +16,6 @@ from .imagenet import ImageNet ...@@ -16,3 +16,6 @@ from .imagenet import ImageNet
from .voc import VOCDetection from .voc import VOCDetection
from .coco import CocoDetection from .coco import CocoDetection
from .seg_dataset import SegDataset from .seg_dataset import SegDataset
from .easydata_cls import EasyDataCls
from .easydata_det import EasyDataDet
from .easydata_seg import EasyDataSeg
\ No newline at end of file
...@@ -19,7 +19,6 @@ import random ...@@ -19,7 +19,6 @@ import random
import numpy as np import numpy as np
import paddlex.utils.logging as logging import paddlex.utils.logging as logging
import paddlex as pst import paddlex as pst
from pycocotools.coco import COCO
from .voc import VOCDetection from .voc import VOCDetection
from .dataset import is_pic from .dataset import is_pic
...@@ -35,7 +34,7 @@ class CocoDetection(VOCDetection): ...@@ -35,7 +34,7 @@ class CocoDetection(VOCDetection):
系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。 系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。 buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread' parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。 线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。 shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
""" """
...@@ -47,6 +46,8 @@ class CocoDetection(VOCDetection): ...@@ -47,6 +46,8 @@ class CocoDetection(VOCDetection):
buffer_size=100, buffer_size=100,
parallel_method='process', parallel_method='process',
shuffle=False): shuffle=False):
from pycocotools.coco import COCO
super(VOCDetection, self).__init__( super(VOCDetection, self).__init__(
transforms=transforms, transforms=transforms,
num_workers=num_workers, num_workers=num_workers,
...@@ -111,7 +112,7 @@ class CocoDetection(VOCDetection): ...@@ -111,7 +112,7 @@ class CocoDetection(VOCDetection):
im_info = { im_info = {
'im_id': np.array([img_id]).astype('int32'), 'im_id': np.array([img_id]).astype('int32'),
'origin_shape': np.array([im_h, im_w]).astype('int32'), 'image_shape': np.array([im_h, im_w]).astype('int32'),
} }
label_info = { label_info = {
'is_crowd': is_crowd, 'is_crowd': is_crowd,
......
# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
import os.path as osp
import random
import copy
import json
import paddlex.utils.logging as logging
from .imagenet import ImageNet
from .dataset import is_pic
from .dataset import get_encoding
class EasyDataCls(ImageNet):
"""读取EasyDataCls格式的分类数据集,并对样本进行相应的处理。
Args:
data_dir (str): 数据集所在的目录路径。
file_list (str): 描述数据集图片文件和类别id的文件路径(文本内每行路径为相对data_dir的相对路)。
label_list (str): 描述数据集包含的类别信息文件路径。
transforms (paddlex.cls.transforms): 数据集中每个样本的预处理/增强算子。
num_workers (int|str): 数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据
系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核
数的一半。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
"""
def __init__(self,
data_dir,
file_list,
label_list,
transforms=None,
num_workers='auto',
buffer_size=100,
parallel_method='process',
shuffle=False):
super(ImageNet, self).__init__(
transforms=transforms,
num_workers=num_workers,
buffer_size=buffer_size,
parallel_method=parallel_method,
shuffle=shuffle)
self.file_list = list()
self.labels = list()
self._epoch = 0
with open(label_list, encoding=get_encoding(label_list)) as f:
for line in f:
item = line.strip()
self.labels.append(item)
logging.info("Starting to read file list from dataset...")
with open(file_list, encoding=get_encoding(file_list)) as f:
for line in f:
img_file, json_file = [osp.join(data_dir, x) \
for x in line.strip().split()[:2]]
if not is_pic(img_file):
continue
if not osp.isfile(json_file):
continue
if not osp.exists(img_file):
raise IOError(
'The image file {} is not exist!'.format(img_file))
with open(json_file, mode='r', \
encoding=get_encoding(json_file)) as j:
json_info = json.load(j)
label = json_info['labels'][0]['name']
self.file_list.append([img_file, self.labels.index(label)])
self.num_samples = len(self.file_list)
logging.info("{} samples in file {}".format(
len(self.file_list), file_list))
\ No newline at end of file
# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
import os.path as osp
import random
import copy
import json
import cv2
import numpy as np
import paddlex.utils.logging as logging
from .voc import VOCDetection
from .dataset import is_pic
from .dataset import get_encoding
class EasyDataDet(VOCDetection):
"""读取EasyDataDet格式的检测数据集,并对样本进行相应的处理。
Args:
data_dir (str): 数据集所在的目录路径。
file_list (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对data_dir的相对路)。
label_list (str): 描述数据集包含的类别信息文件路径。
transforms (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子。
num_workers (int|str): 数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据
系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的
一半。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
"""
def __init__(self,
data_dir,
file_list,
label_list,
transforms=None,
num_workers='auto',
buffer_size=100,
parallel_method='process',
shuffle=False):
super(VOCDetection, self).__init__(
transforms=transforms,
num_workers=num_workers,
buffer_size=buffer_size,
parallel_method=parallel_method,
shuffle=shuffle)
self.file_list = list()
self.labels = list()
self._epoch = 0
annotations = {}
annotations['images'] = []
annotations['categories'] = []
annotations['annotations'] = []
cname2cid = {}
label_id = 1
with open(label_list, encoding=get_encoding(label_list)) as fr:
for line in fr.readlines():
cname2cid[line.strip()] = label_id
label_id += 1
self.labels.append(line.strip())
logging.info("Starting to read file list from dataset...")
for k, v in cname2cid.items():
annotations['categories'].append({
'supercategory': 'component',
'id': v,
'name': k
})
from pycocotools.mask import decode
ct = 0
ann_ct = 0
with open(file_list, encoding=get_encoding(file_list)) as f:
for line in f:
img_file, json_file = [osp.join(data_dir, x) \
for x in line.strip().split()[:2]]
if not is_pic(img_file):
continue
if not osp.isfile(json_file):
continue
if not osp.exists(img_file):
raise IOError(
'The image file {} is not exist!'.format(img_file))
with open(json_file, mode='r', \
encoding=get_encoding(json_file)) as j:
json_info = json.load(j)
im_id = np.array([ct])
im = cv2.imread(img_file)
im_w = im.shape[1]
im_h = im.shape[0]
objs = json_info['labels']
gt_bbox = np.zeros((len(objs), 4), dtype=np.float32)
gt_class = np.zeros((len(objs), 1), dtype=np.int32)
gt_score = np.ones((len(objs), 1), dtype=np.float32)
is_crowd = np.zeros((len(objs), 1), dtype=np.int32)
difficult = np.zeros((len(objs), 1), dtype=np.int32)
gt_poly = [None] * len(objs)
for i, obj in enumerate(objs):
cname = obj['name']
gt_class[i][0] = cname2cid[cname]
x1 = max(0, obj['x1'])
y1 = max(0, obj['y1'])
x2 = min(im_w - 1, obj['x2'])
y2 = min(im_h - 1, obj['y2'])
gt_bbox[i] = [x1, y1, x2, y2]
is_crowd[i][0] = 0
if 'mask' in obj:
mask_dict = {}
mask_dict['size'] = [im_h, im_w]
mask_dict['counts'] = obj['mask'].encode()
mask = decode(mask_dict)
gt_poly[i] = self.mask2polygon(mask)
annotations['annotations'].append({
'iscrowd':
0,
'image_id':
int(im_id[0]),
'bbox': [x1, y1, x2 - x1 + 1, y2 - y1 + 1],
'area':
float((x2 - x1 + 1) * (y2 - y1 + 1)),
'segmentation':
[[x1, y1, x1, y2, x2, y2, x2, y1]] if gt_poly[i] is None else gt_poly[i],
'category_id':
cname2cid[cname],
'id':
ann_ct,
'difficult':
0
})
ann_ct += 1
im_info = {
'im_id': im_id,
'image_shape': np.array([im_h, im_w]).astype('int32'),
}
label_info = {
'is_crowd': is_crowd,
'gt_class': gt_class,
'gt_bbox': gt_bbox,
'gt_score': gt_score,
'difficult': difficult
}
if None not in gt_poly:
label_info['gt_poly'] = gt_poly
voc_rec = (im_info, label_info)
if len(objs) != 0:
self.file_list.append([img_file, voc_rec])
ct += 1
annotations['images'].append({
'height':
im_h,
'width':
im_w,
'id':
int(im_id[0]),
'file_name':
osp.split(img_file)[1]
})
if not len(self.file_list) > 0:
raise Exception('not found any voc record in %s' % (file_list))
logging.info("{} samples in file {}".format(
len(self.file_list), file_list))
self.num_samples = len(self.file_list)
from pycocotools.coco import COCO
self.coco_gt = COCO()
self.coco_gt.dataset = annotations
self.coco_gt.createIndex()
def mask2polygon(self, mask):
contours, hierarchy = cv2.findContours(
(mask).astype(np.uint8), cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
segmentation = []
for contour in contours:
contour_list = contour.flatten().tolist()
if len(contour_list) > 4:
segmentation.append(contour_list)
return segmentation
\ No newline at end of file
# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
import os.path as osp
import random
import copy
import json
import cv2
import numpy as np
import paddlex.utils.logging as logging
from .dataset import Dataset
from .dataset import get_encoding
from .dataset import is_pic
class EasyDataSeg(Dataset):
"""读取EasyDataSeg语义分割任务数据集,并对样本进行相应的处理。
Args:
data_dir (str): 数据集所在的目录路径。
file_list (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对data_dir的相对路)。
label_list (str): 描述数据集包含的类别信息文件路径。
transforms (list): 数据集中每个样本的预处理/增强算子。
num_workers (int): 数据集中样本在预处理过程中的线程或进程数。默认为4。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
"""
def __init__(self,
data_dir,
file_list,
label_list,
transforms=None,
num_workers='auto',
buffer_size=100,
parallel_method='process',
shuffle=False):
super(EasyDataSeg, self).__init__(
transforms=transforms,
num_workers=num_workers,
buffer_size=buffer_size,
parallel_method=parallel_method,
shuffle=shuffle)
self.file_list = list()
self.labels = list()
self._epoch = 0
from pycocotools.mask import decode
cname2cid = {}
label_id = 0
with open(label_list, encoding=get_encoding(label_list)) as fr:
for line in fr.readlines():
cname2cid[line.strip()] = label_id
label_id += 1
self.labels.append(line.strip())
with open(file_list, encoding=get_encoding(file_list)) as f:
for line in f:
img_file, json_file = [osp.join(data_dir, x) \
for x in line.strip().split()[:2]]
if not is_pic(img_file):
continue
if not osp.isfile(json_file):
continue
if not osp.exists(img_file):
raise IOError(
'The image file {} is not exist!'.format(img_file))
with open(json_file, mode='r', \
encoding=get_encoding(json_file)) as j:
json_info = json.load(j)
im = cv2.imread(img_file)
im_w = im.shape[1]
im_h = im.shape[0]
objs = json_info['labels']
lable_npy = np.zeros([im_h, im_w]).astype('uint8')
for i, obj in enumerate(objs):
cname = obj['name']
cid = cname2cid[cname]
mask_dict = {}
mask_dict['size'] = [im_h, im_w]
mask_dict['counts'] = obj['mask'].encode()
mask = decode(mask_dict)
mask *= cid
conflict_index = np.where(((lable_npy > 0) & (mask == cid)) == True)
mask[conflict_index] = 0
lable_npy += mask
self.file_list.append([img_file, lable_npy])
self.num_samples = len(self.file_list)
logging.info("{} samples in file {}".format(
len(self.file_list), file_list))
def iterator(self):
self._epoch += 1
self._pos = 0
files = copy.deepcopy(self.file_list)
if self.shuffle:
random.shuffle(files)
files = files[:self.num_samples]
self.num_samples = len(files)
for f in files:
lable_npy = f[1]
sample = [f[0], None, lable_npy]
yield sample
...@@ -35,7 +35,7 @@ class ImageNet(Dataset): ...@@ -35,7 +35,7 @@ class ImageNet(Dataset):
数的一半。 数的一半。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。 buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread' parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。 线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。 shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
""" """
......
...@@ -33,7 +33,7 @@ class SegDataset(Dataset): ...@@ -33,7 +33,7 @@ class SegDataset(Dataset):
num_workers (int): 数据集中样本在预处理过程中的线程或进程数。默认为4。 num_workers (int): 数据集中样本在预处理过程中的线程或进程数。默认为4。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。 buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread' parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。 线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。 shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
""" """
......
...@@ -17,8 +17,8 @@ import copy ...@@ -17,8 +17,8 @@ import copy
import os.path as osp import os.path as osp
import random import random
import numpy as np import numpy as np
from collections import OrderedDict
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from pycocotools.coco import COCO
import paddlex.utils.logging as logging import paddlex.utils.logging as logging
from .dataset import Dataset from .dataset import Dataset
from .dataset import is_pic from .dataset import is_pic
...@@ -38,7 +38,7 @@ class VOCDetection(Dataset): ...@@ -38,7 +38,7 @@ class VOCDetection(Dataset):
一半。 一半。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。 buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread' parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。 线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。 shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
""" """
...@@ -51,6 +51,7 @@ class VOCDetection(Dataset): ...@@ -51,6 +51,7 @@ class VOCDetection(Dataset):
buffer_size=100, buffer_size=100,
parallel_method='process', parallel_method='process',
shuffle=False): shuffle=False):
from pycocotools.coco import COCO
super(VOCDetection, self).__init__( super(VOCDetection, self).__init__(
transforms=transforms, transforms=transforms,
num_workers=num_workers, num_workers=num_workers,
...@@ -66,7 +67,7 @@ class VOCDetection(Dataset): ...@@ -66,7 +67,7 @@ class VOCDetection(Dataset):
annotations['categories'] = [] annotations['categories'] = []
annotations['annotations'] = [] annotations['annotations'] = []
cname2cid = {} cname2cid = OrderedDict()
label_id = 1 label_id = 1
with open(label_list, 'r', encoding=get_encoding(label_list)) as fr: with open(label_list, 'r', encoding=get_encoding(label_list)) as fr:
for line in fr.readlines(): for line in fr.readlines():
...@@ -145,7 +146,7 @@ class VOCDetection(Dataset): ...@@ -145,7 +146,7 @@ class VOCDetection(Dataset):
im_info = { im_info = {
'im_id': im_id, 'im_id': im_id,
'origin_shape': np.array([im_h, im_w]).astype('int32'), 'image_shape': np.array([im_h, im_w]).astype('int32'),
} }
label_info = { label_info = {
'is_crowd': is_crowd, 'is_crowd': is_crowd,
......
...@@ -24,6 +24,7 @@ import json ...@@ -24,6 +24,7 @@ import json
import functools import functools
import paddlex.utils.logging as logging import paddlex.utils.logging as logging
from paddlex.utils import seconds_to_hms from paddlex.utils import seconds_to_hms
from paddlex.utils.utils import EarlyStop
import paddlex import paddlex
from collections import OrderedDict from collections import OrderedDict
from os import path as osp from os import path as osp
...@@ -334,7 +335,9 @@ class BaseAPI: ...@@ -334,7 +335,9 @@ class BaseAPI:
save_interval_epochs=1, save_interval_epochs=1,
log_interval_steps=10, log_interval_steps=10,
save_dir='output', save_dir='output',
use_vdl=False): use_vdl=False,
early_stop=False,
early_stop_patience=5):
if not osp.isdir(save_dir): if not osp.isdir(save_dir):
if osp.exists(save_dir): if osp.exists(save_dir):
os.remove(save_dir) os.remove(save_dir)
...@@ -396,6 +399,9 @@ class BaseAPI: ...@@ -396,6 +399,9 @@ class BaseAPI:
train_step_component = OrderedDict() train_step_component = OrderedDict()
eval_component = OrderedDict() eval_component = OrderedDict()
thresh = 0.0001
if early_stop:
earlystop = EarlyStop(early_stop_patience, thresh)
best_accuracy_key = "" best_accuracy_key = ""
best_accuracy = -1.0 best_accuracy = -1.0
best_model_epoch = 1 best_model_epoch = 1
...@@ -507,3 +513,6 @@ class BaseAPI: ...@@ -507,3 +513,6 @@ class BaseAPI:
'Current evaluated best model in eval_dataset is epoch_{}, {}={}' 'Current evaluated best model in eval_dataset is epoch_{}, {}={}'
.format(best_model_epoch, best_accuracy_key, .format(best_model_epoch, best_accuracy_key,
best_accuracy)) best_accuracy))
if eval_dataset is not None and early_stop:
if earlystop(current_accuracy):
break
...@@ -110,7 +110,9 @@ class BaseClassifier(BaseAPI): ...@@ -110,7 +110,9 @@ class BaseClassifier(BaseAPI):
lr_decay_gamma=0.1, lr_decay_gamma=0.1,
use_vdl=False, use_vdl=False,
sensitivities_file=None, sensitivities_file=None,
eval_metric_loss=0.05): eval_metric_loss=0.05,
early_stop=False,
early_stop_patience=5):
"""训练。 """训练。
Args: Args:
...@@ -132,6 +134,9 @@ class BaseClassifier(BaseAPI): ...@@ -132,6 +134,9 @@ class BaseClassifier(BaseAPI):
sensitivities_file (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT', sensitivities_file (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',
则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。 则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
eval_metric_loss (float): 可容忍的精度损失。默认为0.05。 eval_metric_loss (float): 可容忍的精度损失。默认为0.05。
early_stop (bool): 是否使用提前终止训练策略。默认值为False。
early_stop_patience (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内
连续下降或持平,则终止训练。默认值为5。
Raises: Raises:
ValueError: 模型从inference model进行加载。 ValueError: 模型从inference model进行加载。
...@@ -166,7 +171,9 @@ class BaseClassifier(BaseAPI): ...@@ -166,7 +171,9 @@ class BaseClassifier(BaseAPI):
save_interval_epochs=save_interval_epochs, save_interval_epochs=save_interval_epochs,
log_interval_steps=log_interval_steps, log_interval_steps=log_interval_steps,
save_dir=save_dir, save_dir=save_dir,
use_vdl=use_vdl) use_vdl=use_vdl,
early_stop=early_stop,
early_stop_patience=early_stop_patience)
def evaluate(self, def evaluate(self,
eval_dataset, eval_dataset,
......
...@@ -232,7 +232,9 @@ class DeepLabv3p(BaseAPI): ...@@ -232,7 +232,9 @@ class DeepLabv3p(BaseAPI):
lr_decay_power=0.9, lr_decay_power=0.9,
use_vdl=False, use_vdl=False,
sensitivities_file=None, sensitivities_file=None,
eval_metric_loss=0.05): eval_metric_loss=0.05,
early_stop=False,
early_stop_patience=5):
"""训练。 """训练。
Args: Args:
...@@ -253,6 +255,9 @@ class DeepLabv3p(BaseAPI): ...@@ -253,6 +255,9 @@ class DeepLabv3p(BaseAPI):
sensitivities_file (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT', sensitivities_file (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',
则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。 则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
eval_metric_loss (float): 可容忍的精度损失。默认为0.05。 eval_metric_loss (float): 可容忍的精度损失。默认为0.05。
early_stop (bool): 是否使用提前终止训练策略。默认值为False。
early_stop_patience (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内
连续下降或持平,则终止训练。默认值为5。
Raises: Raises:
ValueError: 模型从inference model进行加载。 ValueError: 模型从inference model进行加载。
...@@ -289,7 +294,9 @@ class DeepLabv3p(BaseAPI): ...@@ -289,7 +294,9 @@ class DeepLabv3p(BaseAPI):
save_interval_epochs=save_interval_epochs, save_interval_epochs=save_interval_epochs,
log_interval_steps=log_interval_steps, log_interval_steps=log_interval_steps,
save_dir=save_dir, save_dir=save_dir,
use_vdl=use_vdl) use_vdl=use_vdl,
early_stop=early_stop,
early_stop_patience=early_stop_patience)
def evaluate(self, def evaluate(self,
eval_dataset, eval_dataset,
......
...@@ -32,7 +32,7 @@ class FasterRCNN(BaseAPI): ...@@ -32,7 +32,7 @@ class FasterRCNN(BaseAPI):
Args: Args:
num_classes (int): 包含了背景类的类别数。默认为81。 num_classes (int): 包含了背景类的类别数。默认为81。
backbone (str): FasterRCNN的backbone网络,取值范围为['ResNet18', 'ResNet50', backbone (str): FasterRCNN的backbone网络,取值范围为['ResNet18', 'ResNet50',
'ResNet50vd', 'ResNet101', 'ResNet101vd']。默认为'ResNet50'。 'ResNet50_vd', 'ResNet101', 'ResNet101_vd']。默认为'ResNet50'。
with_fpn (bool): 是否使用FPN结构。默认为True。 with_fpn (bool): 是否使用FPN结构。默认为True。
aspect_ratios (list): 生成anchor高宽比的可选值。默认为[0.5, 1.0, 2.0]。 aspect_ratios (list): 生成anchor高宽比的可选值。默认为[0.5, 1.0, 2.0]。
anchor_sizes (list): 生成anchor大小的可选值。默认为[32, 64, 128, 256, 512]。 anchor_sizes (list): 生成anchor大小的可选值。默认为[32, 64, 128, 256, 512]。
...@@ -47,7 +47,7 @@ class FasterRCNN(BaseAPI): ...@@ -47,7 +47,7 @@ class FasterRCNN(BaseAPI):
self.init_params = locals() self.init_params = locals()
super(FasterRCNN, self).__init__('detector') super(FasterRCNN, self).__init__('detector')
backbones = [ backbones = [
'ResNet18', 'ResNet50', 'ResNet50vd', 'ResNet101', 'ResNet101vd' 'ResNet18', 'ResNet50', 'ResNet50_vd', 'ResNet101', 'ResNet101_vd'
] ]
assert backbone in backbones, "backbone should be one of {}".format( assert backbone in backbones, "backbone should be one of {}".format(
backbones) backbones)
...@@ -67,7 +67,7 @@ class FasterRCNN(BaseAPI): ...@@ -67,7 +67,7 @@ class FasterRCNN(BaseAPI):
elif backbone_name == 'ResNet50': elif backbone_name == 'ResNet50':
layers = 50 layers = 50
variant = 'b' variant = 'b'
elif backbone_name == 'ResNet50vd': elif backbone_name == 'ResNet50_vd':
layers = 50 layers = 50
variant = 'd' variant = 'd'
norm_type = 'affine_channel' norm_type = 'affine_channel'
...@@ -75,7 +75,7 @@ class FasterRCNN(BaseAPI): ...@@ -75,7 +75,7 @@ class FasterRCNN(BaseAPI):
layers = 101 layers = 101
variant = 'b' variant = 'b'
norm_type = 'affine_channel' norm_type = 'affine_channel'
elif backbone_name == 'ResNet101vd': elif backbone_name == 'ResNet101_vd':
layers = 101 layers = 101
variant = 'd' variant = 'd'
norm_type = 'affine_channel' norm_type = 'affine_channel'
...@@ -165,7 +165,9 @@ class FasterRCNN(BaseAPI): ...@@ -165,7 +165,9 @@ class FasterRCNN(BaseAPI):
lr_decay_epochs=[8, 11], lr_decay_epochs=[8, 11],
lr_decay_gamma=0.1, lr_decay_gamma=0.1,
metric=None, metric=None,
use_vdl=False): use_vdl=False,
early_stop=False,
early_stop_patience=5):
"""训练。 """训练。
Args: Args:
...@@ -178,7 +180,7 @@ class FasterRCNN(BaseAPI): ...@@ -178,7 +180,7 @@ class FasterRCNN(BaseAPI):
log_interval_steps (int): 训练日志输出间隔(单位:迭代次数)。默认为20。 log_interval_steps (int): 训练日志输出间隔(单位:迭代次数)。默认为20。
save_dir (str): 模型保存路径。默认值为'output'。 save_dir (str): 模型保存路径。默认值为'output'。
pretrain_weights (str): 若指定为路径时,则加载路径下预训练模型;若为字符串'IMAGENET', pretrain_weights (str): 若指定为路径时,则加载路径下预训练模型;若为字符串'IMAGENET',
则自动下载在ImageNet图片数据上预训练的模型权重;若为None,则不使用预训练模型。默认为None 则自动下载在ImageNet图片数据上预训练的模型权重;若为None,则不使用预训练模型。默认为'IMAGENET'
optimizer (paddle.fluid.optimizer): 优化器。当该参数为None时,使用默认优化器: optimizer (paddle.fluid.optimizer): 优化器。当该参数为None时,使用默认优化器:
fluid.layers.piecewise_decay衰减策略,fluid.optimizer.Momentum优化方法。 fluid.layers.piecewise_decay衰减策略,fluid.optimizer.Momentum优化方法。
learning_rate (float): 默认优化器的初始学习率。默认为0.0025。 learning_rate (float): 默认优化器的初始学习率。默认为0.0025。
...@@ -188,6 +190,9 @@ class FasterRCNN(BaseAPI): ...@@ -188,6 +190,9 @@ class FasterRCNN(BaseAPI):
lr_decay_gamma (float): 默认优化器的学习率衰减率。默认为0.1。 lr_decay_gamma (float): 默认优化器的学习率衰减率。默认为0.1。
metric (bool): 训练过程中评估的方式,取值范围为['COCO', 'VOC']。默认值为None。 metric (bool): 训练过程中评估的方式,取值范围为['COCO', 'VOC']。默认值为None。
use_vdl (bool): 是否使用VisualDL进行可视化。默认值为False。 use_vdl (bool): 是否使用VisualDL进行可视化。默认值为False。
early_stop (bool): 是否使用提前终止训练策略。默认值为False。
early_stop_patience (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内
连续下降或持平,则终止训练。默认值为5。
Raises: Raises:
ValueError: 评估类型不在指定列表中。 ValueError: 评估类型不在指定列表中。
...@@ -196,11 +201,12 @@ class FasterRCNN(BaseAPI): ...@@ -196,11 +201,12 @@ class FasterRCNN(BaseAPI):
if metric is None: if metric is None:
if isinstance(train_dataset, paddlex.datasets.CocoDetection): if isinstance(train_dataset, paddlex.datasets.CocoDetection):
metric = 'COCO' metric = 'COCO'
elif isinstance(train_dataset, paddlex.datasets.VOCDetection): elif isinstance(train_dataset, paddlex.datasets.VOCDetection) or \
isinstance(train_dataset, paddlex.datasets.EasyDataDet):
metric = 'VOC' metric = 'VOC'
else: else:
raise ValueError( raise ValueError(
"train_dataset should be datasets.VOCDetection or datasets.COCODetection." "train_dataset should be datasets.VOCDetection or datasets.COCODetection or datasets.EasyDataDet."
) )
assert metric in ['COCO', 'VOC'], "Metric only support 'VOC' or 'COCO'" assert metric in ['COCO', 'VOC'], "Metric only support 'VOC' or 'COCO'"
self.metric = metric self.metric = metric
...@@ -235,7 +241,9 @@ class FasterRCNN(BaseAPI): ...@@ -235,7 +241,9 @@ class FasterRCNN(BaseAPI):
save_interval_epochs=save_interval_epochs, save_interval_epochs=save_interval_epochs,
log_interval_steps=log_interval_steps, log_interval_steps=log_interval_steps,
save_dir=save_dir, save_dir=save_dir,
use_vdl=use_vdl) use_vdl=use_vdl,
early_stop=early_stop,
early_stop_patience=early_stop_patience)
def evaluate(self, def evaluate(self,
eval_dataset, eval_dataset,
......
...@@ -32,7 +32,7 @@ class MaskRCNN(FasterRCNN): ...@@ -32,7 +32,7 @@ class MaskRCNN(FasterRCNN):
Args: Args:
num_classes (int): 包含了背景类的类别数。默认为81。 num_classes (int): 包含了背景类的类别数。默认为81。
backbone (str): MaskRCNN的backbone网络,取值范围为['ResNet18', 'ResNet50', backbone (str): MaskRCNN的backbone网络,取值范围为['ResNet18', 'ResNet50',
'ResNet50vd', 'ResNet101', 'ResNet101vd']。默认为'ResNet50'。 'ResNet50_vd', 'ResNet101', 'ResNet101_vd']。默认为'ResNet50'。
with_fpn (bool): 是否使用FPN结构。默认为True。 with_fpn (bool): 是否使用FPN结构。默认为True。
aspect_ratios (list): 生成anchor高宽比的可选值。默认为[0.5, 1.0, 2.0]。 aspect_ratios (list): 生成anchor高宽比的可选值。默认为[0.5, 1.0, 2.0]。
anchor_sizes (list): 生成anchor大小的可选值。默认为[32, 64, 128, 256, 512]。 anchor_sizes (list): 生成anchor大小的可选值。默认为[32, 64, 128, 256, 512]。
...@@ -46,7 +46,7 @@ class MaskRCNN(FasterRCNN): ...@@ -46,7 +46,7 @@ class MaskRCNN(FasterRCNN):
anchor_sizes=[32, 64, 128, 256, 512]): anchor_sizes=[32, 64, 128, 256, 512]):
self.init_params = locals() self.init_params = locals()
backbones = [ backbones = [
'ResNet18', 'ResNet50', 'ResNet50vd', 'ResNet101', 'ResNet101vd' 'ResNet18', 'ResNet50', 'ResNet50_vd', 'ResNet101', 'ResNet101_vd'
] ]
assert backbone in backbones, "backbone should be one of {}".format( assert backbone in backbones, "backbone should be one of {}".format(
backbones) backbones)
...@@ -130,7 +130,9 @@ class MaskRCNN(FasterRCNN): ...@@ -130,7 +130,9 @@ class MaskRCNN(FasterRCNN):
lr_decay_epochs=[8, 11], lr_decay_epochs=[8, 11],
lr_decay_gamma=0.1, lr_decay_gamma=0.1,
metric=None, metric=None,
use_vdl=False): use_vdl=False,
early_stop=False,
early_stop_patience=5):
"""训练。 """训练。
Args: Args:
...@@ -153,17 +155,21 @@ class MaskRCNN(FasterRCNN): ...@@ -153,17 +155,21 @@ class MaskRCNN(FasterRCNN):
lr_decay_gamma (float): 默认优化器的学习率衰减率。默认为0.1。 lr_decay_gamma (float): 默认优化器的学习率衰减率。默认为0.1。
metric (bool): 训练过程中评估的方式,取值范围为['COCO', 'VOC']。 metric (bool): 训练过程中评估的方式,取值范围为['COCO', 'VOC']。
use_vdl (bool): 是否使用VisualDL进行可视化。默认值为False。 use_vdl (bool): 是否使用VisualDL进行可视化。默认值为False。
early_stop (bool): 是否使用提前终止训练策略。默认值为False。
early_stop_patience (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内
连续下降或持平,则终止训练。默认值为5。
Raises: Raises:
ValueError: 评估类型不在指定列表中。 ValueError: 评估类型不在指定列表中。
ValueError: 模型从inference model进行加载。 ValueError: 模型从inference model进行加载。
""" """
if metric is None: if metric is None:
if isinstance(train_dataset, paddlex.datasets.CocoDetection): if isinstance(train_dataset, paddlex.datasets.CocoDetection) or \
isinstance(train_dataset, paddlex.datasets.EasyDataDet):
metric = 'COCO' metric = 'COCO'
else: else:
raise Exception( raise Exception(
"train_dataset should be datasets.COCODetection.") "train_dataset should be datasets.COCODetection or datasets.EasyDataDet.")
assert metric in ['COCO', 'VOC'], "Metric only support 'VOC' or 'COCO'" assert metric in ['COCO', 'VOC'], "Metric only support 'VOC' or 'COCO'"
self.metric = metric self.metric = metric
if not self.trainable: if not self.trainable:
...@@ -201,7 +207,9 @@ class MaskRCNN(FasterRCNN): ...@@ -201,7 +207,9 @@ class MaskRCNN(FasterRCNN):
save_interval_epochs=save_interval_epochs, save_interval_epochs=save_interval_epochs,
log_interval_steps=log_interval_steps, log_interval_steps=log_interval_steps,
save_dir=save_dir, save_dir=save_dir,
use_vdl=use_vdl) use_vdl=use_vdl,
early_stop=early_stop,
early_stop_patience=early_stop_patience)
def evaluate(self, def evaluate(self,
eval_dataset, eval_dataset,
......
...@@ -15,9 +15,6 @@ ...@@ -15,9 +15,6 @@
import os.path as osp import os.path as osp
import tqdm import tqdm
import numpy as np import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from .prune import cal_model_size from .prune import cal_model_size
from paddleslim.prune import load_sensitivities from paddleslim.prune import load_sensitivities
...@@ -30,6 +27,10 @@ def visualize(model, sensitivities_file, save_dir='./'): ...@@ -30,6 +27,10 @@ def visualize(model, sensitivities_file, save_dir='./'):
model (paddlex.cv.models): paddlex中的模型。 model (paddlex.cv.models): paddlex中的模型。
sensitivities_file (str): 敏感度文件存储路径。 sensitivities_file (str): 敏感度文件存储路径。
""" """
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
program = model.test_prog program = model.test_prog
place = model.places[0] place = model.places[0]
fig = plt.figure() fig = plt.figure()
......
...@@ -119,7 +119,9 @@ class UNet(DeepLabv3p): ...@@ -119,7 +119,9 @@ class UNet(DeepLabv3p):
lr_decay_power=0.9, lr_decay_power=0.9,
use_vdl=False, use_vdl=False,
sensitivities_file=None, sensitivities_file=None,
eval_metric_loss=0.05): eval_metric_loss=0.05,
early_stop=False,
early_stop_patience=5):
"""训练。 """训练。
Args: Args:
...@@ -140,12 +142,17 @@ class UNet(DeepLabv3p): ...@@ -140,12 +142,17 @@ class UNet(DeepLabv3p):
sensitivities_file (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT', sensitivities_file (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',
则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。 则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
eval_metric_loss (float): 可容忍的精度损失。默认为0.05。 eval_metric_loss (float): 可容忍的精度损失。默认为0.05。
early_stop (bool): 是否使用提前终止训练策略。默认值为False。
early_stop_patience (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内
连续下降或持平,则终止训练。默认值为5。
Raises: Raises:
ValueError: 模型从inference model进行加载。 ValueError: 模型从inference model进行加载。
""" """
return super(UNet, self).train( return super(
num_epochs, train_dataset, train_batch_size, eval_dataset, UNet,
save_interval_epochs, log_interval_steps, save_dir, self).train(num_epochs, train_dataset, train_batch_size,
pretrain_weights, optimizer, learning_rate, lr_decay_power, eval_dataset, save_interval_epochs, log_interval_steps,
use_vdl, sensitivities_file, eval_metric_loss) save_dir, pretrain_weights, optimizer, learning_rate,
lr_decay_power, use_vdl, sensitivities_file,
eval_metric_loss, early_stop, early_stop_patience)
...@@ -14,10 +14,8 @@ ...@@ -14,10 +14,8 @@
import os import os
import cv2 import cv2
import colorsys
import numpy as np import numpy as np
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw
import paddlex.utils.logging as logging import paddlex.utils.logging as logging
from .detection_eval import fixed_linspace, backup_linspace, loadRes from .detection_eval import fixed_linspace, backup_linspace, loadRes
...@@ -28,13 +26,13 @@ def visualize_detection(image, result, threshold=0.5, save_dir='./'): ...@@ -28,13 +26,13 @@ def visualize_detection(image, result, threshold=0.5, save_dir='./'):
""" """
image_name = os.path.split(image)[-1] image_name = os.path.split(image)[-1]
image = Image.open(image).convert('RGB') image = cv2.imread(image)
image = draw_bbox_mask(image, result, threshold=threshold) image = draw_bbox_mask(image, result, threshold=threshold)
if save_dir is not None: if save_dir is not None:
if not os.path.exists(save_dir): if not os.path.exists(save_dir):
os.makedirs(save_dir) os.makedirs(save_dir)
out_path = os.path.join(save_dir, 'visualize_{}'.format(image_name)) out_path = os.path.join(save_dir, 'visualize_{}'.format(image_name))
image.save(out_path, quality=95) cv2.imwrite(out_path, image)
logging.info('The visualized result is saved as {}'.format(out_path)) logging.info('The visualized result is saved as {}'.format(out_path))
else: else:
return image return image
...@@ -123,49 +121,163 @@ def clip_bbox(bbox): ...@@ -123,49 +121,163 @@ def clip_bbox(bbox):
return xmin, ymin, xmax, ymax return xmin, ymin, xmax, ymax
def draw_bbox_mask(image, results, threshold=0.5, alpha=0.7): def draw_bbox_mask(image, results, threshold=0.5):
import matplotlib
matplotlib.use('Agg')
import matplotlib as mpl
import matplotlib.figure as mplfigure
import matplotlib.colors as mplc
from matplotlib.backends.backend_agg import FigureCanvasAgg
# refer to https://github.com/facebookresearch/detectron2/blob/master/detectron2/utils/visualizer.py
def _change_color_brightness(color, brightness_factor):
assert brightness_factor >= -1.0 and brightness_factor <= 1.0
color = mplc.to_rgb(color)
polygon_color = colorsys.rgb_to_hls(*mplc.to_rgb(color))
modified_lightness = polygon_color[1] + (
brightness_factor * polygon_color[1])
modified_lightness = 0.0 if modified_lightness < 0.0 else modified_lightness
modified_lightness = 1.0 if modified_lightness > 1.0 else modified_lightness
modified_color = colorsys.hls_to_rgb(
polygon_color[0], modified_lightness, polygon_color[2])
return modified_color
_SMALL_OBJECT_AREA_THRESH = 1000
# setup figure
width, height = image.shape[1], image.shape[0]
scale = 1
fig = mplfigure.Figure(frameon=False)
dpi = fig.get_dpi()
fig.set_size_inches(
(width * scale + 1e-2) / dpi,
(height * scale + 1e-2) / dpi,
)
canvas = FigureCanvasAgg(fig)
ax = fig.add_axes([0.0, 0.0, 1.0, 1.0])
ax.axis("off")
ax.set_xlim(0.0, width)
ax.set_ylim(height)
default_font_size = max(np.sqrt(height * width) // 90, 10 // scale)
linewidth = max(default_font_size / 4, 1)
labels = list() labels = list()
for dt in np.array(results): for dt in np.array(results):
if dt['category'] not in labels: if dt['category'] not in labels:
labels.append(dt['category']) labels.append(dt['category'])
color_map = get_color_map_list(len(labels)) color_map = get_color_map_list(256)
keep_results = []
areas = []
for dt in np.array(results): for dt in np.array(results):
cname, bbox, score = dt['category'], dt['bbox'], dt['score'] cname, bbox, score = dt['category'], dt['bbox'], dt['score']
if score < threshold: if score < threshold:
continue continue
keep_results.append(dt)
areas.append(bbox[2] * bbox[3])
areas = np.asarray(areas)
sorted_idxs = np.argsort(-areas).tolist()
keep_results = [keep_results[k]
for k in sorted_idxs] if len(keep_results) > 0 else []
for dt in np.array(keep_results):
cname, bbox, score = dt['category'], dt['bbox'], dt['score']
xmin, ymin, w, h = bbox xmin, ymin, w, h = bbox
xmax = xmin + w xmax = xmin + w
ymax = ymin + h ymax = ymin + h
color = tuple(color_map[labels.index(cname)]) color = tuple(color_map[labels.index(cname) + 2])
color = [c / 255. for c in color]
# draw bbox # draw bbox
draw = ImageDraw.Draw(image) ax.add_patch(
draw.line([(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin), mpl.patches.Rectangle(
(xmin, ymin)], (xmin, ymin),
width=2, w,
fill=color) h,
fill=False,
# draw label edgecolor=color,
text = "{} {:.2f}".format(cname, score) linewidth=linewidth * scale,
tw, th = draw.textsize(text) alpha=0.8,
draw.rectangle([(xmin + 1, ymin - th), (xmin + tw + 1, ymin)], linestyle="-",
fill=color) ))
draw.text((xmin + 1, ymin - th), text, fill=(255, 255, 255))
# draw mask # draw mask
if 'mask' in dt: if 'mask' in dt:
mask = dt['mask'] mask = dt['mask']
color_mask = np.array(color_map[labels.index( mask = np.ascontiguousarray(mask)
dt['category'])]).astype('float32') res = cv2.findContours(
img_array = np.array(image).astype('float32') mask.astype("uint8"), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
idx = np.nonzero(mask) hierarchy = res[-1]
img_array[idx[0], idx[1], :] *= 1.0 - alpha alpha = 0.5
img_array[idx[0], idx[1], :] += alpha * color_mask if hierarchy is not None:
image = Image.fromarray(img_array.astype('uint8')) has_holes = (hierarchy.reshape(-1, 4)[:, 3] >= 0).sum() > 0
return image res = res[-2]
res = [x.flatten() for x in res]
res = [x for x in res if len(x) >= 6]
for segment in res:
segment = segment.reshape(-1, 2)
edge_color = mplc.to_rgb(color) + (1, )
polygon = mpl.patches.Polygon(
segment,
fill=True,
facecolor=mplc.to_rgb(color) + (alpha, ),
edgecolor=edge_color,
linewidth=max(default_font_size // 15 * scale, 1),
)
ax.add_patch(polygon)
# draw label
text_pos = (xmin, ymin)
horiz_align = "left"
instance_area = w * h
if (instance_area < _SMALL_OBJECT_AREA_THRESH * scale
or h < 40 * scale):
if ymin >= height - 5:
text_pos = (xmin, ymin)
else:
text_pos = (xmin, ymax)
height_ratio = h / np.sqrt(height * width)
font_size = (np.clip((height_ratio - 0.02) / 0.08 + 1, 1.2, 2) * 0.5 *
default_font_size)
text = "{} {:.2f}".format(cname, score)
color = np.maximum(list(mplc.to_rgb(color)), 0.2)
color[np.argmax(color)] = max(0.8, np.max(color))
color = _change_color_brightness(color, brightness_factor=0.7)
ax.text(
text_pos[0],
text_pos[1],
text,
size=font_size * scale,
family="sans-serif",
bbox={
"facecolor": "black",
"alpha": 0.8,
"pad": 0.7,
"edgecolor": "none"
},
verticalalignment="top",
horizontalalignment=horiz_align,
color=color,
zorder=10,
rotation=0,
)
s, (width, height) = canvas.print_to_buffer()
buffer = np.frombuffer(s, dtype="uint8")
img_rgba = buffer.reshape(height, width, 4)
rgb, alpha = np.split(img_rgba, [3], axis=2)
try:
import numexpr as ne
visualized_image = ne.evaluate(
"image * (1 - alpha / 255.0) + rgb * (alpha / 255.0)")
except ImportError:
alpha = alpha.astype("float32") / 255.0
visualized_image = image * (1 - alpha) + rgb * alpha
visualized_image = visualized_image.astype("uint8")
return visualized_image
def draw_pr_curve(eval_details_file=None, def draw_pr_curve(eval_details_file=None,
...@@ -190,6 +302,9 @@ def draw_pr_curve(eval_details_file=None, ...@@ -190,6 +302,9 @@ def draw_pr_curve(eval_details_file=None,
raise Exception("There is no predicted bbox.") raise Exception("There is no predicted bbox.")
if pred_mask is not None and len(pred_mask) == 0: if pred_mask is not None and len(pred_mask) == 0:
raise Exception("There is no predicted mask.") raise Exception("There is no predicted mask.")
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from pycocotools.coco import COCO from pycocotools.coco import COCO
from pycocotools.cocoeval import COCOeval from pycocotools.cocoeval import COCOeval
coco = COCO() coco = COCO()
......
...@@ -164,7 +164,9 @@ class YOLOv3(BaseAPI): ...@@ -164,7 +164,9 @@ class YOLOv3(BaseAPI):
metric=None, metric=None,
use_vdl=False, use_vdl=False,
sensitivities_file=None, sensitivities_file=None,
eval_metric_loss=0.05): eval_metric_loss=0.05,
early_stop=False,
early_stop_patience=5):
"""训练。 """训练。
Args: Args:
...@@ -177,7 +179,7 @@ class YOLOv3(BaseAPI): ...@@ -177,7 +179,7 @@ class YOLOv3(BaseAPI):
log_interval_steps (int): 训练日志输出间隔(单位:迭代次数)。默认为10。 log_interval_steps (int): 训练日志输出间隔(单位:迭代次数)。默认为10。
save_dir (str): 模型保存路径。默认值为'output'。 save_dir (str): 模型保存路径。默认值为'output'。
pretrain_weights (str): 若指定为路径时,则加载路径下预训练模型;若为字符串'IMAGENET', pretrain_weights (str): 若指定为路径时,则加载路径下预训练模型;若为字符串'IMAGENET',
则自动下载在ImageNet图片数据上预训练的模型权重;若为None,则不使用预训练模型。默认为None 则自动下载在ImageNet图片数据上预训练的模型权重;若为None,则不使用预训练模型。默认为'IMAGENET'
optimizer (paddle.fluid.optimizer): 优化器。当该参数为None时,使用默认优化器: optimizer (paddle.fluid.optimizer): 优化器。当该参数为None时,使用默认优化器:
fluid.layers.piecewise_decay衰减策略,fluid.optimizer.Momentum优化方法。 fluid.layers.piecewise_decay衰减策略,fluid.optimizer.Momentum优化方法。
learning_rate (float): 默认优化器的学习率。默认为1.0/8000。 learning_rate (float): 默认优化器的学习率。默认为1.0/8000。
...@@ -190,6 +192,9 @@ class YOLOv3(BaseAPI): ...@@ -190,6 +192,9 @@ class YOLOv3(BaseAPI):
sensitivities_file (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT', sensitivities_file (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',
则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。 则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
eval_metric_loss (float): 可容忍的精度损失。默认为0.05。 eval_metric_loss (float): 可容忍的精度损失。默认为0.05。
early_stop (bool): 是否使用提前终止训练策略。默认值为False。
early_stop_patience (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内
连续下降或持平,则终止训练。默认值为5。
Raises: Raises:
ValueError: 评估类型不在指定列表中。 ValueError: 评估类型不在指定列表中。
...@@ -200,11 +205,12 @@ class YOLOv3(BaseAPI): ...@@ -200,11 +205,12 @@ class YOLOv3(BaseAPI):
if metric is None: if metric is None:
if isinstance(train_dataset, paddlex.datasets.CocoDetection): if isinstance(train_dataset, paddlex.datasets.CocoDetection):
metric = 'COCO' metric = 'COCO'
elif isinstance(train_dataset, paddlex.datasets.VOCDetection): elif isinstance(train_dataset, paddlex.datasets.VOCDetection) or \
isinstance(train_dataset, paddlex.datasets.EasyDataDet):
metric = 'VOC' metric = 'VOC'
else: else:
raise ValueError( raise ValueError(
"train_dataset should be datasets.VOCDetection or datasets.COCODetection." "train_dataset should be datasets.VOCDetection or datasets.COCODetection or datasets.EasyDataDet."
) )
assert metric in ['COCO', 'VOC'], "Metric only support 'VOC' or 'COCO'" assert metric in ['COCO', 'VOC'], "Metric only support 'VOC' or 'COCO'"
self.metric = metric self.metric = metric
...@@ -240,7 +246,9 @@ class YOLOv3(BaseAPI): ...@@ -240,7 +246,9 @@ class YOLOv3(BaseAPI):
save_interval_epochs=save_interval_epochs, save_interval_epochs=save_interval_epochs,
log_interval_steps=log_interval_steps, log_interval_steps=log_interval_steps,
save_dir=save_dir, save_dir=save_dir,
use_vdl=use_vdl) use_vdl=use_vdl,
early_stop=early_stop,
early_stop_patience=early_stop_patience)
def evaluate(self, def evaluate(self,
eval_dataset, eval_dataset,
......
...@@ -58,8 +58,8 @@ class Compose: ...@@ -58,8 +58,8 @@ class Compose:
im (str/np.ndarray): 图像路径/图像np.ndarray数据。 im (str/np.ndarray): 图像路径/图像np.ndarray数据。
im_info (dict): 存储与图像相关的信息,dict中的字段如下: im_info (dict): 存储与图像相关的信息,dict中的字段如下:
- im_id (np.ndarray): 图像序列号,形状为(1,)。 - im_id (np.ndarray): 图像序列号,形状为(1,)。
- origin_shape (np.ndarray): 图像原始大小,形状为(2,), - image_shape (np.ndarray): 图像原始大小,形状为(2,),
origin_shape[0]为高,origin_shape[1]为宽。 image_shape[0]为高,image_shape[1]为宽。
- mixup (list): list为[im, im_info, label_info],分别对应 - mixup (list): list为[im, im_info, label_info],分别对应
与当前图像进行mixup的图像np.ndarray数据、图像相关信息、标注框相关信息; 与当前图像进行mixup的图像np.ndarray数据、图像相关信息、标注框相关信息;
注意,当前epoch若无需进行mixup,则无该字段。 注意,当前epoch若无需进行mixup,则无该字段。
...@@ -93,9 +93,6 @@ class Compose: ...@@ -93,9 +93,6 @@ class Compose:
# make default im_info with [h, w, 1] # make default im_info with [h, w, 1]
im_info['im_resize_info'] = np.array( im_info['im_resize_info'] = np.array(
[im.shape[0], im.shape[1], 1.], dtype=np.float32) [im.shape[0], im.shape[1], 1.], dtype=np.float32)
# copy augment_shape from origin_shape
im_info['augment_shape'] = np.array([im.shape[0],
im.shape[1]]).astype('int32')
if not self.use_mixup: if not self.use_mixup:
if 'mixup' in im_info: if 'mixup' in im_info:
del im_info['mixup'] del im_info['mixup']
...@@ -407,16 +404,13 @@ class RandomHorizontalFlip: ...@@ -407,16 +404,13 @@ class RandomHorizontalFlip:
raise TypeError( raise TypeError(
'Cannot do RandomHorizontalFlip! ' + 'Cannot do RandomHorizontalFlip! ' +
'Becasuse the im_info and label_info can not be None!') 'Becasuse the im_info and label_info can not be None!')
if 'augment_shape' not in im_info:
raise TypeError('Cannot do RandomHorizontalFlip! ' + \
'Becasuse augment_shape is not in im_info!')
if 'gt_bbox' not in label_info: if 'gt_bbox' not in label_info:
raise TypeError('Cannot do RandomHorizontalFlip! ' + \ raise TypeError('Cannot do RandomHorizontalFlip! ' + \
'Becasuse gt_bbox is not in label_info!') 'Becasuse gt_bbox is not in label_info!')
augment_shape = im_info['augment_shape'] image_shape = im_info['image_shape']
gt_bbox = label_info['gt_bbox'] gt_bbox = label_info['gt_bbox']
height = augment_shape[0] height = image_shape[0]
width = augment_shape[1] width = image_shape[1]
if np.random.uniform(0, 1) < self.prob: if np.random.uniform(0, 1) < self.prob:
im = horizontal_flip(im) im = horizontal_flip(im)
...@@ -587,7 +581,7 @@ class MixupImage: ...@@ -587,7 +581,7 @@ class MixupImage:
(2)拼接原图像标注框和mixup图像标注框。 (2)拼接原图像标注框和mixup图像标注框。
(3)拼接原图像标注框类别和mixup图像标注框类别。 (3)拼接原图像标注框类别和mixup图像标注框类别。
(4)原图像标注框混合得分乘以factor,mixup图像标注框混合得分乘以(1-factor),叠加2个结果。 (4)原图像标注框混合得分乘以factor,mixup图像标注框混合得分乘以(1-factor),叠加2个结果。
3. 更新im_info中的augment_shape信息。 3. 更新im_info中的image_shape信息。
Args: Args:
alpha (float): 随机beta分布的下限。默认为1.5。 alpha (float): 随机beta分布的下限。默认为1.5。
...@@ -630,7 +624,7 @@ class MixupImage: ...@@ -630,7 +624,7 @@ class MixupImage:
当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、 当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、
存储与标注框相关信息的字典。 存储与标注框相关信息的字典。
其中,im_info更新字段为: 其中,im_info更新字段为:
- augment_shape (np.ndarray): mixup后的图像高、宽二者组成的np.ndarray,形状为(2,)。 - image_shape (np.ndarray): mixup后的图像高、宽二者组成的np.ndarray,形状为(2,)。
im_info删除的字段: im_info删除的字段:
- mixup (list): 与当前字段进行mixup的图像相关信息。 - mixup (list): 与当前字段进行mixup的图像相关信息。
label_info更新字段为: label_info更新字段为:
...@@ -694,7 +688,7 @@ class MixupImage: ...@@ -694,7 +688,7 @@ class MixupImage:
label_info['gt_score'] = gt_score label_info['gt_score'] = gt_score
label_info['gt_class'] = gt_class label_info['gt_class'] = gt_class
label_info['is_crowd'] = is_crowd label_info['is_crowd'] = is_crowd
im_info['augment_shape'] = np.array([im.shape[0], im_info['image_shape'] = np.array([im.shape[0],
im.shape[1]]).astype('int32') im.shape[1]]).astype('int32')
im_info.pop('mixup') im_info.pop('mixup')
if label_info is None: if label_info is None:
...@@ -741,7 +735,7 @@ class RandomExpand: ...@@ -741,7 +735,7 @@ class RandomExpand:
当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、 当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、
存储与标注框相关信息的字典。 存储与标注框相关信息的字典。
其中,im_info更新字段为: 其中,im_info更新字段为:
- augment_shape (np.ndarray): 扩张后的图像高、宽二者组成的np.ndarray,形状为(2,)。 - image_shape (np.ndarray): 扩张后的图像高、宽二者组成的np.ndarray,形状为(2,)。
label_info更新字段为: label_info更新字段为:
- gt_bbox (np.ndarray): 随机扩张后真实标注框坐标,形状为(n, 4), - gt_bbox (np.ndarray): 随机扩张后真实标注框坐标,形状为(n, 4),
其中n代表真实标注框的个数。 其中n代表真实标注框的个数。
...@@ -754,9 +748,6 @@ class RandomExpand: ...@@ -754,9 +748,6 @@ class RandomExpand:
raise TypeError( raise TypeError(
'Cannot do RandomExpand! ' + 'Cannot do RandomExpand! ' +
'Becasuse the im_info and label_info can not be None!') 'Becasuse the im_info and label_info can not be None!')
if 'augment_shape' not in im_info:
raise TypeError('Cannot do RandomExpand! ' + \
'Becasuse augment_shape is not in im_info!')
if 'gt_bbox' not in label_info or \ if 'gt_bbox' not in label_info or \
'gt_class' not in label_info: 'gt_class' not in label_info:
raise TypeError('Cannot do RandomExpand! ' + \ raise TypeError('Cannot do RandomExpand! ' + \
...@@ -764,9 +755,9 @@ class RandomExpand: ...@@ -764,9 +755,9 @@ class RandomExpand:
if np.random.uniform(0., 1.) < self.prob: if np.random.uniform(0., 1.) < self.prob:
return (im, im_info, label_info) return (im, im_info, label_info)
augment_shape = im_info['augment_shape'] image_shape = im_info['image_shape']
height = int(augment_shape[0]) height = int(image_shape[0])
width = int(augment_shape[1]) width = int(image_shape[1])
expand_ratio = np.random.uniform(1., self.ratio) expand_ratio = np.random.uniform(1., self.ratio)
h = int(height * expand_ratio) h = int(height * expand_ratio)
...@@ -779,7 +770,7 @@ class RandomExpand: ...@@ -779,7 +770,7 @@ class RandomExpand:
canvas *= np.array(self.fill_value, dtype=np.float32) canvas *= np.array(self.fill_value, dtype=np.float32)
canvas[y:y + height, x:x + width, :] = im canvas[y:y + height, x:x + width, :] = im
im_info['augment_shape'] = np.array([h, w]).astype('int32') im_info['image_shape'] = np.array([h, w]).astype('int32')
if 'gt_bbox' in label_info and len(label_info['gt_bbox']) > 0: if 'gt_bbox' in label_info and len(label_info['gt_bbox']) > 0:
label_info['gt_bbox'] += np.array([x, y] * 2, dtype=np.float32) label_info['gt_bbox'] += np.array([x, y] * 2, dtype=np.float32)
if 'gt_poly' in label_info and len(label_info['gt_poly']) > 0: if 'gt_poly' in label_info and len(label_info['gt_poly']) > 0:
...@@ -835,7 +826,9 @@ class RandomCrop: ...@@ -835,7 +826,9 @@ class RandomCrop:
tuple: 当label_info为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典; tuple: 当label_info为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、 当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、
存储与标注框相关信息的字典。 存储与标注框相关信息的字典。
其中,label_info更新字段为: 其中,im_info更新字段为:
- image_shape (np.ndarray): 扩裁剪的图像高、宽二者组成的np.ndarray,形状为(2,)。
label_info更新字段为:
- gt_bbox (np.ndarray): 随机裁剪后真实标注框坐标,形状为(n, 4), - gt_bbox (np.ndarray): 随机裁剪后真实标注框坐标,形状为(n, 4),
其中n代表真实标注框的个数。 其中n代表真实标注框的个数。
- gt_class (np.ndarray): 随机裁剪后每个真实标注框对应的类别序号,形状为(n, 1), - gt_class (np.ndarray): 随机裁剪后每个真实标注框对应的类别序号,形状为(n, 1),
...@@ -850,9 +843,6 @@ class RandomCrop: ...@@ -850,9 +843,6 @@ class RandomCrop:
raise TypeError( raise TypeError(
'Cannot do RandomCrop! ' + 'Cannot do RandomCrop! ' +
'Becasuse the im_info and label_info can not be None!') 'Becasuse the im_info and label_info can not be None!')
if 'augment_shape' not in im_info:
raise TypeError('Cannot do RandomCrop! ' + \
'Becasuse augment_shape is not in im_info!')
if 'gt_bbox' not in label_info or \ if 'gt_bbox' not in label_info or \
'gt_class' not in label_info: 'gt_class' not in label_info:
raise TypeError('Cannot do RandomCrop! ' + \ raise TypeError('Cannot do RandomCrop! ' + \
...@@ -861,9 +851,9 @@ class RandomCrop: ...@@ -861,9 +851,9 @@ class RandomCrop:
if len(label_info['gt_bbox']) == 0: if len(label_info['gt_bbox']) == 0:
return (im, im_info, label_info) return (im, im_info, label_info)
augment_shape = im_info['augment_shape'] image_shape = im_info['image_shape']
w = augment_shape[1] w = image_shape[1]
h = augment_shape[0] h = image_shape[0]
gt_bbox = label_info['gt_bbox'] gt_bbox = label_info['gt_bbox']
thresholds = list(self.thresholds) thresholds = list(self.thresholds)
if self.allow_no_crop: if self.allow_no_crop:
...@@ -922,7 +912,7 @@ class RandomCrop: ...@@ -922,7 +912,7 @@ class RandomCrop:
label_info['gt_bbox'] = np.take(cropped_box, valid_ids, axis=0) label_info['gt_bbox'] = np.take(cropped_box, valid_ids, axis=0)
label_info['gt_class'] = np.take( label_info['gt_class'] = np.take(
label_info['gt_class'], valid_ids, axis=0) label_info['gt_class'], valid_ids, axis=0)
im_info['augment_shape'] = np.array( im_info['image_shape'] = np.array(
[crop_box[3] - crop_box[1], [crop_box[3] - crop_box[1],
crop_box[2] - crop_box[0]]).astype('int32') crop_box[2] - crop_box[0]]).astype('int32')
if 'gt_score' in label_info: if 'gt_score' in label_info:
...@@ -993,7 +983,7 @@ class ArrangeFasterRCNN: ...@@ -993,7 +983,7 @@ class ArrangeFasterRCNN:
im_resize_info = im_info['im_resize_info'] im_resize_info = im_info['im_resize_info']
im_id = im_info['im_id'] im_id = im_info['im_id']
im_shape = np.array( im_shape = np.array(
(im_info['augment_shape'][0], im_info['augment_shape'][1], 1), (im_info['image_shape'][0], im_info['image_shape'][1], 1),
dtype=np.float32) dtype=np.float32)
gt_bbox = label_info['gt_bbox'] gt_bbox = label_info['gt_bbox']
gt_class = label_info['gt_class'] gt_class = label_info['gt_class']
...@@ -1006,7 +996,7 @@ class ArrangeFasterRCNN: ...@@ -1006,7 +996,7 @@ class ArrangeFasterRCNN:
'Becasuse the im_info can not be None!') 'Becasuse the im_info can not be None!')
im_resize_info = im_info['im_resize_info'] im_resize_info = im_info['im_resize_info']
im_shape = np.array( im_shape = np.array(
(im_info['augment_shape'][0], im_info['augment_shape'][1], 1), (im_info['image_shape'][0], im_info['image_shape'][1], 1),
dtype=np.float32) dtype=np.float32)
outputs = (im, im_resize_info, im_shape) outputs = (im, im_resize_info, im_shape)
return outputs return outputs
...@@ -1086,7 +1076,7 @@ class ArrangeMaskRCNN: ...@@ -1086,7 +1076,7 @@ class ArrangeMaskRCNN:
'Becasuse the im_info can not be None!') 'Becasuse the im_info can not be None!')
im_resize_info = im_info['im_resize_info'] im_resize_info = im_info['im_resize_info']
im_shape = np.array( im_shape = np.array(
(im_info['augment_shape'][0], im_info['augment_shape'][1], 1), (im_info['image_shape'][0], im_info['image_shape'][1], 1),
dtype=np.float32) dtype=np.float32)
if self.mode == 'eval': if self.mode == 'eval':
im_id = im_info['im_id'] im_id = im_info['im_id']
...@@ -1137,7 +1127,7 @@ class ArrangeYOLOv3: ...@@ -1137,7 +1127,7 @@ class ArrangeYOLOv3:
raise TypeError( raise TypeError(
'Cannot do ArrangeYolov3! ' + 'Cannot do ArrangeYolov3! ' +
'Becasuse the im_info and label_info can not be None!') 'Becasuse the im_info and label_info can not be None!')
im_shape = im_info['augment_shape'] im_shape = im_info['image_shape']
if len(label_info['gt_bbox']) != len(label_info['gt_class']): if len(label_info['gt_bbox']) != len(label_info['gt_class']):
raise ValueError("gt num mismatch: bbox and class.") raise ValueError("gt num mismatch: bbox and class.")
if len(label_info['gt_bbox']) != len(label_info['gt_score']): if len(label_info['gt_bbox']) != len(label_info['gt_score']):
...@@ -1161,7 +1151,7 @@ class ArrangeYOLOv3: ...@@ -1161,7 +1151,7 @@ class ArrangeYOLOv3:
raise TypeError( raise TypeError(
'Cannot do ArrangeYolov3! ' + 'Cannot do ArrangeYolov3! ' +
'Becasuse the im_info and label_info can not be None!') 'Becasuse the im_info and label_info can not be None!')
im_shape = im_info['augment_shape'] im_shape = im_info['image_shape']
if len(label_info['gt_bbox']) != len(label_info['gt_class']): if len(label_info['gt_bbox']) != len(label_info['gt_class']):
raise ValueError("gt num mismatch: bbox and class.") raise ValueError("gt num mismatch: bbox and class.")
im_id = im_info['im_id'] im_id = im_info['im_id']
...@@ -1180,6 +1170,6 @@ class ArrangeYOLOv3: ...@@ -1180,6 +1170,6 @@ class ArrangeYOLOv3:
if im_info is None: if im_info is None:
raise TypeError('Cannot do ArrangeYolov3! ' + raise TypeError('Cannot do ArrangeYolov3! ' +
'Becasuse the im_info can not be None!') 'Becasuse the im_info can not be None!')
im_shape = im_info['augment_shape'] im_shape = im_info['image_shape']
outputs = (im, im_shape) outputs = (im, im_shape)
return outputs return outputs
...@@ -66,8 +66,8 @@ class Compose: ...@@ -66,8 +66,8 @@ class Compose:
if self.to_rgb: if self.to_rgb:
im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB) im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
if label is not None: if label is not None:
if not isinstance(label, np.ndarray):
label = np.asarray(Image.open(label)) label = np.asarray(Image.open(label))
for op in self.transforms: for op in self.transforms:
outputs = op(im, im_info, label) outputs = op(im, im_info, label)
im = outputs[0] im = outputs[0]
......
...@@ -220,3 +220,39 @@ def load_pretrain_weights(exe, main_prog, weights_dir, fuse_bn=False): ...@@ -220,3 +220,39 @@ def load_pretrain_weights(exe, main_prog, weights_dir, fuse_bn=False):
len(vars_to_load), weights_dir)) len(vars_to_load), weights_dir))
if fuse_bn: if fuse_bn:
fuse_bn_weights(exe, main_prog, weights_dir) fuse_bn_weights(exe, main_prog, weights_dir)
class EarlyStop:
def __init__(self, patience, thresh):
self.patience = patience
self.counter = 0
self.score = None
self.max = 0
self.thresh = thresh
if patience < 1:
raise Exception("Argument patience should be a positive integer.")
def __call__(self, current_score):
if self.score is None:
self.score = current_score
return False
elif current_score > self.max:
self.counter = 0
self.score = current_score
self.max = current_score
return False
else:
if (abs(self.score - current_score) < self.thresh
or current_score < self.score):
self.counter += 1
self.score = current_score
logging.debug(
"EarlyStopping: %i / %i" % (self.counter, self.patience))
if self.counter >= self.patience:
logging.info("EarlyStopping: Stop training")
return True
return False
else:
self.counter = 0
self.score = current_score
return False
...@@ -29,7 +29,8 @@ setuptools.setup( ...@@ -29,7 +29,8 @@ setuptools.setup(
packages=setuptools.find_packages(), packages=setuptools.find_packages(),
setup_requires=['cython', 'numpy', 'sklearn'], setup_requires=['cython', 'numpy', 'sklearn'],
install_requires=[ install_requires=[
'pycocotools', 'pyyaml', 'colorama', 'tqdm', 'visualdl==1.3.0', "pycocotools;platform_system!='Windows'",
'pyyaml', 'colorama', 'tqdm', 'visualdl==1.3.0',
'paddleslim==1.0.1', 'paddlehub>=1.6.2' 'paddleslim==1.0.1', 'paddlehub>=1.6.2'
], ],
classifiers=[ classifiers=[
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册