Browse Source

fix wrong links in docs/gui

FlyingQianMM 3 years ago
parent
commit
b781b3c619
73 changed files with 955 additions and 2181 deletions
  1. 1 1
      PaddleDetection
  2. 9 1
      README.md
  3. 6 3
      deploy/cpp/docs/compile/paddle/linux.md
  4. 6 6
      deploy/cpp/docs/csharp_deploy/model_infer.cpp
  5. 18 0
      docs/CHANGELOG.md
  6. 7 10
      docs/Resful_API/docs/readme.md
  7. 4 2
      docs/apis/datasets.md
  8. 44 41
      docs/apis/models/classification.md
  9. 31 4
      docs/apis/models/detection.md
  10. 1 1
      docs/apis/prediction.md
  11. 1 1
      docs/apis/visualize.md
  12. 1 1
      docs/data/annotation/classification.md
  13. 40 0
      docs/data/format/detection.md
  14. 40 0
      docs/data/format/instance_segmentation.md
  15. 1 1
      docs/gui/README.md
  16. 20 0
      docs/gui/first_meet.md
  17. 8 8
      docs/quick_start_API.md
  18. 2 2
      docs/quick_start_GUI.md
  19. 1 1
      paddlex/__init__.py
  20. 3 0
      paddlex/cls.py
  21. 2 2
      paddlex/cv/datasets/voc.py
  22. 20 17
      paddlex/cv/models/base.py
  23. 156 42
      paddlex/cv/models/classifier.py
  24. 160 74
      paddlex/cv/models/detector.py
  25. 0 48
      paddlex/cv/models/utils/ema.py
  26. 1 1
      paddlex/paddleseg/cvlibs/param_init.py
  27. 1 1
      paddlex/paddleseg/models/losses/binary_cross_entropy_loss.py
  28. 3 3
      paddlex/paddleseg/models/losses/lovasz_loss.py
  29. 24 5
      paddlex/ppdet/engine/trainer.py
  30. 0 13
      paddlex/ppdet/model_zoo/tests/__init__.py
  31. 0 48
      paddlex/ppdet/model_zoo/tests/test_get_model.py
  32. 0 68
      paddlex/ppdet/model_zoo/tests/test_list_model.py
  33. 13 0
      paddlex/ppdet/modeling/architectures/meta_arch.py
  34. 2 5
      paddlex/ppdet/modeling/backbones/blazenet.py
  35. 1 1
      paddlex/ppdet/modeling/backbones/esnet.py
  36. 3 3
      paddlex/ppdet/modeling/backbones/hrnet.py
  37. 3 3
      paddlex/ppdet/modeling/backbones/lcnet.py
  38. 14 14
      paddlex/ppdet/modeling/backbones/lite_hrnet.py
  39. 2 9
      paddlex/ppdet/modeling/backbones/mobilenet_v1.py
  40. 4 8
      paddlex/ppdet/modeling/backbones/mobilenet_v3.py
  41. 4 8
      paddlex/ppdet/modeling/backbones/resnet.py
  42. 10 5
      paddlex/ppdet/modeling/backbones/shufflenet_v2.py
  43. 6 1
      paddlex/ppdet/modeling/backbones/swin_transformer.py
  44. 1 4
      paddlex/ppdet/modeling/layers.py
  45. 15 19
      paddlex/ppdet/modeling/mot/tracker/base_jde_tracker.py
  46. 85 27
      paddlex/ppdet/modeling/mot/tracker/jde_tracker.py
  47. 1 3
      paddlex/ppdet/modeling/necks/bifpn.py
  48. 2 5
      paddlex/ppdet/modeling/necks/blazeface_fpn.py
  49. 0 135
      paddlex/ppdet/modeling/necks/pan.py
  50. 6 9
      paddlex/ppdet/modeling/ops.py
  51. 23 7
      paddlex/ppdet/modeling/post_process.py
  52. 2 2
      paddlex/ppdet/modeling/proposal_generator/target.py
  53. 3 3
      paddlex/ppdet/modeling/reid/pplcnet_embedding.py
  54. 4 2
      paddlex/ppdet/modeling/reid/resnet.py
  55. 0 13
      paddlex/ppdet/modeling/tests/__init__.py
  56. BIN
      paddlex/ppdet/modeling/tests/imgs/coco2017_val2017_000000000139.jpg
  57. BIN
      paddlex/ppdet/modeling/tests/imgs/coco2017_val2017_000000000724.jpg
  58. 0 69
      paddlex/ppdet/modeling/tests/test_architectures.py
  59. 0 74
      paddlex/ppdet/modeling/tests/test_base.py
  60. 0 62
      paddlex/ppdet/modeling/tests/test_mstest.py
  61. 0 838
      paddlex/ppdet/modeling/tests/test_ops.py
  62. 0 415
      paddlex/ppdet/modeling/tests/test_yolov3_loss.py
  63. 4 2
      paddlex/ppdet/optimizer.py
  64. 8 4
      paddlex/ppdet/utils/checkpoint.py
  65. 22 0
      paddlex/utils/checkpoint.py
  66. 0 8
      paddlex_restful/restful/app.py
  67. 1 1
      paddlex_restful/restful/project/train/classification.py
  68. 38 1
      paddlex_restful/restful/project/train/detection.py
  69. 2 2
      requirements.txt
  70. 6 6
      setup.py
  71. 1 1
      static/docs/apis/visualize.md
  72. 50 0
      tutorials/train/image_classification/pplcnet.py
  73. 8 7
      tutorials/train/object_detection/picodet.py

+ 1 - 1
PaddleDetection

@@ -1 +1 @@
-Subproject commit d41b085d66946219351d8d99c2b3d95756489624
+Subproject commit 692d732994660ceba82c75034c802eb1138239cf

+ 9 - 1
README.md

@@ -5,7 +5,7 @@
 
 <p align="left">
     <a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache%202-red.svg"></a>
-    <a href="https://github.com/PaddlePaddle/PaddleOCR/releases"><img src="https://img.shields.io/github/release/PaddlePaddle/PaddleX.svg"></a>
+    <a href="https://github.com/PaddlePaddle/PaddleX/releases"><img src="https://img.shields.io/github/release/PaddlePaddle/PaddleX.svg"></a>
     <a href=""><img src="https://img.shields.io/badge/python-3.6+-orange.svg"></a>
     <a href=""><img src="https://img.shields.io/badge/os-linux%2C%20win%2C%20mac-yellow.svg"></a>
     <a href=""><img src="https://img.shields.io/badge/QQ_Group-957286141-52B6EF?style=social&logo=tencent-qq&logoColor=000&logoWidth=20"></a>
@@ -27,6 +27,14 @@
 
 
 ## 近期动态
+
+2021.12.10 PaddleX发布2.1.0版本
+* 新增超轻量分类模型PPLCNet,在Intel CPU上,单张图像预测速度约5ms,ImageNet-1K数据集上Top1识别准确率达到80.82%,超越ResNet152的模型效果 [欢迎体验](./tutorials/train/image_classification/pplcnet.py)
+* 新增轻量级检测特色模型PP-PicoDet,第一个在1M参数量之内mAP(0.5:0.95)超越30+(输入416像素时),网络预测在ARM CPU下可达150FPS [欢迎体验](./tutorials/train/object_detection/picodet.py)
+* 升级PaddleX Restful API,支持飞桨动态图开发模式 [欢迎体验](docs/Resful_API/docs/readme.md)
+* 新增检测模型负样本训练策略 [欢迎体验](docs/data/format/detection.md#3)
+* 新增python轻量级服务化部署 [欢迎体验](./docs/hub_serving_deploy.md)
+
 2021.09.10 PaddleX发布2.0.0正式版本。
 - 全新发布Manufacture SDK,支持多模型串联部署。[欢迎体验](./deploy/cpp/docs/manufacture_sdk)
 - PaddleX部署全面升级,支持飞桨视觉套件PaddleDetection、PaddleClas、PaddleSeg、PaddleX的端到端统一部署能力。[欢迎体验](./deploy/cpp/docs/deployment.md)

+ 6 - 3
deploy/cpp/docs/compile/paddle/linux.md

@@ -39,7 +39,10 @@ PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及
 ```
 
 ### Step 3. 修改编译参数
-根据自己的系统环境,修改`PaddleX/deploy/cpp/script/build.sh`脚本中的参数,主要修改的参数为以下几个
+根据自己的系统环境,修改`PaddleX/deploy/cpp/
+
+
+/build.sh`脚本中的参数,主要修改的参数为以下几个
 | 参数          | 说明                                                                                 |
 | :------------ | :----------------------------------------------------------------------------------- |
 | WITH_GPU      | ON或OFF,表示是否使用GPU,当下载的为CPU预测库时,设为OFF                             |
@@ -54,7 +57,7 @@ PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及
 ### Step 4. 编译
 修改完build.sh后执行编译:  
 ```commandline
-sh script/build.sh
+sh scripts/build.sh
 ```
 
 **[注意]**:
@@ -63,7 +66,7 @@ sh script/build.sh
 
 #### 编译环境无法联网导致编译失败?
 
-> 编译过程,会调用script/bootstrap.sh联网下载opencv、openssl,以及yaml依赖包,如无法联网,用户按照下操作手动下载
+> 编译过程,会调用scripts/bootstrap.sh联网下载opencv、openssl,以及yaml依赖包,如无法联网,用户按照下操作手动下载
 >
 > 1. 根据系统版本,点击右侧链接下载不同版本的opencv依赖 [Ubuntu 16.04](https://bj.bcebos.com/paddleseg/deploy/opencv3.4.6gcc4.8ffmpeg.tar.gz2)/[Ubuntu 18.04](https://bj.bcebos.com/paddlex/deploy/opencv3.4.6gcc4.8ffmpeg_ubuntu_18.04.tar.gz2)
 > 2. 解压下载的opencv依赖(解压后目录名为opencv3.4.6gcc4.8ffmpeg),创建目录`PaddleX/deploy/cpp/deps`,将解压后的目录拷贝至该创建的目录下

+ 6 - 6
deploy/cpp/docs/csharp_deploy/model_infer.cpp

@@ -24,7 +24,7 @@ PaddleDeploy::Model* model;
 * paddlex_model_type: When Model_Type is paddlx, the type of actual Paddlex model returned - det, seg, clas
 *
 */
-extern "C" void InitModel(const char* model_type, const char* model_filename, const char* params_filename, const char* cfg_file, bool use_gpu, int gpu_id, char* paddlex_model_type)
+extern "C" __declspec(dllexport) void InitModel(const char* model_type, const char* model_filename, const char* params_filename, const char* cfg_file, bool use_gpu, int gpu_id, char* paddlex_model_type)
 {
 	// create model
 	model = PaddleDeploy::CreateModel(model_type);  //FLAGS_model_type
@@ -83,7 +83,7 @@ extern "C" void InitModel(const char* model_type, const char* model_filename, co
 *
 * extern "C"
 */
-extern "C" void Det_ModelPredict(const unsigned char* img, int nWidth, int nHeight, int nChannel, float* output, int* nBoxesNum, char* LabelList)
+extern "C" __declspec(dllexport) void Det_ModelPredict(const unsigned char* img, int nWidth, int nHeight, int nChannel, float* output, int* nBoxesNum, char* LabelList)
 {
 	// prepare data
 	std::vector<cv::Mat> imgs;
@@ -143,7 +143,7 @@ extern "C" void Det_ModelPredict(const unsigned char* img, int nWidth, int nHeig
 *
 * extern "C"
 */
-extern "C" void Seg_ModelPredict(const unsigned char* img, int nWidth, int nHeight, int nChannel, unsigned char* output)
+extern "C" __declspec(dllexport) void Seg_ModelPredict(const unsigned char* img, int nWidth, int nHeight, int nChannel, unsigned char* output)
 {
 	// prepare data
 	std::vector<cv::Mat> imgs;
@@ -192,7 +192,7 @@ extern "C" void Seg_ModelPredict(const unsigned char* img, int nWidth, int nHeig
 *
 * extern "C"
 */
-extern "C" void Cls_ModelPredict(const unsigned char* img, int nWidth, int nHeight, int nChannel, float* score, char* category, int* category_id)
+extern "C" __declspec(dllexport) void Cls_ModelPredict(const unsigned char* img, int nWidth, int nHeight, int nChannel, float* score, char* category, int* category_id)
 {
 	// prepare data
 	std::vector<cv::Mat> imgs;
@@ -245,7 +245,7 @@ extern "C" void Cls_ModelPredict(const unsigned char* img, int nWidth, int nHeig
 *
 * extern "C"
 */
-extern "C" void Mask_ModelPredict(const unsigned char* img, int nWidth, int nHeight, int nChannel, float* box_output, unsigned char* mask_output, int* nBoxesNum, char* LabelList)
+extern "C" __declspec(dllexport) void Mask_ModelPredict(const unsigned char* img, int nWidth, int nHeight, int nChannel, float* box_output, unsigned char* mask_output, int* nBoxesNum, char* LabelList)
 {
 	// prepare data
 	std::vector<cv::Mat> imgs;
@@ -305,7 +305,7 @@ extern "C" void Mask_ModelPredict(const unsigned char* img, int nWidth, int nHei
 *
 * extern "C"
 */
-extern "C" void DestructModel()
+extern "C" __declspec(dllexport) void DestructModel()
 {
 	delete model;
 	std::cout << "destruct model success" << std::endl;

+ 18 - 0
docs/CHANGELOG.md

@@ -2,6 +2,24 @@
 
 ## 最新版本信息
 
+- **2021.12.10 v2.1.0**
+
+  * PaddleX API
+    - 新增超轻量分类模型PPLCNet,在Intel CPU上,单张图像预测速度约5ms,ImageNet-1K数据集上Top1识别准确率达到80.82%,超越ResNet152的模型效果
+    - 新增轻量级检测特色模型PP-PicoDet,第一个在1M参数量之内mAP(0.5:0.95)超越30+(输入416像素时),网络预测在ARM CPU下可达150FPS
+    - 新增检测模型负样本训练策略
+
+  * 预测部署
+    - 新增python轻量级服务化部署
+
+  * PaddleX GUI
+    - 新增超轻量分类模型PPLCNet、轻量级检测特色模型PP-PicoDet
+
+  * PaddleX Restful API
+    - 全面升级,支持飞桨动态图开发模式
+    - 提供Html Demo两种前端调用方式
+
+
 - **2021.09.10 v2.0.0**
 
   * PaddleX API

+ 7 - 10
docs/Resful_API/docs/readme.md

@@ -31,19 +31,16 @@ PaddleX RESTful是基于PaddleX开发的RESTful API。对于开发者来说只
 
 在该示例中PaddleX_Restful运行在一台带GPU的linux服务器下,用户通过其他电脑连接该服务器进行远程的操作。
 ### 1  环境准备
-在服务器下载PaddlePaddle和PaddleX及其他依赖
-
-* 下载PaddlePaddle
 
-`pip install paddlepaddle-gpu -i `
+在服务器下载PaddlePaddle和PaddleX及其他依赖
 
-* 下载PaddleX
+* 下载安装PaddleX>=2.1.0和PaddlePaddle>= 2.2.0
 
-pip install paddlex==1.3.11 -i
+详见[PaddleX API快速安装](../../../docs/quick_start_API.md#PaddleX-200安装)
 
 * 下载pycuda(如果不使用GPU,该项可不进行下载)
 
-pip install pycuda -i
+pip install pycuda
 
 ### 2  启动Restful服务
 
@@ -127,10 +124,10 @@ pip install pycuda -i
 
 
 * ## 客户端图形化界面
-客户端操作流程和Web界面基本一致,提供了MAC和Windows版本两种,用户可自行下载并操作
 
-- [MAC](https://bj.bcebos.com/paddlex/PaddleX_Remote_GUI/mac/PaddleX_Remote_GUI.zip)
-- [Windows](https://bj.bcebos.com/paddlex/PaddleX_Remote_GUI/windows/PaddleX_Remote_GUI.zip)
+客户端操作流程和Web界面基本一致,提供了Windows版本,用户可自行下载并操作
+
+Windows客户端即将到来!
 
 ### 4  Restful 二次开发说明
 

+ 4 - 2
docs/apis/datasets.md

@@ -30,7 +30,7 @@ paddlex.datasets.ImageNet(data_dir, file_list, label_list, transforms=None, num_
 ## <h2 id="2">paddlex.datasets.VOCDetection</h2>
 > **用于目标检测模型**  
 ```python
-paddlex.datasets.VOCDetection(data_dir, file_list, label_list, transforms=None, num_workers='auto', shuffle=False)
+paddlex.datasets.VOCDetection(data_dir, file_list, label_list, transforms=None, num_workers='auto', shuffle=False, allow_empty=False, empty_ratio=1.)
 ```
 
 > 读取PascalVOC格式的检测数据集,并对样本进行相应的处理。PascalVOC数据集格式的介绍可查看文档:[数据集格式说明](../data/format/detection.md)  
@@ -46,6 +46,7 @@ paddlex.datasets.VOCDetection(data_dir, file_list, label_list, transforms=None,
 > > * **num_workers** (int|str):数据集中样本在预处理过程中的进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
 > > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
 > > * **allow_empty** (bool): 是否加载负样本。默认为False。
+> > * **empty_ratio** (float): 用于指定负样本占总样本数的比例。如果小于0或大于等于1,则保留全部的负样本。默认为1。
 
 ### <h3 id="21">cluster_yolo_anchor</h3>
 
@@ -144,7 +145,7 @@ model.train(
 ## <h2 id="3">paddlex.datasets.CocoDetection</h2>
 > **用于实例分割/目标检测模型**  
 ```python
-paddlex.datasets.CocoDetection(data_dir, ann_file, transforms=None, num_workers='auto', shuffle=False)
+paddlex.datasets.CocoDetection(data_dir, ann_file, transforms=None, num_workers='auto', shuffle=False, allow_empty=False, empty_ratio=1.)
 ```
 
 > 读取MSCOCO格式的检测数据集,并对样本进行相应的处理,该格式的数据集同样可以应用到实例分割模型的训练中。MSCOCO数据集格式的介绍可查看文档:[数据集格式说明](../data/format/instance_segmentation.md)  
@@ -159,6 +160,7 @@ paddlex.datasets.CocoDetection(data_dir, ann_file, transforms=None, num_workers=
 > > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。  
 > > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
 > > * **allow_empty** (bool): 是否加载负样本。默认为False。
+> > * **empty_ratio** (float): 用于指定负样本占总样本数的比例。如果小于0或大于等于1,则保留全部的负样本。默认为1。
 
 ### <h3 id="31">cluster_yolo_anchor</h3>
 

+ 44 - 41
docs/apis/models/classification.md

@@ -25,7 +25,7 @@ paddlex.cls.ResNet50(num_classes=1000)
 ### <h3 id="11">train</h3>
 
 ```python
-train(self, num_epochs, train_dataset, train_batch_size=64, eval_dataset=None, optimizer=None, save_interval_epochs=1, log_interval_steps=10, save_dir='output', pretrain_weights='IMAGENET', learning_rate=.025, warmup_steps=0, warmup_start_lr=0.0, lr_decay_epochs=(30, 60, 90), lr_decay_gamma=0.1, early_stop=False, early_stop_patience=5, use_vdl=True)
+train(self, num_epochs, train_dataset, train_batch_size=64, eval_dataset=None, optimizer=None, save_interval_epochs=1, log_interval_steps=10, save_dir='output', pretrain_weights='IMAGENET', learning_rate=.025, warmup_steps=0, warmup_start_lr=0.0, lr_decay_epochs=(30, 60, 90), lr_decay_gamma=0.1, label_smoothing=None, early_stop=False, early_stop_patience=5, use_vdl=True)
 ```
 >
 > **参数**
@@ -44,6 +44,7 @@ train(self, num_epochs, train_dataset, train_batch_size=64, eval_dataset=None, o
 - **warmup_start_lr**(float): 默认优化器的warmup起始学习率,默认为0.0。
 - **lr_decay_epochs** (list): 默认优化器的学习率衰减轮数。默认为[30, 60, 90]。
 - **lr_decay_gamma** (float): 默认优化器的学习率衰减率。默认为0.1。
+- **label_smoothing** (float, bool or None): 是否使用标签平滑。若为float,表示标签平滑系数。若为True,使用系数为0.1的标签平滑。若为None或False,则不采用标签平滑。默认为None。
 - **early_stop** (bool): 是否使用提前终止训练策略。默认为False。
 - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认为5。
 - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认为True。
@@ -171,43 +172,45 @@ quant_aware_train(self, num_epochs, train_dataset, train_batch_size=64, eval_dat
 
 PaddleX提供了共计38种分类模型,所有分类模型均提供同`ResNet50`相同的训练`train`,评估`evaluate`,预测`predict`,敏感度分析`analyze_sensitivity`,剪裁`prune`和在线量化`quant_aware_train`接口,各模型效果可参考[模型库](../../appendix/model_zoo.md)。
 
-| 模型              | 接口                    |
-| :---------------- | :---------------------- |
-| ResNet18          | paddlex.cls.ResNet18(num_classes=1000) |
-| ResNet18_vd       | paddlex.cls.ResNet18_vd(num_classes=1000) |
-| ResNet34          | paddlex.cls.ResNet34(num_classes=1000) |
-| ResNet34_vd          | paddlex.cls.ResNet34_vd(num_classes=1000) |
-| ResNet50          | paddlex.cls.ResNet50(num_classes=1000) |
-| ResNet50_vd       | paddlex.cls.ResNet50_vd(num_classes=1000) |
-| ResNet50_vd_ssld    | paddlex.cls.ResNet50_vd_ssld(num_classes=1000) |
-| ResNet101          | paddlex.cls.ResNet101(num_classes=1000) |
-| ResNet101_vd        | paddlex.cls.ResNet101_vd(num_classes=1000) |
-| ResNet101_vd_ssld   | paddlex.cls.ResNet101_vd_ssld(num_classes=1000) |
-| ResNet152 | paddlex.cls.ResNet152(num_classes=1000) |
-| ResNet152_vd | paddlex.cls.ResNet152_vd(num_classes=1000) |
-| ResNet200_vd | paddlex.cls.ResNet200_vd(num_classes=1000) |
-| DarkNet53      | paddlex.cls.DarkNet53(num_classes=1000) |
-| MobileNetV1         | paddlex.cls.MobileNetV1(num_classes=1000, scale=1.0) |
-| MobileNetV2       | paddlex.cls.MobileNetV2(num_classes=1000, scale=1.0) |
-| MobileNetV3_small       | paddlex.cls.MobileNetV3_small(num_classes=1000, scale=1.0) |
-| MobileNetV3_small_ssld  | paddlex.cls.MobileNetV3_small_ssld(num_classes=1000, scale=1.0) |
-| MobileNetV3_large   | paddlex.cls.MobileNetV3_large(num_classes=1000, scale=1.0) |
-| MobileNetV3_large_ssld | paddlex.cls.MobileNetV3_large_ssld(num_classes=1000) |
-| Xception41     | paddlex.cls.Xception41(num_classes=1000) |
-| Xception65     | paddlex.cls.Xception65(num_classes=1000) |
-| Xception71     | paddlex.cls.Xception71(num_classes=1000) |
-| ShuffleNetV2     | paddlex.cls.ShuffleNetV2(num_classes=1000, scale=1.0) |
-| ShuffleNetV2_swish     | paddlex.cls.ShuffleNetV2_swish(num_classes=1000) |
-| DenseNet121      | paddlex.cls.DenseNet121(num_classes=1000) |
-| DenseNet161       | paddlex.cls.DenseNet161(num_classes=1000) |
-| DenseNet169       | paddlex.cls.DenseNet169(num_classes=1000) |
-| DenseNet201       | paddlex.cls.DenseNet201(num_classes=1000) |
-| DenseNet264       | paddlex.cls.DenseNet264(num_classes=1000) |
-| HRNet_W18_C       | paddlex.cls.HRNet_W18_C(num_classes=1000) |
-| HRNet_W30_C       | paddlex.cls.HRNet_W30_C(num_classes=1000) |
-| HRNet_W32_C       | paddlex.cls.HRNet_W32_C(num_classes=1000) |
-| HRNet_W40_C       | paddlex.cls.HRNet_W40_C(num_classes=1000) |
-| HRNet_W44_C       | paddlex.cls.HRNet_W44_C(num_classes=1000) |
-| HRNet_W48_C       | paddlex.cls.HRNet_W48_C(num_classes=1000) |
-| HRNet_W64_C       | paddlex.cls.HRNet_W64_C(num_classes=1000) |
-| AlexNet         | paddlex.cls.AlexNet(num_classes=1000) |
+| 模型                     | 接口                                                              |
+|:-----------------------|:----------------------------------------------------------------|
+| PPLCNet                | paddlex.cls.PPLCNet(num_classes=1000)                           |
+| PPLCNet_ssld                | paddlex.cls.PPLCNet_ssld(num_classes=1000)                      |
+| ResNet18               | paddlex.cls.ResNet18(num_classes=1000)                          |
+| ResNet18_vd            | paddlex.cls.ResNet18_vd(num_classes=1000)                       |
+| ResNet34               | paddlex.cls.ResNet34(num_classes=1000)                          |
+| ResNet34_vd            | paddlex.cls.ResNet34_vd(num_classes=1000)                       |
+| ResNet50               | paddlex.cls.ResNet50(num_classes=1000)                          |
+| ResNet50_vd            | paddlex.cls.ResNet50_vd(num_classes=1000)                       |
+| ResNet50_vd_ssld       | paddlex.cls.ResNet50_vd_ssld(num_classes=1000)                  |
+| ResNet101              | paddlex.cls.ResNet101(num_classes=1000)                         |
+| ResNet101_vd           | paddlex.cls.ResNet101_vd(num_classes=1000)                      |
+| ResNet101_vd_ssld      | paddlex.cls.ResNet101_vd_ssld(num_classes=1000)                 |
+| ResNet152              | paddlex.cls.ResNet152(num_classes=1000)                         |
+| ResNet152_vd           | paddlex.cls.ResNet152_vd(num_classes=1000)                      |
+| ResNet200_vd           | paddlex.cls.ResNet200_vd(num_classes=1000)                      |
+| DarkNet53              | paddlex.cls.DarkNet53(num_classes=1000)                         |
+| MobileNetV1            | paddlex.cls.MobileNetV1(num_classes=1000, scale=1.0)            |
+| MobileNetV2            | paddlex.cls.MobileNetV2(num_classes=1000, scale=1.0)            |
+| MobileNetV3_small      | paddlex.cls.MobileNetV3_small(num_classes=1000, scale=1.0)      |
+| MobileNetV3_small_ssld | paddlex.cls.MobileNetV3_small_ssld(num_classes=1000, scale=1.0) |
+| MobileNetV3_large      | paddlex.cls.MobileNetV3_large(num_classes=1000, scale=1.0)      |
+| MobileNetV3_large_ssld | paddlex.cls.MobileNetV3_large_ssld(num_classes=1000)            |
+| Xception41             | paddlex.cls.Xception41(num_classes=1000)                        |
+| Xception65             | paddlex.cls.Xception65(num_classes=1000)                        |
+| Xception71             | paddlex.cls.Xception71(num_classes=1000)                        |
+| ShuffleNetV2           | paddlex.cls.ShuffleNetV2(num_classes=1000, scale=1.0)           |
+| ShuffleNetV2_swish     | paddlex.cls.ShuffleNetV2_swish(num_classes=1000)                |
+| DenseNet121            | paddlex.cls.DenseNet121(num_classes=1000)                       |
+| DenseNet161            | paddlex.cls.DenseNet161(num_classes=1000)                       |
+| DenseNet169            | paddlex.cls.DenseNet169(num_classes=1000)                       |
+| DenseNet201            | paddlex.cls.DenseNet201(num_classes=1000)                       |
+| DenseNet264            | paddlex.cls.DenseNet264(num_classes=1000)                       |
+| HRNet_W18_C            | paddlex.cls.HRNet_W18_C(num_classes=1000)                       |
+| HRNet_W30_C            | paddlex.cls.HRNet_W30_C(num_classes=1000)                       |
+| HRNet_W32_C            | paddlex.cls.HRNet_W32_C(num_classes=1000)                       |
+| HRNet_W40_C            | paddlex.cls.HRNet_W40_C(num_classes=1000)                       |
+| HRNet_W44_C            | paddlex.cls.HRNet_W44_C(num_classes=1000)                       |
+| HRNet_W48_C            | paddlex.cls.HRNet_W48_C(num_classes=1000)                       |
+| HRNet_W64_C            | paddlex.cls.HRNet_W64_C(num_classes=1000)                       |
+| AlexNet                | paddlex.cls.AlexNet(num_classes=1000)                           |

+ 31 - 4
docs/apis/models/detection.md

@@ -11,8 +11,9 @@
   * [quant_aware_train](#16)
 * [paddlex.det.PPYOLO](#2)
 * [paddlex.det.PPYOLOTiny](#3)
-* [paddlex.det.YOLOv3](#4)
-* [paddlex.det.FasterRCNN](#5)
+* [paddlex.det.PicoDet](#4)
+* [paddlex.det.YOLOv3](#5)
+* [paddlex.det.FasterRCNN](#6)
 
 
 ## <h2 id="1">paddlex.det.PPYOLOv2</h2>
@@ -262,7 +263,33 @@ paddlex.det.PPYOLOTiny(num_classes=80, backbone='MobileNetV3', anchors=[[10, 15]
 > - prune 剪裁接口说明同 [PPYOLOv2模型prune接口](#prune)
 > - quant_aware_train 在线量化接口说明同 [PPYOLOv2模型quant_aware_train接口](#quant_aware_train)
 
-## <h2 id="4">paddlex.det.YOLOv3</h2>
+
+## <h2 id="4">paddlex.det.PicoDet</h2>
+
+```python
+paddlex.det.PicoDet(num_classes=80, backbone='ESNet_m', nms_score_threshold=.025, nms_topk=1000, nms_keep_topk=100, nms_iou_threshold=.6)
+```
+
+> 构建PicoDet检测器。
+
+> **参数**
+>
+> > - **num_classes** (int): 类别数。默认为80。
+> > - **backbone** (str): PicoDet的backbone网络,取值范围为['ESNet_s', 'ESNet_m', 'ESNet_l', 'LCNet', 'MobileNetV3', 'ResNet18_vd']。默认为'ESNet_m'。
+> > - **nms_score_threshold** (float): 检测框的置信度得分阈值,置信度得分低于阈值的框应该被忽略。默认为0.01。
+> > - **nms_topk** (int): 进行NMS时,根据置信度保留的最大检测框数。默认为1000。
+> > - **nms_keep_topk** (int): 进行NMS后,每个图像要保留的总检测框数。默认为100。
+> > - **nms_iou_threshold** (float): 进行NMS时,用于剔除检测框IoU的阈值。默认为0.6。
+
+> - train 训练接口说明同 [PPYOLOv2模型train接口](#train)
+> - evaluate 评估接口说明同 [PPYOLOv2模型evaluate接口](#evaluate)
+> - predict 预测接口说明同 [PPYOLOv2模型predict接口](#predict)
+> - analyze_sensitivity 敏感度分析接口说明同 [PPYOLOv2模型analyze_sensivity接口](#analyze_sensitivity)
+> - prune 剪裁接口说明同 [PPYOLOv2模型prune接口](#prune)
+> - quant_aware_train 在线量化接口说明同 [PPYOLOv2模型quant_aware_train接口](#quant_aware_train)
+
+
+## <h2 id="5">paddlex.det.YOLOv3</h2>
 
 ```python
 paddlex.det.YOLOv3(num_classes=80, backbone='MobileNetV1', anchors=[[10, 13], [16, 30], [33, 23], [30, 61], [62, 45], [59, 119], [116, 90], [156, 198], [373, 326]], anchor_masks=[[6, 7, 8], [3, 4, 5], [0, 1, 2]], ignore_threshold=0.7, nms_score_threshold=0.01, nms_topk=1000, nms_keep_topk=100, nms_iou_threshold=0.45, label_smooth=False)
@@ -291,7 +318,7 @@ paddlex.det.YOLOv3(num_classes=80, backbone='MobileNetV1', anchors=[[10, 13], [1
 > - quant_aware_train 在线量化接口说明同 [PPYOLOv2模型quant_aware_train接口](#quant_aware_train)
 
 
-## <h2 id="5">paddlex.det.FasterRCNN</h2>
+## <h2 id="6">paddlex.det.FasterRCNN</h2>
 
 ```python
 paddlex.det.FasterRCNN(num_classes=80, backbone='ResNet50', with_fpn=True, aspect_ratios=[0.5, 1.0, 2.0], anchor_sizes=[[32], [64], [128], [256], [512]], keep_top_k=100, nms_threshold=0.5, score_threshold=0.05, fpn_num_channels=256, rpn_batch_size_per_im=256, rpn_fg_fraction=0.5, test_pre_nms_top_n=None, test_post_nms_top_n=1000)

+ 1 - 1
docs/apis/prediction.md

@@ -21,7 +21,7 @@ Predict Result: [{'category_id': 549, 'category': 'envelope', 'score': 0.2906293
 
 ![](images/test.jpg)
 
-- 分类模型predict接口[说明文档](./apis/models/classification.md#predict)
+- 分类模型predict接口[说明文档](./models/classification.md#predict)
 
 
 ## 目标检测

+ 1 - 1
docs/apis/visualize.md

@@ -96,7 +96,7 @@ paddlex.det.coco_error_analysis(eval_details_file=None, gt=None, pred_bbox=None,
 
 ![](images/detection_analysis.jpg)
 
-左图显示的是`person`类的分析结果,图显示的是所有类别整体的分析结果。
+左图显示的是`person`类的分析结果,图显示的是所有类别整体的分析结果。
 
 分析图表展示了7条Precision-Recall(PR)曲线,每一条曲线表示的Average Precision (AP)比它左边那条高,原因是逐步放宽了评估要求。以`person`类为例,各条PR曲线的评估要求解释如下:
 

+ 1 - 1
docs/data/annotation/classification.md

@@ -1,4 +1,4 @@
-# 图像分数据标注
+# 图像分数据标注
 
 图像分类标注是一项最基础,最简单的标注任务,图像分类无需标注工具,只需将属于同一类的图片放在同一个文件夹下即可,例如下所示目录结构,
 ```

+ 40 - 0
docs/data/format/detection.md

@@ -6,6 +6,7 @@
   * [数据文件夹结构](#11)
   * [训练集、验证集列表和类别标签列表](#12)
 * [数据加载](#2)
+* [添加负样本](#3)
 
 
 ## <h2 id="1">数据格式</h2>
@@ -97,5 +98,44 @@ eval_dataset = pdx.datasets.VOCDetection(
                         file_list='./MyDataset/val_list.txt',
                         label_list='MyDataset/labels.txt',
                         transforms=eval_transforms)
+```
+
+## <h2 id="3">添加负样本</h2>
+
+检测任务支持添加负样本进行训练以降低误检率,代码示例如下:
+
+```python
+import paddlex as pdx
+from paddlex import transforms as T
+
+train_transforms = T.Compose([
+    T.RandomResizeByShort(
+        short_sizes=[640, 672, 704, 736, 768, 800],
+        max_size=1333,
+        interp='CUBIC'),
+    T.RandomHorizontalFlip(),
+    T.Normalize(
+            mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
+])
+
+# 情况一:train_list中已经包含负样本
+train_dataset = pdx.datasets.VOCDetection(
+                        data_dir='./MyDataset',
+                        file_list='./MyDataset/train_list.txt',
+                        label_list='./MyDataset/labels.txt',
+                        transforms=train_transforms,
+                        allow_empty=True,   # 是否加载负样本
+                        empty_ratio=1.)   # 用于指定负样本占总样本数的比例。如果小于0或大于等于1,则保留全部的负样本。默认为1。
+
+# 情况二:train_list中仅包含正样本,负样本在单独的路径下
+train_dataset = pdx.datasets.VOCDetection(
+                        data_dir='./MyDataset',
+                        file_list='./MyDataset/train_list.txt',
+                        label_list='./MyDataset/labels.txt',
+                        transforms=train_transforms)
+train_dataset.add_negative_samples(
+                        image_dir='path/to/negative/images',   # 背景图片所在的文件夹目录。
+                        empty_ratio=1)   # 用于指定负样本占总样本数的比例。如果为None,保留数据集初始化是设置的`empty_ratio`值,
+                                         # 否则更新原有`empty_ratio`值。如果小于0或大于等于1,则保留全部的负样本。默认为1。
 
 ```

+ 40 - 0
docs/data/format/instance_segmentation.md

@@ -6,6 +6,7 @@
   * [数据文件夹结构](#11)
   * [训练集、验证集列表](#12)
 * [数据加载](#2)
+* [添加负样本](#3)
 
 
 ## <h2 id="1">数据格式</h2>
@@ -65,3 +66,42 @@ eval_dataset = pdx.dataset.CocoDetection(
                     ann_file='./MyDataset/val.json',
                     transforms=eval_transforms)
 ```
+
+
+## <h2 id="3">添加负样本</h2>
+
+实例分割任务支持添加负样本进行训练以降低误检率,代码示例如下:
+
+```python
+import paddlex as pdx
+from paddlex import transforms as T
+
+train_transforms = T.Compose([
+    T.RandomResizeByShort(
+        short_sizes=[640, 672, 704, 736, 768, 800],
+        max_size=1333,
+        interp='CUBIC'),
+    T.RandomHorizontalFlip(),
+    T.Normalize(
+            mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
+])
+
+# 情况一:ann_file中已经包含负样本
+train_dataset = pdx.dataset.CocoDetection(
+                    data_dir='./MyDataset/JPEGImages',
+                    ann_file='./MyDataset/train.json',
+                    transforms=train_transforms,
+                    allow_empty=True,   # 是否加载负样本
+                    empty_ratio=1.)   # 用于指定负样本占总样本数的比例。如果小于0或大于等于1,则保留全部的负样本。默认为1。
+
+# 情况二:train_list中仅包含正样本,负样本在单独的路径下
+train_dataset = pdx.dataset.CocoDetection(
+                    data_dir='./MyDataset/JPEGImages',
+                    ann_file='./MyDataset/train.json',
+                    transforms=train_transforms)
+train_dataset.add_negative_samples(
+                        image_dir='path/to/negative/images',   # 背景图片所在的文件夹目录。
+                        empty_ratio=1)   # 用于指定负样本占总样本数的比例。如果为None,保留数据集初始化是设置的`empty_ratio`值,
+                                         # 否则更新原有`empty_ratio`值。如果小于0或大于等于1,则保留全部的负样本。默认为1。
+
+```

+ 1 - 1
docs/gui/README.md

@@ -17,4 +17,4 @@ PaddleX可视化客户端是PaddleX API的衍生品,它在集成API功能的
 
 ## 安装使用
 - 请参考[安装方式文档](./download.md)下载安装PaddleX可视化客户端。
-- 请参考[快速开始](./quick_start.md)查看[视频教程](./quick_start.md/#视频教程)和[文档教程](./quick_start.md/#文档教程)。
+- 请参考[快速开始](../quick_start_GUI.md)查看[视频教程](../quick_start_GUI.md#视频教程)和[文档教程](../quick_start_GUI.md#文档教程)。

+ 20 - 0
docs/gui/first_meet.md

@@ -0,0 +1,20 @@
+# PaddleX可视化客户端介绍
+
+PaddleX可视化客户端基于PaddleX开发的可视化深度学习模型训练套件,目前支持训练视觉领域的图像分类、目标检测、实例分割和语义分割四大任务。开发者以点选、键入的方式快速体验深度学习模型开发的全流程。可以作为您提升深度学习模型开发效率的工具。
+
+PaddleX GUI 当前提供Windows,Mac,Ubuntu三种版本一键绿色安装的方式。请至飞桨官网:https://www.paddlepaddle.org.cn/paddle/paddleX 下载您需要的版本。
+
+## 功能
+PaddleX可视化客户端是PaddleX API的衍生品,它在集成API功能的基础上,额外提供了可视化分析、评估等附加功能,致力于为开发者带来极致顺畅的开发体验。其拥有以下独特的功能:
+- **全流程打通**:PaddleX GUI覆盖深度学习模型开发必经的 **数据处理** 、 **超参配置** 、 **模型训练及优化** 、 **模型发布** 全流程,无需开发一行代码,即可得到高性能深度学习推理模型。
+- **数据集智能分析**:详细的数据结构说明,并提供 **数据标签自动校验** 。支持 **可视化数据预览** 、 **数据分布图表展示** 、 **一键数据集切分** 等实用功能
+- **自动超参推荐**:集成飞桨团队长时间产业实践经验,根据用户选择的模型类别、骨架网络等,提供多种针对性优化的 **预训练模型** ,并 **提供推荐超参配置** ,可 **一键开启多种优化策略**
+- **可视化模型评估**:集成 **可视化分析工具:VisualDL** , 以线性图表的形式展示精度、学习率等关键参数在训练过程中的变化趋势。提供 **混淆矩阵** 等实用方法,帮助快速定位问题,加速调参。模型评估报告一键导出,方便项目复盘分析。
+- **模型裁剪及量化**:一键启动模型裁剪、量化,在不同阶段为开发者提供模型优化的策略,满足不同环境对模型性能的需求。
+- **预训练模型管理**:可对历史训练模型进行保存及管理,未进行裁剪的模型可以保存为预训练模型,在后续任务中使用。
+- **可视化模型测试**:客户端直接展示模型预测效果,无需上线即可进行效果评估
+- **模型多端部署**:点选式选择模型发布平台、格式,一键导出预测模型,并匹配完善的模型预测部署说明文档,贴心助力产业端到端项目落地
+
+## 安装使用
+- 请参考[安装方式文档](./download.md)下载安装PaddleX可视化客户端。
+- 请参考[快速开始](./quick_start.md)查看[视频教程](./quick_start.md/#视频教程)和[文档教程](./quick_start.md/#文档教程)。

+ 8 - 8
docs/quick_start_API.md

@@ -3,7 +3,7 @@
 
 ## 目录
 - [快速安装](#快速安装)
-    - [PaddleX 2.0.0安装](#PaddleX-200安装)
+    - [PaddleX 2.1.0安装](#PaddleX-210安装)
     - [PaddleX develop安装](#PaddleX-develop安装)
 - [使用前置说明](#使用前置说明)
     - [PaddleX的模型训练](#PaddleX的模型训练)
@@ -18,17 +18,17 @@
     - <a href=#加载训练保存的模型预测>加载训练保存的模型预测</a>
 
 ## 快速安装
-以下安装过程默认用户已安装好**paddlepaddle-gpu或paddlepaddle(版本大于或等于2.1.2)**,paddlepaddle安装方式参照[飞桨官网](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/release/2.0.0/install/pip/windows-pip.html)
+以下安装过程默认用户已安装好**paddlepaddle-gpu或paddlepaddle(版本大于或等于2.2.0)**,paddlepaddle安装方式参照[飞桨官网](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/release/2.0.0/install/pip/windows-pip.html)
 
-### PaddleX 2.0.0安装
+### PaddleX 2.1.0安装
 **我们推荐大家先安装Anacaonda,而后在新建的conoda环境中使用上述pip安装方式**。Anaconda是一个开源的Python发行版本,其包含了conda、Python等180多个科学包及其依赖项。使用Anaconda可以通过创建多个独立的Python环境,避免用户的Python环境安装太多不同版本依赖导致冲突。参考[Anaconda安装PaddleX文档](./appendix/anaconda_install.md)
 
 - Linux / macOS 操作系统
 
-使用pip安装方式安装2.0.0版本:
+使用pip安装方式安装2.1.0版本:
 
 ```commandline
-pip install paddlex==2.0.0 -i https://mirror.baidu.com/pypi/simple
+pip install paddlex==2.1.0 -i https://mirror.baidu.com/pypi/simple
 ```
 
 paddlepaddle已集成pycocotools包,但也有pycocotools无法随paddlepaddle成功安装的情况。因PaddleX依赖pycocotools包,如遇到pycocotools安装失败,可参照如下方式安装pycocotools:
@@ -39,10 +39,10 @@ pip install pycocotools
 ```
 
 - Windows 操作系统
-使用pip安装方式安装2.0.0版本:
+使用pip安装方式安装2.1.0版本:
 
 ```commandline
-pip install paddlex==2.0.0 -i https://mirror.baidu.com/pypi/simple
+pip install paddlex==2.1.0 -i https://mirror.baidu.com/pypi/simple
 ```
 
 因PaddleX依赖pycocotools包,Windows安装时可能会提示`Microsoft Visual C++ 14.0 is required`,从而导致安装出错,[点击下载VC build tools](https://go.microsoft.com/fwlink/?LinkId=691126)安装再执行如下pip命令
@@ -65,7 +65,7 @@ pip install -r requirements.txt
 python setup.py install
 ```
 
-如遇到pycocotools安装失败,参考[PaddleX 2.0.0安装](./install.md#paddlex-200安装)中介绍的解决方法。
+如遇到pycocotools安装失败,参考[PaddleX 2.1.0安装](./install.md#paddlex-210安装)中介绍的解决方法。
 
 ## 使用前置说明
 

+ 2 - 2
docs/quick_start_GUI.md

@@ -18,8 +18,8 @@
 ## 快速安装
 ### 下载安装
 下载地址:https://www.paddlepaddle.org.cn/paddlex
-目前最新版本的GUI(Version 2.0.0)仅提供WIN和Linux版,暂未提供Mac版,若需在Mac上使用GUI,推荐安装Mac版历史版本Version 1.1.7
-- 特别说明:GUI 2.0要求CUDA >=11.0, cuDNN >= 8.0
+目前最新版本的GUI(Version 2.1.0)仅提供WIN和Linux版,暂未提供Mac版,若需在Mac上使用GUI,推荐安装Mac版历史版本Version 1.1.7
+- 特别说明:GUI 2.1要求CUDA >=11.0, cuDNN >= 8.0
 - WIN版下载后双击选择安装路径即可
 - Mac/Linux版下载后解压即可
 

+ 1 - 1
paddlex/__init__.py

@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-__version__ = '2.0.0'
+__version__ = '2.1.0'
 
 from paddlex.utils.env import get_environ_info, init_parallel_env
 init_parallel_env()

+ 3 - 0
paddlex/cls.py

@@ -77,3 +77,6 @@ Xception71 = cv.models.Xception71
 
 ShuffleNetV2 = cv.models.ShuffleNetV2
 ShuffleNetV2_swish = cv.models.ShuffleNetV2_swish
+
+PPLCNet = cv.models.PPLCNet
+PPLCNet_ssld = cv.models.PPLCNet_ssld

+ 2 - 2
paddlex/cv/datasets/voc.py

@@ -217,8 +217,8 @@ class VOCDetection(Dataset):
 
                     if not (x2 >= x1 and y2 >= y1):
                         logging.warning(
-                            "Bounding box for object {} does not satisfy x1 <= x2 and y1 <= y2, "
-                            "so this object is skipped".format(i))
+                            "Bounding box for object {} does not satisfy xmin {} <= xmax {} and ymin {} <= ymax {}, "
+                            "so this object is skipped. xml file: {}".format(i, x1, x2, y1, y2, xml_file))
                         continue
 
                     gt_bbox[i, :] = [x1, y1, x2, y2]

+ 20 - 17
paddlex/cv/models/base.py

@@ -48,6 +48,8 @@ class BaseModel:
         self.train_data_loader = None
         self.eval_data_loader = None
         self.eval_metrics = None
+        self.best_accuracy = -1.
+        self.best_model_epoch = -1
         # 是否使用多卡间同步BatchNorm均值和方差
         self.sync_bn = False
         self.status = 'Normal'
@@ -115,6 +117,8 @@ class BaseModel:
             with open(osp.join(resume_checkpoint, "model.yml")) as f:
                 info = yaml.load(f.read(), Loader=yaml.Loader)
                 self.completed_epochs = info['completed_epochs']
+                self.best_accuracy = info['_Attributes']['best_accuracy']
+                self.best_model_epoch = info['_Attributes']['best_model_epoch']
             load_checkpoint(
                 self.net,
                 self.optimizer,
@@ -125,7 +129,12 @@ class BaseModel:
         info = dict()
         info['version'] = paddlex.__version__
         info['Model'] = self.__class__.__name__
-        info['_Attributes'] = {'model_type': self.model_type}
+        info['_Attributes'] = dict(
+            [('model_type', self.model_type),
+             ('num_classes', self.num_classes), ('labels', self.labels),
+             ('fixed_input_shape', self.fixed_input_shape),
+             ('best_accuracy', self.best_accuracy),
+             ('best_model_epoch', self.best_model_epoch)])
         if 'self' in self.init_params:
             del self.init_params['self']
         if '__class__' in self.init_params:
@@ -137,10 +146,6 @@ class BaseModel:
 
         info['_init_params'] = self.init_params
 
-        info['_Attributes']['num_classes'] = self.num_classes
-        info['_Attributes']['labels'] = self.labels
-        info['_Attributes']['fixed_input_shape'] = self.fixed_input_shape
-
         try:
             primary_metric_key = list(self.eval_metrics.keys())[0]
             primary_metric_value = float(self.eval_metrics[primary_metric_key])
@@ -317,9 +322,6 @@ class BaseModel:
             eval_batch_size = train_batch_size
             eval_epoch_time = 0
 
-        best_accuracy_key = ""
-        best_accuracy = -1.0
-        best_model_epoch = -1
         current_step = 0
         for i in range(start_epoch, num_epochs):
             self.net.train()
@@ -384,11 +386,12 @@ class BaseModel:
                          .format(i + 1, train_avg_metrics.log()))
             self.completed_epochs += 1
 
-            # 每间隔save_interval_epochs, 在验证集上评估和对模型进行保存
             if ema is not None:
                 weight = copy.deepcopy(self.net.state_dict())
                 self.net.set_state_dict(ema.apply())
             eval_epoch_tic = time.time()
+
+            # 每间隔save_interval_epochs, 在验证集上评估和对模型进行保存
             if (i + 1) % save_interval_epochs == 0 or i == num_epochs - 1:
                 if eval_dataset is not None and eval_dataset.num_samples > 0:
                     eval_result = self.evaluate(
@@ -410,16 +413,16 @@ class BaseModel:
                             i + 1, dict2str(self.eval_metrics)))
                         best_accuracy_key = list(self.eval_metrics.keys())[0]
                         current_accuracy = self.eval_metrics[best_accuracy_key]
-                        if current_accuracy > best_accuracy:
-                            best_accuracy = current_accuracy
-                            best_model_epoch = i + 1
+                        if current_accuracy > self.best_accuracy:
+                            self.best_accuracy = current_accuracy
+                            self.best_model_epoch = i + 1
                             best_model_dir = osp.join(save_dir, "best_model")
                             self.save_model(save_dir=best_model_dir)
-                        if best_model_epoch > 0:
+                        if self.best_model_epoch > 0:
                             logging.info(
                                 'Current evaluated best model on eval_dataset is epoch_{}, {}={}'
-                                .format(best_model_epoch, best_accuracy_key,
-                                        best_accuracy))
+                                .format(self.best_model_epoch,
+                                        best_accuracy_key, self.best_accuracy))
                     eval_epoch_time = time.time() - eval_epoch_tic
 
                 current_save_dir = osp.join(save_dir, "epoch_{}".format(i + 1))
@@ -444,11 +447,11 @@ class BaseModel:
             criterion({'l1_norm', 'fpgm'}, optional): Pruning criterion. Defaults to 'l1_norm'.
             save_dir(str, optional): The directory to save sensitivity file of the model. Defaults to 'output'.
         """
-        if self.__class__.__name__ in ['FasterRCNN', 'MaskRCNN']:
+        if self.__class__.__name__ in {'FasterRCNN', 'MaskRCNN', 'PicoDet'}:
             raise Exception("{} does not support pruning currently!".format(
                 self.__class__.__name__))
 
-        assert criterion in ['l1_norm', 'fpgm'], \
+        assert criterion in {'l1_norm', 'fpgm'}, \
             "Pruning criterion {} is not supported. Please choose from ['l1_norm', 'fpgm']"
         arrange_transforms(
             model_type=self.model_type,

+ 156 - 42
paddlex/cv/models/classifier.py

@@ -37,7 +37,7 @@ __all__ = [
     "DenseNet121", "DenseNet161", "DenseNet169", "DenseNet201", "DenseNet264",
     "HRNet_W18_C", "HRNet_W30_C", "HRNet_W32_C", "HRNet_W40_C", "HRNet_W44_C",
     "HRNet_W48_C", "HRNet_W64_C", "Xception41", "Xception65", "Xception71",
-    "ShuffleNetV2", "ShuffleNetV2_swish"
+    "ShuffleNetV2", "ShuffleNetV2_swish", "PPLCNet", "PPLCNet_ssld"
 ]
 
 
@@ -152,58 +152,59 @@ class BaseClassifier(BaseModel):
                           lr_decay_epochs,
                           lr_decay_gamma,
                           num_steps_each_epoch,
-                          decay_coff=1e-04,
-                          lr_method='Linear',
+                          reg_coeff=1e-04,
+                          scheduler='Piecewise',
                           num_epochs=None):
-        if warmup_steps > 0:
-            if lr_method == 'Linear':
-                if warmup_steps > lr_decay_epochs[0] * num_steps_each_epoch:
-                    logging.error(
-                        "In function train(), parameters should satisfy: "
-                        "warmup_steps <= lr_decay_epochs[0]*num_samples_in_train_dataset.",
-                        exit=False)
-                    logging.error(
-                        "See this doc for more information: "
-                        "https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/parameters.md",
-                        exit=False)
-                    logging.error(
-                        "warmup_steps should be less than {} or lr_decay_epochs[0] greater than {}, "
-                        "please modify 'lr_decay_epochs' or 'warmup_steps' in train function".
-                        format(lr_decay_epochs[0] * num_steps_each_epoch,
-                               warmup_steps // num_steps_each_epoch))
-            elif lr_method == 'Cosine':
-                if num_epochs is None:
-                    logging.error(
-                        "num_epochs must be set when using cosine learning rate method, but received is {}".
-                        format(num_epochs),
-                        exit=False)
-                if warmup_steps > num_epochs * num_steps_each_epoch:
-                    logging.error(
-                        "In function train(), parameters should satisfy: "
-                        "warmup_steps <= num_epochs*num_samples_in_train_dataset.",
-                        exit=False)
-                    logging.error(
-                        "See this doc for more information: "
-                        "https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/parameters.md",
-                        exit=False)
-                    logging.error(
-                        "warmup_steps should be less than {}, "
-                        "please modify 'num_epochs' or 'warmup_steps' in train function".
-                        format(num_epochs * num_steps_each_epoch))
-        if lr_method == 'Linear':
+        if scheduler.lower() == 'piecewise':
+            if warmup_steps > 0 and warmup_steps > lr_decay_epochs[
+                    0] * num_steps_each_epoch:
+                logging.error(
+                    "In function train(), parameters must satisfy: "
+                    "warmup_steps <= lr_decay_epochs[0] * num_samples_in_train_dataset. "
+                    "See this doc for more information: "
+                    "https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/parameters.md",
+                    exit=False)
+                logging.error(
+                    "Either `warmup_steps` be less than {} or lr_decay_epochs[0] be greater than {} "
+                    "must be satisfied, please modify 'warmup_steps' or 'lr_decay_epochs' in train function".
+                    format(lr_decay_epochs[0] * num_steps_each_epoch,
+                           warmup_steps // num_steps_each_epoch),
+                    exit=True)
             boundaries = [b * num_steps_each_epoch for b in lr_decay_epochs]
             values = [
                 learning_rate * (lr_decay_gamma**i)
                 for i in range(len(lr_decay_epochs) + 1)
             ]
             scheduler = paddle.optimizer.lr.PiecewiseDecay(boundaries, values)
-        elif lr_method == 'Cosine':
+        elif scheduler.lower() == 'cosine':
+            if num_epochs is None:
+                logging.error(
+                    "`num_epochs` must be set while using cosine annealing decay scheduler, but received {}".
+                    format(num_epochs),
+                    exit=False)
+            if warmup_steps > 0 and warmup_steps > num_epochs * num_steps_each_epoch:
+                logging.error(
+                    "In function train(), parameters must satisfy: "
+                    "warmup_steps <= num_epochs * num_samples_in_train_dataset. "
+                    "See this doc for more information: "
+                    "https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/parameters.md",
+                    exit=False)
+                logging.error(
+                    "`warmup_steps` must be less than the total number of steps({}), "
+                    "please modify 'num_epochs' or 'warmup_steps' in train function".
+                    format(num_epochs * num_steps_each_epoch),
+                    exit=True)
             T_max = num_epochs * num_steps_each_epoch - warmup_steps
-            scheduler = paddle.lr.CosineAnnealingDecay(
+            scheduler = paddle.optimizer.lr.CosineAnnealingDecay(
                 learning_rate=learning_rate,
                 T_max=T_max,
                 eta_min=0.0,
                 last_epoch=-1)
+        else:
+            logging.error(
+                "Invalid learning rate scheduler: {}!".format(scheduler),
+                exit=True)
+
         if warmup_steps > 0:
             scheduler = paddle.optimizer.lr.LinearWarmup(
                 learning_rate=scheduler,
@@ -214,7 +215,7 @@ class BaseClassifier(BaseModel):
         optimizer = paddle.optimizer.Momentum(
             scheduler,
             momentum=.9,
-            weight_decay=paddle.regularizer.L2Decay(coeff=decay_coff),
+            weight_decay=paddle.regularizer.L2Decay(coeff=reg_coeff),
             parameters=parameters)
         return optimizer
 
@@ -873,3 +874,116 @@ class ShuffleNetV2_swish(BaseClassifier):
                 shape=image_shape, name='image', dtype='float32')
         ]
         return input_spec
+
+
+class PPLCNet(BaseClassifier):
+    def __init__(self, num_classes=1000, scale=1., **params):
+        supported_scale = [.25, .35, .5, .75, 1., 1.5, 2., 2.5]
+        if scale not in supported_scale:
+            logging.warning("scale={} is not supported by PPLCNet, "
+                            "scale is forcibly set to 1.0".format(scale))
+            scale = 1.0
+        model_name = 'PPLCNet_x' + str(float(scale)).replace('.', '_')
+        super(PPLCNet, self).__init__(
+            model_name=model_name, num_classes=num_classes, **params)
+
+    def train(self,
+              num_epochs,
+              train_dataset,
+              train_batch_size=64,
+              eval_dataset=None,
+              optimizer=None,
+              save_interval_epochs=1,
+              log_interval_steps=10,
+              save_dir='output',
+              pretrain_weights='IMAGENET',
+              learning_rate=.1,
+              warmup_steps=0,
+              warmup_start_lr=0.0,
+              lr_decay_epochs=(30, 60, 90),
+              lr_decay_gamma=0.1,
+              label_smoothing=None,
+              early_stop=False,
+              early_stop_patience=5,
+              use_vdl=True,
+              resume_checkpoint=None):
+        """
+        Train the model.
+        Args:
+            num_epochs(int): The number of epochs.
+            train_dataset(paddlex.dataset): Training dataset.
+            train_batch_size(int, optional): Total batch size among all cards used in training. Defaults to 64.
+            eval_dataset(paddlex.dataset, optional):
+                Evaluation dataset. If None, the model will not be evaluated during training process. Defaults to None.
+            optimizer(paddle.optimizer.Optimizer or None, optional):
+                Optimizer used for training. If None, a default optimizer is used. Defaults to None.
+            save_interval_epochs(int, optional): Epoch interval for saving the model. Defaults to 1.
+            log_interval_steps(int, optional): Step interval for printing training information. Defaults to 10.
+            save_dir(str, optional): Directory to save the model. Defaults to 'output'.
+            pretrain_weights(str or None, optional):
+                None or name/path of pretrained weights. If None, no pretrained weights will be loaded.
+                At most one of `resume_checkpoint` and `pretrain_weights` can be set simultaneously.
+                Defaults to 'IMAGENET'.
+            learning_rate(float, optional): Learning rate for training. Defaults to .025.
+            warmup_steps(int, optional): The number of steps of warm-up training. Defaults to 0.
+            warmup_start_lr(float, optional): Start learning rate of warm-up training. Defaults to 0..
+            lr_decay_epochs(List[int] or Tuple[int], optional):
+                Epoch milestones for learning rate decay. Defaults to (20, 60, 90).
+            lr_decay_gamma(float, optional): Gamma coefficient of learning rate decay, default .1.
+            label_smoothing(float, bool or None, optional): Whether to adopt label smoothing or not.
+                If float, the value refer to epsilon coefficient of label smoothing. If False or None, label smoothing
+                will not be adopted. Otherwise, adopt label smoothing with epsilon equals to 0.1. Defaults to None.
+            early_stop(bool, optional): Whether to adopt early stop strategy. Defaults to False.
+            early_stop_patience(int, optional): Early stop patience. Defaults to 5.
+            use_vdl(bool, optional): Whether to use VisualDL to monitor the training process. Defaults to True.
+            resume_checkpoint(str or None, optional): The path of the checkpoint to resume training from.
+                If None, no training checkpoint will be resumed. At most one of `resume_checkpoint` and
+                `pretrain_weights` can be set simultaneously. Defaults to None.
+
+        """
+        if optimizer is None:
+            num_steps_each_epoch = len(train_dataset) // train_batch_size
+            optimizer = self.default_optimizer(
+                parameters=self.net.parameters(),
+                learning_rate=learning_rate,
+                warmup_steps=warmup_steps,
+                warmup_start_lr=warmup_start_lr,
+                lr_decay_epochs=lr_decay_epochs,
+                lr_decay_gamma=lr_decay_gamma,
+                num_steps_each_epoch=num_steps_each_epoch,
+                reg_coeff=3e-5,
+                scheduler='Cosine',
+                num_epochs=num_epochs)
+        super(PPLCNet, self).train(
+            num_epochs=num_epochs,
+            train_dataset=train_dataset,
+            train_batch_size=train_batch_size,
+            eval_dataset=eval_dataset,
+            optimizer=optimizer,
+            save_interval_epochs=save_interval_epochs,
+            log_interval_steps=log_interval_steps,
+            save_dir=save_dir,
+            pretrain_weights=pretrain_weights,
+            learning_rate=learning_rate,
+            warmup_steps=warmup_steps,
+            warmup_start_lr=warmup_start_lr,
+            lr_decay_epochs=lr_decay_epochs,
+            lr_decay_gamma=lr_decay_gamma,
+            label_smoothing=label_smoothing,
+            early_stop=early_stop,
+            early_stop_patience=early_stop_patience,
+            use_vdl=use_vdl,
+            resume_checkpoint=resume_checkpoint)
+
+
+class PPLCNet_ssld(PPLCNet):
+    def __init__(self, num_classes=1000, scale=1., **params):
+        supported_scale = [.5, 1., 2.5]
+        if scale not in supported_scale:
+            logging.warning("scale={} is not supported by PPLCNet, "
+                            "scale is forcibly set to 1.0".format(scale))
+            scale = 1.0
+        model_name = 'PPLCNet_x' + str(float(scale)).replace('.', '_')
+        super(PPLCNet, self).__init__(
+            model_name=model_name, num_classes=num_classes, **params)
+        self.model_name = model_name + '_ssld'

+ 160 - 74
paddlex/cv/models/detector.py

@@ -31,7 +31,7 @@ from paddlex.cv.transforms.batch_operators import BatchCompose, BatchRandomResiz
 from paddlex.cv.transforms import arrange_transforms
 from .base import BaseModel
 from .utils.det_metrics import VOCMetric, COCOMetric
-from .utils.ema import ExponentialMovingAverage
+from paddlex.ppdet.optimizer import ModelEMA
 from paddlex.utils.checkpoint import det_pretrain_weights_dict
 
 __all__ = [
@@ -112,25 +112,66 @@ class BaseDetector(BaseModel):
 
         return outputs
 
-    def default_optimizer(self, parameters, learning_rate, warmup_steps,
-                          warmup_start_lr, lr_decay_epochs, lr_decay_gamma,
-                          num_steps_each_epoch):
-        boundaries = [b * num_steps_each_epoch for b in lr_decay_epochs]
-        values = [(lr_decay_gamma**i) * learning_rate
-                  for i in range(len(lr_decay_epochs) + 1)]
-        scheduler = paddle.optimizer.lr.PiecewiseDecay(
-            boundaries=boundaries, values=values)
-        if warmup_steps > 0:
-            if warmup_steps > lr_decay_epochs[0] * num_steps_each_epoch:
+    def default_optimizer(self,
+                          parameters,
+                          learning_rate,
+                          warmup_steps,
+                          warmup_start_lr,
+                          lr_decay_epochs,
+                          lr_decay_gamma,
+                          num_steps_each_epoch,
+                          reg_coeff=1e-04,
+                          scheduler='Piecewise',
+                          num_epochs=None):
+        if scheduler.lower() == 'piecewise':
+            if warmup_steps > 0 and warmup_steps > lr_decay_epochs[
+                    0] * num_steps_each_epoch:
+                logging.error(
+                    "In function train(), parameters must satisfy: "
+                    "warmup_steps <= lr_decay_epochs[0] * num_samples_in_train_dataset. "
+                    "See this doc for more information: "
+                    "https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/parameters.md",
+                    exit=False)
+                logging.error(
+                    "Either `warmup_steps` be less than {} or lr_decay_epochs[0] be greater than {} "
+                    "must be satisfied, please modify 'warmup_steps' or 'lr_decay_epochs' in train function".
+                    format(lr_decay_epochs[0] * num_steps_each_epoch,
+                           warmup_steps // num_steps_each_epoch),
+                    exit=True)
+            boundaries = [b * num_steps_each_epoch for b in lr_decay_epochs]
+            values = [(lr_decay_gamma**i) * learning_rate
+                      for i in range(len(lr_decay_epochs) + 1)]
+            scheduler = paddle.optimizer.lr.PiecewiseDecay(boundaries, values)
+        elif scheduler.lower() == 'cosine':
+            if num_epochs is None:
                 logging.error(
-                    "In function train(), parameters should satisfy: "
-                    "warmup_steps <= lr_decay_epochs[0]*num_samples_in_train_dataset",
+                    "`num_epochs` must be set while using cosine annealing decay scheduler, but received {}".
+                    format(num_epochs),
                     exit=False)
+            if warmup_steps > 0 and warmup_steps > num_epochs * num_steps_each_epoch:
                 logging.error(
+                    "In function train(), parameters must satisfy: "
+                    "warmup_steps <= num_epochs * num_samples_in_train_dataset. "
                     "See this doc for more information: "
                     "https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/parameters.md",
                     exit=False)
+                logging.error(
+                    "`warmup_steps` must be less than the total number of steps({}), "
+                    "please modify 'num_epochs' or 'warmup_steps' in train function".
+                    format(num_epochs * num_steps_each_epoch),
+                    exit=True)
+            T_max = num_epochs * num_steps_each_epoch - warmup_steps
+            scheduler = paddle.optimizer.lr.CosineAnnealingDecay(
+                learning_rate=learning_rate,
+                T_max=T_max,
+                eta_min=0.0,
+                last_epoch=-1)
+        else:
+            logging.error(
+                "Invalid learning rate scheduler: {}!".format(scheduler),
+                exit=True)
 
+        if warmup_steps > 0:
             scheduler = paddle.optimizer.lr.LinearWarmup(
                 learning_rate=scheduler,
                 warmup_steps=warmup_steps,
@@ -139,7 +180,7 @@ class BaseDetector(BaseModel):
         optimizer = paddle.optimizer.Momentum(
             scheduler,
             momentum=.9,
-            weight_decay=paddle.regularizer.L2Decay(coeff=1e-04),
+            weight_decay=paddle.regularizer.L2Decay(coeff=reg_coeff),
             parameters=parameters)
         return optimizer
 
@@ -275,8 +316,7 @@ class BaseDetector(BaseModel):
                                  'ESNet_' in self.backbone_name))
 
         if use_ema:
-            ema = ExponentialMovingAverage(
-                decay=.9998, model=self.net, use_thres_step=True)
+            ema = ModelEMA(model=self.net, decay=.9998, use_thres_step=True)
         else:
             ema = None
         # start train loop
@@ -593,8 +633,8 @@ class PicoDet(BaseDetector):
                  num_classes=80,
                  backbone='ESNet_m',
                  nms_score_threshold=.025,
-                 nms_top_k=1000,
-                 nms_keep_top_k=100,
+                 nms_topk=1000,
+                 nms_keep_topk=100,
                  nms_iou_threshold=.6,
                  **params):
         self.init_params = locals()
@@ -691,8 +731,8 @@ class PicoDet(BaseDetector):
             assigner = ppdet.modeling.SimOTAAssigner(
                 candidate_topk=10, iou_weight=6, num_classes=num_classes)
             nms = ppdet.modeling.MultiClassNMS(
-                nms_top_k=nms_top_k,
-                keep_top_k=nms_keep_top_k,
+                nms_top_k=nms_topk,
+                keep_top_k=nms_keep_topk,
                 score_threshold=nms_score_threshold,
                 nms_threshold=nms_iou_threshold)
             head = ppdet.modeling.PicoHead(
@@ -783,6 +823,92 @@ class PicoDet(BaseDetector):
         self.fixed_input_shape = image_shape
         return self._define_input_spec(image_shape)
 
+    def train(self,
+              num_epochs,
+              train_dataset,
+              train_batch_size=64,
+              eval_dataset=None,
+              optimizer=None,
+              save_interval_epochs=1,
+              log_interval_steps=10,
+              save_dir='output',
+              pretrain_weights='IMAGENET',
+              learning_rate=.001,
+              warmup_steps=0,
+              warmup_start_lr=0.0,
+              lr_decay_epochs=(216, 243),
+              lr_decay_gamma=0.1,
+              metric=None,
+              use_ema=False,
+              early_stop=False,
+              early_stop_patience=5,
+              use_vdl=True,
+              resume_checkpoint=None):
+        """
+        Train the model.
+        Args:
+            num_epochs(int): The number of epochs.
+            train_dataset(paddlex.dataset): Training dataset.
+            train_batch_size(int, optional): Total batch size among all cards used in training. Defaults to 64.
+            eval_dataset(paddlex.dataset, optional):
+                Evaluation dataset. If None, the model will not be evaluated during training process. Defaults to None.
+            optimizer(paddle.optimizer.Optimizer or None, optional):
+                Optimizer used for training. If None, a default optimizer is used. Defaults to None.
+            save_interval_epochs(int, optional): Epoch interval for saving the model. Defaults to 1.
+            log_interval_steps(int, optional): Step interval for printing training information. Defaults to 10.
+            save_dir(str, optional): Directory to save the model. Defaults to 'output'.
+            pretrain_weights(str or None, optional):
+                None or name/path of pretrained weights. If None, no pretrained weights will be loaded. Defaults to 'IMAGENET'.
+            learning_rate(float, optional): Learning rate for training. Defaults to .001.
+            warmup_steps(int, optional): The number of steps of warm-up training. Defaults to 0.
+            warmup_start_lr(float, optional): Start learning rate of warm-up training. Defaults to 0..
+            lr_decay_epochs(list or tuple, optional): Epoch milestones for learning rate decay. Defaults to (216, 243).
+            lr_decay_gamma(float, optional): Gamma coefficient of learning rate decay. Defaults to .1.
+            metric({'VOC', 'COCO', None}, optional):
+                Evaluation metric. If None, determine the metric according to the dataset format. Defaults to None.
+            use_ema(bool, optional): Whether to use exponential moving average strategy. Defaults to False.
+            early_stop(bool, optional): Whether to adopt early stop strategy. Defaults to False.
+            early_stop_patience(int, optional): Early stop patience. Defaults to 5.
+            use_vdl(bool, optional): Whether to use VisualDL to monitor the training process. Defaults to True.
+            resume_checkpoint(str or None, optional): The path of the checkpoint to resume training from.
+                If None, no training checkpoint will be resumed. At most one of `resume_checkpoint` and
+                `pretrain_weights` can be set simultaneously. Defaults to None.
+        """
+        if optimizer is None:
+            num_steps_each_epoch = len(train_dataset) // train_batch_size
+            optimizer = self.default_optimizer(
+                parameters=self.net.parameters(),
+                learning_rate=learning_rate,
+                warmup_steps=warmup_steps,
+                warmup_start_lr=warmup_start_lr,
+                lr_decay_epochs=lr_decay_epochs,
+                lr_decay_gamma=lr_decay_gamma,
+                num_steps_each_epoch=num_steps_each_epoch,
+                reg_coeff=4e-05,
+                scheduler='Cosine',
+                num_epochs=num_epochs)
+        super(PicoDet, self).train(
+            num_epochs=num_epochs,
+            train_dataset=train_dataset,
+            train_batch_size=train_batch_size,
+            eval_dataset=eval_dataset,
+            optimizer=optimizer,
+            save_interval_epochs=save_interval_epochs,
+            log_interval_steps=log_interval_steps,
+            save_dir=save_dir,
+            pretrain_weights=pretrain_weights,
+            learning_rate=learning_rate,
+            warmup_steps=warmup_steps,
+            warmup_start_lr=warmup_start_lr,
+            lr_decay_epochs=lr_decay_epochs,
+            lr_decay_gamma=lr_decay_gamma,
+            metric=metric,
+            use_ema=use_ema,
+            early_stop=early_stop,
+            early_stop_patience=early_stop_patience,
+            use_vdl=use_vdl,
+            resume_checkpoint=resume_checkpoint)
+
 
 class YOLOv3(BaseDetector):
     def __init__(self,
@@ -1226,42 +1352,22 @@ class FasterRCNN(BaseDetector):
         """
         if train_dataset.pos_num < len(train_dataset.file_list):
             train_dataset.num_workers = 0
-            if train_batch_size != 1:
-                train_batch_size = 1
-                logging.warning(
-                    "Training RCNN models with negative samples only support batch size equals to 1 "
-                    "on a single gpu/cpu card, `train_batch_size` is forcibly set to 1."
-                )
-            nranks = paddle.distributed.get_world_size()
-            local_rank = paddle.distributed.get_rank()
-            # single card training
-            if nranks < 2 or local_rank == 0:
-                super(FasterRCNN, self).train(
-                    num_epochs, train_dataset, train_batch_size, eval_dataset,
-                    optimizer, save_interval_epochs, log_interval_steps,
-                    save_dir, pretrain_weights, learning_rate, warmup_steps,
-                    warmup_start_lr, lr_decay_epochs, lr_decay_gamma, metric,
-                    use_ema, early_stop, early_stop_patience, use_vdl,
-                    resume_checkpoint)
-        else:
-            super(FasterRCNN, self).train(
-                num_epochs, train_dataset, train_batch_size, eval_dataset,
-                optimizer, save_interval_epochs, log_interval_steps, save_dir,
-                pretrain_weights, learning_rate, warmup_steps, warmup_start_lr,
-                lr_decay_epochs, lr_decay_gamma, metric, use_ema, early_stop,
-                early_stop_patience, use_vdl, resume_checkpoint)
+        super(FasterRCNN, self).train(
+            num_epochs, train_dataset, train_batch_size, eval_dataset,
+            optimizer, save_interval_epochs, log_interval_steps, save_dir,
+            pretrain_weights, learning_rate, warmup_steps, warmup_start_lr,
+            lr_decay_epochs, lr_decay_gamma, metric, use_ema, early_stop,
+            early_stop_patience, use_vdl, resume_checkpoint)
 
     def _compose_batch_transform(self, transforms, mode='train'):
         if mode == 'train':
             default_batch_transforms = [
                 _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
             ]
-            collate_batch = False
         else:
             default_batch_transforms = [
                 _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
             ]
-            collate_batch = True
         custom_batch_transforms = []
         for i, op in enumerate(transforms.transforms):
             if isinstance(op, (BatchRandomResize, BatchRandomResizeByShort)):
@@ -1274,7 +1380,7 @@ class FasterRCNN(BaseDetector):
 
         batch_transforms = BatchCompose(
             custom_batch_transforms + default_batch_transforms,
-            collate_batch=collate_batch)
+            collate_batch=False)
 
         return batch_transforms
 
@@ -2074,42 +2180,22 @@ class MaskRCNN(BaseDetector):
         """
         if train_dataset.pos_num < len(train_dataset.file_list):
             train_dataset.num_workers = 0
-            if train_batch_size != 1:
-                train_batch_size = 1
-                logging.warning(
-                    "Training RCNN models with negative samples only support batch size equals to 1 "
-                    "on a single gpu/cpu card, `train_batch_size` is forcibly set to 1."
-                )
-            nranks = paddle.distributed.get_world_size()
-            local_rank = paddle.distributed.get_rank()
-            # single card training
-            if nranks < 2 or local_rank == 0:
-                super(MaskRCNN, self).train(
-                    num_epochs, train_dataset, train_batch_size, eval_dataset,
-                    optimizer, save_interval_epochs, log_interval_steps,
-                    save_dir, pretrain_weights, learning_rate, warmup_steps,
-                    warmup_start_lr, lr_decay_epochs, lr_decay_gamma, metric,
-                    use_ema, early_stop, early_stop_patience, use_vdl,
-                    resume_checkpoint)
-        else:
-            super(MaskRCNN, self).train(
-                num_epochs, train_dataset, train_batch_size, eval_dataset,
-                optimizer, save_interval_epochs, log_interval_steps, save_dir,
-                pretrain_weights, learning_rate, warmup_steps, warmup_start_lr,
-                lr_decay_epochs, lr_decay_gamma, metric, use_ema, early_stop,
-                early_stop_patience, use_vdl, resume_checkpoint)
+        super(MaskRCNN, self).train(
+            num_epochs, train_dataset, train_batch_size, eval_dataset,
+            optimizer, save_interval_epochs, log_interval_steps, save_dir,
+            pretrain_weights, learning_rate, warmup_steps, warmup_start_lr,
+            lr_decay_epochs, lr_decay_gamma, metric, use_ema, early_stop,
+            early_stop_patience, use_vdl, resume_checkpoint)
 
     def _compose_batch_transform(self, transforms, mode='train'):
         if mode == 'train':
             default_batch_transforms = [
                 _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
             ]
-            collate_batch = False
         else:
             default_batch_transforms = [
                 _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
             ]
-            collate_batch = True
         custom_batch_transforms = []
         for i, op in enumerate(transforms.transforms):
             if isinstance(op, (BatchRandomResize, BatchRandomResizeByShort)):
@@ -2122,7 +2208,7 @@ class MaskRCNN(BaseDetector):
 
         batch_transforms = BatchCompose(
             custom_batch_transforms + default_batch_transforms,
-            collate_batch=collate_batch)
+            collate_batch=False)
 
         return batch_transforms
 

+ 0 - 48
paddlex/cv/models/utils/ema.py

@@ -1,48 +0,0 @@
-# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
-#
-# 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.
-
-import paddle
-
-
-class ExponentialMovingAverage(object):
-    def __init__(self, decay, model, use_thres_step=False):
-        self.step = 0
-        self.decay = decay
-        self.shadow = dict()
-        for k, v in model.state_dict().items():
-            self.shadow[k] = paddle.zeros_like(v)
-        self.use_thres_step = use_thres_step
-
-    def update(self, model):
-        if self.use_thres_step:
-            decay = min(self.decay, (1 + self.step) / (10 + self.step))
-        else:
-            decay = self.decay
-        self._decay = decay
-        model_dict = model.state_dict()
-        for k, v in self.shadow.items():
-            v = decay * v + (1 - decay) * model_dict[k]
-            v.stop_gradient = True
-            self.shadow[k] = v
-        self.step += 1
-
-    def apply(self):
-        if self.step == 0:
-            return self.shadow
-        state_dict = dict()
-        for k, v in self.shadow.items():
-            v = v / (1 - self._decay**self.step)
-            v.stop_gradient = True
-            state_dict[k] = v
-        return state_dict

+ 1 - 1
paddlex/paddleseg/cvlibs/param_init.py

@@ -58,7 +58,7 @@ def normal_init(param, **kwargs):
 
 
 def kaiming_normal_init(param, **kwargs):
-    """
+    r"""
     Initialize the input tensor with Kaiming Normal initialization.
 
     This function implements the `param` initialization from the paper

+ 1 - 1
paddlex/paddleseg/models/losses/binary_cross_entropy_loss.py

@@ -21,7 +21,7 @@ from paddlex.paddleseg.cvlibs import manager
 
 @manager.LOSSES.add_component
 class BCELoss(nn.Layer):
-    """
+    r"""
     This operator combines the sigmoid layer and the :ref:`api_nn_loss_BCELoss` layer.
     Also, we can see it as the combine of ``sigmoid_cross_entropy_with_logits``
     layer and some reduce operations.

+ 3 - 3
paddlex/paddleseg/models/losses/lovasz_loss.py

@@ -41,7 +41,7 @@ class LovaszSoftmaxLoss(nn.Layer):
         self.classes = classes
 
     def forward(self, logits, labels):
-        """
+        r"""
         Forward computation.
 
         Args:
@@ -68,7 +68,7 @@ class LovaszHingeLoss(nn.Layer):
         self.ignore_index = ignore_index
 
     def forward(self, logits, labels):
-        """
+        r"""
         Forward computation.
 
         Args:
@@ -111,7 +111,7 @@ def binary_channel_to_unary(logits, eps=1e-9):
 
 
 def lovasz_hinge_flat(logits, labels):
-    """
+    r"""
     Binary Lovasz hinge loss.
 
     Args:

+ 24 - 5
paddlex/ppdet/engine/trainer.py

@@ -33,6 +33,7 @@ from paddle.static import InputSpec
 from paddlex.ppdet.optimizer import ModelEMA
 
 from paddlex.ppdet.core.workspace import create
+from paddlex.ppdet.modeling.architectures.meta_arch import BaseArch
 from paddlex.ppdet.utils.checkpoint import load_weight, load_pretrain_weight
 from paddlex.ppdet.utils.visualizer import visualize_results, save_result
 from paddlex.ppdet.metrics import Metric, COCOMetric, VOCMetric, WiderFaceMetric, get_infer_results, KeyPointTopDownCOCOEval, KeyPointTopDownMPIIEval
@@ -111,8 +112,12 @@ class Trainer(object):
         if self.mode == 'eval':
             self._eval_batch_sampler = paddle.io.BatchSampler(
                 self.dataset, batch_size=self.cfg.EvalReader['batch_size'])
-            self.loader = create('{}Reader'.format(self.mode.capitalize()))(
-                self.dataset, cfg.worker_num, self._eval_batch_sampler)
+            reader_name = '{}Reader'.format(self.mode.capitalize())
+            # If metric is VOC, need to be set collate_batch=False.
+            if cfg.metric == 'VOC':
+                cfg[reader_name]['collate_batch'] = False
+            self.loader = create(reader_name)(self.dataset, cfg.worker_num,
+                                              self._eval_batch_sampler)
         # TestDataset build after user set images, skip loader creation here
 
         # build optimizer in train mode
@@ -336,6 +341,12 @@ class Trainer(object):
         assert self.mode == 'train', "Model not in 'train' mode"
         Init_mark = False
 
+        sync_bn = (
+            getattr(self.cfg, 'norm_type', None) in [None, 'sync_bn'] and
+            self.cfg.use_gpu and self._nranks > 1)
+        if sync_bn:
+            self.model = BaseArch.convert_sync_batchnorm(self.model)
+
         model = self.model
         if self.cfg.get('fleet', False):
             model = fleet.distributed_model(model)
@@ -364,7 +375,9 @@ class Trainer(object):
         self.status['training_staus'] = stats.TrainingStats(self.cfg.log_iter)
 
         if self.cfg.get('print_flops', False):
-            self._flops(self.loader)
+            flops_loader = create('{}Reader'.format(self.mode.capitalize()))(
+                self.dataset, self.cfg.worker_num)
+            self._flops(flops_loader)
         profiler_options = self.cfg.get('profiler_options', None)
 
         self._compose_callback.on_train_begin(self.status)
@@ -436,6 +449,9 @@ class Trainer(object):
                         paddle.io.BatchSampler(
                             self._eval_dataset,
                             batch_size=self.cfg.EvalReader['batch_size'])
+                    # If metric is VOC, need to be set collate_batch=False.
+                    if self.cfg.metric == 'VOC':
+                        self.cfg['EvalReader']['collate_batch'] = False
                     self._eval_loader = create('EvalReader')(
                         self._eval_dataset,
                         self.cfg.worker_num,
@@ -463,7 +479,9 @@ class Trainer(object):
         self.status['mode'] = 'eval'
         self.model.eval()
         if self.cfg.get('print_flops', False):
-            self._flops(loader)
+            flops_loader = create('{}Reader'.format(self.mode.capitalize()))(
+                self.dataset, self.cfg.worker_num, self._eval_batch_sampler)
+            self._flops(flops_loader)
         for step_id, data in enumerate(loader):
             self.status['step_id'] = step_id
             self._compose_callback.on_step_begin(self.status)
@@ -514,7 +532,8 @@ class Trainer(object):
         self.status['mode'] = 'test'
         self.model.eval()
         if self.cfg.get('print_flops', False):
-            self._flops(loader)
+            flops_loader = create('TestReader')(self.dataset, 0)
+            self._flops(flops_loader)
         results = []
         for step_id, data in enumerate(loader):
             self.status['step_id'] = step_id

+ 0 - 13
paddlex/ppdet/model_zoo/tests/__init__.py

@@ -1,13 +0,0 @@
-#   Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
-#
-# 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.

+ 0 - 48
paddlex/ppdet/model_zoo/tests/test_get_model.py

@@ -1,48 +0,0 @@
-#   Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
-#
-# 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
-from __future__ import division
-from __future__ import print_function
-
-import os
-import paddle
-import paddlex.ppdet
-import unittest
-
-# NOTE: weights downloading costs time, we choose
-#       a small model for unittesting
-MODEL_NAME = 'ppyolo/ppyolo_tiny_650e_coco'
-
-
-class TestGetConfigFile(unittest.TestCase):
-    def test_main(self):
-        try:
-            cfg_file = ppdet.model_zoo.get_config_file(MODEL_NAME)
-            assert os.path.isfile(cfg_file)
-        except:
-            self.assertTrue(False)
-
-
-class TestGetModel(unittest.TestCase):
-    def test_main(self):
-        try:
-            model = ppdet.model_zoo.get_model(MODEL_NAME)
-            assert isinstance(model, paddle.nn.Layer)
-        except:
-            self.assertTrue(False)
-
-
-if __name__ == '__main__':
-    unittest.main()

+ 0 - 68
paddlex/ppdet/model_zoo/tests/test_list_model.py

@@ -1,68 +0,0 @@
-#   Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
-#
-# 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
-from __future__ import division
-from __future__ import print_function
-
-import unittest
-import paddlex.ppdet
-
-
-class TestListModel(unittest.TestCase):
-    def setUp(self):
-        self._filter = []
-
-    def test_main(self):
-        try:
-            ppdet.model_zoo.list_model(self._filter)
-            self.assertTrue(True)
-        except:
-            self.assertTrue(False)
-
-
-class TestListModelYOLO(TestListModel):
-    def setUp(self):
-        self._filter = ['yolo']
-
-
-class TestListModelRCNN(TestListModel):
-    def setUp(self):
-        self._filter = ['rcnn']
-
-
-class TestListModelSSD(TestListModel):
-    def setUp(self):
-        self._filter = ['ssd']
-
-
-class TestListModelMultiFilter(TestListModel):
-    def setUp(self):
-        self._filter = ['yolo', 'darknet']
-
-
-class TestListModelError(unittest.TestCase):
-    def setUp(self):
-        self._filter = ['xxx']
-
-    def test_main(self):
-        try:
-            ppdet.model_zoo.list_model(self._filter)
-            self.assertTrue(False)
-        except ValueError:
-            self.assertTrue(True)
-
-
-if __name__ == '__main__':
-    unittest.main()

+ 13 - 0
paddlex/ppdet/modeling/architectures/meta_arch.py

@@ -126,3 +126,16 @@ class BaseArch(nn.Layer):
 
     def get_pred(self, ):
         raise NotImplementedError("Should implement get_pred method!")
+
+    @classmethod
+    def convert_sync_batchnorm(cls, layer):
+        layer_output = layer
+        if getattr(layer, 'norm_type', None) == 'sync_bn':
+            layer_output = nn.SyncBatchNorm.convert_sync_batchnorm(layer)
+        else:
+            for name, sublayer in layer.named_children():
+                layer_output.add_sublayer(name,
+                                          cls.convert_sync_batchnorm(sublayer))
+
+        del layer
+        return layer_output

+ 2 - 5
paddlex/ppdet/modeling/backbones/blazenet.py

@@ -58,11 +58,8 @@ class ConvBNLayer(nn.Layer):
                 learning_rate=conv_lr, initializer=KaimingNormal()),
             bias_attr=False)
 
-        if norm_type == 'sync_bn':
-            self._batch_norm = nn.SyncBatchNorm(out_channels)
-        else:
-            self._batch_norm = nn.BatchNorm(
-                out_channels, act=None, use_global_stats=False)
+        if norm_type in ['bn', 'sync_bn']:
+            self._batch_norm = nn.BatchNorm2D(out_channels)
 
     def forward(self, x):
         x = self._conv(x)

+ 1 - 1
paddlex/ppdet/modeling/backbones/esnet.py

@@ -20,7 +20,7 @@ import paddle
 import paddle.nn as nn
 import paddle.nn.functional as F
 from paddle import ParamAttr
-from paddle.nn import Conv2D, MaxPool2D, AdaptiveAvgPool2D, BatchNorm
+from paddle.nn import Conv2D, MaxPool2D, AdaptiveAvgPool2D
 from paddle.nn.initializer import KaimingNormal
 from paddle.regularizer import L2Decay
 

+ 3 - 3
paddlex/ppdet/modeling/backbones/hrnet.py

@@ -62,11 +62,11 @@ class ConvNormLayer(nn.Layer):
             learning_rate=norm_lr, regularizer=L2Decay(norm_decay))
         bias_attr = ParamAttr(
             learning_rate=norm_lr, regularizer=L2Decay(norm_decay))
-        global_stats = True if freeze_norm else False
+        global_stats = True if freeze_norm else None
         if norm_type in ['bn', 'sync_bn']:
-            self.norm = nn.BatchNorm(
+            self.norm = nn.BatchNorm2D(
                 ch_out,
-                param_attr=param_attr,
+                weight_attr=param_attr,
                 bias_attr=bias_attr,
                 use_global_stats=global_stats)
         elif norm_type == 'gn':

+ 3 - 3
paddlex/ppdet/modeling/backbones/lcnet.py

@@ -19,7 +19,7 @@ from __future__ import print_function
 import paddle
 import paddle.nn as nn
 from paddle import ParamAttr
-from paddle.nn import AdaptiveAvgPool2D, BatchNorm, Conv2D, Dropout, Linear
+from paddle.nn import AdaptiveAvgPool2D, Conv2D
 from paddle.regularizer import L2Decay
 from paddle.nn.initializer import KaimingNormal
 
@@ -81,9 +81,9 @@ class ConvBNLayer(nn.Layer):
             weight_attr=ParamAttr(initializer=KaimingNormal()),
             bias_attr=False)
 
-        self.bn = BatchNorm(
+        self.bn = nn.BatchNorm2D(
             num_filters,
-            param_attr=ParamAttr(regularizer=L2Decay(0.0)),
+            weight_attr=ParamAttr(regularizer=L2Decay(0.0)),
             bias_attr=ParamAttr(regularizer=L2Decay(0.0)))
         self.hardswish = nn.Hardswish()
 

+ 14 - 14
paddlex/ppdet/modeling/backbones/lite_hrnet.py

@@ -56,11 +56,11 @@ class ConvNormLayer(nn.Layer):
                 regularizer=L2Decay(norm_decay), )
             bias_attr = ParamAttr(
                 learning_rate=norm_lr, regularizer=L2Decay(norm_decay))
-            global_stats = True if freeze_norm else False
+            global_stats = True if freeze_norm else None
             if norm_type in ['bn', 'sync_bn']:
-                self.norm = nn.BatchNorm(
+                self.norm = nn.BatchNorm2D(
                     ch_out,
-                    param_attr=param_attr,
+                    weight_attr=param_attr,
                     bias_attr=bias_attr,
                     use_global_stats=global_stats, )
             elif norm_type == 'gn':
@@ -582,7 +582,7 @@ class LiteHRNetModule(nn.Layer):
                                 stride=1,
                                 padding=0,
                                 bias=False, ),
-                            nn.BatchNorm(self.in_channels[i]),
+                            nn.BatchNorm2D(self.in_channels[i]),
                             nn.Upsample(
                                 scale_factor=2**(j - i), mode='nearest')))
                 elif j == i:
@@ -601,7 +601,7 @@ class LiteHRNetModule(nn.Layer):
                                         padding=1,
                                         groups=self.in_channels[j],
                                         bias=False, ),
-                                    nn.BatchNorm(self.in_channels[j]),
+                                    nn.BatchNorm2D(self.in_channels[j]),
                                     L.Conv2d(
                                         self.in_channels[j],
                                         self.in_channels[i],
@@ -609,7 +609,7 @@ class LiteHRNetModule(nn.Layer):
                                         stride=1,
                                         padding=0,
                                         bias=False, ),
-                                    nn.BatchNorm(self.in_channels[i])))
+                                    nn.BatchNorm2D(self.in_channels[i])))
                         else:
                             conv_downsamples.append(
                                 nn.Sequential(
@@ -621,7 +621,7 @@ class LiteHRNetModule(nn.Layer):
                                         padding=1,
                                         groups=self.in_channels[j],
                                         bias=False, ),
-                                    nn.BatchNorm(self.in_channels[j]),
+                                    nn.BatchNorm2D(self.in_channels[j]),
                                     L.Conv2d(
                                         self.in_channels[j],
                                         self.in_channels[j],
@@ -629,7 +629,7 @@ class LiteHRNetModule(nn.Layer):
                                         stride=1,
                                         padding=0,
                                         bias=False, ),
-                                    nn.BatchNorm(self.in_channels[j]),
+                                    nn.BatchNorm2D(self.in_channels[j]),
                                     nn.ReLU()))
 
                     fuse_layer.append(nn.Sequential(*conv_downsamples))
@@ -777,7 +777,7 @@ class LiteHRNet(nn.Layer):
                                 padding=1,
                                 groups=num_channels_pre_layer[i],
                                 bias=False),
-                            nn.BatchNorm(num_channels_pre_layer[i]),
+                            nn.BatchNorm2D(num_channels_pre_layer[i]),
                             L.Conv2d(
                                 num_channels_pre_layer[i],
                                 num_channels_cur_layer[i],
@@ -785,7 +785,7 @@ class LiteHRNet(nn.Layer):
                                 stride=1,
                                 padding=0,
                                 bias=False, ),
-                            nn.BatchNorm(num_channels_cur_layer[i]),
+                            nn.BatchNorm2D(num_channels_cur_layer[i]),
                             nn.ReLU()))
                 else:
                     transition_layers.append(None)
@@ -802,7 +802,7 @@ class LiteHRNet(nn.Layer):
                                 stride=2,
                                 padding=1,
                                 bias=False, ),
-                            nn.BatchNorm(num_channels_pre_layer[-1]),
+                            nn.BatchNorm2D(num_channels_pre_layer[-1]),
                             L.Conv2d(
                                 num_channels_pre_layer[-1],
                                 num_channels_cur_layer[i]
@@ -812,9 +812,9 @@ class LiteHRNet(nn.Layer):
                                 stride=1,
                                 padding=0,
                                 bias=False, ),
-                            nn.BatchNorm(num_channels_cur_layer[i]
-                                         if j == i - num_branches_pre else
-                                         num_channels_pre_layer[-1]),
+                            nn.BatchNorm2D(num_channels_cur_layer[i]
+                                           if j == i - num_branches_pre else
+                                           num_channels_pre_layer[-1]),
                             nn.ReLU()))
                 transition_layers.append(nn.Sequential(*conv_downsamples))
         return nn.LayerList(transition_layers)

+ 2 - 9
paddlex/ppdet/modeling/backbones/mobilenet_v1.py

@@ -59,16 +59,9 @@ class ConvBNLayer(nn.Layer):
 
         param_attr = ParamAttr(regularizer=L2Decay(norm_decay))
         bias_attr = ParamAttr(regularizer=L2Decay(norm_decay))
-        if norm_type == 'sync_bn':
-            self._batch_norm = nn.SyncBatchNorm(
+        if norm_type in ['sync_bn', 'bn']:
+            self._batch_norm = nn.BatchNorm2D(
                 out_channels, weight_attr=param_attr, bias_attr=bias_attr)
-        else:
-            self._batch_norm = nn.BatchNorm(
-                out_channels,
-                act=None,
-                param_attr=param_attr,
-                bias_attr=bias_attr,
-                use_global_stats=False)
 
     def forward(self, x):
         x = self._conv(x)

+ 4 - 8
paddlex/ppdet/modeling/backbones/mobilenet_v3.py

@@ -74,15 +74,11 @@ class ConvBNLayer(nn.Layer):
             learning_rate=norm_lr,
             regularizer=L2Decay(norm_decay),
             trainable=False if freeze_norm else True)
-        global_stats = True if freeze_norm else False
-        if norm_type == 'sync_bn':
-            self.bn = nn.SyncBatchNorm(
-                out_c, weight_attr=param_attr, bias_attr=bias_attr)
-        else:
-            self.bn = nn.BatchNorm(
+        global_stats = True if freeze_norm else None
+        if norm_type in ['sync_bn', 'bn']:
+            self.bn = nn.BatchNorm2D(
                 out_c,
-                act=None,
-                param_attr=param_attr,
+                weight_attr=param_attr,
                 bias_attr=bias_attr,
                 use_global_stats=global_stats)
         norm_params = self.bn.parameters()

+ 4 - 8
paddlex/ppdet/modeling/backbones/resnet.py

@@ -100,15 +100,11 @@ class ConvNormLayer(nn.Layer):
             regularizer=L2Decay(norm_decay),
             trainable=False if freeze_norm else True)
 
-        global_stats = True if freeze_norm else False
-        if norm_type == 'sync_bn':
-            self.norm = nn.SyncBatchNorm(
-                ch_out, weight_attr=param_attr, bias_attr=bias_attr)
-        else:
-            self.norm = nn.BatchNorm(
+        global_stats = True if freeze_norm else None
+        if norm_type in ['sync_bn', 'bn']:
+            self.norm = nn.BatchNorm2D(
                 ch_out,
-                act=None,
-                param_attr=param_attr,
+                weight_attr=param_attr,
                 bias_attr=bias_attr,
                 use_global_stats=global_stats)
         norm_params = self.norm.parameters()

+ 10 - 5
paddlex/ppdet/modeling/backbones/shufflenet_v2.py

@@ -19,7 +19,8 @@ from __future__ import print_function
 import paddle
 import paddle.nn as nn
 from paddle import ParamAttr
-from paddle.nn import Conv2D, MaxPool2D, AdaptiveAvgPool2D, BatchNorm
+import paddle.nn.functional as F
+from paddle.nn import Conv2D, MaxPool2D, AdaptiveAvgPool2D, BatchNorm2D
 from paddle.nn.initializer import KaimingNormal
 from paddle.regularizer import L2Decay
 
@@ -51,15 +52,19 @@ class ConvBNLayer(nn.Layer):
             weight_attr=ParamAttr(initializer=KaimingNormal()),
             bias_attr=False)
 
-        self._batch_norm = BatchNorm(
+        self._batch_norm = BatchNorm2D(
             out_channels,
-            param_attr=ParamAttr(regularizer=L2Decay(0.0)),
-            bias_attr=ParamAttr(regularizer=L2Decay(0.0)),
-            act=act)
+            weight_attr=ParamAttr(regularizer=L2Decay(0.0)),
+            bias_attr=ParamAttr(regularizer=L2Decay(0.0)))
+        if act == "hard_swish":
+            act = 'hardswish'
+        self.act = act
 
     def forward(self, inputs):
         y = self._conv(inputs)
         y = self._batch_norm(y)
+        if self.act:
+            y = getattr(F, self.act)(y)
         return y
 
 

+ 6 - 1
paddlex/ppdet/modeling/backbones/swin_transformer.py

@@ -493,8 +493,13 @@ class BasicLayer(nn.Layer):
         cnt = 0
         for h in h_slices:
             for w in w_slices:
-                img_mask[:, h, w, :] = cnt
+                try:
+                    img_mask[:, h, w, :] = cnt
+                except:
+                    pass
+
                 cnt += 1
+
         mask_windows = window_partition(
             img_mask, self.window_size)  # nW, window_size, window_size, 1
         mask_windows = mask_windows.reshape(

+ 1 - 4
paddlex/ppdet/modeling/layers.py

@@ -176,12 +176,9 @@ class ConvNormLayer(nn.Layer):
             learning_rate=norm_lr,
             regularizer=L2Decay(norm_decay)
             if norm_decay is not None else None)
-        if norm_type == 'bn':
+        if norm_type in ['bn', 'sync_bn']:
             self.norm = nn.BatchNorm2D(
                 ch_out, weight_attr=param_attr, bias_attr=bias_attr)
-        elif norm_type == 'sync_bn':
-            self.norm = nn.SyncBatchNorm(
-                ch_out, weight_attr=param_attr, bias_attr=bias_attr)
         elif norm_type == 'gn':
             self.norm = nn.GroupNorm(
                 num_groups=norm_groups,

+ 15 - 19
paddlex/ppdet/modeling/mot/tracker/base_jde_tracker.py

@@ -102,31 +102,26 @@ class BaseTrack(object):
 @register
 @serializable
 class STrack(BaseTrack):
-    def __init__(self,
-                 tlwh,
-                 score,
-                 temp_feat,
-                 num_classes,
-                 cls_id,
-                 buff_size=30):
-        # object class id
-        self.cls_id = cls_id
+    def __init__(self, tlwh, score, cls_id, buff_size=30, temp_feat=None):
         # wait activate
         self._tlwh = np.asarray(tlwh, dtype=np.float)
+        self.score = score
+        self.cls_id = cls_id
+        self.track_len = 0
+
         self.kalman_filter = None
         self.mean, self.covariance = None, None
         self.is_activated = False
 
-        self.score = score
-        self.track_len = 0
-
-        self.smooth_feat = None
-        self.update_features(temp_feat)
-        self.features = deque([], maxlen=buff_size)
-        self.alpha = 0.9
+        self.use_reid = True if temp_feat is not None else False
+        if self.use_reid:
+            self.smooth_feat = None
+            self.update_features(temp_feat)
+            self.features = deque([], maxlen=buff_size)
+            self.alpha = 0.9
 
     def update_features(self, feat):
-        # L2 normalizing
+        # L2 normalizing, this function has no use for BYTETracker
         feat /= np.linalg.norm(feat)
         self.curr_feat = feat
         if self.smooth_feat is None:
@@ -182,7 +177,8 @@ class STrack(BaseTrack):
     def re_activate(self, new_track, frame_id, new_id=False):
         self.mean, self.covariance = self.kalman_filter.update(
             self.mean, self.covariance, self.tlwh_to_xyah(new_track.tlwh))
-        self.update_features(new_track.curr_feat)
+        if self.use_reid:
+            self.update_features(new_track.curr_feat)
         self.track_len = 0
         self.state = TrackState.Tracked
         self.is_activated = True
@@ -201,7 +197,7 @@ class STrack(BaseTrack):
         self.is_activated = True  # set flag 'activated'
 
         self.score = new_track.score
-        if update_feature:
+        if update_feature and self.use_reid:
             self.update_features(new_track.curr_feat)
 
     @property

+ 85 - 27
paddlex/ppdet/modeling/mot/tracker/jde_tracker.py

@@ -58,6 +58,7 @@ class JDETracker(object):
     """
 
     def __init__(self,
+                 use_byte=False,
                  num_classes=1,
                  det_thresh=0.3,
                  track_buffer=30,
@@ -66,11 +67,14 @@ class JDETracker(object):
                  tracked_thresh=0.7,
                  r_tracked_thresh=0.5,
                  unconfirmed_thresh=0.7,
-                 motion='KalmanFilter',
                  conf_thres=0,
+                 match_thres=0.8,
+                 low_conf_thres=0.2,
+                 motion='KalmanFilter',
                  metric_type='euclidean'):
+        self.use_byte = use_byte
         self.num_classes = num_classes
-        self.det_thresh = det_thresh
+        self.det_thresh = det_thresh if not use_byte else conf_thres + 0.1
         self.track_buffer = track_buffer
         self.min_box_area = min_box_area
         self.vertical_ratio = vertical_ratio
@@ -78,9 +82,12 @@ class JDETracker(object):
         self.tracked_thresh = tracked_thresh
         self.r_tracked_thresh = r_tracked_thresh
         self.unconfirmed_thresh = unconfirmed_thresh
+        self.conf_thres = conf_thres
+        self.match_thres = match_thres
+        self.low_conf_thres = low_conf_thres
+
         if motion == 'KalmanFilter':
             self.motion = KalmanFilter()
-        self.conf_thres = conf_thres
         self.metric_type = metric_type
 
         self.frame_id = 0
@@ -91,7 +98,7 @@ class JDETracker(object):
         self.max_time_lost = 0
         # max_time_lost will be calculated: int(frame_rate / 30.0 * track_buffer)
 
-    def update(self, pred_dets, pred_embs):
+    def update(self, pred_dets, pred_embs=None):
         """
         Processes the image frame and finds bounding box(detections).
         Associates the detection with corresponding tracklets and also handles
@@ -123,7 +130,10 @@ class JDETracker(object):
         for cls_id in range(self.num_classes):
             cls_idx = (pred_dets[:, 5:] == cls_id).squeeze(-1)
             pred_dets_dict[cls_id] = pred_dets[cls_idx]
-            pred_embs_dict[cls_id] = pred_embs[cls_idx]
+            if pred_embs is not None:
+                pred_embs_dict[cls_id] = pred_embs[cls_idx]
+            else:
+                pred_embs_dict[cls_id] = None
 
         for cls_id in range(self.num_classes):
             """ Step 1: Get detections by class"""
@@ -132,13 +142,24 @@ class JDETracker(object):
             remain_inds = (pred_dets_cls[:, 4:5] > self.conf_thres).squeeze(-1)
             if remain_inds.sum() > 0:
                 pred_dets_cls = pred_dets_cls[remain_inds]
-                pred_embs_cls = pred_embs_cls[remain_inds]
-                detections = [
-                    STrack(
-                        STrack.tlbr_to_tlwh(tlbrs[:4]), tlbrs[4], f,
-                        self.num_classes, cls_id, 30)
-                    for (tlbrs, f) in zip(pred_dets_cls, pred_embs_cls)
-                ]
+                if self.use_byte:
+                    detections = [
+                        STrack(
+                            STrack.tlbr_to_tlwh(tlbrs[:4]),
+                            tlbrs[4],
+                            cls_id,
+                            30,
+                            temp_feat=None) for tlbrs in pred_dets_cls
+                    ]
+                else:
+                    pred_embs_cls = pred_embs_cls[remain_inds]
+                    detections = [
+                        STrack(
+                            STrack.tlbr_to_tlwh(tlbrs[:4]), tlbrs[4], cls_id,
+                            30, temp_feat)
+                        for (tlbrs, temp_feat
+                             ) in zip(pred_dets_cls, pred_embs_cls)
+                    ]
             else:
                 detections = []
             ''' Add newly detected tracklets to tracked_stracks'''
@@ -160,12 +181,20 @@ class JDETracker(object):
             # Predict the current location with KalmanFilter
             STrack.multi_predict(track_pool_dict[cls_id], self.motion)
 
-            dists = matching.embedding_distance(
-                track_pool_dict[cls_id], detections, metric=self.metric_type)
-            dists = matching.fuse_motion(self.motion, dists,
-                                         track_pool_dict[cls_id], detections)
-            matches, u_track, u_detection = matching.linear_assignment(
-                dists, thresh=self.tracked_thresh)
+            if self.use_byte:
+                dists = matching.iou_distance(track_pool_dict[cls_id],
+                                              detections)
+                matches, u_track, u_detection = matching.linear_assignment(
+                    dists, thresh=self.match_thres)  #
+            else:
+                dists = matching.embedding_distance(
+                    track_pool_dict[cls_id],
+                    detections,
+                    metric=self.metric_type)
+                dists = matching.fuse_motion(
+                    self.motion, dists, track_pool_dict[cls_id], detections)
+                matches, u_track, u_detection = matching.linear_assignment(
+                    dists, thresh=self.tracked_thresh)
 
             for i_tracked, idet in matches:
                 # i_tracked is the id of the track and idet is the detection
@@ -183,19 +212,48 @@ class JDETracker(object):
 
             # None of the steps below happen if there are no undetected tracks.
             """ Step 3: Second association, with IOU"""
-            detections = [detections[i] for i in u_detection]
-            r_tracked_stracks = []
-            for i in u_track:
-                if track_pool_dict[cls_id][i].state == TrackState.Tracked:
-                    r_tracked_stracks.append(track_pool_dict[cls_id][i])
+            if self.use_byte:
+                inds_low = pred_dets_dict[cls_id][:, 4:5] > self.low_conf_thres
+                inds_high = pred_dets_dict[cls_id][:, 4:5] < self.conf_thres
+                inds_second = np.logical_and(inds_low, inds_high).squeeze(-1)
+                pred_dets_cls_second = pred_dets_dict[cls_id][inds_second]
 
-            dists = matching.iou_distance(r_tracked_stracks, detections)
-            matches, u_track, u_detection = matching.linear_assignment(
-                dists, thresh=self.r_tracked_thresh)
+                # association the untrack to the low score detections
+                if len(pred_dets_cls_second) > 0:
+                    detections_second = [
+                        STrack(
+                            STrack.tlbr_to_tlwh(tlbrs[:4]),
+                            tlbrs[4],
+                            cls_id,
+                            30,
+                            temp_feat=None)
+                        for tlbrs in pred_dets_cls_second[:, :5]
+                    ]
+                else:
+                    detections_second = []
+                r_tracked_stracks = [
+                    track_pool_dict[cls_id][i] for i in u_track
+                    if track_pool_dict[cls_id][i].state == TrackState.Tracked
+                ]
+                dists = matching.iou_distance(r_tracked_stracks,
+                                              detections_second)
+                matches, u_track, u_detection_second = matching.linear_assignment(
+                    dists, thresh=0.4)  # not r_tracked_thresh
+            else:
+                detections = [detections[i] for i in u_detection]
+                r_tracked_stracks = []
+                for i in u_track:
+                    if track_pool_dict[cls_id][i].state == TrackState.Tracked:
+                        r_tracked_stracks.append(track_pool_dict[cls_id][i])
+                dists = matching.iou_distance(r_tracked_stracks, detections)
+
+                matches, u_track, u_detection = matching.linear_assignment(
+                    dists, thresh=self.r_tracked_thresh)
 
             for i_tracked, idet in matches:
                 track = r_tracked_stracks[i_tracked]
-                det = detections[idet]
+                det = detections[
+                    idet] if not self.use_byte else detections_second[idet]
                 if track.state == TrackState.Tracked:
                     track.update(det, self.frame_id)
                     activated_tracks_dict[cls_id].append(track)

+ 1 - 3
paddlex/ppdet/modeling/necks/bifpn.py

@@ -52,10 +52,8 @@ class SeparableConvLayer(nn.Layer):
         self.pointwise_conv = nn.Conv2D(in_channels, self.out_channels, 1)
 
         # norm type
-        if self.norm_type == 'bn':
+        if self.norm_type in ['bn', 'sync_bn']:
             self.norm = nn.BatchNorm2D(self.out_channels)
-        elif self.norm_type == 'sync_bn':
-            self.norm = nn.SyncBatchNorm(self.out_channels)
         elif self.norm_type == 'gn':
             self.norm = nn.GroupNorm(
                 num_groups=self.norm_groups, num_channels=self.out_channels)

+ 2 - 5
paddlex/ppdet/modeling/necks/blazeface_fpn.py

@@ -54,11 +54,8 @@ class ConvBNLayer(nn.Layer):
                 learning_rate=conv_lr, initializer=KaimingNormal()),
             bias_attr=False)
 
-        if norm_type == 'sync_bn':
-            self._batch_norm = nn.SyncBatchNorm(out_channels)
-        else:
-            self._batch_norm = nn.BatchNorm(
-                out_channels, act=None, use_global_stats=False)
+        if norm_type in ['sync_bn', 'bn']:
+            self._batch_norm = nn.BatchNorm2D(out_channels)
 
     def forward(self, x):
         x = self._conv(x)

+ 0 - 135
paddlex/ppdet/modeling/necks/pan.py

@@ -1,135 +0,0 @@
-# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
-#
-# 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.
-
-import numpy as np
-import paddle
-import paddle.nn as nn
-import paddle.nn.functional as F
-from paddle import ParamAttr
-from paddle.nn.initializer import XavierUniform
-from paddle.regularizer import L2Decay
-from paddlex.ppdet.core.workspace import register, serializable
-from paddlex.ppdet.modeling.layers import ConvNormLayer
-from ..shape_spec import ShapeSpec
-
-__all__ = ['PAN']
-
-
-@register
-@serializable
-class PAN(nn.Layer):
-    """
-    Path Aggregation Network, see https://arxiv.org/abs/1803.01534
-
-    Args:
-        in_channels (list[int]): input channels of each level which can be
-            derived from the output shape of backbone by from_config
-        out_channel (list[int]): output channel of each level
-        spatial_scales (list[float]): the spatial scales between input feature
-            maps and original input image which can be derived from the output
-            shape of backbone by from_config
-        has_extra_convs (bool): whether to add extra conv to the last level.
-            default False
-        extra_stage (int): the number of extra stages added to the last level.
-            default 1
-        use_c5 (bool): Whether to use c5 as the input of extra stage,
-            otherwise p5 is used. default True
-        norm_type (string|None): The normalization type in FPN module. If
-            norm_type is None, norm will not be used after conv and if
-            norm_type is string, bn, gn, sync_bn are available. default None
-        norm_decay (float): weight decay for normalization layer weights.
-            default 0.
-        freeze_norm (bool): whether to freeze normalization layer.
-            default False
-        relu_before_extra_convs (bool): whether to add relu before extra convs.
-            default False
-    """
-
-    def __init__(self,
-                 in_channels,
-                 out_channel,
-                 spatial_scales=[0.125, 0.0625, 0.03125],
-                 start_level=0,
-                 end_level=-1,
-                 norm_type=None):
-        super(PAN, self).__init__()
-        self.out_channel = out_channel
-        self.num_ins = len(in_channels)
-        self.spatial_scales = spatial_scales
-        if end_level == -1:
-            self.end_level = self.num_ins
-        else:
-            # if end_level < inputs, no extra level is allowed
-            self.end_level = end_level
-            assert end_level <= len(in_channels)
-        self.start_level = start_level
-        self.norm_type = norm_type
-        self.lateral_convs = []
-
-        for i in range(self.start_level, self.end_level):
-            in_c = in_channels[i - self.start_level]
-            if self.norm_type is not None:
-                lateral = self.add_sublayer(
-                    'pan_lateral' + str(i),
-                    ConvNormLayer(
-                        ch_in=in_c,
-                        ch_out=self.out_channel,
-                        filter_size=1,
-                        stride=1,
-                        norm_type=self.norm_type,
-                        norm_decay=self.norm_decay,
-                        freeze_norm=self.freeze_norm,
-                        initializer=XavierUniform(fan_out=in_c)))
-            else:
-                lateral = self.add_sublayer(
-                    'pan_lateral' + str(i),
-                    nn.Conv2D(
-                        in_channels=in_c,
-                        out_channels=self.out_channel,
-                        kernel_size=1,
-                        weight_attr=ParamAttr(
-                            initializer=XavierUniform(fan_out=in_c))))
-            self.lateral_convs.append(lateral)
-
-    @classmethod
-    def from_config(cls, cfg, input_shape):
-        return {'in_channels': [i.channels for i in input_shape], }
-
-    def forward(self, body_feats):
-        laterals = []
-        for i, lateral_conv in enumerate(self.lateral_convs):
-            laterals.append(lateral_conv(body_feats[i + self.start_level]))
-        num_levels = len(laterals)
-        for i in range(1, num_levels):
-            lvl = num_levels - i
-            upsample = F.interpolate(
-                laterals[lvl],
-                scale_factor=2.,
-                mode='bilinear', )
-            laterals[lvl - 1] += upsample
-
-        outs = [laterals[i] for i in range(num_levels)]
-        for i in range(0, num_levels - 1):
-            outs[i + 1] += F.interpolate(
-                outs[i], scale_factor=0.5, mode='bilinear')
-
-        return outs
-
-    @property
-    def out_shape(self):
-        return [
-            ShapeSpec(
-                channels=self.out_channel, stride=1. / s)
-            for s in self.spatial_scales
-        ]

+ 6 - 9
paddlex/ppdet/modeling/ops.py

@@ -50,10 +50,6 @@ def batch_norm(ch,
                freeze_norm=False,
                initializer=None,
                data_format='NCHW'):
-    if norm_type == 'sync_bn':
-        batch_norm = nn.SyncBatchNorm
-    else:
-        batch_norm = nn.BatchNorm2D
 
     norm_lr = 0. if freeze_norm else 1.
     weight_attr = ParamAttr(
@@ -66,11 +62,12 @@ def batch_norm(ch,
         regularizer=L2Decay(norm_decay),
         trainable=False if freeze_norm else True)
 
-    norm_layer = batch_norm(
-        ch,
-        weight_attr=weight_attr,
-        bias_attr=bias_attr,
-        data_format=data_format)
+    if norm_type in ['sync_bn', 'bn']:
+        norm_layer = nn.BatchNorm2D(
+            ch,
+            weight_attr=weight_attr,
+            bias_attr=bias_attr,
+            data_format=data_format)
 
     norm_params = norm_layer.parameters()
     if freeze_norm:

+ 23 - 7
paddlex/ppdet/modeling/post_process.py

@@ -42,10 +42,6 @@ class BBoxPostProcess(nn.Layer):
         self.num_classes = num_classes
         self.decode = decode
         self.nms = nms
-        self.fake_bboxes = paddle.to_tensor(
-            np.array(
-                [[-1, 0.0, 0.0, 0.0, 0.0, 0.0]], dtype='float32'))
-        self.fake_bbox_num = paddle.to_tensor(np.array([1], dtype='int32'))
 
     def forward(self, head_out, rois, im_shape, scale_factor):
         """
@@ -91,9 +87,28 @@ class BBoxPostProcess(nn.Layer):
                 including labels, scores and bboxes.
         """
 
-        if bboxes.shape[0] == 0:
-            bboxes = self.fake_bboxes
-            bbox_num = self.fake_bbox_num
+        bboxes_list = []
+        bbox_num_list = []
+        id_start = 0
+        fake_bboxes = paddle.to_tensor(
+            np.array(
+                [[-1, 0.0, 0.0, 0.0, 0.0, 0.0]], dtype='float32'))
+        fake_bbox_num = paddle.to_tensor(np.array([1], dtype='int32'))
+
+        # add fake bbox when output is empty for each batch
+        for i in range(bbox_num.shape[0]):
+            if bbox_num[i] == 0:
+                bboxes_i = fake_bboxes
+                bbox_num_i = fake_bbox_num
+                id_start += 1
+            else:
+                bboxes_i = bboxes[id_start:id_start + bbox_num[i], :]
+                bbox_num_i = bbox_num[i]
+                id_start += bbox_num[i]
+            bboxes_list.append(bboxes_i)
+            bbox_num_list.append(bbox_num_i)
+        bboxes = paddle.concat(bboxes_list)
+        bbox_num = paddle.concat(bbox_num_list)
 
         origin_shape = paddle.floor(im_shape / scale_factor + 0.5)
 
@@ -157,6 +172,7 @@ class MaskPostProcess(object):
         """
         Paste the mask prediction to the original image.
         """
+
         x0, y0, x1, y1 = paddle.split(boxes, 4, axis=1)
         masks = paddle.unsqueeze(masks, [0, 1])
         img_y = paddle.arange(0, im_h, dtype='float32') + 0.5

+ 2 - 2
paddlex/ppdet/modeling/proposal_generator/target.py

@@ -52,8 +52,8 @@ def rpn_anchor_target(anchors,
             labels = paddle.scatter(labels, fg_inds, paddle.ones_like(fg_inds))
         # Step3: make output
         if gt_bbox.shape[0] == 0:
-            matched_gt_boxes = paddle.zeros([0, 4])
-            tgt_delta = paddle.zeros([0, 4])
+            matched_gt_boxes = paddle.zeros([matches.shape[0], 4])
+            tgt_delta = paddle.zeros([matches.shape[0], 4])
         else:
             matched_gt_boxes = paddle.gather(gt_bbox, matches)
             tgt_delta = bbox2delta(anchors, matched_gt_boxes, weights)

+ 3 - 3
paddlex/ppdet/modeling/reid/pplcnet_embedding.py

@@ -21,7 +21,7 @@ import paddle.nn as nn
 import paddle.nn.functional as F
 from paddle.nn.initializer import Normal, Constant
 from paddle import ParamAttr
-from paddle.nn import AdaptiveAvgPool2D, BatchNorm, Conv2D, Linear
+from paddle.nn import AdaptiveAvgPool2D, BatchNorm2D, Conv2D, Linear
 from paddle.regularizer import L2Decay
 from paddle.nn.initializer import KaimingNormal, XavierNormal
 from paddlex.ppdet.core.workspace import register
@@ -76,9 +76,9 @@ class ConvBNLayer(nn.Layer):
             weight_attr=ParamAttr(initializer=KaimingNormal()),
             bias_attr=False)
 
-        self.bn = BatchNorm(
+        self.bn = BatchNorm2D(
             num_filters,
-            param_attr=ParamAttr(regularizer=L2Decay(0.0)),
+            weight_attr=ParamAttr(regularizer=L2Decay(0.0)),
             bias_attr=ParamAttr(regularizer=L2Decay(0.0)))
         self.hardswish = nn.Hardswish()
 

+ 4 - 2
paddlex/ppdet/modeling/reid/resnet.py

@@ -55,12 +55,14 @@ class ConvBNLayer(nn.Layer):
             bias_attr=False,
             data_format=data_format)
 
-        self._batch_norm = nn.BatchNorm(
-            num_filters, act=act, data_layout=data_format)
+        self._batch_norm = nn.BatchNorm2D(num_filters, data_layout=data_format)
+        self.act = act
 
     def forward(self, inputs):
         y = self._conv(inputs)
         y = self._batch_norm(y)
+        if self.act:
+            y = getattr(F, self.act)(y)
         return y
 
 

+ 0 - 13
paddlex/ppdet/modeling/tests/__init__.py

@@ -1,13 +0,0 @@
-#   Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
-#
-# 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.

BIN
paddlex/ppdet/modeling/tests/imgs/coco2017_val2017_000000000139.jpg


BIN
paddlex/ppdet/modeling/tests/imgs/coco2017_val2017_000000000724.jpg


+ 0 - 69
paddlex/ppdet/modeling/tests/test_architectures.py

@@ -1,69 +0,0 @@
-#   Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
-#
-# 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
-from __future__ import division
-from __future__ import print_function
-
-import unittest
-import paddlex.ppdet
-
-
-class TestFasterRCNN(unittest.TestCase):
-    def setUp(self):
-        self.set_config()
-
-    def set_config(self):
-        self.cfg_file = 'configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.yml'
-
-    def test_trainer(self):
-        # Trainer __init__ will build model and DataLoader
-        # 'train' and 'eval' mode include dataset loading
-        # use 'test' mode to simplify tests
-        cfg = ppdet.core.workspace.load_config(self.cfg_file)
-        trainer = ppdet.engine.Trainer(cfg, mode='test')
-
-
-class TestMaskRCNN(TestFasterRCNN):
-    def set_config(self):
-        self.cfg_file = 'configs/mask_rcnn/mask_rcnn_r50_fpn_1x_coco.yml'
-
-
-class TestCascadeRCNN(TestFasterRCNN):
-    def set_config(self):
-        self.cfg_file = 'configs/cascade_rcnn/cascade_rcnn_r50_fpn_1x_coco.yml'
-
-
-class TestYolov3(TestFasterRCNN):
-    def set_config(self):
-        self.cfg_file = 'configs/yolov3/yolov3_darknet53_270e_coco.yml'
-
-
-class TestSSD(TestFasterRCNN):
-    def set_config(self):
-        self.cfg_file = 'configs/ssd/ssd_vgg16_300_240e_voc.yml'
-
-
-class TestGFL(TestFasterRCNN):
-    def set_config(self):
-        self.cfg_file = 'configs/gfl/gfl_r50_fpn_1x_coco.yml'
-
-
-class TestPicoDet(TestFasterRCNN):
-    def set_config(self):
-        self.cfg_file = 'configs/picodet/picodet_s_320_coco.yml'
-
-
-if __name__ == '__main__':
-    unittest.main()

+ 0 - 74
paddlex/ppdet/modeling/tests/test_base.py

@@ -1,74 +0,0 @@
-#   Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
-#
-# 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 print_function
-import unittest
-
-import contextlib
-
-import paddle
-import paddle.fluid as fluid
-from paddle.fluid.framework import Program
-from paddle.fluid import core
-
-
-class LayerTest(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        cls.seed = 111
-
-    @classmethod
-    def tearDownClass(cls):
-        pass
-
-    def _get_place(self, force_to_use_cpu=False):
-        # this option for ops that only have cpu kernel
-        if force_to_use_cpu:
-            return core.CPUPlace()
-        else:
-            if core.is_compiled_with_cuda():
-                return core.CUDAPlace(0)
-            return core.CPUPlace()
-
-    @contextlib.contextmanager
-    def static_graph(self):
-        paddle.enable_static()
-        scope = fluid.core.Scope()
-        program = Program()
-        with fluid.scope_guard(scope):
-            with fluid.program_guard(program):
-                paddle.seed(self.seed)
-                paddle.framework.random._manual_program_seed(self.seed)
-                yield
-
-    def get_static_graph_result(self,
-                                feed,
-                                fetch_list,
-                                with_lod=False,
-                                force_to_use_cpu=False):
-        exe = fluid.Executor(self._get_place(force_to_use_cpu))
-        exe.run(fluid.default_startup_program())
-        return exe.run(fluid.default_main_program(),
-                       feed=feed,
-                       fetch_list=fetch_list,
-                       return_numpy=(not with_lod))
-
-    @contextlib.contextmanager
-    def dynamic_graph(self, force_to_use_cpu=False):
-        paddle.disable_static()
-        with fluid.dygraph.guard(
-                self._get_place(force_to_use_cpu=force_to_use_cpu)):
-            paddle.seed(self.seed)
-            paddle.framework.random._manual_program_seed(self.seed)
-            yield

+ 0 - 62
paddlex/ppdet/modeling/tests/test_mstest.py

@@ -1,62 +0,0 @@
-#   Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
-#
-# 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
-from __future__ import division
-from __future__ import print_function
-
-import os
-import unittest
-from paddlex.ppdet.core.workspace import load_config
-from paddlex.ppdet.engine import Trainer
-
-
-class TestMultiScaleInference(unittest.TestCase):
-    def setUp(self):
-        self.set_config()
-
-    def set_config(self):
-        self.mstest_cfg_file = 'configs/faster_rcnn/faster_rcnn_r34_fpn_multiscaletest_1x_coco.yml'
-
-    # test evaluation with multi scale test
-    def test_eval_mstest(self):
-        cfg = load_config(self.mstest_cfg_file)
-        trainer = Trainer(cfg, mode='eval')
-
-        cfg.weights = 'https://paddledet.bj.bcebos.com/models/faster_rcnn_r34_fpn_1x_coco.pdparams'
-        trainer.load_weights(cfg.weights)
-
-        trainer.evaluate()
-
-    # test inference with multi scale test
-    def test_infer_mstest(self):
-        cfg = load_config(self.mstest_cfg_file)
-        trainer = Trainer(cfg, mode='test')
-
-        cfg.weights = 'https://paddledet.bj.bcebos.com/models/faster_rcnn_r34_fpn_1x_coco.pdparams'
-        trainer.load_weights(cfg.weights)
-        tests_img_root = os.path.join(os.path.dirname(__file__), 'imgs')
-
-        # input images to predict
-        imgs = [
-            'coco2017_val2017_000000000139.jpg',
-            'coco2017_val2017_000000000724.jpg'
-        ]
-        imgs = [os.path.join(tests_img_root, img) for img in imgs]
-        trainer.predict(
-            imgs, draw_threshold=0.5, output_dir='output', save_txt=True)
-
-
-if __name__ == '__main__':
-    unittest.main()

+ 0 - 838
paddlex/ppdet/modeling/tests/test_ops.py

@@ -1,838 +0,0 @@
-#   Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
-#
-# 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 print_function
-import os, sys
-# add python path of PadleDetection to sys.path
-parent_path = os.path.abspath(os.path.join(__file__, *(['..'] * 4)))
-if parent_path not in sys.path:
-    sys.path.append(parent_path)
-
-import unittest
-import numpy as np
-
-import paddle
-import paddle.fluid as fluid
-from paddle.fluid.dygraph import base
-
-import paddlex.ppdet.modeling.ops as ops
-from paddlex.ppdet.modeling.tests.test_base import LayerTest
-
-
-def make_rois(h, w, rois_num, output_size):
-    rois = np.zeros((0, 4)).astype('float32')
-    for roi_num in rois_num:
-        roi = np.zeros((roi_num, 4)).astype('float32')
-        roi[:, 0] = np.random.randint(0, h - output_size[0], size=roi_num)
-        roi[:, 1] = np.random.randint(0, w - output_size[1], size=roi_num)
-        roi[:, 2] = np.random.randint(roi[:, 0] + output_size[0], h)
-        roi[:, 3] = np.random.randint(roi[:, 1] + output_size[1], w)
-        rois = np.vstack((rois, roi))
-    return rois
-
-
-def softmax(x):
-    # clip to shiftx, otherwise, when calc loss with
-    # log(exp(shiftx)), may get log(0)=INF
-    shiftx = (x - np.max(x)).clip(-64.)
-    exps = np.exp(shiftx)
-    return exps / np.sum(exps)
-
-
-class TestCollectFpnProposals(LayerTest):
-    def test_collect_fpn_proposals(self):
-        multi_bboxes_np = []
-        multi_scores_np = []
-        rois_num_per_level_np = []
-        for i in range(4):
-            bboxes_np = np.random.rand(5, 4).astype('float32')
-            scores_np = np.random.rand(5, 1).astype('float32')
-            rois_num = np.array([2, 3]).astype('int32')
-            multi_bboxes_np.append(bboxes_np)
-            multi_scores_np.append(scores_np)
-            rois_num_per_level_np.append(rois_num)
-
-        with self.static_graph():
-            multi_bboxes = []
-            multi_scores = []
-            rois_num_per_level = []
-            for i in range(4):
-                bboxes = paddle.static.data(
-                    name='rois' + str(i),
-                    shape=[5, 4],
-                    dtype='float32',
-                    lod_level=1)
-                scores = paddle.static.data(
-                    name='scores' + str(i),
-                    shape=[5, 1],
-                    dtype='float32',
-                    lod_level=1)
-                rois_num = paddle.static.data(
-                    name='rois_num' + str(i), shape=[None], dtype='int32')
-
-                multi_bboxes.append(bboxes)
-                multi_scores.append(scores)
-                rois_num_per_level.append(rois_num)
-
-            fpn_rois, rois_num = ops.collect_fpn_proposals(
-                multi_bboxes,
-                multi_scores,
-                2,
-                5,
-                10,
-                rois_num_per_level=rois_num_per_level)
-            feed = {}
-            for i in range(4):
-                feed['rois' + str(i)] = multi_bboxes_np[i]
-                feed['scores' + str(i)] = multi_scores_np[i]
-                feed['rois_num' + str(i)] = rois_num_per_level_np[i]
-            fpn_rois_stat, rois_num_stat = self.get_static_graph_result(
-                feed=feed, fetch_list=[fpn_rois, rois_num], with_lod=True)
-            fpn_rois_stat = np.array(fpn_rois_stat)
-            rois_num_stat = np.array(rois_num_stat)
-
-        with self.dynamic_graph():
-            multi_bboxes_dy = []
-            multi_scores_dy = []
-            rois_num_per_level_dy = []
-            for i in range(4):
-                bboxes_dy = base.to_variable(multi_bboxes_np[i])
-                scores_dy = base.to_variable(multi_scores_np[i])
-                rois_num_dy = base.to_variable(rois_num_per_level_np[i])
-                multi_bboxes_dy.append(bboxes_dy)
-                multi_scores_dy.append(scores_dy)
-                rois_num_per_level_dy.append(rois_num_dy)
-            fpn_rois_dy, rois_num_dy = ops.collect_fpn_proposals(
-                multi_bboxes_dy,
-                multi_scores_dy,
-                2,
-                5,
-                10,
-                rois_num_per_level=rois_num_per_level_dy)
-            fpn_rois_dy = fpn_rois_dy.numpy()
-            rois_num_dy = rois_num_dy.numpy()
-
-        self.assertTrue(np.array_equal(fpn_rois_stat, fpn_rois_dy))
-        self.assertTrue(np.array_equal(rois_num_stat, rois_num_dy))
-
-    def test_collect_fpn_proposals_error(self):
-        def generate_input(bbox_type, score_type, name):
-            multi_bboxes = []
-            multi_scores = []
-            for i in range(4):
-                bboxes = paddle.static.data(
-                    name='rois' + name + str(i),
-                    shape=[10, 4],
-                    dtype=bbox_type,
-                    lod_level=1)
-                scores = paddle.static.data(
-                    name='scores' + name + str(i),
-                    shape=[10, 1],
-                    dtype=score_type,
-                    lod_level=1)
-                multi_bboxes.append(bboxes)
-                multi_scores.append(scores)
-            return multi_bboxes, multi_scores
-
-        with self.static_graph():
-            bbox1 = paddle.static.data(
-                name='rois', shape=[5, 10, 4], dtype='float32', lod_level=1)
-            score1 = paddle.static.data(
-                name='scores', shape=[5, 10, 1], dtype='float32', lod_level=1)
-            bbox2, score2 = generate_input('int32', 'float32', '2')
-            self.assertRaises(
-                TypeError,
-                ops.collect_fpn_proposals,
-                multi_rois=bbox1,
-                multi_scores=score1,
-                min_level=2,
-                max_level=5,
-                post_nms_top_n=2000)
-            self.assertRaises(
-                TypeError,
-                ops.collect_fpn_proposals,
-                multi_rois=bbox2,
-                multi_scores=score2,
-                min_level=2,
-                max_level=5,
-                post_nms_top_n=2000)
-
-        paddle.disable_static()
-
-
-class TestDistributeFpnProposals(LayerTest):
-    def test_distribute_fpn_proposals(self):
-        rois_np = np.random.rand(10, 4).astype('float32')
-        rois_num_np = np.array([4, 6]).astype('int32')
-        with self.static_graph():
-            rois = paddle.static.data(
-                name='rois', shape=[10, 4], dtype='float32')
-            rois_num = paddle.static.data(
-                name='rois_num', shape=[None], dtype='int32')
-            multi_rois, restore_ind, rois_num_per_level = ops.distribute_fpn_proposals(
-                fpn_rois=rois,
-                min_level=2,
-                max_level=5,
-                refer_level=4,
-                refer_scale=224,
-                rois_num=rois_num)
-            fetch_list = multi_rois + [restore_ind] + rois_num_per_level
-            output_stat = self.get_static_graph_result(
-                feed={'rois': rois_np,
-                      'rois_num': rois_num_np},
-                fetch_list=fetch_list,
-                with_lod=True)
-            output_stat_np = []
-            for output in output_stat:
-                output_np = np.array(output)
-                if len(output_np) > 0:
-                    output_stat_np.append(output_np)
-
-        with self.dynamic_graph():
-            rois_dy = base.to_variable(rois_np)
-            rois_num_dy = base.to_variable(rois_num_np)
-            multi_rois_dy, restore_ind_dy, rois_num_per_level_dy = ops.distribute_fpn_proposals(
-                fpn_rois=rois_dy,
-                min_level=2,
-                max_level=5,
-                refer_level=4,
-                refer_scale=224,
-                rois_num=rois_num_dy)
-            output_dy = multi_rois_dy + [restore_ind_dy
-                                         ] + rois_num_per_level_dy
-            output_dy_np = []
-            for output in output_dy:
-                output_np = output.numpy()
-                if len(output_np) > 0:
-                    output_dy_np.append(output_np)
-
-        for res_stat, res_dy in zip(output_stat_np, output_dy_np):
-            self.assertTrue(np.array_equal(res_stat, res_dy))
-
-    def test_distribute_fpn_proposals_error(self):
-        with self.static_graph():
-            fpn_rois = paddle.static.data(
-                name='data_error', shape=[10, 4], dtype='int32', lod_level=1)
-            self.assertRaises(
-                TypeError,
-                ops.distribute_fpn_proposals,
-                fpn_rois=fpn_rois,
-                min_level=2,
-                max_level=5,
-                refer_level=4,
-                refer_scale=224)
-
-        paddle.disable_static()
-
-
-class TestROIAlign(LayerTest):
-    def test_roi_align(self):
-        b, c, h, w = 2, 12, 20, 20
-        inputs_np = np.random.rand(b, c, h, w).astype('float32')
-        rois_num = [4, 6]
-        output_size = (7, 7)
-        rois_np = make_rois(h, w, rois_num, output_size)
-        rois_num_np = np.array(rois_num).astype('int32')
-        with self.static_graph():
-            inputs = paddle.static.data(
-                name='inputs', shape=[b, c, h, w], dtype='float32')
-            rois = paddle.static.data(
-                name='rois', shape=[10, 4], dtype='float32')
-            rois_num = paddle.static.data(
-                name='rois_num', shape=[None], dtype='int32')
-
-            output = ops.roi_align(
-                input=inputs,
-                rois=rois,
-                output_size=output_size,
-                rois_num=rois_num)
-            output_np, = self.get_static_graph_result(
-                feed={
-                    'inputs': inputs_np,
-                    'rois': rois_np,
-                    'rois_num': rois_num_np
-                },
-                fetch_list=output,
-                with_lod=False)
-
-        with self.dynamic_graph():
-            inputs_dy = base.to_variable(inputs_np)
-            rois_dy = base.to_variable(rois_np)
-            rois_num_dy = base.to_variable(rois_num_np)
-
-            output_dy = ops.roi_align(
-                input=inputs_dy,
-                rois=rois_dy,
-                output_size=output_size,
-                rois_num=rois_num_dy)
-            output_dy_np = output_dy.numpy()
-
-        self.assertTrue(np.array_equal(output_np, output_dy_np))
-
-    def test_roi_align_error(self):
-        with self.static_graph():
-            inputs = paddle.static.data(
-                name='inputs', shape=[2, 12, 20, 20], dtype='float32')
-            rois = paddle.static.data(
-                name='data_error', shape=[10, 4], dtype='int32', lod_level=1)
-            self.assertRaises(
-                TypeError,
-                ops.roi_align,
-                input=inputs,
-                rois=rois,
-                output_size=(7, 7))
-
-        paddle.disable_static()
-
-
-class TestROIPool(LayerTest):
-    def test_roi_pool(self):
-        b, c, h, w = 2, 12, 20, 20
-        inputs_np = np.random.rand(b, c, h, w).astype('float32')
-        rois_num = [4, 6]
-        output_size = (7, 7)
-        rois_np = make_rois(h, w, rois_num, output_size)
-        rois_num_np = np.array(rois_num).astype('int32')
-        with self.static_graph():
-            inputs = paddle.static.data(
-                name='inputs', shape=[b, c, h, w], dtype='float32')
-            rois = paddle.static.data(
-                name='rois', shape=[10, 4], dtype='float32')
-            rois_num = paddle.static.data(
-                name='rois_num', shape=[None], dtype='int32')
-
-            output, _ = ops.roi_pool(
-                input=inputs,
-                rois=rois,
-                output_size=output_size,
-                rois_num=rois_num)
-            output_np, = self.get_static_graph_result(
-                feed={
-                    'inputs': inputs_np,
-                    'rois': rois_np,
-                    'rois_num': rois_num_np
-                },
-                fetch_list=[output],
-                with_lod=False)
-
-        with self.dynamic_graph():
-            inputs_dy = base.to_variable(inputs_np)
-            rois_dy = base.to_variable(rois_np)
-            rois_num_dy = base.to_variable(rois_num_np)
-
-            output_dy, _ = ops.roi_pool(
-                input=inputs_dy,
-                rois=rois_dy,
-                output_size=output_size,
-                rois_num=rois_num_dy)
-            output_dy_np = output_dy.numpy()
-
-        self.assertTrue(np.array_equal(output_np, output_dy_np))
-
-    def test_roi_pool_error(self):
-        with self.static_graph():
-            inputs = paddle.static.data(
-                name='inputs', shape=[2, 12, 20, 20], dtype='float32')
-            rois = paddle.static.data(
-                name='data_error', shape=[10, 4], dtype='int32', lod_level=1)
-            self.assertRaises(
-                TypeError,
-                ops.roi_pool,
-                input=inputs,
-                rois=rois,
-                output_size=(7, 7))
-
-        paddle.disable_static()
-
-
-class TestIoUSimilarity(LayerTest):
-    def test_iou_similarity(self):
-        b, c, h, w = 2, 12, 20, 20
-        inputs_np = np.random.rand(b, c, h, w).astype('float32')
-        output_size = (7, 7)
-        x_np = make_rois(h, w, [20], output_size)
-        y_np = make_rois(h, w, [10], output_size)
-        with self.static_graph():
-            x = paddle.static.data(name='x', shape=[20, 4], dtype='float32')
-            y = paddle.static.data(name='y', shape=[10, 4], dtype='float32')
-
-            iou = ops.iou_similarity(x=x, y=y)
-            iou_np, = self.get_static_graph_result(
-                feed={
-                    'x': x_np,
-                    'y': y_np,
-                },
-                fetch_list=[iou],
-                with_lod=False)
-
-        with self.dynamic_graph():
-            x_dy = base.to_variable(x_np)
-            y_dy = base.to_variable(y_np)
-
-            iou_dy = ops.iou_similarity(x=x_dy, y=y_dy)
-            iou_dy_np = iou_dy.numpy()
-
-        self.assertTrue(np.array_equal(iou_np, iou_dy_np))
-
-
-class TestBipartiteMatch(LayerTest):
-    def test_bipartite_match(self):
-        distance = np.random.random((20, 10)).astype('float32')
-        with self.static_graph():
-            x = paddle.static.data(name='x', shape=[20, 10], dtype='float32')
-
-            match_indices, match_dist = ops.bipartite_match(
-                x, match_type='per_prediction', dist_threshold=0.5)
-            match_indices_np, match_dist_np = self.get_static_graph_result(
-                feed={'x': distance, },
-                fetch_list=[match_indices, match_dist],
-                with_lod=False)
-
-        with self.dynamic_graph():
-            x_dy = base.to_variable(distance)
-
-            match_indices_dy, match_dist_dy = ops.bipartite_match(
-                x_dy, match_type='per_prediction', dist_threshold=0.5)
-            match_indices_dy_np = match_indices_dy.numpy()
-            match_dist_dy_np = match_dist_dy.numpy()
-
-        self.assertTrue(np.array_equal(match_indices_np, match_indices_dy_np))
-        self.assertTrue(np.array_equal(match_dist_np, match_dist_dy_np))
-
-
-class TestYoloBox(LayerTest):
-    def test_yolo_box(self):
-
-        # x shape [N C H W], C=K * (5 + class_num), class_num=10, K=2
-        np_x = np.random.random([1, 30, 7, 7]).astype('float32')
-        np_origin_shape = np.array([[608, 608]], dtype='int32')
-        class_num = 10
-        conf_thresh = 0.01
-        downsample_ratio = 32
-        scale_x_y = 1.2
-
-        # static
-        with self.static_graph():
-            # x shape [N C H W], C=K * (5 + class_num), class_num=10, K=2
-            x = paddle.static.data(
-                name='x', shape=[1, 30, 7, 7], dtype='float32')
-            origin_shape = paddle.static.data(
-                name='origin_shape', shape=[1, 2], dtype='int32')
-
-            boxes, scores = ops.yolo_box(
-                x,
-                origin_shape, [10, 13, 30, 13],
-                class_num,
-                conf_thresh,
-                downsample_ratio,
-                scale_x_y=scale_x_y)
-
-            boxes_np, scores_np = self.get_static_graph_result(
-                feed={
-                    'x': np_x,
-                    'origin_shape': np_origin_shape,
-                },
-                fetch_list=[boxes, scores],
-                with_lod=False)
-
-        # dygraph
-        with self.dynamic_graph():
-            x_dy = fluid.layers.assign(np_x)
-            origin_shape_dy = fluid.layers.assign(np_origin_shape)
-
-            boxes_dy, scores_dy = ops.yolo_box(
-                x_dy,
-                origin_shape_dy, [10, 13, 30, 13],
-                10,
-                0.01,
-                32,
-                scale_x_y=scale_x_y)
-
-            boxes_dy_np = boxes_dy.numpy()
-            scores_dy_np = scores_dy.numpy()
-
-        self.assertTrue(np.array_equal(boxes_np, boxes_dy_np))
-        self.assertTrue(np.array_equal(scores_np, scores_dy_np))
-
-    def test_yolo_box_error(self):
-        with self.static_graph():
-            # x shape [N C H W], C=K * (5 + class_num), class_num=10, K=2
-            x = paddle.static.data(
-                name='x', shape=[1, 30, 7, 7], dtype='float32')
-            origin_shape = paddle.static.data(
-                name='origin_shape', shape=[1, 2], dtype='int32')
-
-            self.assertRaises(
-                TypeError,
-                ops.yolo_box,
-                x,
-                origin_shape, [10, 13, 30, 13],
-                10.123,
-                0.01,
-                32,
-                scale_x_y=1.2)
-
-        paddle.disable_static()
-
-
-class TestPriorBox(LayerTest):
-    def test_prior_box(self):
-        input_np = np.random.rand(2, 10, 32, 32).astype('float32')
-        image_np = np.random.rand(2, 10, 40, 40).astype('float32')
-        min_sizes = [2, 4]
-        with self.static_graph():
-            input = paddle.static.data(
-                name='input', shape=[2, 10, 32, 32], dtype='float32')
-            image = paddle.static.data(
-                name='image', shape=[2, 10, 40, 40], dtype='float32')
-
-            box, var = ops.prior_box(
-                input=input,
-                image=image,
-                min_sizes=min_sizes,
-                clip=True,
-                flip=True)
-            box_np, var_np = self.get_static_graph_result(
-                feed={
-                    'input': input_np,
-                    'image': image_np,
-                },
-                fetch_list=[box, var],
-                with_lod=False)
-
-        with self.dynamic_graph():
-            inputs_dy = base.to_variable(input_np)
-            image_dy = base.to_variable(image_np)
-
-            box_dy, var_dy = ops.prior_box(
-                input=inputs_dy,
-                image=image_dy,
-                min_sizes=min_sizes,
-                clip=True,
-                flip=True)
-            box_dy_np = box_dy.numpy()
-            var_dy_np = var_dy.numpy()
-
-        self.assertTrue(np.array_equal(box_np, box_dy_np))
-        self.assertTrue(np.array_equal(var_np, var_dy_np))
-
-    def test_prior_box_error(self):
-        with self.static_graph():
-            input = paddle.static.data(
-                name='input', shape=[2, 10, 32, 32], dtype='int32')
-            image = paddle.static.data(
-                name='image', shape=[2, 10, 40, 40], dtype='int32')
-            self.assertRaises(
-                TypeError,
-                ops.prior_box,
-                input=input,
-                image=image,
-                min_sizes=[2, 4],
-                clip=True,
-                flip=True)
-
-        paddle.disable_static()
-
-
-class TestMulticlassNms(LayerTest):
-    def test_multiclass_nms(self):
-        boxes_np = np.random.rand(10, 81, 4).astype('float32')
-        scores_np = np.random.rand(10, 81).astype('float32')
-        rois_num_np = np.array([2, 8]).astype('int32')
-        with self.static_graph():
-            boxes = paddle.static.data(
-                name='bboxes',
-                shape=[None, 81, 4],
-                dtype='float32',
-                lod_level=1)
-            scores = paddle.static.data(
-                name='scores', shape=[None, 81], dtype='float32', lod_level=1)
-            rois_num = paddle.static.data(
-                name='rois_num', shape=[None], dtype='int32')
-
-            output = ops.multiclass_nms(
-                bboxes=boxes,
-                scores=scores,
-                background_label=0,
-                score_threshold=0.5,
-                nms_top_k=400,
-                nms_threshold=0.3,
-                keep_top_k=200,
-                normalized=False,
-                return_index=True,
-                rois_num=rois_num)
-            out_np, index_np, nms_rois_num_np = self.get_static_graph_result(
-                feed={
-                    'bboxes': boxes_np,
-                    'scores': scores_np,
-                    'rois_num': rois_num_np
-                },
-                fetch_list=output,
-                with_lod=True)
-            out_np = np.array(out_np)
-            index_np = np.array(index_np)
-            nms_rois_num_np = np.array(nms_rois_num_np)
-
-        with self.dynamic_graph():
-            boxes_dy = base.to_variable(boxes_np)
-            scores_dy = base.to_variable(scores_np)
-            rois_num_dy = base.to_variable(rois_num_np)
-
-            out_dy, index_dy, nms_rois_num_dy = ops.multiclass_nms(
-                bboxes=boxes_dy,
-                scores=scores_dy,
-                background_label=0,
-                score_threshold=0.5,
-                nms_top_k=400,
-                nms_threshold=0.3,
-                keep_top_k=200,
-                normalized=False,
-                return_index=True,
-                rois_num=rois_num_dy)
-            out_dy_np = out_dy.numpy()
-            index_dy_np = index_dy.numpy()
-            nms_rois_num_dy_np = nms_rois_num_dy.numpy()
-
-        self.assertTrue(np.array_equal(out_np, out_dy_np))
-        self.assertTrue(np.array_equal(index_np, index_dy_np))
-        self.assertTrue(np.array_equal(nms_rois_num_np, nms_rois_num_dy_np))
-
-    def test_multiclass_nms_error(self):
-        with self.static_graph():
-            boxes = paddle.static.data(
-                name='bboxes', shape=[81, 4], dtype='float32', lod_level=1)
-            scores = paddle.static.data(
-                name='scores', shape=[81], dtype='float32', lod_level=1)
-            rois_num = paddle.static.data(
-                name='rois_num', shape=[40, 41], dtype='int32')
-            self.assertRaises(
-                TypeError,
-                ops.multiclass_nms,
-                boxes=boxes,
-                scores=scores,
-                background_label=0,
-                score_threshold=0.5,
-                nms_top_k=400,
-                nms_threshold=0.3,
-                keep_top_k=200,
-                normalized=False,
-                return_index=True,
-                rois_num=rois_num)
-
-
-class TestMatrixNMS(LayerTest):
-    def test_matrix_nms(self):
-        N, M, C = 7, 1200, 21
-        BOX_SIZE = 4
-        nms_top_k = 400
-        keep_top_k = 200
-        score_threshold = 0.01
-        post_threshold = 0.
-
-        scores_np = np.random.random((N * M, C)).astype('float32')
-        scores_np = np.apply_along_axis(softmax, 1, scores_np)
-        scores_np = np.reshape(scores_np, (N, M, C))
-        scores_np = np.transpose(scores_np, (0, 2, 1))
-
-        boxes_np = np.random.random((N, M, BOX_SIZE)).astype('float32')
-        boxes_np[:, :, 0:2] = boxes_np[:, :, 0:2] * 0.5
-        boxes_np[:, :, 2:4] = boxes_np[:, :, 2:4] * 0.5 + 0.5
-
-        with self.static_graph():
-            boxes = paddle.static.data(
-                name='boxes', shape=[N, M, BOX_SIZE], dtype='float32')
-            scores = paddle.static.data(
-                name='scores', shape=[N, C, M], dtype='float32')
-            out, index, _ = ops.matrix_nms(
-                bboxes=boxes,
-                scores=scores,
-                score_threshold=score_threshold,
-                post_threshold=post_threshold,
-                nms_top_k=nms_top_k,
-                keep_top_k=keep_top_k,
-                return_index=True)
-            out_np, index_np = self.get_static_graph_result(
-                feed={'boxes': boxes_np,
-                      'scores': scores_np},
-                fetch_list=[out, index],
-                with_lod=True)
-
-        with self.dynamic_graph():
-            boxes_dy = base.to_variable(boxes_np)
-            scores_dy = base.to_variable(scores_np)
-
-            out_dy, index_dy, _ = ops.matrix_nms(
-                bboxes=boxes_dy,
-                scores=scores_dy,
-                score_threshold=score_threshold,
-                post_threshold=post_threshold,
-                nms_top_k=nms_top_k,
-                keep_top_k=keep_top_k,
-                return_index=True)
-            out_dy_np = out_dy.numpy()
-            index_dy_np = index_dy.numpy()
-
-        self.assertTrue(np.array_equal(out_np, out_dy_np))
-        self.assertTrue(np.array_equal(index_np, index_dy_np))
-
-    def test_matrix_nms_error(self):
-        with self.static_graph():
-            bboxes = paddle.static.data(
-                name='bboxes', shape=[7, 1200, 4], dtype='float32')
-            scores = paddle.static.data(
-                name='data_error', shape=[7, 21, 1200], dtype='int32')
-            self.assertRaises(
-                TypeError,
-                ops.matrix_nms,
-                bboxes=bboxes,
-                scores=scores,
-                score_threshold=0.01,
-                post_threshold=0.,
-                nms_top_k=400,
-                keep_top_k=200,
-                return_index=True)
-
-        paddle.disable_static()
-
-
-class TestBoxCoder(LayerTest):
-    def test_box_coder(self):
-
-        prior_box_np = np.random.random((81, 4)).astype('float32')
-        prior_box_var_np = np.random.random((81, 4)).astype('float32')
-        target_box_np = np.random.random((20, 81, 4)).astype('float32')
-
-        # static
-        with self.static_graph():
-            prior_box = paddle.static.data(
-                name='prior_box', shape=[81, 4], dtype='float32')
-            prior_box_var = paddle.static.data(
-                name='prior_box_var', shape=[81, 4], dtype='float32')
-            target_box = paddle.static.data(
-                name='target_box', shape=[20, 81, 4], dtype='float32')
-
-            boxes = ops.box_coder(
-                prior_box=prior_box,
-                prior_box_var=prior_box_var,
-                target_box=target_box,
-                code_type="decode_center_size",
-                box_normalized=False)
-
-            boxes_np, = self.get_static_graph_result(
-                feed={
-                    'prior_box': prior_box_np,
-                    'prior_box_var': prior_box_var_np,
-                    'target_box': target_box_np,
-                },
-                fetch_list=[boxes],
-                with_lod=False)
-
-        # dygraph
-        with self.dynamic_graph():
-            prior_box_dy = base.to_variable(prior_box_np)
-            prior_box_var_dy = base.to_variable(prior_box_var_np)
-            target_box_dy = base.to_variable(target_box_np)
-
-            boxes_dy = ops.box_coder(
-                prior_box=prior_box_dy,
-                prior_box_var=prior_box_var_dy,
-                target_box=target_box_dy,
-                code_type="decode_center_size",
-                box_normalized=False)
-
-            boxes_dy_np = boxes_dy.numpy()
-
-            self.assertTrue(np.array_equal(boxes_np, boxes_dy_np))
-
-    def test_box_coder_error(self):
-        with self.static_graph():
-            prior_box = paddle.static.data(
-                name='prior_box', shape=[81, 4], dtype='int32')
-            prior_box_var = paddle.static.data(
-                name='prior_box_var', shape=[81, 4], dtype='float32')
-            target_box = paddle.static.data(
-                name='target_box', shape=[20, 81, 4], dtype='float32')
-
-            self.assertRaises(TypeError, ops.box_coder, prior_box,
-                              prior_box_var, target_box)
-
-        paddle.disable_static()
-
-
-class TestGenerateProposals(LayerTest):
-    def test_generate_proposals(self):
-        scores_np = np.random.rand(2, 3, 4, 4).astype('float32')
-        bbox_deltas_np = np.random.rand(2, 12, 4, 4).astype('float32')
-        im_shape_np = np.array([[8, 8], [6, 6]]).astype('float32')
-        anchors_np = np.reshape(np.arange(4 * 4 * 3 * 4),
-                                [4, 4, 3, 4]).astype('float32')
-        variances_np = np.ones((4, 4, 3, 4)).astype('float32')
-
-        with self.static_graph():
-            scores = paddle.static.data(
-                name='scores', shape=[2, 3, 4, 4], dtype='float32')
-            bbox_deltas = paddle.static.data(
-                name='bbox_deltas', shape=[2, 12, 4, 4], dtype='float32')
-            im_shape = paddle.static.data(
-                name='im_shape', shape=[2, 2], dtype='float32')
-            anchors = paddle.static.data(
-                name='anchors', shape=[4, 4, 3, 4], dtype='float32')
-            variances = paddle.static.data(
-                name='var', shape=[4, 4, 3, 4], dtype='float32')
-            rois, roi_probs, rois_num = ops.generate_proposals(
-                scores,
-                bbox_deltas,
-                im_shape,
-                anchors,
-                variances,
-                pre_nms_top_n=10,
-                post_nms_top_n=5,
-                return_rois_num=True)
-            rois_stat, roi_probs_stat, rois_num_stat = self.get_static_graph_result(
-                feed={
-                    'scores': scores_np,
-                    'bbox_deltas': bbox_deltas_np,
-                    'im_shape': im_shape_np,
-                    'anchors': anchors_np,
-                    'var': variances_np
-                },
-                fetch_list=[rois, roi_probs, rois_num],
-                with_lod=True)
-
-        with self.dynamic_graph():
-            scores_dy = base.to_variable(scores_np)
-            bbox_deltas_dy = base.to_variable(bbox_deltas_np)
-            im_shape_dy = base.to_variable(im_shape_np)
-            anchors_dy = base.to_variable(anchors_np)
-            variances_dy = base.to_variable(variances_np)
-            rois, roi_probs, rois_num = ops.generate_proposals(
-                scores_dy,
-                bbox_deltas_dy,
-                im_shape_dy,
-                anchors_dy,
-                variances_dy,
-                pre_nms_top_n=10,
-                post_nms_top_n=5,
-                return_rois_num=True)
-            rois_dy = rois.numpy()
-            roi_probs_dy = roi_probs.numpy()
-            rois_num_dy = rois_num.numpy()
-
-        self.assertTrue(np.array_equal(np.array(rois_stat), rois_dy))
-        self.assertTrue(np.array_equal(np.array(roi_probs_stat), roi_probs_dy))
-        self.assertTrue(np.array_equal(np.array(rois_num_stat), rois_num_dy))
-
-
-if __name__ == '__main__':
-    unittest.main()

+ 0 - 415
paddlex/ppdet/modeling/tests/test_yolov3_loss.py

@@ -1,415 +0,0 @@
-#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
-#
-# 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 division
-
-import unittest
-
-import paddle
-from paddle import fluid
-# add python path of PadleDetection to sys.path
-import os
-import sys
-parent_path = os.path.abspath(os.path.join(__file__, *(['..'] * 4)))
-if parent_path not in sys.path:
-    sys.path.append(parent_path)
-
-from paddlex.ppdet.modeling.losses import YOLOv3Loss
-from paddlex.ppdet.data.transform.op_helper import jaccard_overlap
-import numpy as np
-
-
-def _split_ioup(output, an_num, num_classes):
-    """
-    Split output feature map to output, predicted iou
-    along channel dimension
-    """
-    ioup = fluid.layers.slice(output, axes=[1], starts=[0], ends=[an_num])
-    ioup = fluid.layers.sigmoid(ioup)
-    oriout = fluid.layers.slice(
-        output, axes=[1], starts=[an_num], ends=[an_num * (num_classes + 6)])
-    return (ioup, oriout)
-
-
-def _split_output(output, an_num, num_classes):
-    """
-    Split output feature map to x, y, w, h, objectness, classification
-    along channel dimension
-    """
-    x = fluid.layers.strided_slice(
-        output,
-        axes=[1],
-        starts=[0],
-        ends=[output.shape[1]],
-        strides=[5 + num_classes])
-    y = fluid.layers.strided_slice(
-        output,
-        axes=[1],
-        starts=[1],
-        ends=[output.shape[1]],
-        strides=[5 + num_classes])
-    w = fluid.layers.strided_slice(
-        output,
-        axes=[1],
-        starts=[2],
-        ends=[output.shape[1]],
-        strides=[5 + num_classes])
-    h = fluid.layers.strided_slice(
-        output,
-        axes=[1],
-        starts=[3],
-        ends=[output.shape[1]],
-        strides=[5 + num_classes])
-    obj = fluid.layers.strided_slice(
-        output,
-        axes=[1],
-        starts=[4],
-        ends=[output.shape[1]],
-        strides=[5 + num_classes])
-    clss = []
-    stride = output.shape[1] // an_num
-    for m in range(an_num):
-        clss.append(
-            fluid.layers.slice(
-                output,
-                axes=[1],
-                starts=[stride * m + 5],
-                ends=[stride * m + 5 + num_classes]))
-    cls = fluid.layers.transpose(
-        fluid.layers.stack(
-            clss, axis=1), perm=[0, 1, 3, 4, 2])
-    return (x, y, w, h, obj, cls)
-
-
-def _split_target(target):
-    """
-    split target to x, y, w, h, objectness, classification
-    along dimension 2
-    target is in shape [N, an_num, 6 + class_num, H, W]
-    """
-    tx = target[:, :, 0, :, :]
-    ty = target[:, :, 1, :, :]
-    tw = target[:, :, 2, :, :]
-    th = target[:, :, 3, :, :]
-    tscale = target[:, :, 4, :, :]
-    tobj = target[:, :, 5, :, :]
-    tcls = fluid.layers.transpose(target[:, :, 6:, :, :], perm=[0, 1, 3, 4, 2])
-    tcls.stop_gradient = True
-    return (tx, ty, tw, th, tscale, tobj, tcls)
-
-
-def _calc_obj_loss(output, obj, tobj, gt_box, batch_size, anchors, num_classes,
-                   downsample, ignore_thresh, scale_x_y):
-    # A prediction bbox overlap any gt_bbox over ignore_thresh,
-    # objectness loss will be ignored, process as follows:
-    # 1. get pred bbox, which is same with YOLOv3 infer mode, use yolo_box here
-    # NOTE: img_size is set as 1.0 to get noramlized pred bbox
-    bbox, prob = fluid.layers.yolo_box(
-        x=output,
-        img_size=fluid.layers.ones(
-            shape=[batch_size, 2], dtype="int32"),
-        anchors=anchors,
-        class_num=num_classes,
-        conf_thresh=0.,
-        downsample_ratio=downsample,
-        clip_bbox=False,
-        scale_x_y=scale_x_y)
-    # 2. split pred bbox and gt bbox by sample, calculate IoU between pred bbox
-    #    and gt bbox in each sample
-    if batch_size > 1:
-        preds = fluid.layers.split(bbox, batch_size, dim=0)
-        gts = fluid.layers.split(gt_box, batch_size, dim=0)
-    else:
-        preds = [bbox]
-        gts = [gt_box]
-        probs = [prob]
-    ious = []
-    for pred, gt in zip(preds, gts):
-
-        def box_xywh2xyxy(box):
-            x = box[:, 0]
-            y = box[:, 1]
-            w = box[:, 2]
-            h = box[:, 3]
-            return fluid.layers.stack(
-                [
-                    x - w / 2.,
-                    y - h / 2.,
-                    x + w / 2.,
-                    y + h / 2.,
-                ], axis=1)
-
-        pred = fluid.layers.squeeze(pred, axes=[0])
-        gt = box_xywh2xyxy(fluid.layers.squeeze(gt, axes=[0]))
-        ious.append(fluid.layers.iou_similarity(pred, gt))
-    iou = fluid.layers.stack(ious, axis=0)
-    # 3. Get iou_mask by IoU between gt bbox and prediction bbox,
-    #    Get obj_mask by tobj(holds gt_score), calculate objectness loss
-    max_iou = fluid.layers.reduce_max(iou, dim=-1)
-    iou_mask = fluid.layers.cast(max_iou <= ignore_thresh, dtype="float32")
-    output_shape = fluid.layers.shape(output)
-    an_num = len(anchors) // 2
-    iou_mask = fluid.layers.reshape(iou_mask, (-1, an_num, output_shape[2],
-                                               output_shape[3]))
-    iou_mask.stop_gradient = True
-    # NOTE: tobj holds gt_score, obj_mask holds object existence mask
-    obj_mask = fluid.layers.cast(tobj > 0., dtype="float32")
-    obj_mask.stop_gradient = True
-    # For positive objectness grids, objectness loss should be calculated
-    # For negative objectness grids, objectness loss is calculated only iou_mask == 1.0
-    loss_obj = fluid.layers.sigmoid_cross_entropy_with_logits(obj, obj_mask)
-    loss_obj_pos = fluid.layers.reduce_sum(loss_obj * tobj, dim=[1, 2, 3])
-    loss_obj_neg = fluid.layers.reduce_sum(
-        loss_obj * (1.0 - obj_mask) * iou_mask, dim=[1, 2, 3])
-    return loss_obj_pos, loss_obj_neg
-
-
-def fine_grained_loss(output,
-                      target,
-                      gt_box,
-                      batch_size,
-                      num_classes,
-                      anchors,
-                      ignore_thresh,
-                      downsample,
-                      scale_x_y=1.,
-                      eps=1e-10):
-    an_num = len(anchors) // 2
-    x, y, w, h, obj, cls = _split_output(output, an_num, num_classes)
-    tx, ty, tw, th, tscale, tobj, tcls = _split_target(target)
-
-    tscale_tobj = tscale * tobj
-
-    scale_x_y = scale_x_y
-
-    if (abs(scale_x_y - 1.0) < eps):
-        loss_x = fluid.layers.sigmoid_cross_entropy_with_logits(
-            x, tx) * tscale_tobj
-        loss_x = fluid.layers.reduce_sum(loss_x, dim=[1, 2, 3])
-        loss_y = fluid.layers.sigmoid_cross_entropy_with_logits(
-            y, ty) * tscale_tobj
-        loss_y = fluid.layers.reduce_sum(loss_y, dim=[1, 2, 3])
-    else:
-        dx = scale_x_y * fluid.layers.sigmoid(x) - 0.5 * (scale_x_y - 1.0)
-        dy = scale_x_y * fluid.layers.sigmoid(y) - 0.5 * (scale_x_y - 1.0)
-        loss_x = fluid.layers.abs(dx - tx) * tscale_tobj
-        loss_x = fluid.layers.reduce_sum(loss_x, dim=[1, 2, 3])
-        loss_y = fluid.layers.abs(dy - ty) * tscale_tobj
-        loss_y = fluid.layers.reduce_sum(loss_y, dim=[1, 2, 3])
-
-    # NOTE: we refined loss function of (w, h) as L1Loss
-    loss_w = fluid.layers.abs(w - tw) * tscale_tobj
-    loss_w = fluid.layers.reduce_sum(loss_w, dim=[1, 2, 3])
-    loss_h = fluid.layers.abs(h - th) * tscale_tobj
-    loss_h = fluid.layers.reduce_sum(loss_h, dim=[1, 2, 3])
-
-    loss_obj_pos, loss_obj_neg = _calc_obj_loss(
-        output, obj, tobj, gt_box, batch_size, anchors, num_classes,
-        downsample, ignore_thresh, scale_x_y)
-
-    loss_cls = fluid.layers.sigmoid_cross_entropy_with_logits(cls, tcls)
-    loss_cls = fluid.layers.elementwise_mul(loss_cls, tobj, axis=0)
-    loss_cls = fluid.layers.reduce_sum(loss_cls, dim=[1, 2, 3, 4])
-
-    loss_xys = fluid.layers.reduce_mean(loss_x + loss_y)
-    loss_whs = fluid.layers.reduce_mean(loss_w + loss_h)
-    loss_objs = fluid.layers.reduce_mean(loss_obj_pos + loss_obj_neg)
-    loss_clss = fluid.layers.reduce_mean(loss_cls)
-
-    losses_all = {
-        "loss_xy": fluid.layers.sum(loss_xys),
-        "loss_wh": fluid.layers.sum(loss_whs),
-        "loss_loc": fluid.layers.sum(loss_xys) + fluid.layers.sum(loss_whs),
-        "loss_obj": fluid.layers.sum(loss_objs),
-        "loss_cls": fluid.layers.sum(loss_clss),
-    }
-    return losses_all, x, y, tx, ty
-
-
-def gt2yolotarget(gt_bbox, gt_class, gt_score, anchors, mask, num_classes,
-                  size, stride):
-    grid_h, grid_w = size
-    h, w = grid_h * stride, grid_w * stride
-    an_hw = np.array(anchors) / np.array([[w, h]])
-    target = np.zeros(
-        (len(mask), 6 + num_classes, grid_h, grid_w), dtype=np.float32)
-    for b in range(gt_bbox.shape[0]):
-        gx, gy, gw, gh = gt_bbox[b, :]
-        cls = gt_class[b]
-        score = gt_score[b]
-        if gw <= 0. or gh <= 0. or score <= 0.:
-            continue
-
-        # find best match anchor index
-        best_iou = 0.
-        best_idx = -1
-        for an_idx in range(an_hw.shape[0]):
-            iou = jaccard_overlap(
-                [0., 0., gw, gh],
-                [0., 0., an_hw[an_idx, 0], an_hw[an_idx, 1]])
-            if iou > best_iou:
-                best_iou = iou
-                best_idx = an_idx
-
-        gi = int(gx * grid_w)
-        gj = int(gy * grid_h)
-
-        # gtbox should be regresed in this layes if best match
-        # anchor index in anchor mask of this layer
-        if best_idx in mask:
-            best_n = mask.index(best_idx)
-
-            # x, y, w, h, scale
-            target[best_n, 0, gj, gi] = gx * grid_w - gi
-            target[best_n, 1, gj, gi] = gy * grid_h - gj
-            target[best_n, 2, gj, gi] = np.log(gw * w / anchors[best_idx][0])
-            target[best_n, 3, gj, gi] = np.log(gh * h / anchors[best_idx][1])
-            target[best_n, 4, gj, gi] = 2.0 - gw * gh
-
-            # objectness record gt_score
-            # if target[best_n, 5, gj, gi] > 0:
-            #     print('find 1 duplicate')
-            target[best_n, 5, gj, gi] = score
-
-            # classification
-            target[best_n, 6 + cls, gj, gi] = 1.
-
-    return target
-
-
-class TestYolov3LossOp(unittest.TestCase):
-    def setUp(self):
-        self.initTestCase()
-        x = np.random.uniform(0, 1, self.x_shape).astype('float64')
-        gtbox = np.random.random(size=self.gtbox_shape).astype('float64')
-        gtlabel = np.random.randint(0, self.class_num, self.gtbox_shape[:2])
-        gtmask = np.random.randint(0, 2, self.gtbox_shape[:2])
-        gtbox = gtbox * gtmask[:, :, np.newaxis]
-        gtlabel = gtlabel * gtmask
-
-        gtscore = np.ones(self.gtbox_shape[:2]).astype('float64')
-        if self.gtscore:
-            gtscore = np.random.random(self.gtbox_shape[:2]).astype('float64')
-
-        target = []
-        for box, label, score in zip(gtbox, gtlabel, gtscore):
-            target.append(
-                gt2yolotarget(box, label, score, self.anchors,
-                              self.anchor_mask, self.class_num, (
-                                  self.h, self.w), self.downsample_ratio))
-
-        self.target = np.array(target).astype('float64')
-
-        self.mask_anchors = []
-        for i in self.anchor_mask:
-            self.mask_anchors.extend(self.anchors[i])
-        self.x = x
-        self.gtbox = gtbox
-        self.gtlabel = gtlabel
-        self.gtscore = gtscore
-
-    def initTestCase(self):
-        self.b = 8
-        self.h = 19
-        self.w = 19
-        self.anchors = [[10, 13], [16, 30], [33, 23], [30, 61], [62, 45],
-                        [59, 119], [116, 90], [156, 198], [373, 326]]
-        self.anchor_mask = [6, 7, 8]
-        self.na = len(self.anchor_mask)
-        self.class_num = 80
-        self.ignore_thresh = 0.7
-        self.downsample_ratio = 32
-        self.x_shape = (self.b, len(self.anchor_mask) * (5 + self.class_num),
-                        self.h, self.w)
-        self.gtbox_shape = (self.b, 40, 4)
-        self.gtscore = True
-        self.use_label_smooth = False
-        self.scale_x_y = 1.
-
-    def test_loss(self):
-        x, gtbox, gtlabel, gtscore, target = self.x, self.gtbox, self.gtlabel, self.gtscore, self.target
-        yolo_loss = YOLOv3Loss(
-            ignore_thresh=self.ignore_thresh,
-            label_smooth=self.use_label_smooth,
-            num_classes=self.class_num,
-            downsample=self.downsample_ratio,
-            scale_x_y=self.scale_x_y)
-        x = paddle.to_tensor(x.astype(np.float32))
-        gtbox = paddle.to_tensor(gtbox.astype(np.float32))
-        gtlabel = paddle.to_tensor(gtlabel.astype(np.float32))
-        gtscore = paddle.to_tensor(gtscore.astype(np.float32))
-        t = paddle.to_tensor(target.astype(np.float32))
-        anchor = [self.anchors[i] for i in self.anchor_mask]
-        (yolo_loss1, px, py, tx, ty) = fine_grained_loss(
-            output=x,
-            target=t,
-            gt_box=gtbox,
-            batch_size=self.b,
-            num_classes=self.class_num,
-            anchors=self.mask_anchors,
-            ignore_thresh=self.ignore_thresh,
-            downsample=self.downsample_ratio,
-            scale_x_y=self.scale_x_y)
-        yolo_loss2 = yolo_loss.yolov3_loss(
-            x, t, gtbox, anchor, self.downsample_ratio, self.scale_x_y)
-        for k in yolo_loss2:
-            self.assertAlmostEqual(
-                yolo_loss1[k].numpy()[0],
-                yolo_loss2[k].numpy()[0],
-                delta=1e-2,
-                msg=k)
-
-
-class TestYolov3LossNoGTScore(TestYolov3LossOp):
-    def initTestCase(self):
-        self.b = 1
-        self.h = 76
-        self.w = 76
-        self.anchors = [[10, 13], [16, 30], [33, 23], [30, 61], [62, 45],
-                        [59, 119], [116, 90], [156, 198], [373, 326]]
-        self.anchor_mask = [0, 1, 2]
-        self.na = len(self.anchor_mask)
-        self.class_num = 80
-        self.ignore_thresh = 0.7
-        self.downsample_ratio = 8
-        self.x_shape = (self.b, len(self.anchor_mask) * (5 + self.class_num),
-                        self.h, self.w)
-        self.gtbox_shape = (self.b, 40, 4)
-        self.gtscore = False
-        self.use_label_smooth = False
-        self.scale_x_y = 1.
-
-
-class TestYolov3LossWithScaleXY(TestYolov3LossOp):
-    def initTestCase(self):
-        self.b = 5
-        self.h = 38
-        self.w = 38
-        self.anchors = [[10, 13], [16, 30], [33, 23], [30, 61], [62, 45],
-                        [59, 119], [116, 90], [156, 198], [373, 326]]
-        self.anchor_mask = [3, 4, 5]
-        self.na = len(self.anchor_mask)
-        self.class_num = 80
-        self.ignore_thresh = 0.7
-        self.downsample_ratio = 16
-        self.x_shape = (self.b, len(self.anchor_mask) * (5 + self.class_num),
-                        self.h, self.w)
-        self.gtbox_shape = (self.b, 40, 4)
-        self.gtscore = True
-        self.use_label_smooth = False
-        self.scale_x_y = 1.2
-
-
-if __name__ == "__main__":
-    unittest.main()

+ 4 - 2
paddlex/ppdet/optimizer.py

@@ -42,9 +42,10 @@ class CosineDecay(object):
             the max_iters is much larger than the warmup iter
     """
 
-    def __init__(self, max_epochs=1000, use_warmup=True):
+    def __init__(self, max_epochs=1000, use_warmup=True, eta_min=0):
         self.max_epochs = max_epochs
         self.use_warmup = use_warmup
+        self.eta_min = eta_min
 
     def __call__(self,
                  base_lr=None,
@@ -66,7 +67,8 @@ class CosineDecay(object):
                 value.append(decayed_lr)
             return optimizer.lr.PiecewiseDecay(boundary, value)
 
-        return optimizer.lr.CosineAnnealingDecay(base_lr, T_max=max_iters)
+        return optimizer.lr.CosineAnnealingDecay(
+            base_lr, T_max=max_iters, eta_min=self.eta_min)
 
 
 @serializable

+ 8 - 4
paddlex/ppdet/utils/checkpoint.py

@@ -124,7 +124,7 @@ def match_state_dict(model_state_dict, weight_state_dict):
     weight_keys = sorted(weight_state_dict.keys())
 
     def match(a, b):
-        if a.startswith('backbone.res5'):
+        if b.startswith('backbone.res5'):
             # In Faster RCNN, res5 pretrained weights have prefix of backbone,
             # however, the corresponding model weights have difficult prefix,
             # bbox_head.
@@ -139,10 +139,14 @@ def match_state_dict(model_state_dict, weight_state_dict):
     max_id = match_matrix.argmax(1)
     max_len = match_matrix.max(1)
     max_id[max_len == 0] = -1
+
+    load_id = set(max_id)
+    load_id.discard(-1)
     not_load_weight_name = []
-    for match_idx in range(len(max_id)):
-        if match_idx < len(weight_keys) and max_id[match_idx] == -1:
-            not_load_weight_name.append(weight_keys[match_idx])
+    for idx in range(len(weight_keys)):
+        if idx not in load_id:
+            not_load_weight_name.append(weight_keys[idx])
+
     if len(not_load_weight_name) > 0:
         logger.info('{} in pretrained weight is not used in the model, '
                     'and its will not be loaded'.format(not_load_weight_name))

+ 22 - 0
paddlex/utils/checkpoint.py

@@ -85,6 +85,28 @@ cityscapes_weights = {
 }
 
 imagenet_weights = {
+    'PPLCNet_x0_25_IMAGENET':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_25_pretrained.pdparams',
+    'PPLCNet_x0_35_IMAGENET':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_35_pretrained.pdparams',
+    'PPLCNet_x0_5_IMAGENET':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_5_pretrained.pdparams',
+    'PPLCNet_x0_75_IMAGENET':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_75_pretrained.pdparams',
+    'PPLCNet_x1_0_IMAGENET':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x1_0_pretrained.pdparams',
+    'PPLCNet_x1_5_IMAGENET':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x1_5_pretrained.pdparams',
+    'PPLCNet_x2_0_IMAGENET':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x2_0_pretrained.pdparams',
+    'PPLCNet_x2_5_IMAGENET':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x2_5_pretrained.pdparams',
+    'PPLCNet_x0_5_ssld_IMAGENET':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_5_ssld_pretrained.pdparams',
+    'PPLCNet_x1_0_ssld_IMAGENET':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x1_0_ssld_pretrained.pdparams',
+    'PPLCNet_x2_5_ssld_IMAGENET':
+    'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x2_5_ssld_pretrained.pdparams',
     'ResNet18_IMAGENET':
     'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/ResNet18_pretrained.pdparams',
     'ResNet34_IMAGENET':

+ 0 - 8
paddlex_restful/restful/app.py

@@ -569,9 +569,6 @@ def task_evaluate():
         ret = get_evaluate_result(data, SD.workspace)
         if ret['evaluate_status'] == TaskStatus.XEVALUATED and ret[
                 'result'] is not None:
-            if 'Confusion_Matrix' in ret['result']:
-                ret['result']['Confusion_Matrix'] = ret['result'][
-                    'Confusion_Matrix']
             ret['result'] = CustomEncoder().encode(ret['result'])
             ret['result'] = json.loads(ret['result'])
         ret['evaluate_status'] = ret['evaluate_status'].value
@@ -893,16 +890,11 @@ def model():
                 return ret
         from .model import get_model_details
         ret = get_model_details(data, SD.workspace)
-        ret['eval_result']['Confusion_Matrix'] = ret['eval_result'][
-            'Confusion_Matrix'].tolist()
         ret['eval_result'] = CustomEncoder().encode(ret['eval_result'])
         ret['task_params'] = CustomEncoder().encode(ret['task_params'])
         return ret
     if request.method == 'POST':
         if data['type'] == 'pretrained':
-            if 'eval_results' in data:
-                data['eval_results']['Confusion_Matrix'] = np.array(data[
-                    'eval_results']['Confusion_Matrix'])
             from .model import create_pretrained_model
             ret = create_pretrained_model(data, SD.workspace,
                                           SD.monitored_processes)

+ 1 - 1
paddlex_restful/restful/project/train/classification.py

@@ -70,7 +70,7 @@ def build_optimizer(parameters, step_each_epoch, params):
     num_epochs = params.num_epochs
     if params.lr_policy == 'Cosine':
         learning_rate = paddle.optimizer.lr.CosineAnnealingDecay(
-            learning_rate=.001, T_max=step_each_epoch * num_epochs)
+            learning_rate=learning_rate, T_max=step_each_epoch * num_epochs)
     elif params.lr_policy == 'Linear':
         learning_rate = paddle.optimizer.lr.PolynomialDecay(
             learning_rate=learning_rate,

+ 38 - 1
paddlex_restful/restful/project/train/detection.py

@@ -90,6 +90,40 @@ def build_rcnn_transforms(params):
     return train_transforms, eval_transforms
 
 
+def build_pico_transforms(params):
+    from paddlex import transforms as T
+    target_size = params.image_shape[0]
+    dt_list = []
+    dt_list.extend([
+        T.RandomDistort(
+            brightness_range=params.brightness_range,
+            brightness_prob=params.brightness_prob,
+            contrast_range=params.contrast_range,
+            contrast_prob=params.contrast_prob,
+            saturation_range=params.saturation_range,
+            saturation_prob=params.saturation_prob,
+            hue_range=params.hue_range,
+            hue_prob=params.hue_prob),
+    ])
+    crop_image = params.crop_image
+    if crop_image:
+        dt_list.append(T.RandomCrop())
+    dt_list.extend([
+        T.Resize(
+            target_size=target_size, interp='RANDOM'),
+        T.RandomHorizontalFlip(prob=params.horizontal_flip_prob), T.Normalize(
+            mean=params.image_mean, std=params.image_std)
+    ])
+    train_transforms = T.Compose(dt_list)
+    eval_transforms = T.Compose([
+        T.Resize(
+            target_size=target_size, interp='CUBIC'),
+        T.Normalize(
+            mean=params.image_mean, std=params.image_std),
+    ])
+    return train_transforms, eval_transforms
+
+
 def build_voc_datasets(dataset_path, train_transforms, eval_transforms):
     import paddlex as pdx
     train_file_list = osp.join(dataset_path, 'train_list.txt')
@@ -157,6 +191,8 @@ def train(task_path, dataset_path, params):
     pdx.log_level = 3
     if params.model in ['YOLOv3', 'PPYOLO', 'PPYOLOTiny', 'PPYOLOv2']:
         train_transforms, eval_transforms = build_yolo_transforms(params)
+    elif params.model in ['PicoDet']:
+        train_transforms, eval_transforms = build_pico_transforms(params)
     elif params.model in ['FasterRCNN', 'MaskRCNN']:
         train_transforms, eval_transforms = build_rcnn_transforms(params)
     if osp.exists(osp.join(dataset_path, 'JPEGImages')) and \
@@ -194,7 +230,8 @@ def train(task_path, dataset_path, params):
         # prune
         dataset = eval_dataset or train_dataset
         im_shape = dataset[0]['image'].shape[:2]
-        if getattr(model, 'with_fpn', False):
+        if getattr(model, 'with_fpn',
+                   False) or model.__class__.__name__ == 'PicoDet':
             im_shape[0] = int(np.ceil(im_shape[0] / 32) * 32)
             im_shape[1] = int(np.ceil(im_shape[1] / 32) * 32)
         inputs = [{

+ 2 - 2
requirements.txt

@@ -4,9 +4,9 @@ colorama
 cython
 pycocotools
 visualdl >= 2.1.1
-paddleslim == 2.1.1
+paddleslim == 2.2.1
 shapely
-paddlepaddle-gpu == 2.2.0
+paddlepaddle-gpu >= 2.2.0
 opencv-python
 scikit-learn==0.23.2
 lap

+ 6 - 6
setup.py

@@ -13,13 +13,12 @@
 # limitations under the License.
 
 import setuptools
-import sys
 
-long_description = "PaddlePaddle Entire Process Development Toolkit"
+long_description = "PaddlePaddle End-to-End Development Toolkit"
 
 setuptools.setup(
     name="paddlex",
-    version='2.0.0',
+    version='2.1.0',
     author="paddlex",
     author_email="paddlex@baidu.com",
     description=long_description,
@@ -38,9 +37,10 @@ setuptools.setup(
     include_data_files=True,
     setup_requires=['cython', 'numpy'],
     install_requires=[
-        "pycocotools", 'pyyaml', 'colorama', 'tqdm', 'paddleslim==2.1.1',
-        'visualdl>=2.1.1', 'shapely>=1.7.0', 'opencv-python', 'scipy', 'lap',
-        'motmetrics', 'scikit-learn==0.23.2', 'chardet', 'flask_cors'
+        "pycocotools", 'pyyaml', 'colorama', 'tqdm', 'paddleslim==2.2.1',
+        'visualdl>=2.2.2', 'shapely>=1.7.0', 'opencv-python', 'scipy', 'lap',
+        'motmetrics', 'scikit-learn==0.23.2', 'chardet', 'flask_cors',
+        'openpyxl'
     ],
     classifiers=[
         "Programming Language :: Python :: 3",

+ 1 - 1
static/docs/apis/visualize.md

@@ -151,7 +151,7 @@ paddlex.det.coco_error_analysis(eval_details_file=None, gt=None, pred_bbox=None,
 
 ![](images/detection_analysis.jpg)
 
-左图显示的是`person`类的分析结果,图显示的是所有类别整体的分析结果。
+左图显示的是`person`类的分析结果,图显示的是所有类别整体的分析结果。
 
 分析图表展示了7条Precision-Recall(PR)曲线,每一条曲线表示的Average Precision (AP)比它左边那条高,原因是逐步放宽了评估要求。以`person`类为例,各条PR曲线的评估要求解释如下:
 

+ 50 - 0
tutorials/train/image_classification/pplcnet.py

@@ -0,0 +1,50 @@
+import paddlex as pdx
+from paddlex import transforms as T
+
+# 下载和解压蔬菜分类数据集
+veg_dataset = 'https://bj.bcebos.com/paddlex/datasets/vegetables_cls.tar.gz'
+pdx.utils.download_and_decompress(veg_dataset, path='./')
+
+# 定义训练和验证时的transforms
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/apis/transforms/transforms.md
+train_transforms = T.Compose(
+    [T.RandomCrop(crop_size=224), T.RandomHorizontalFlip(), T.Normalize()])
+
+eval_transforms = T.Compose([
+    T.ResizeByShort(short_size=256), T.CenterCrop(crop_size=224), T.Normalize()
+])
+
+# 定义训练和验证所用的数据集
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/apis/datasets.md
+train_dataset = pdx.datasets.ImageNet(
+    data_dir='vegetables_cls',
+    file_list='vegetables_cls/train_list.txt',
+    label_list='vegetables_cls/labels.txt',
+    transforms=train_transforms,
+    shuffle=True)
+
+eval_dataset = pdx.datasets.ImageNet(
+    data_dir='vegetables_cls',
+    file_list='vegetables_cls/val_list.txt',
+    label_list='vegetables_cls/labels.txt',
+    transforms=eval_transforms)
+
+# 初始化模型,并进行训练
+# 可使用VisualDL查看训练指标,参考https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/visualdl.md
+num_classes = len(train_dataset.labels)
+model = pdx.cls.PPLCNet(num_classes=num_classes, scale=1)
+
+# API说明:https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/apis/models/classification.md
+# 各参数介绍与调整说明:https://github.com/PaddlePaddle/PaddleX/tree/develop/docs/parameters.md
+model.train(
+    num_epochs=10,
+    pretrain_weights='IMAGENET',
+    train_dataset=train_dataset,
+    train_batch_size=64,
+    eval_dataset=eval_dataset,
+    lr_decay_epochs=[4, 6, 8],
+    learning_rate=0.1,
+    save_dir='output/pplcnet',
+    log_interval_steps=10,
+    label_smoothing=.1,
+    use_vdl=True)

+ 8 - 7
tutorials/train/object_detection/picodet.py

@@ -44,15 +44,16 @@ model = pdx.det.PicoDet(num_classes=num_classes, backbone='ESNet_l')
 # API说明:https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/apis/models/detection.md
 # 各参数介绍与调整说明:https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/parameters.md
 model.train(
-    num_epochs=300,
+    num_epochs=20,
     train_dataset=train_dataset,
-    train_batch_size=7,
+    train_batch_size=14,
     eval_dataset=eval_dataset,
     pretrain_weights='COCO',
     learning_rate=.05,
-    warmup_steps=300,
-    warmup_start_lr=0.0,
-    save_interval_epochs=5,
-    lr_decay_epochs=[85, 135],
-    save_dir='output/piconet_esnet_l',
+    warmup_steps=24,
+    warmup_start_lr=0.005,
+    save_interval_epochs=1,
+    lr_decay_epochs=[6, 8, 11],
+    use_ema=True,
+    save_dir='output/picodet_esnet_l',
     use_vdl=True)