Bladeren bron

fix wrong links in docs/gui

FlyingQianMM 3 jaren geleden
bovenliggende
commit
b781b3c619
73 gewijzigde bestanden met toevoegingen van 955 en 2181 verwijderingen
  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">
 <p align="left">
     <a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache%202-red.svg"></a>
     <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/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/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>
     <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正式版本。
 2021.09.10 PaddleX发布2.0.0正式版本。
 - 全新发布Manufacture SDK,支持多模型串联部署。[欢迎体验](./deploy/cpp/docs/manufacture_sdk)
 - 全新发布Manufacture SDK,支持多模型串联部署。[欢迎体验](./deploy/cpp/docs/manufacture_sdk)
 - PaddleX部署全面升级,支持飞桨视觉套件PaddleDetection、PaddleClas、PaddleSeg、PaddleX的端到端统一部署能力。[欢迎体验](./deploy/cpp/docs/deployment.md)
 - 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. 修改编译参数
 ### Step 3. 修改编译参数
-根据自己的系统环境,修改`PaddleX/deploy/cpp/script/build.sh`脚本中的参数,主要修改的参数为以下几个
+根据自己的系统环境,修改`PaddleX/deploy/cpp/
+
+
+/build.sh`脚本中的参数,主要修改的参数为以下几个
 | 参数          | 说明                                                                                 |
 | 参数          | 说明                                                                                 |
 | :------------ | :----------------------------------------------------------------------------------- |
 | :------------ | :----------------------------------------------------------------------------------- |
 | WITH_GPU      | ON或OFF,表示是否使用GPU,当下载的为CPU预测库时,设为OFF                             |
 | WITH_GPU      | ON或OFF,表示是否使用GPU,当下载的为CPU预测库时,设为OFF                             |
@@ -54,7 +57,7 @@ PaddlePaddle C++ 预测库针对是否使用GPU、是否支持TensorRT、以及
 ### Step 4. 编译
 ### Step 4. 编译
 修改完build.sh后执行编译:  
 修改完build.sh后执行编译:  
 ```commandline
 ```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)
 > 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`,将解压后的目录拷贝至该创建的目录下
 > 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
 * 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
 	// create model
 	model = PaddleDeploy::CreateModel(model_type);  //FLAGS_model_type
 	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"
 */
 */
-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
 	// prepare data
 	std::vector<cv::Mat> imgs;
 	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"
 */
 */
-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
 	// prepare data
 	std::vector<cv::Mat> imgs;
 	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"
 */
 */
-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
 	// prepare data
 	std::vector<cv::Mat> imgs;
 	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"
 */
 */
-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
 	// prepare data
 	std::vector<cv::Mat> imgs;
 	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"
 */
 */
-extern "C" void DestructModel()
+extern "C" __declspec(dllexport) void DestructModel()
 {
 {
 	delete model;
 	delete model;
 	std::cout << "destruct model success" << std::endl;
 	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**
 - **2021.09.10 v2.0.0**
 
 
   * PaddleX API
   * PaddleX API

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

@@ -31,19 +31,16 @@ PaddleX RESTful是基于PaddleX开发的RESTful API。对于开发者来说只
 
 
 在该示例中PaddleX_Restful运行在一台带GPU的linux服务器下,用户通过其他电脑连接该服务器进行远程的操作。
 在该示例中PaddleX_Restful运行在一台带GPU的linux服务器下,用户通过其他电脑连接该服务器进行远程的操作。
 ### 1  环境准备
 ### 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,该项可不进行下载)
 * 下载pycuda(如果不使用GPU,该项可不进行下载)
 
 
-pip install pycuda -i
+pip install pycuda
 
 
 ### 2  启动Restful服务
 ### 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  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>
 ## <h2 id="2">paddlex.datasets.VOCDetection</h2>
 > **用于目标检测模型**  
 > **用于目标检测模型**  
 ```python
 ```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)  
 > 读取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核数的一半。
 > > * **num_workers** (int|str):数据集中样本在预处理过程中的进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
 > > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
 > > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
 > > * **allow_empty** (bool): 是否加载负样本。默认为False。
 > > * **allow_empty** (bool): 是否加载负样本。默认为False。
+> > * **empty_ratio** (float): 用于指定负样本占总样本数的比例。如果小于0或大于等于1,则保留全部的负样本。默认为1。
 
 
 ### <h3 id="21">cluster_yolo_anchor</h3>
 ### <h3 id="21">cluster_yolo_anchor</h3>
 
 
@@ -144,7 +145,7 @@ model.train(
 ## <h2 id="3">paddlex.datasets.CocoDetection</h2>
 ## <h2 id="3">paddlex.datasets.CocoDetection</h2>
 > **用于实例分割/目标检测模型**  
 > **用于实例分割/目标检测模型**  
 ```python
 ```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)  
 > 读取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核数的一半。  
 > > * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。  
 > > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
 > > * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
 > > * **allow_empty** (bool): 是否加载负样本。默认为False。
 > > * **allow_empty** (bool): 是否加载负样本。默认为False。
+> > * **empty_ratio** (float): 用于指定负样本占总样本数的比例。如果小于0或大于等于1,则保留全部的负样本。默认为1。
 
 
 ### <h3 id="31">cluster_yolo_anchor</h3>
 ### <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>
 ### <h3 id="11">train</h3>
 
 
 ```python
 ```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。
 - **warmup_start_lr**(float): 默认优化器的warmup起始学习率,默认为0.0。
 - **lr_decay_epochs** (list): 默认优化器的学习率衰减轮数。默认为[30, 60, 90]。
 - **lr_decay_epochs** (list): 默认优化器的学习率衰减轮数。默认为[30, 60, 90]。
 - **lr_decay_gamma** (float): 默认优化器的学习率衰减率。默认为0.1。
 - **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** (bool): 是否使用提前终止训练策略。默认为False。
 - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认为5。
 - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认为5。
 - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认为True。
 - **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)。
 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)
   * [quant_aware_train](#16)
 * [paddlex.det.PPYOLO](#2)
 * [paddlex.det.PPYOLO](#2)
 * [paddlex.det.PPYOLOTiny](#3)
 * [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>
 ## <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)
 > - prune 剪裁接口说明同 [PPYOLOv2模型prune接口](#prune)
 > - quant_aware_train 在线量化接口说明同 [PPYOLOv2模型quant_aware_train接口](#quant_aware_train)
 > - 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
 ```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)
 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)
 > - 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
 ```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)
 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)
 ![](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)
 ![](images/detection_analysis.jpg)
 
 
-左图显示的是`person`类的分析结果,图显示的是所有类别整体的分析结果。
+左图显示的是`person`类的分析结果,图显示的是所有类别整体的分析结果。
 
 
 分析图表展示了7条Precision-Recall(PR)曲线,每一条曲线表示的Average Precision (AP)比它左边那条高,原因是逐步放宽了评估要求。以`person`类为例,各条PR曲线的评估要求解释如下:
 分析图表展示了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)
   * [数据文件夹结构](#11)
   * [训练集、验证集列表和类别标签列表](#12)
   * [训练集、验证集列表和类别标签列表](#12)
 * [数据加载](#2)
 * [数据加载](#2)
+* [添加负样本](#3)
 
 
 
 
 ## <h2 id="1">数据格式</h2>
 ## <h2 id="1">数据格式</h2>
@@ -97,5 +98,44 @@ eval_dataset = pdx.datasets.VOCDetection(
                         file_list='./MyDataset/val_list.txt',
                         file_list='./MyDataset/val_list.txt',
                         label_list='MyDataset/labels.txt',
                         label_list='MyDataset/labels.txt',
                         transforms=eval_transforms)
                         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)
   * [数据文件夹结构](#11)
   * [训练集、验证集列表](#12)
   * [训练集、验证集列表](#12)
 * [数据加载](#2)
 * [数据加载](#2)
+* [添加负样本](#3)
 
 
 
 
 ## <h2 id="1">数据格式</h2>
 ## <h2 id="1">数据格式</h2>
@@ -65,3 +66,42 @@ eval_dataset = pdx.dataset.CocoDetection(
                     ann_file='./MyDataset/val.json',
                     ann_file='./MyDataset/val.json',
                     transforms=eval_transforms)
                     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可视化客户端。
 - 请参考[安装方式文档](./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 develop安装](#PaddleX-develop安装)
 - [使用前置说明](#使用前置说明)
 - [使用前置说明](#使用前置说明)
     - [PaddleX的模型训练](#PaddleX的模型训练)
     - [PaddleX的模型训练](#PaddleX的模型训练)
@@ -18,17 +18,17 @@
     - <a href=#加载训练保存的模型预测>加载训练保存的模型预测</a>
     - <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)
 **我们推荐大家先安装Anacaonda,而后在新建的conoda环境中使用上述pip安装方式**。Anaconda是一个开源的Python发行版本,其包含了conda、Python等180多个科学包及其依赖项。使用Anaconda可以通过创建多个独立的Python环境,避免用户的Python环境安装太多不同版本依赖导致冲突。参考[Anaconda安装PaddleX文档](./appendix/anaconda_install.md)
 
 
 - Linux / macOS 操作系统
 - Linux / macOS 操作系统
 
 
-使用pip安装方式安装2.0.0版本:
+使用pip安装方式安装2.1.0版本:
 
 
 ```commandline
 ```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:
 paddlepaddle已集成pycocotools包,但也有pycocotools无法随paddlepaddle成功安装的情况。因PaddleX依赖pycocotools包,如遇到pycocotools安装失败,可参照如下方式安装pycocotools:
@@ -39,10 +39,10 @@ pip install pycocotools
 ```
 ```
 
 
 - Windows 操作系统
 - Windows 操作系统
-使用pip安装方式安装2.0.0版本:
+使用pip安装方式安装2.1.0版本:
 
 
 ```commandline
 ```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命令
 因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
 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
 下载地址: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版下载后双击选择安装路径即可
 - WIN版下载后双击选择安装路径即可
 - Mac/Linux版下载后解压即可
 - Mac/Linux版下载后解压即可
 
 

+ 1 - 1
paddlex/__init__.py

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

+ 3 - 0
paddlex/cls.py

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

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

@@ -37,7 +37,7 @@ __all__ = [
     "DenseNet121", "DenseNet161", "DenseNet169", "DenseNet201", "DenseNet264",
     "DenseNet121", "DenseNet161", "DenseNet169", "DenseNet201", "DenseNet264",
     "HRNet_W18_C", "HRNet_W30_C", "HRNet_W32_C", "HRNet_W40_C", "HRNet_W44_C",
     "HRNet_W18_C", "HRNet_W30_C", "HRNet_W32_C", "HRNet_W40_C", "HRNet_W44_C",
     "HRNet_W48_C", "HRNet_W64_C", "Xception41", "Xception65", "Xception71",
     "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_epochs,
                           lr_decay_gamma,
                           lr_decay_gamma,
                           num_steps_each_epoch,
                           num_steps_each_epoch,
-                          decay_coff=1e-04,
-                          lr_method='Linear',
+                          reg_coeff=1e-04,
+                          scheduler='Piecewise',
                           num_epochs=None):
                           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]
             boundaries = [b * num_steps_each_epoch for b in lr_decay_epochs]
             values = [
             values = [
                 learning_rate * (lr_decay_gamma**i)
                 learning_rate * (lr_decay_gamma**i)
                 for i in range(len(lr_decay_epochs) + 1)
                 for i in range(len(lr_decay_epochs) + 1)
             ]
             ]
             scheduler = paddle.optimizer.lr.PiecewiseDecay(boundaries, values)
             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
             T_max = num_epochs * num_steps_each_epoch - warmup_steps
-            scheduler = paddle.lr.CosineAnnealingDecay(
+            scheduler = paddle.optimizer.lr.CosineAnnealingDecay(
                 learning_rate=learning_rate,
                 learning_rate=learning_rate,
                 T_max=T_max,
                 T_max=T_max,
                 eta_min=0.0,
                 eta_min=0.0,
                 last_epoch=-1)
                 last_epoch=-1)
+        else:
+            logging.error(
+                "Invalid learning rate scheduler: {}!".format(scheduler),
+                exit=True)
+
         if warmup_steps > 0:
         if warmup_steps > 0:
             scheduler = paddle.optimizer.lr.LinearWarmup(
             scheduler = paddle.optimizer.lr.LinearWarmup(
                 learning_rate=scheduler,
                 learning_rate=scheduler,
@@ -214,7 +215,7 @@ class BaseClassifier(BaseModel):
         optimizer = paddle.optimizer.Momentum(
         optimizer = paddle.optimizer.Momentum(
             scheduler,
             scheduler,
             momentum=.9,
             momentum=.9,
-            weight_decay=paddle.regularizer.L2Decay(coeff=decay_coff),
+            weight_decay=paddle.regularizer.L2Decay(coeff=reg_coeff),
             parameters=parameters)
             parameters=parameters)
         return optimizer
         return optimizer
 
 
@@ -873,3 +874,116 @@ class ShuffleNetV2_swish(BaseClassifier):
                 shape=image_shape, name='image', dtype='float32')
                 shape=image_shape, name='image', dtype='float32')
         ]
         ]
         return input_spec
         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 paddlex.cv.transforms import arrange_transforms
 from .base import BaseModel
 from .base import BaseModel
 from .utils.det_metrics import VOCMetric, COCOMetric
 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
 from paddlex.utils.checkpoint import det_pretrain_weights_dict
 
 
 __all__ = [
 __all__ = [
@@ -112,25 +112,66 @@ class BaseDetector(BaseModel):
 
 
         return outputs
         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(
                 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)
                     exit=False)
+            if warmup_steps > 0 and warmup_steps > num_epochs * num_steps_each_epoch:
                 logging.error(
                 logging.error(
+                    "In function train(), parameters must satisfy: "
+                    "warmup_steps <= num_epochs * num_samples_in_train_dataset. "
                     "See this doc for more information: "
                     "See this doc for more information: "
                     "https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/parameters.md",
                     "https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/parameters.md",
                     exit=False)
                     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(
             scheduler = paddle.optimizer.lr.LinearWarmup(
                 learning_rate=scheduler,
                 learning_rate=scheduler,
                 warmup_steps=warmup_steps,
                 warmup_steps=warmup_steps,
@@ -139,7 +180,7 @@ class BaseDetector(BaseModel):
         optimizer = paddle.optimizer.Momentum(
         optimizer = paddle.optimizer.Momentum(
             scheduler,
             scheduler,
             momentum=.9,
             momentum=.9,
-            weight_decay=paddle.regularizer.L2Decay(coeff=1e-04),
+            weight_decay=paddle.regularizer.L2Decay(coeff=reg_coeff),
             parameters=parameters)
             parameters=parameters)
         return optimizer
         return optimizer
 
 
@@ -275,8 +316,7 @@ class BaseDetector(BaseModel):
                                  'ESNet_' in self.backbone_name))
                                  'ESNet_' in self.backbone_name))
 
 
         if use_ema:
         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:
         else:
             ema = None
             ema = None
         # start train loop
         # start train loop
@@ -593,8 +633,8 @@ class PicoDet(BaseDetector):
                  num_classes=80,
                  num_classes=80,
                  backbone='ESNet_m',
                  backbone='ESNet_m',
                  nms_score_threshold=.025,
                  nms_score_threshold=.025,
-                 nms_top_k=1000,
-                 nms_keep_top_k=100,
+                 nms_topk=1000,
+                 nms_keep_topk=100,
                  nms_iou_threshold=.6,
                  nms_iou_threshold=.6,
                  **params):
                  **params):
         self.init_params = locals()
         self.init_params = locals()
@@ -691,8 +731,8 @@ class PicoDet(BaseDetector):
             assigner = ppdet.modeling.SimOTAAssigner(
             assigner = ppdet.modeling.SimOTAAssigner(
                 candidate_topk=10, iou_weight=6, num_classes=num_classes)
                 candidate_topk=10, iou_weight=6, num_classes=num_classes)
             nms = ppdet.modeling.MultiClassNMS(
             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,
                 score_threshold=nms_score_threshold,
                 nms_threshold=nms_iou_threshold)
                 nms_threshold=nms_iou_threshold)
             head = ppdet.modeling.PicoHead(
             head = ppdet.modeling.PicoHead(
@@ -783,6 +823,92 @@ class PicoDet(BaseDetector):
         self.fixed_input_shape = image_shape
         self.fixed_input_shape = image_shape
         return self._define_input_spec(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):
 class YOLOv3(BaseDetector):
     def __init__(self,
     def __init__(self,
@@ -1226,42 +1352,22 @@ class FasterRCNN(BaseDetector):
         """
         """
         if train_dataset.pos_num < len(train_dataset.file_list):
         if train_dataset.pos_num < len(train_dataset.file_list):
             train_dataset.num_workers = 0
             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'):
     def _compose_batch_transform(self, transforms, mode='train'):
         if mode == 'train':
         if mode == 'train':
             default_batch_transforms = [
             default_batch_transforms = [
                 _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
                 _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
             ]
             ]
-            collate_batch = False
         else:
         else:
             default_batch_transforms = [
             default_batch_transforms = [
                 _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
                 _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
             ]
             ]
-            collate_batch = True
         custom_batch_transforms = []
         custom_batch_transforms = []
         for i, op in enumerate(transforms.transforms):
         for i, op in enumerate(transforms.transforms):
             if isinstance(op, (BatchRandomResize, BatchRandomResizeByShort)):
             if isinstance(op, (BatchRandomResize, BatchRandomResizeByShort)):
@@ -1274,7 +1380,7 @@ class FasterRCNN(BaseDetector):
 
 
         batch_transforms = BatchCompose(
         batch_transforms = BatchCompose(
             custom_batch_transforms + default_batch_transforms,
             custom_batch_transforms + default_batch_transforms,
-            collate_batch=collate_batch)
+            collate_batch=False)
 
 
         return batch_transforms
         return batch_transforms
 
 
@@ -2074,42 +2180,22 @@ class MaskRCNN(BaseDetector):
         """
         """
         if train_dataset.pos_num < len(train_dataset.file_list):
         if train_dataset.pos_num < len(train_dataset.file_list):
             train_dataset.num_workers = 0
             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'):
     def _compose_batch_transform(self, transforms, mode='train'):
         if mode == 'train':
         if mode == 'train':
             default_batch_transforms = [
             default_batch_transforms = [
                 _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
                 _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
             ]
             ]
-            collate_batch = False
         else:
         else:
             default_batch_transforms = [
             default_batch_transforms = [
                 _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
                 _BatchPadding(pad_to_stride=32 if self.with_fpn else -1)
             ]
             ]
-            collate_batch = True
         custom_batch_transforms = []
         custom_batch_transforms = []
         for i, op in enumerate(transforms.transforms):
         for i, op in enumerate(transforms.transforms):
             if isinstance(op, (BatchRandomResize, BatchRandomResizeByShort)):
             if isinstance(op, (BatchRandomResize, BatchRandomResizeByShort)):
@@ -2122,7 +2208,7 @@ class MaskRCNN(BaseDetector):
 
 
         batch_transforms = BatchCompose(
         batch_transforms = BatchCompose(
             custom_batch_transforms + default_batch_transforms,
             custom_batch_transforms + default_batch_transforms,
-            collate_batch=collate_batch)
+            collate_batch=False)
 
 
         return batch_transforms
         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):
 def kaiming_normal_init(param, **kwargs):
-    """
+    r"""
     Initialize the input tensor with Kaiming Normal initialization.
     Initialize the input tensor with Kaiming Normal initialization.
 
 
     This function implements the `param` initialization from the paper
     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
 @manager.LOSSES.add_component
 class BCELoss(nn.Layer):
 class BCELoss(nn.Layer):
-    """
+    r"""
     This operator combines the sigmoid layer and the :ref:`api_nn_loss_BCELoss` layer.
     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``
     Also, we can see it as the combine of ``sigmoid_cross_entropy_with_logits``
     layer and some reduce operations.
     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
         self.classes = classes
 
 
     def forward(self, logits, labels):
     def forward(self, logits, labels):
-        """
+        r"""
         Forward computation.
         Forward computation.
 
 
         Args:
         Args:
@@ -68,7 +68,7 @@ class LovaszHingeLoss(nn.Layer):
         self.ignore_index = ignore_index
         self.ignore_index = ignore_index
 
 
     def forward(self, logits, labels):
     def forward(self, logits, labels):
-        """
+        r"""
         Forward computation.
         Forward computation.
 
 
         Args:
         Args:
@@ -111,7 +111,7 @@ def binary_channel_to_unary(logits, eps=1e-9):
 
 
 
 
 def lovasz_hinge_flat(logits, labels):
 def lovasz_hinge_flat(logits, labels):
-    """
+    r"""
     Binary Lovasz hinge loss.
     Binary Lovasz hinge loss.
 
 
     Args:
     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.optimizer import ModelEMA
 
 
 from paddlex.ppdet.core.workspace import create
 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.checkpoint import load_weight, load_pretrain_weight
 from paddlex.ppdet.utils.visualizer import visualize_results, save_result
 from paddlex.ppdet.utils.visualizer import visualize_results, save_result
 from paddlex.ppdet.metrics import Metric, COCOMetric, VOCMetric, WiderFaceMetric, get_infer_results, KeyPointTopDownCOCOEval, KeyPointTopDownMPIIEval
 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':
         if self.mode == 'eval':
             self._eval_batch_sampler = paddle.io.BatchSampler(
             self._eval_batch_sampler = paddle.io.BatchSampler(
                 self.dataset, batch_size=self.cfg.EvalReader['batch_size'])
                 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
         # TestDataset build after user set images, skip loader creation here
 
 
         # build optimizer in train mode
         # build optimizer in train mode
@@ -336,6 +341,12 @@ class Trainer(object):
         assert self.mode == 'train', "Model not in 'train' mode"
         assert self.mode == 'train', "Model not in 'train' mode"
         Init_mark = False
         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
         model = self.model
         if self.cfg.get('fleet', False):
         if self.cfg.get('fleet', False):
             model = fleet.distributed_model(model)
             model = fleet.distributed_model(model)
@@ -364,7 +375,9 @@ class Trainer(object):
         self.status['training_staus'] = stats.TrainingStats(self.cfg.log_iter)
         self.status['training_staus'] = stats.TrainingStats(self.cfg.log_iter)
 
 
         if self.cfg.get('print_flops', False):
         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)
         profiler_options = self.cfg.get('profiler_options', None)
 
 
         self._compose_callback.on_train_begin(self.status)
         self._compose_callback.on_train_begin(self.status)
@@ -436,6 +449,9 @@ class Trainer(object):
                         paddle.io.BatchSampler(
                         paddle.io.BatchSampler(
                             self._eval_dataset,
                             self._eval_dataset,
                             batch_size=self.cfg.EvalReader['batch_size'])
                             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_loader = create('EvalReader')(
                         self._eval_dataset,
                         self._eval_dataset,
                         self.cfg.worker_num,
                         self.cfg.worker_num,
@@ -463,7 +479,9 @@ class Trainer(object):
         self.status['mode'] = 'eval'
         self.status['mode'] = 'eval'
         self.model.eval()
         self.model.eval()
         if self.cfg.get('print_flops', False):
         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):
         for step_id, data in enumerate(loader):
             self.status['step_id'] = step_id
             self.status['step_id'] = step_id
             self._compose_callback.on_step_begin(self.status)
             self._compose_callback.on_step_begin(self.status)
@@ -514,7 +532,8 @@ class Trainer(object):
         self.status['mode'] = 'test'
         self.status['mode'] = 'test'
         self.model.eval()
         self.model.eval()
         if self.cfg.get('print_flops', False):
         if self.cfg.get('print_flops', False):
-            self._flops(loader)
+            flops_loader = create('TestReader')(self.dataset, 0)
+            self._flops(flops_loader)
         results = []
         results = []
         for step_id, data in enumerate(loader):
         for step_id, data in enumerate(loader):
             self.status['step_id'] = step_id
             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, ):
     def get_pred(self, ):
         raise NotImplementedError("Should implement get_pred method!")
         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()),
                 learning_rate=conv_lr, initializer=KaimingNormal()),
             bias_attr=False)
             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):
     def forward(self, x):
         x = self._conv(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 as nn
 import paddle.nn.functional as F
 import paddle.nn.functional as F
 from paddle import ParamAttr
 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.nn.initializer import KaimingNormal
 from paddle.regularizer import L2Decay
 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))
             learning_rate=norm_lr, regularizer=L2Decay(norm_decay))
         bias_attr = ParamAttr(
         bias_attr = ParamAttr(
             learning_rate=norm_lr, regularizer=L2Decay(norm_decay))
             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']:
         if norm_type in ['bn', 'sync_bn']:
-            self.norm = nn.BatchNorm(
+            self.norm = nn.BatchNorm2D(
                 ch_out,
                 ch_out,
-                param_attr=param_attr,
+                weight_attr=param_attr,
                 bias_attr=bias_attr,
                 bias_attr=bias_attr,
                 use_global_stats=global_stats)
                 use_global_stats=global_stats)
         elif norm_type == 'gn':
         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
 import paddle.nn as nn
 import paddle.nn as nn
 from paddle import ParamAttr
 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.regularizer import L2Decay
 from paddle.nn.initializer import KaimingNormal
 from paddle.nn.initializer import KaimingNormal
 
 
@@ -81,9 +81,9 @@ class ConvBNLayer(nn.Layer):
             weight_attr=ParamAttr(initializer=KaimingNormal()),
             weight_attr=ParamAttr(initializer=KaimingNormal()),
             bias_attr=False)
             bias_attr=False)
 
 
-        self.bn = BatchNorm(
+        self.bn = nn.BatchNorm2D(
             num_filters,
             num_filters,
-            param_attr=ParamAttr(regularizer=L2Decay(0.0)),
+            weight_attr=ParamAttr(regularizer=L2Decay(0.0)),
             bias_attr=ParamAttr(regularizer=L2Decay(0.0)))
             bias_attr=ParamAttr(regularizer=L2Decay(0.0)))
         self.hardswish = nn.Hardswish()
         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), )
                 regularizer=L2Decay(norm_decay), )
             bias_attr = ParamAttr(
             bias_attr = ParamAttr(
                 learning_rate=norm_lr, regularizer=L2Decay(norm_decay))
                 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']:
             if norm_type in ['bn', 'sync_bn']:
-                self.norm = nn.BatchNorm(
+                self.norm = nn.BatchNorm2D(
                     ch_out,
                     ch_out,
-                    param_attr=param_attr,
+                    weight_attr=param_attr,
                     bias_attr=bias_attr,
                     bias_attr=bias_attr,
                     use_global_stats=global_stats, )
                     use_global_stats=global_stats, )
             elif norm_type == 'gn':
             elif norm_type == 'gn':
@@ -582,7 +582,7 @@ class LiteHRNetModule(nn.Layer):
                                 stride=1,
                                 stride=1,
                                 padding=0,
                                 padding=0,
                                 bias=False, ),
                                 bias=False, ),
-                            nn.BatchNorm(self.in_channels[i]),
+                            nn.BatchNorm2D(self.in_channels[i]),
                             nn.Upsample(
                             nn.Upsample(
                                 scale_factor=2**(j - i), mode='nearest')))
                                 scale_factor=2**(j - i), mode='nearest')))
                 elif j == i:
                 elif j == i:
@@ -601,7 +601,7 @@ class LiteHRNetModule(nn.Layer):
                                         padding=1,
                                         padding=1,
                                         groups=self.in_channels[j],
                                         groups=self.in_channels[j],
                                         bias=False, ),
                                         bias=False, ),
-                                    nn.BatchNorm(self.in_channels[j]),
+                                    nn.BatchNorm2D(self.in_channels[j]),
                                     L.Conv2d(
                                     L.Conv2d(
                                         self.in_channels[j],
                                         self.in_channels[j],
                                         self.in_channels[i],
                                         self.in_channels[i],
@@ -609,7 +609,7 @@ class LiteHRNetModule(nn.Layer):
                                         stride=1,
                                         stride=1,
                                         padding=0,
                                         padding=0,
                                         bias=False, ),
                                         bias=False, ),
-                                    nn.BatchNorm(self.in_channels[i])))
+                                    nn.BatchNorm2D(self.in_channels[i])))
                         else:
                         else:
                             conv_downsamples.append(
                             conv_downsamples.append(
                                 nn.Sequential(
                                 nn.Sequential(
@@ -621,7 +621,7 @@ class LiteHRNetModule(nn.Layer):
                                         padding=1,
                                         padding=1,
                                         groups=self.in_channels[j],
                                         groups=self.in_channels[j],
                                         bias=False, ),
                                         bias=False, ),
-                                    nn.BatchNorm(self.in_channels[j]),
+                                    nn.BatchNorm2D(self.in_channels[j]),
                                     L.Conv2d(
                                     L.Conv2d(
                                         self.in_channels[j],
                                         self.in_channels[j],
                                         self.in_channels[j],
                                         self.in_channels[j],
@@ -629,7 +629,7 @@ class LiteHRNetModule(nn.Layer):
                                         stride=1,
                                         stride=1,
                                         padding=0,
                                         padding=0,
                                         bias=False, ),
                                         bias=False, ),
-                                    nn.BatchNorm(self.in_channels[j]),
+                                    nn.BatchNorm2D(self.in_channels[j]),
                                     nn.ReLU()))
                                     nn.ReLU()))
 
 
                     fuse_layer.append(nn.Sequential(*conv_downsamples))
                     fuse_layer.append(nn.Sequential(*conv_downsamples))
@@ -777,7 +777,7 @@ class LiteHRNet(nn.Layer):
                                 padding=1,
                                 padding=1,
                                 groups=num_channels_pre_layer[i],
                                 groups=num_channels_pre_layer[i],
                                 bias=False),
                                 bias=False),
-                            nn.BatchNorm(num_channels_pre_layer[i]),
+                            nn.BatchNorm2D(num_channels_pre_layer[i]),
                             L.Conv2d(
                             L.Conv2d(
                                 num_channels_pre_layer[i],
                                 num_channels_pre_layer[i],
                                 num_channels_cur_layer[i],
                                 num_channels_cur_layer[i],
@@ -785,7 +785,7 @@ class LiteHRNet(nn.Layer):
                                 stride=1,
                                 stride=1,
                                 padding=0,
                                 padding=0,
                                 bias=False, ),
                                 bias=False, ),
-                            nn.BatchNorm(num_channels_cur_layer[i]),
+                            nn.BatchNorm2D(num_channels_cur_layer[i]),
                             nn.ReLU()))
                             nn.ReLU()))
                 else:
                 else:
                     transition_layers.append(None)
                     transition_layers.append(None)
@@ -802,7 +802,7 @@ class LiteHRNet(nn.Layer):
                                 stride=2,
                                 stride=2,
                                 padding=1,
                                 padding=1,
                                 bias=False, ),
                                 bias=False, ),
-                            nn.BatchNorm(num_channels_pre_layer[-1]),
+                            nn.BatchNorm2D(num_channels_pre_layer[-1]),
                             L.Conv2d(
                             L.Conv2d(
                                 num_channels_pre_layer[-1],
                                 num_channels_pre_layer[-1],
                                 num_channels_cur_layer[i]
                                 num_channels_cur_layer[i]
@@ -812,9 +812,9 @@ class LiteHRNet(nn.Layer):
                                 stride=1,
                                 stride=1,
                                 padding=0,
                                 padding=0,
                                 bias=False, ),
                                 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()))
                             nn.ReLU()))
                 transition_layers.append(nn.Sequential(*conv_downsamples))
                 transition_layers.append(nn.Sequential(*conv_downsamples))
         return nn.LayerList(transition_layers)
         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))
         param_attr = ParamAttr(regularizer=L2Decay(norm_decay))
         bias_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)
                 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):
     def forward(self, x):
         x = self._conv(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,
             learning_rate=norm_lr,
             regularizer=L2Decay(norm_decay),
             regularizer=L2Decay(norm_decay),
             trainable=False if freeze_norm else True)
             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,
                 out_c,
-                act=None,
-                param_attr=param_attr,
+                weight_attr=param_attr,
                 bias_attr=bias_attr,
                 bias_attr=bias_attr,
                 use_global_stats=global_stats)
                 use_global_stats=global_stats)
         norm_params = self.bn.parameters()
         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),
             regularizer=L2Decay(norm_decay),
             trainable=False if freeze_norm else True)
             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,
                 ch_out,
-                act=None,
-                param_attr=param_attr,
+                weight_attr=param_attr,
                 bias_attr=bias_attr,
                 bias_attr=bias_attr,
                 use_global_stats=global_stats)
                 use_global_stats=global_stats)
         norm_params = self.norm.parameters()
         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
 import paddle.nn as nn
 import paddle.nn as nn
 from paddle import ParamAttr
 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.nn.initializer import KaimingNormal
 from paddle.regularizer import L2Decay
 from paddle.regularizer import L2Decay
 
 
@@ -51,15 +52,19 @@ class ConvBNLayer(nn.Layer):
             weight_attr=ParamAttr(initializer=KaimingNormal()),
             weight_attr=ParamAttr(initializer=KaimingNormal()),
             bias_attr=False)
             bias_attr=False)
 
 
-        self._batch_norm = BatchNorm(
+        self._batch_norm = BatchNorm2D(
             out_channels,
             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):
     def forward(self, inputs):
         y = self._conv(inputs)
         y = self._conv(inputs)
         y = self._batch_norm(y)
         y = self._batch_norm(y)
+        if self.act:
+            y = getattr(F, self.act)(y)
         return y
         return y
 
 
 
 

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

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

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

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

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

@@ -102,31 +102,26 @@ class BaseTrack(object):
 @register
 @register
 @serializable
 @serializable
 class STrack(BaseTrack):
 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
         # wait activate
         self._tlwh = np.asarray(tlwh, dtype=np.float)
         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.kalman_filter = None
         self.mean, self.covariance = None, None
         self.mean, self.covariance = None, None
         self.is_activated = False
         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):
     def update_features(self, feat):
-        # L2 normalizing
+        # L2 normalizing, this function has no use for BYTETracker
         feat /= np.linalg.norm(feat)
         feat /= np.linalg.norm(feat)
         self.curr_feat = feat
         self.curr_feat = feat
         if self.smooth_feat is None:
         if self.smooth_feat is None:
@@ -182,7 +177,8 @@ class STrack(BaseTrack):
     def re_activate(self, new_track, frame_id, new_id=False):
     def re_activate(self, new_track, frame_id, new_id=False):
         self.mean, self.covariance = self.kalman_filter.update(
         self.mean, self.covariance = self.kalman_filter.update(
             self.mean, self.covariance, self.tlwh_to_xyah(new_track.tlwh))
             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.track_len = 0
         self.state = TrackState.Tracked
         self.state = TrackState.Tracked
         self.is_activated = True
         self.is_activated = True
@@ -201,7 +197,7 @@ class STrack(BaseTrack):
         self.is_activated = True  # set flag 'activated'
         self.is_activated = True  # set flag 'activated'
 
 
         self.score = new_track.score
         self.score = new_track.score
-        if update_feature:
+        if update_feature and self.use_reid:
             self.update_features(new_track.curr_feat)
             self.update_features(new_track.curr_feat)
 
 
     @property
     @property

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

@@ -58,6 +58,7 @@ class JDETracker(object):
     """
     """
 
 
     def __init__(self,
     def __init__(self,
+                 use_byte=False,
                  num_classes=1,
                  num_classes=1,
                  det_thresh=0.3,
                  det_thresh=0.3,
                  track_buffer=30,
                  track_buffer=30,
@@ -66,11 +67,14 @@ class JDETracker(object):
                  tracked_thresh=0.7,
                  tracked_thresh=0.7,
                  r_tracked_thresh=0.5,
                  r_tracked_thresh=0.5,
                  unconfirmed_thresh=0.7,
                  unconfirmed_thresh=0.7,
-                 motion='KalmanFilter',
                  conf_thres=0,
                  conf_thres=0,
+                 match_thres=0.8,
+                 low_conf_thres=0.2,
+                 motion='KalmanFilter',
                  metric_type='euclidean'):
                  metric_type='euclidean'):
+        self.use_byte = use_byte
         self.num_classes = num_classes
         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.track_buffer = track_buffer
         self.min_box_area = min_box_area
         self.min_box_area = min_box_area
         self.vertical_ratio = vertical_ratio
         self.vertical_ratio = vertical_ratio
@@ -78,9 +82,12 @@ class JDETracker(object):
         self.tracked_thresh = tracked_thresh
         self.tracked_thresh = tracked_thresh
         self.r_tracked_thresh = r_tracked_thresh
         self.r_tracked_thresh = r_tracked_thresh
         self.unconfirmed_thresh = unconfirmed_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':
         if motion == 'KalmanFilter':
             self.motion = KalmanFilter()
             self.motion = KalmanFilter()
-        self.conf_thres = conf_thres
         self.metric_type = metric_type
         self.metric_type = metric_type
 
 
         self.frame_id = 0
         self.frame_id = 0
@@ -91,7 +98,7 @@ class JDETracker(object):
         self.max_time_lost = 0
         self.max_time_lost = 0
         # max_time_lost will be calculated: int(frame_rate / 30.0 * track_buffer)
         # 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).
         Processes the image frame and finds bounding box(detections).
         Associates the detection with corresponding tracklets and also handles
         Associates the detection with corresponding tracklets and also handles
@@ -123,7 +130,10 @@ class JDETracker(object):
         for cls_id in range(self.num_classes):
         for cls_id in range(self.num_classes):
             cls_idx = (pred_dets[:, 5:] == cls_id).squeeze(-1)
             cls_idx = (pred_dets[:, 5:] == cls_id).squeeze(-1)
             pred_dets_dict[cls_id] = pred_dets[cls_idx]
             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):
         for cls_id in range(self.num_classes):
             """ Step 1: Get detections by class"""
             """ 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)
             remain_inds = (pred_dets_cls[:, 4:5] > self.conf_thres).squeeze(-1)
             if remain_inds.sum() > 0:
             if remain_inds.sum() > 0:
                 pred_dets_cls = pred_dets_cls[remain_inds]
                 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:
             else:
                 detections = []
                 detections = []
             ''' Add newly detected tracklets to tracked_stracks'''
             ''' Add newly detected tracklets to tracked_stracks'''
@@ -160,12 +181,20 @@ class JDETracker(object):
             # Predict the current location with KalmanFilter
             # Predict the current location with KalmanFilter
             STrack.multi_predict(track_pool_dict[cls_id], self.motion)
             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:
             for i_tracked, idet in matches:
                 # i_tracked is the id of the track and idet is the detection
                 # 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.
             # None of the steps below happen if there are no undetected tracks.
             """ Step 3: Second association, with IOU"""
             """ 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:
             for i_tracked, idet in matches:
                 track = r_tracked_stracks[i_tracked]
                 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:
                 if track.state == TrackState.Tracked:
                     track.update(det, self.frame_id)
                     track.update(det, self.frame_id)
                     activated_tracks_dict[cls_id].append(track)
                     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)
         self.pointwise_conv = nn.Conv2D(in_channels, self.out_channels, 1)
 
 
         # norm type
         # norm type
-        if self.norm_type == 'bn':
+        if self.norm_type in ['bn', 'sync_bn']:
             self.norm = nn.BatchNorm2D(self.out_channels)
             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':
         elif self.norm_type == 'gn':
             self.norm = nn.GroupNorm(
             self.norm = nn.GroupNorm(
                 num_groups=self.norm_groups, num_channels=self.out_channels)
                 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()),
                 learning_rate=conv_lr, initializer=KaimingNormal()),
             bias_attr=False)
             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):
     def forward(self, x):
         x = self._conv(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,
                freeze_norm=False,
                initializer=None,
                initializer=None,
                data_format='NCHW'):
                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.
     norm_lr = 0. if freeze_norm else 1.
     weight_attr = ParamAttr(
     weight_attr = ParamAttr(
@@ -66,11 +62,12 @@ def batch_norm(ch,
         regularizer=L2Decay(norm_decay),
         regularizer=L2Decay(norm_decay),
         trainable=False if freeze_norm else True)
         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()
     norm_params = norm_layer.parameters()
     if freeze_norm:
     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.num_classes = num_classes
         self.decode = decode
         self.decode = decode
         self.nms = nms
         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):
     def forward(self, head_out, rois, im_shape, scale_factor):
         """
         """
@@ -91,9 +87,28 @@ class BBoxPostProcess(nn.Layer):
                 including labels, scores and bboxes.
                 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)
         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.
         Paste the mask prediction to the original image.
         """
         """
+
         x0, y0, x1, y1 = paddle.split(boxes, 4, axis=1)
         x0, y0, x1, y1 = paddle.split(boxes, 4, axis=1)
         masks = paddle.unsqueeze(masks, [0, 1])
         masks = paddle.unsqueeze(masks, [0, 1])
         img_y = paddle.arange(0, im_h, dtype='float32') + 0.5
         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))
             labels = paddle.scatter(labels, fg_inds, paddle.ones_like(fg_inds))
         # Step3: make output
         # Step3: make output
         if gt_bbox.shape[0] == 0:
         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:
         else:
             matched_gt_boxes = paddle.gather(gt_bbox, matches)
             matched_gt_boxes = paddle.gather(gt_bbox, matches)
             tgt_delta = bbox2delta(anchors, matched_gt_boxes, weights)
             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
 import paddle.nn.functional as F
 from paddle.nn.initializer import Normal, Constant
 from paddle.nn.initializer import Normal, Constant
 from paddle import ParamAttr
 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.regularizer import L2Decay
 from paddle.nn.initializer import KaimingNormal, XavierNormal
 from paddle.nn.initializer import KaimingNormal, XavierNormal
 from paddlex.ppdet.core.workspace import register
 from paddlex.ppdet.core.workspace import register
@@ -76,9 +76,9 @@ class ConvBNLayer(nn.Layer):
             weight_attr=ParamAttr(initializer=KaimingNormal()),
             weight_attr=ParamAttr(initializer=KaimingNormal()),
             bias_attr=False)
             bias_attr=False)
 
 
-        self.bn = BatchNorm(
+        self.bn = BatchNorm2D(
             num_filters,
             num_filters,
-            param_attr=ParamAttr(regularizer=L2Decay(0.0)),
+            weight_attr=ParamAttr(regularizer=L2Decay(0.0)),
             bias_attr=ParamAttr(regularizer=L2Decay(0.0)))
             bias_attr=ParamAttr(regularizer=L2Decay(0.0)))
         self.hardswish = nn.Hardswish()
         self.hardswish = nn.Hardswish()
 
 

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

@@ -55,12 +55,14 @@ class ConvBNLayer(nn.Layer):
             bias_attr=False,
             bias_attr=False,
             data_format=data_format)
             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):
     def forward(self, inputs):
         y = self._conv(inputs)
         y = self._conv(inputs)
         y = self._batch_norm(y)
         y = self._batch_norm(y)
+        if self.act:
+            y = getattr(F, self.act)(y)
         return 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
             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.max_epochs = max_epochs
         self.use_warmup = use_warmup
         self.use_warmup = use_warmup
+        self.eta_min = eta_min
 
 
     def __call__(self,
     def __call__(self,
                  base_lr=None,
                  base_lr=None,
@@ -66,7 +67,8 @@ class CosineDecay(object):
                 value.append(decayed_lr)
                 value.append(decayed_lr)
             return optimizer.lr.PiecewiseDecay(boundary, value)
             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
 @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())
     weight_keys = sorted(weight_state_dict.keys())
 
 
     def match(a, b):
     def match(a, b):
-        if a.startswith('backbone.res5'):
+        if b.startswith('backbone.res5'):
             # In Faster RCNN, res5 pretrained weights have prefix of backbone,
             # In Faster RCNN, res5 pretrained weights have prefix of backbone,
             # however, the corresponding model weights have difficult prefix,
             # however, the corresponding model weights have difficult prefix,
             # bbox_head.
             # bbox_head.
@@ -139,10 +139,14 @@ def match_state_dict(model_state_dict, weight_state_dict):
     max_id = match_matrix.argmax(1)
     max_id = match_matrix.argmax(1)
     max_len = match_matrix.max(1)
     max_len = match_matrix.max(1)
     max_id[max_len == 0] = -1
     max_id[max_len == 0] = -1
+
+    load_id = set(max_id)
+    load_id.discard(-1)
     not_load_weight_name = []
     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:
     if len(not_load_weight_name) > 0:
         logger.info('{} in pretrained weight is not used in the model, '
         logger.info('{} in pretrained weight is not used in the model, '
                     'and its will not be loaded'.format(not_load_weight_name))
                     '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 = {
 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':
     'ResNet18_IMAGENET':
     'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/ResNet18_pretrained.pdparams',
     'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/ResNet18_pretrained.pdparams',
     'ResNet34_IMAGENET':
     'ResNet34_IMAGENET':

+ 0 - 8
paddlex_restful/restful/app.py

@@ -569,9 +569,6 @@ def task_evaluate():
         ret = get_evaluate_result(data, SD.workspace)
         ret = get_evaluate_result(data, SD.workspace)
         if ret['evaluate_status'] == TaskStatus.XEVALUATED and ret[
         if ret['evaluate_status'] == TaskStatus.XEVALUATED and ret[
                 'result'] is not None:
                 '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'] = CustomEncoder().encode(ret['result'])
             ret['result'] = json.loads(ret['result'])
             ret['result'] = json.loads(ret['result'])
         ret['evaluate_status'] = ret['evaluate_status'].value
         ret['evaluate_status'] = ret['evaluate_status'].value
@@ -893,16 +890,11 @@ def model():
                 return ret
                 return ret
         from .model import get_model_details
         from .model import get_model_details
         ret = get_model_details(data, SD.workspace)
         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['eval_result'] = CustomEncoder().encode(ret['eval_result'])
         ret['task_params'] = CustomEncoder().encode(ret['task_params'])
         ret['task_params'] = CustomEncoder().encode(ret['task_params'])
         return ret
         return ret
     if request.method == 'POST':
     if request.method == 'POST':
         if data['type'] == 'pretrained':
         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
             from .model import create_pretrained_model
             ret = create_pretrained_model(data, SD.workspace,
             ret = create_pretrained_model(data, SD.workspace,
                                           SD.monitored_processes)
                                           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
     num_epochs = params.num_epochs
     if params.lr_policy == 'Cosine':
     if params.lr_policy == 'Cosine':
         learning_rate = paddle.optimizer.lr.CosineAnnealingDecay(
         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':
     elif params.lr_policy == 'Linear':
         learning_rate = paddle.optimizer.lr.PolynomialDecay(
         learning_rate = paddle.optimizer.lr.PolynomialDecay(
             learning_rate=learning_rate,
             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
     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):
 def build_voc_datasets(dataset_path, train_transforms, eval_transforms):
     import paddlex as pdx
     import paddlex as pdx
     train_file_list = osp.join(dataset_path, 'train_list.txt')
     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
     pdx.log_level = 3
     if params.model in ['YOLOv3', 'PPYOLO', 'PPYOLOTiny', 'PPYOLOv2']:
     if params.model in ['YOLOv3', 'PPYOLO', 'PPYOLOTiny', 'PPYOLOv2']:
         train_transforms, eval_transforms = build_yolo_transforms(params)
         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']:
     elif params.model in ['FasterRCNN', 'MaskRCNN']:
         train_transforms, eval_transforms = build_rcnn_transforms(params)
         train_transforms, eval_transforms = build_rcnn_transforms(params)
     if osp.exists(osp.join(dataset_path, 'JPEGImages')) and \
     if osp.exists(osp.join(dataset_path, 'JPEGImages')) and \
@@ -194,7 +230,8 @@ def train(task_path, dataset_path, params):
         # prune
         # prune
         dataset = eval_dataset or train_dataset
         dataset = eval_dataset or train_dataset
         im_shape = dataset[0]['image'].shape[:2]
         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[0] = int(np.ceil(im_shape[0] / 32) * 32)
             im_shape[1] = int(np.ceil(im_shape[1] / 32) * 32)
             im_shape[1] = int(np.ceil(im_shape[1] / 32) * 32)
         inputs = [{
         inputs = [{

+ 2 - 2
requirements.txt

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

+ 6 - 6
setup.py

@@ -13,13 +13,12 @@
 # limitations under the License.
 # limitations under the License.
 
 
 import setuptools
 import setuptools
-import sys
 
 
-long_description = "PaddlePaddle Entire Process Development Toolkit"
+long_description = "PaddlePaddle End-to-End Development Toolkit"
 
 
 setuptools.setup(
 setuptools.setup(
     name="paddlex",
     name="paddlex",
-    version='2.0.0',
+    version='2.1.0',
     author="paddlex",
     author="paddlex",
     author_email="paddlex@baidu.com",
     author_email="paddlex@baidu.com",
     description=long_description,
     description=long_description,
@@ -38,9 +37,10 @@ setuptools.setup(
     include_data_files=True,
     include_data_files=True,
     setup_requires=['cython', 'numpy'],
     setup_requires=['cython', 'numpy'],
     install_requires=[
     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=[
     classifiers=[
         "Programming Language :: Python :: 3",
         "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)
 ![](images/detection_analysis.jpg)
 
 
-左图显示的是`person`类的分析结果,图显示的是所有类别整体的分析结果。
+左图显示的是`person`类的分析结果,图显示的是所有类别整体的分析结果。
 
 
 分析图表展示了7条Precision-Recall(PR)曲线,每一条曲线表示的Average Precision (AP)比它左边那条高,原因是逐步放宽了评估要求。以`person`类为例,各条PR曲线的评估要求解释如下:
 分析图表展示了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
 # API说明:https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/apis/models/detection.md
 # 各参数介绍与调整说明:https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/parameters.md
 # 各参数介绍与调整说明:https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/parameters.md
 model.train(
 model.train(
-    num_epochs=300,
+    num_epochs=20,
     train_dataset=train_dataset,
     train_dataset=train_dataset,
-    train_batch_size=7,
+    train_batch_size=14,
     eval_dataset=eval_dataset,
     eval_dataset=eval_dataset,
     pretrain_weights='COCO',
     pretrain_weights='COCO',
     learning_rate=.05,
     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)
     use_vdl=True)